From 4b7d52a28e9a02d55574b2f08b8030ba179caeff Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 28 Aug 2026 09:10:23 +0200 Subject: fix(files): make "Filter files" search the whole group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The filter only ever matched files whose folder was exactly the current path, so at the top of a group — where every row is a root, never a loose file — typing in the field did nothing at all. With text in the field the panel now searches every file in the group by name or containing folder, wherever it lives, and hides the folder rows (you are searching, not browsing). Each result shows its folder as a sub-line; clicking it clears the filter and opens that folder. Empty field restores the normal folder-by-folder view unchanged. Also hardened the sort comparators against an entry missing `name`/`type`. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018gKJ85aZyvEwarXMFzFEwi --- .../src/meshbay_hub/static/files-app.js | 25 ++++++++++++++++------ .../meshbay-hub/src/meshbay_hub/static/style.css | 10 +++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) (limited to 'packages/meshbay-hub/src') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/files-app.js b/packages/meshbay-hub/src/meshbay_hub/static/files-app.js index 16ab9f6..c4b221e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/files-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/files-app.js @@ -139,12 +139,21 @@ function FilesPanel({ setSortKey(key); }, [sortKey]); + // With text in the field the user is searching, not browsing: match every + // file in the whole group, wherever it lives, by name or by the folder it + // sits in — and hide the folder rows, there is nothing to walk into. The + // old code only ever filtered files whose folder was exactly `currentPath`, + // so at the top of a group — where the rows are roots, never loose files — + // typing did nothing at all. + const q = filter.trim().toLowerCase(); const dirs = new Set(); const filteredEntries = entries.filter(e => { const ePath = e.path || ''; - if (ePath === currentPath) { - return !filter || e.name.toLowerCase().includes(filter.toLowerCase()); + if (q) { + return (e.name || '').toLowerCase().includes(q) + || ePath.toLowerCase().includes(q); } + if (ePath === currentPath) return true; if (!currentPath && ePath) { dirs.add(ePath.split('/')[0]); } else if (currentPath && ePath.startsWith(currentPath + '/')) { @@ -156,16 +165,17 @@ function FilesPanel({ const sorted = [...filteredEntries].sort((a, b) => { let cmp = 0; - if (sortKey === 'name') cmp = a.name.localeCompare(b.name); + if (sortKey === 'name') cmp = (a.name || '').localeCompare(b.name || ''); else if (sortKey === 'size') cmp = a.size - b.size; - else if (sortKey === 'type') cmp = a.type.localeCompare(b.type); + else if (sortKey === 'type') cmp = (a.type || '').localeCompare(b.type || ''); else if (sortKey === 'date') cmp = a.added_at - b.added_at; return sortAsc ? cmp : -cmp; }); // The node's own listing, so an empty folder is visible, plus anything implied - // by a file path in case the two ever disagree. - for (const d of nodeDirs) { + // by a file path in case the two ever disagree. Skipped while searching — the + // results are a flat list of matches, not a folder view. + if (!q) for (const d of nodeDirs) { if (!currentPath && !d.includes('/')) dirs.add(d); else if (currentPath && d.startsWith(currentPath + '/')) { const rest = d.slice(currentPath.length + 1); @@ -396,6 +406,9 @@ function FilesPanel({ ? html` onPreview(e)}>${e.name}` : e.name } + ${q && e.path && html` + { setFilter(''); setCurrentPath(e.path); }}>${e.path}`} ${formatSize(e.size)} ${e.type} diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index 61a5e3b..cd9882e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -817,6 +817,16 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } word-break: break-word; min-width: 0; } +/* Where a search result lives, under its name; click to open that folder + and clear the filter. */ +.file-loc { + display: block; + margin-top: 2px; + font-size: 0.78em; + color: var(--text-dim); + cursor: pointer; +} +.file-loc:hover { color: var(--accent); text-decoration: underline; } .file-size { white-space: nowrap; color: var(--text-secondary); } .file-type { color: var(--text-dim); } .file-date { white-space: nowrap; color: var(--text-dim); } -- cgit v1.2.3