mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 06:29:42 +02:00
feat(file-viewer): show hidden files and folders
The tree endpoint has accepted `showHidden=true` since it was written; the panel hardcoded `showHidden=false`, so dot-prefixed entries were unreachable from the File Viewer and opening one meant guessing its path. Adds a `.*` toggle to the panel header. It re-fetches instead of re-rendering the cached tree (the filtering is server-side), preserves the expanded directories so toggling does not collapse the tree, and persists per-device to its own `codeman:fileBrowserShowHidden` key. That key is deliberately not part of the app-settings object, which `saveAppSettings()` rebuilds from the settings-modal DOM and would drop it on the next save. Default is OFF, so an untouched install behaves exactly as before. Closes #221 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -615,6 +615,11 @@ class CodemanApp {
|
||||
this.fileBrowserFilter = '';
|
||||
this.fileBrowserAllExpanded = false;
|
||||
this.fileBrowserDragListeners = null;
|
||||
// Show hidden (dot-prefixed) files and folders in the File Viewer tree.
|
||||
// Per-device, persisted to its own localStorage key by panels-ui.js. Safe to
|
||||
// call a mixin method here: instantiation is deferred to DOMContentLoaded,
|
||||
// so every module's Object.assign has already run.
|
||||
this.fileBrowserShowHidden = this._loadFileBrowserShowHidden?.() ?? false;
|
||||
this.filePreviewContent = '';
|
||||
|
||||
// Toast container cache (methods in panels-ui.js)
|
||||
|
||||
@@ -407,6 +407,7 @@
|
||||
<div class="file-browser-header">
|
||||
<span class="file-browser-title">Files</span>
|
||||
<div class="file-browser-actions">
|
||||
<button class="btn-icon-sm btn-file-browser-hidden" onclick="app.toggleFileBrowserHidden()" title="Show hidden files and folders" aria-label="Show hidden files and folders" aria-pressed="false" id="fileBrowserHiddenBtn">.*</button>
|
||||
<button class="btn-icon-sm" onclick="app.refreshFileBrowser()" title="Refresh">↻</button>
|
||||
<button class="btn-icon-sm" onclick="app.toggleFileBrowserExpand()" title="Expand/Collapse All" id="fileBrowserExpandBtn">⊞</button>
|
||||
<button class="btn-icon-sm" onclick="app.closeFileBrowserPanel()" title="Close">×</button>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
const AWAY_DIGEST_LAST_VIEWED_KEY = 'codeman-away-digest-last-viewed';
|
||||
const FILE_BROWSER_SHOW_HIDDEN_KEY = 'codeman:fileBrowserShowHidden';
|
||||
const AWAY_DIGEST_SECTIONS = [
|
||||
['needsAttention', 'Needs Attention'],
|
||||
['completed', 'Completed'],
|
||||
@@ -2944,18 +2945,56 @@ Object.assign(CodemanApp.prototype, {
|
||||
// File Browser Panel
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
// Hidden files/folders (dot-prefixed) are filtered SERVER-side by
|
||||
// GET /api/sessions/:id/files, so the toggle re-fetches rather than
|
||||
// re-rendering the cached tree (issue #221). The flag is per-device and lives
|
||||
// in its own localStorage key instead of the app-settings object: that object
|
||||
// is rebuilt from the settings-modal DOM on every save, so a key toggled from
|
||||
// outside the modal would be dropped the next time settings are saved.
|
||||
_loadFileBrowserShowHidden() {
|
||||
try {
|
||||
return localStorage.getItem(FILE_BROWSER_SHOW_HIDDEN_KEY) === '1';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_syncFileBrowserHiddenBtn() {
|
||||
const btn = this.$('fileBrowserHiddenBtn');
|
||||
if (!btn) return;
|
||||
const on = this.fileBrowserShowHidden === true;
|
||||
btn.classList.toggle('active', on);
|
||||
btn.setAttribute('aria-pressed', String(on));
|
||||
const label = on ? 'Hide hidden files and folders' : 'Show hidden files and folders';
|
||||
btn.setAttribute('title', label);
|
||||
btn.setAttribute('aria-label', label);
|
||||
},
|
||||
|
||||
async toggleFileBrowserHidden() {
|
||||
this.fileBrowserShowHidden = !this.fileBrowserShowHidden;
|
||||
try {
|
||||
localStorage.setItem(FILE_BROWSER_SHOW_HIDDEN_KEY, this.fileBrowserShowHidden ? '1' : '0');
|
||||
} catch {}
|
||||
this._syncFileBrowserHiddenBtn();
|
||||
// Expanded-directory state is deliberately preserved so toggling does not
|
||||
// collapse the tree the user just navigated.
|
||||
if (this.activeSessionId) await this.loadFileBrowser(this.activeSessionId);
|
||||
},
|
||||
|
||||
async loadFileBrowser(sessionId) {
|
||||
if (!sessionId) return;
|
||||
|
||||
const treeEl = this.$('fileBrowserTree');
|
||||
const statusEl = this.$('fileBrowserStatus');
|
||||
this._syncFileBrowserHiddenBtn();
|
||||
if (!treeEl) return;
|
||||
|
||||
// Show loading state
|
||||
treeEl.innerHTML = '<div class="file-browser-loading">Loading files...</div>';
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/sessions/${sessionId}/files?depth=5&showHidden=false`);
|
||||
const showHidden = this.fileBrowserShowHidden === true;
|
||||
const res = await fetch(`/api/sessions/${sessionId}/files?depth=5&showHidden=${showHidden}`);
|
||||
if (!res.ok) throw new Error('Failed to load files');
|
||||
|
||||
const result = await res.json();
|
||||
@@ -2967,7 +3006,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
// Update status
|
||||
if (statusEl) {
|
||||
const { totalFiles, totalDirectories, truncated } = result.data;
|
||||
statusEl.textContent = `${totalFiles} files, ${totalDirectories} dirs${truncated ? ' (truncated)' : ''}`;
|
||||
statusEl.textContent = `${totalFiles} files, ${totalDirectories} dirs${truncated ? ' (truncated)' : ''}${showHidden ? ' · hidden shown' : ''}`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load file browser:', err);
|
||||
|
||||
@@ -9192,6 +9192,20 @@ kbd {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
/* Show-hidden toggle: a literal `.*` glyph rather than an icon, so its meaning
|
||||
* (dot-prefixed files and folders) survives every skin and font stack. */
|
||||
.btn-file-browser-hidden {
|
||||
font-family: var(--font-mono, monospace);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.05em;
|
||||
}
|
||||
|
||||
.btn-file-browser-hidden.active {
|
||||
color: var(--accent);
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.file-browser-search {
|
||||
padding: 0.4rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
|
||||
Reference in New Issue
Block a user