mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-27 20:20:20 +00:00
Stage 314: PR #1825 — hide workspace file tree cruft by default by @ai-ag2026
This commit is contained in:
@@ -311,6 +311,7 @@ const LOCALES = {
|
||||
terminal_error: 'Terminal error',
|
||||
workspace_empty_no_path: 'No workspace selected. Set a workspace in Settings \u2192 Workspace to browse files.',
|
||||
workspace_empty_dir: 'This workspace is empty.',
|
||||
workspace_show_hidden_files: 'Show hidden files',
|
||||
dialog_confirm_title: 'Confirm action',
|
||||
dialog_prompt_title: 'Enter a value',
|
||||
dialog_confirm_btn: 'Confirm',
|
||||
@@ -1324,6 +1325,7 @@ const LOCALES = {
|
||||
terminal_error: 'ターミナルエラー',
|
||||
workspace_empty_no_path: 'ワークスペースが選択されていません。設定 → ワークスペースで選択してください。',
|
||||
workspace_empty_dir: 'このワークスペースは空です。',
|
||||
workspace_show_hidden_files: 'Show hidden files',
|
||||
dialog_confirm_title: '操作の確認',
|
||||
dialog_prompt_title: '値を入力',
|
||||
dialog_confirm_btn: '確認',
|
||||
@@ -2236,6 +2238,7 @@ const LOCALES = {
|
||||
settings_autosave_failed: 'Не удалось сохранить',
|
||||
settings_autosave_retry: 'Повторить',
|
||||
workspace_empty_dir: 'Это рабочее пространство пусто.',
|
||||
workspace_show_hidden_files: 'Show hidden files',
|
||||
workspace_empty_no_path: 'Рабочее пространство не выбрано. Настройте его в Настройки → Рабочее пространство.',
|
||||
available_personalities: 'Доступные личности:',
|
||||
personality_switch_hint: '\n\nИспользуйте `/personality <name>` для переключения или `/personality none` для сброса.',
|
||||
@@ -3192,6 +3195,7 @@ const LOCALES = {
|
||||
terminal_error: 'Error del terminal',
|
||||
workspace_empty_no_path: 'No hay espacio de trabajo seleccionado. Configure un espacio de trabajo en Ajustes \u2192 Workspace para explorar archivos.',
|
||||
workspace_empty_dir: 'Este espacio de trabajo está vacío.',
|
||||
workspace_show_hidden_files: 'Show hidden files',
|
||||
// workspace.js
|
||||
unsaved_confirm: 'Tienes cambios sin guardar en la vista previa. ¿Descartar y navegar?',
|
||||
save: 'Guardar',
|
||||
@@ -4120,6 +4124,7 @@ const LOCALES = {
|
||||
terminal_error: 'Terminalfehler',
|
||||
workspace_empty_no_path: 'Kein Workspace ausgewählt. Wähle einen Workspace unter Einstellungen \u2192 Workspace, um Dateien zu durchsuchen.',
|
||||
workspace_empty_dir: 'Dieser Workspace ist leer.',
|
||||
workspace_show_hidden_files: 'Versteckte Dateien anzeigen',
|
||||
dialog_confirm_title: 'Aktion bestätigen',
|
||||
dialog_prompt_title: 'Wert eingeben',
|
||||
dialog_confirm_btn: 'Bestätigen',
|
||||
@@ -5063,6 +5068,7 @@ const LOCALES = {
|
||||
|
||||
workspace_empty_no_path: '未选择工作区。请在 设置 → 工作区 中设置工作区以浏览文件。',
|
||||
workspace_empty_dir: '此工作区为空。',
|
||||
workspace_show_hidden_files: 'Show hidden files',
|
||||
no_personalities: '\u6ca1\u6709\u627e\u5230\u4eba\u8bbe\uff08\u53ef\u6dfb\u52a0\u5230 ~/.hermes/personalities/\uff09',
|
||||
available_personalities: '\u53ef\u7528\u4eba\u8bbe\uff1a',
|
||||
personality_switch_hint: '\n\n\u4f7f\u7528 `/personality <name>` \u5207\u6362\uff0c\u6216\u7528 `/personality none` \u6e05\u7a7a\u3002',
|
||||
@@ -5965,6 +5971,7 @@ const LOCALES = {
|
||||
|
||||
workspace_empty_no_path: '未選擇工作區。請在 設定 → 工作區 中設定工作區以瀏覽檔案。',
|
||||
workspace_empty_dir: '此工作區為空。',
|
||||
workspace_show_hidden_files: 'Show hidden files',
|
||||
no_personalities: '\u6c92\u6709\u627e\u5230\u4eba\u8a2d\uff08\u53ef\u6dfb\u52a0\u5230 ~/.hermes/personalities/\uff09',
|
||||
available_personalities: '\u53ef\u7528\u4eba\u8a2d\uff1a',
|
||||
personality_switch_hint: '\n\n\u4f7f\u7528 `/personality <name>` \u5207\u63db\uff0c\u6216\u7528 `/personality none` \u6e05\u7a7a\u3002',
|
||||
@@ -7017,6 +7024,7 @@ const LOCALES = {
|
||||
no_workspace: 'Nenhum workspace',
|
||||
workspace_empty_no_path: 'Nenhum workspace selecionado. Configure em Configurações → Workspace.',
|
||||
workspace_empty_dir: 'Este workspace está vazio.',
|
||||
workspace_show_hidden_files: 'Show hidden files',
|
||||
dialog_confirm_title: 'Confirmar ação',
|
||||
dialog_prompt_title: 'Digite um valor',
|
||||
dialog_confirm_btn: 'Confirmar',
|
||||
@@ -7913,6 +7921,7 @@ const LOCALES = {
|
||||
terminal_error: '터미널 오류',
|
||||
workspace_empty_no_path: 'No workspace selected. Set a workspace in Settings \u2192 Workspace to browse files.',
|
||||
workspace_empty_dir: 'This workspace is empty.',
|
||||
workspace_show_hidden_files: 'Show hidden files',
|
||||
dialog_confirm_title: 'Confirm action',
|
||||
dialog_prompt_title: 'Enter a value',
|
||||
dialog_confirm_btn: 'Confirm',
|
||||
|
||||
@@ -1131,6 +1131,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="breadcrumb-bar" id="breadcrumbBar" style="display:none"></div>
|
||||
<label class="workspace-hidden-toggle" title="Show hidden and system files">
|
||||
<input type="checkbox" id="workspaceShowHiddenFiles" onchange="toggleWorkspaceHiddenFiles(this.checked)">
|
||||
<span data-i18n="workspace_show_hidden_files">Show hidden files</span>
|
||||
</label>
|
||||
<div class="file-tree" id="fileTree"></div>
|
||||
<div id="wsEmptyState" style="display:none;flex:1;align-items:center;justify-content:center;padding:24px 16px;text-align:center;color:var(--muted);font-size:12px;line-height:1.6"></div>
|
||||
<div class="preview-area" id="previewArea">
|
||||
|
||||
@@ -1185,6 +1185,9 @@
|
||||
.close-preview{cursor:pointer;opacity:.6;}.close-preview:hover{opacity:1;}
|
||||
/* Breadcrumb navigation */
|
||||
.breadcrumb-bar{display:flex;align-items:center;gap:2px;padding:6px 12px;font-size:12px;border-bottom:1px solid var(--border);flex-shrink:0;overflow:hidden;white-space:nowrap;}
|
||||
.workspace-hidden-toggle{display:flex;align-items:center;gap:7px;padding:6px 12px;border-bottom:1px solid var(--border);color:var(--muted);font-size:11px;line-height:1.2;user-select:none;cursor:pointer;}
|
||||
.workspace-hidden-toggle:hover{color:var(--text);background:var(--hover-bg);}
|
||||
.workspace-hidden-toggle input{width:13px;height:13px;margin:0;accent-color:var(--accent-text);}
|
||||
.breadcrumb-seg{padding:1px 3px;border-radius:3px;}
|
||||
.breadcrumb-link{color:var(--muted);cursor:pointer;transition:color .12s;}
|
||||
.breadcrumb-link:hover{color:var(--text);background:var(--hover-bg);}
|
||||
|
||||
+37
-4
@@ -1,4 +1,4 @@
|
||||
const S={session:null,messages:[],entries:[],busy:false,pendingFiles:[],toolCalls:[],activeStreamId:null,currentDir:'.',activeProfile:'default'};
|
||||
const S={session:null,messages:[],entries:[],busy:false,pendingFiles:[],toolCalls:[],activeStreamId:null,currentDir:'.',activeProfile:'default',showHiddenWorkspaceFiles:false};
|
||||
const INFLIGHT={}; // keyed by session_id while request in-flight
|
||||
const SESSION_QUEUES={}; // keyed by session_id for queued follow-up turns
|
||||
// Tracks which session's queue to drain in setBusy(false).
|
||||
@@ -6071,6 +6071,38 @@ function renderBreadcrumb(){
|
||||
}
|
||||
}
|
||||
|
||||
const WORKSPACE_HIDDEN_FILE_NAMES=new Set([
|
||||
'.DS_Store','._.DS_Store','.AppleDouble','.Spotlight-V100','.Trashes','.fseventsd',
|
||||
'Thumbs.db','Desktop.ini','ehthumbs.db','$RECYCLE.BIN',
|
||||
'.directory','.git','.svn','.hg','node_modules','__pycache__',
|
||||
'.pytest_cache','.mypy_cache','.ruff_cache','.tox','.venv','venv'
|
||||
]);
|
||||
const WORKSPACE_HIDDEN_FILE_PREFIXES=['._','.Trash-'];
|
||||
function _workspaceShouldHideEntry(item){
|
||||
if(!item||S.showHiddenWorkspaceFiles)return false;
|
||||
const name=String(item.name||'');
|
||||
if(!name)return false;
|
||||
if(WORKSPACE_HIDDEN_FILE_NAMES.has(name))return true;
|
||||
return WORKSPACE_HIDDEN_FILE_PREFIXES.some(prefix=>name.startsWith(prefix));
|
||||
}
|
||||
function _visibleWorkspaceEntries(entries){
|
||||
const list=Array.isArray(entries)?entries:[];
|
||||
return S.showHiddenWorkspaceFiles?list:list.filter(item=>!_workspaceShouldHideEntry(item));
|
||||
}
|
||||
function _syncWorkspaceHiddenToggle(){
|
||||
const el=$('workspaceShowHiddenFiles');
|
||||
if(el)el.checked=!!S.showHiddenWorkspaceFiles;
|
||||
}
|
||||
function toggleWorkspaceHiddenFiles(value){
|
||||
S.showHiddenWorkspaceFiles=!!value;
|
||||
try{localStorage.setItem('hermes-workspace-show-hidden-files',S.showHiddenWorkspaceFiles?'1':'0');}catch(_){}
|
||||
_syncWorkspaceHiddenToggle();
|
||||
renderFileTree();
|
||||
}
|
||||
try{S.showHiddenWorkspaceFiles=localStorage.getItem('hermes-workspace-show-hidden-files')==='1';}catch(_){}
|
||||
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',_syncWorkspaceHiddenToggle);
|
||||
else _syncWorkspaceHiddenToggle();
|
||||
|
||||
// Track expanded directories for tree view
|
||||
if(!S._expandedDirs) S._expandedDirs=new Set();
|
||||
// Cache of fetched directory contents: path -> entries[]
|
||||
@@ -6090,11 +6122,12 @@ function renderFileTree(){
|
||||
}
|
||||
if(emptyEl) emptyEl.style.display='none';
|
||||
box.style.display='';
|
||||
if(!S.entries||!S.entries.length){
|
||||
const visibleEntries=_visibleWorkspaceEntries(S.entries);
|
||||
if(!visibleEntries.length){
|
||||
if(emptyEl){emptyEl.textContent=t('workspace_empty_dir');emptyEl.style.display='flex';}
|
||||
return;
|
||||
}
|
||||
_renderTreeItems(box, S.entries, 0);
|
||||
_renderTreeItems(box, visibleEntries, 0);
|
||||
}
|
||||
|
||||
function _renderTreeItems(container, entries, depth){
|
||||
@@ -6239,7 +6272,7 @@ function _renderTreeItems(container, entries, depth){
|
||||
|
||||
// Render children if directory is expanded
|
||||
if(item.type==='dir'&&S._expandedDirs.has(item.path)){
|
||||
const children=S._dirCache[item.path]||[];
|
||||
const children=_visibleWorkspaceEntries(S._dirCache[item.path]||[]);
|
||||
if(children.length){
|
||||
_renderTreeItems(container, children, depth+1);
|
||||
}else{
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
INDEX_HTML = (ROOT / "static" / "index.html").read_text(encoding="utf-8")
|
||||
UI_JS = (ROOT / "static" / "ui.js").read_text(encoding="utf-8")
|
||||
I18N_JS = (ROOT / "static" / "i18n.js").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_workspace_panel_has_show_hidden_files_toggle():
|
||||
"""File-tree cruft must be recoverable via an explicit user toggle."""
|
||||
assert 'id="workspaceShowHiddenFiles"' in INDEX_HTML
|
||||
assert "toggleWorkspaceHiddenFiles" in UI_JS
|
||||
assert "workspace_show_hidden_files" in I18N_JS
|
||||
|
||||
|
||||
def test_file_tree_filters_common_cruft_by_default():
|
||||
"""macOS/Windows/VCS/cache noise should not render by default."""
|
||||
assert "WORKSPACE_HIDDEN_FILE_NAMES" in UI_JS
|
||||
for name in [".DS_Store", "Thumbs.db", "Desktop.ini", ".git", "__pycache__", "node_modules"]:
|
||||
assert name in UI_JS
|
||||
assert "_visibleWorkspaceEntries" in UI_JS
|
||||
assert "S.showHiddenWorkspaceFiles" in UI_JS
|
||||
assert "_workspaceShouldHideEntry" in UI_JS
|
||||
|
||||
|
||||
def test_hidden_file_toggle_invalidates_tree_render_without_refetch():
|
||||
"""The toggle should re-render cached entries instead of changing workspace state."""
|
||||
assert "function toggleWorkspaceHiddenFiles" in UI_JS
|
||||
assert "renderFileTree()" in UI_JS[UI_JS.index("function toggleWorkspaceHiddenFiles"):]
|
||||
assert "localStorage.setItem('hermes-workspace-show-hidden-files'" in UI_JS
|
||||
Reference in New Issue
Block a user