Add per-folder recursive/top-level-only scanning, fix a dangerous empty-folder-list fallback
- Each source folder now has its own recursive flag (checkbox on add, toggle badge on its chip). scan_folder/sync_metadata_from_folder both honor it, so "top-level only" actually excludes subfolders end-to-end instead of just the tagging queue while sync still scanned recursively. - New /api/set_folder_recursive to flip an existing folder's mode without removing/re-adding it. - SAFETY FIX: found while testing — when the configured folder list is empty, the app fell back to the parent directory of wherever server.py lives, which for a typical install (PHOTON dropped directly in the user's home folder) means defaulting to the entire home directory. Caught this before any scan/start actually ran against it. Fixed so an explicitly-configured empty list (config file exists, list is empty) is respected as "zero folders" instead of silently substituted with a guessed default — the guessed default now only applies on a genuinely fresh install where no config file exists yet at all. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
33
index.html
33
index.html
@@ -89,6 +89,14 @@ button:disabled{opacity:.3;cursor:not-allowed}
|
||||
.folderChip .rm{cursor:pointer;color:var(--dim);border:1px solid var(--line);border-radius:50%;
|
||||
width:16px;height:16px;display:flex;align-items:center;justify-content:center;font-size:11px;line-height:1}
|
||||
.folderChip .rm:hover{color:var(--red);border-color:var(--red)}
|
||||
.folderChip .mode{cursor:pointer;color:var(--dim);border:1px solid var(--line);border-radius:8px;
|
||||
padding:1px 7px;font-size:10px;white-space:nowrap}
|
||||
.folderChip .mode:hover{color:var(--cyan);border-color:var(--cyan)}
|
||||
.folderChip .mode.rec{color:var(--cyan-dim)}
|
||||
|
||||
.chkRecursiveLabel{display:flex;align-items:center;gap:5px;font-family:var(--font-mono);
|
||||
font-size:11px;color:var(--dim);white-space:nowrap;padding:0 4px}
|
||||
.chkRecursiveLabel input{accent-color:var(--cyan)}
|
||||
|
||||
.grid{display:grid;grid-template-columns:330px 1fr;gap:14px}
|
||||
.grid-three{display:grid;grid-template-columns:330px 1fr 320px;gap:14px}
|
||||
@@ -352,6 +360,9 @@ mark{background:rgba(34,211,238,.28);color:#fff;border-radius:2px;padding:0 1px}
|
||||
<div class="controls">
|
||||
<div class="folderRow">
|
||||
<input id="folder" spellcheck="false" placeholder="/path/to/your/photos — add as many source folders as you like">
|
||||
<label class="chkRecursiveLabel" title="If off, only the exact folder is scanned — no subfolders">
|
||||
<input type="checkbox" id="chkRecursive" checked> include subfolders
|
||||
</label>
|
||||
<button id="btnAddFolderTyped" title="Add this typed path as a source folder">+ Add</button>
|
||||
<button id="btnBrowseFolder" title="Open native macOS folder chooser to add a source folder">📁 Browse</button>
|
||||
<input type="file" id="fallbackFolderInput" webkitdirectory style="display:none">
|
||||
@@ -1021,7 +1032,7 @@ async function api(path, body){
|
||||
}
|
||||
|
||||
/* ---------- multi-folder source list ---------- */
|
||||
let currentFolders = [];
|
||||
let currentFolders = []; // [{path, recursive}, ...]
|
||||
|
||||
function renderFolderChips(){
|
||||
const box = $("folderChips");
|
||||
@@ -1030,8 +1041,11 @@ function renderFolderChips(){
|
||||
currentFolders.forEach(f=>{
|
||||
const chip = document.createElement("div");
|
||||
chip.className = "folderChip";
|
||||
chip.innerHTML = `<span class="path" title="${escapeHtml(f)}">${escapeHtml(f)}</span><span class="rm" title="Remove this folder">×</span>`;
|
||||
chip.querySelector(".rm").onclick = ()=>removeFolder(f);
|
||||
chip.innerHTML = `<span class="path" title="${escapeHtml(f.path)}">${escapeHtml(f.path)}</span>
|
||||
<span class="mode ${f.recursive?'rec':''}" title="Click to toggle: currently ${f.recursive?'including subfolders':'top-level only'}">${f.recursive?'⊞ subfolders':'▭ top-level only'}</span>
|
||||
<span class="rm" title="Remove this folder">×</span>`;
|
||||
chip.querySelector(".rm").onclick = ()=>removeFolder(f.path);
|
||||
chip.querySelector(".mode").onclick = ()=>toggleFolderRecursive(f);
|
||||
box.appendChild(chip);
|
||||
});
|
||||
}
|
||||
@@ -1045,8 +1059,13 @@ function applyFolderList(folders){
|
||||
}
|
||||
}
|
||||
|
||||
async function removeFolder(f){
|
||||
const j = await api("/api/remove_folder", {folder: f});
|
||||
async function removeFolder(path){
|
||||
const j = await api("/api/remove_folder", {folder: path});
|
||||
if(j.ok) applyFolderList(j.folders);
|
||||
}
|
||||
|
||||
async function toggleFolderRecursive(f){
|
||||
const j = await api("/api/set_folder_recursive", {folder: f.path, recursive: !f.recursive});
|
||||
if(j.ok) applyFolderList(j.folders);
|
||||
}
|
||||
|
||||
@@ -1055,7 +1074,7 @@ if($("btnAddFolderTyped")){
|
||||
const val = $("folder").value.trim();
|
||||
if(!val) return;
|
||||
$("btnAddFolderTyped").disabled = true;
|
||||
const j = await api("/api/add_folder", {folder: val});
|
||||
const j = await api("/api/add_folder", {folder: val, recursive: $("chkRecursive").checked});
|
||||
$("btnAddFolderTyped").disabled = false;
|
||||
if(j.ok){
|
||||
$("folder").value = "";
|
||||
@@ -1069,7 +1088,7 @@ if($("btnBrowseFolder")){
|
||||
$("btnBrowseFolder").onclick = async ()=>{
|
||||
$("btnBrowseFolder").disabled = true;
|
||||
showToast("Opening folder chooser…", "info");
|
||||
const j = await api("/api/choose_folder", {});
|
||||
const j = await api("/api/choose_folder", {recursive: $("chkRecursive").checked});
|
||||
$("btnBrowseFolder").disabled = false;
|
||||
if(j.ok && j.folder){
|
||||
showToast("Added folder: " + j.folder, "ok");
|
||||
|
||||
Reference in New Issue
Block a user