fix: disable workspace heading affordance without workspace
This commit is contained in:
BIN
docs/pr-media/1842/after-hover-no-workspace.png
Normal file
BIN
docs/pr-media/1842/after-hover-no-workspace.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/pr-media/1842/before-hover-no-workspace.png
Normal file
BIN
docs/pr-media/1842/before-hover-no-workspace.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -1118,7 +1118,7 @@
|
||||
<aside class="rightpanel">
|
||||
<div class="resize-handle" id="rightpanelResize"></div>
|
||||
<div class="panel-header">
|
||||
<span id="workspacePanelHeading" class="workspace-panel-heading" role="button" tabindex="0" title="Workspace root">Workspace</span><span id="workspaceHiddenIndicator" class="workspace-hidden-indicator" data-i18n-title="workspace_hidden_files_visible_title" title="Hidden files are visible — click for options" hidden onclick="toggleWorkspacePrefsMenu(event)"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg><span data-i18n="workspace_hidden_files_visible">hidden visible</span></span>
|
||||
<span id="workspacePanelHeading" class="workspace-panel-heading" title="Workspace">Workspace</span><span id="workspaceHiddenIndicator" class="workspace-hidden-indicator" data-i18n-title="workspace_hidden_files_visible_title" title="Hidden files are visible — click for options" hidden onclick="toggleWorkspacePrefsMenu(event)"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg><span data-i18n="workspace_hidden_files_visible">hidden visible</span></span>
|
||||
<span class="git-badge" id="gitBadge" style="display:none"></span>
|
||||
<div class="panel-actions">
|
||||
<button class="panel-icon-btn has-tooltip has-tooltip--bottom" id="btnCollapseWorkspacePanel" data-tooltip="Hide workspace panel" onclick="toggleWorkspacePanel(false)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"/></svg></button>
|
||||
|
||||
@@ -1160,8 +1160,10 @@
|
||||
@container queries below cut to display:none at hard breakpoints. */
|
||||
.panel-header{padding:12px 16px;border-bottom:1px solid var(--border);font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.1em;display:flex;align-items:center;gap:6px;overflow:visible;}
|
||||
.panel-header > span:first-child{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex-shrink:2;}
|
||||
.workspace-panel-heading{cursor:pointer;border-radius:6px;padding:2px 4px;margin:-2px -4px;}
|
||||
.workspace-panel-heading:hover,.workspace-panel-heading:focus{color:var(--text);background:var(--hover-bg);outline:none;}
|
||||
.workspace-panel-heading{cursor:default;border-radius:6px;padding:2px 4px;margin:-2px -4px;}
|
||||
.workspace-panel-heading.workspace-panel-heading--enabled{cursor:pointer;}
|
||||
.workspace-panel-heading.workspace-panel-heading--enabled:hover,
|
||||
.workspace-panel-heading.workspace-panel-heading--enabled:focus-visible{color:var(--text);background:var(--hover-bg);outline:none;}
|
||||
.git-badge{font-size:9px;font-weight:600;color:var(--muted);background:var(--hover-bg);padding:2px 7px;border-radius:4px;letter-spacing:.02em;white-space:nowrap;font-family:'SF Mono',ui-monospace,monospace;flex-shrink:3;overflow:hidden;min-width:0;}
|
||||
.topbar-source-badge{display:inline-flex;align-items:center;margin-left:6px;padding:2px 7px;border-radius:999px;background:var(--accent-bg);color:var(--accent-text);font-size:10px;font-weight:700;letter-spacing:.02em;vertical-align:middle;}
|
||||
.git-badge.dirty{color:var(--accent-text);background:var(--accent-bg);}
|
||||
|
||||
25
static/ui.js
25
static/ui.js
@@ -3762,6 +3762,7 @@ function syncTopbar(){
|
||||
if(!S.session){
|
||||
document.title=window._botName||'Hermes';
|
||||
if(typeof syncWorkspaceDisplays==='function') syncWorkspaceDisplays();
|
||||
if(typeof _syncWorkspaceHeadingState==='function') _syncWorkspaceHeadingState();
|
||||
if(typeof syncModelChip==='function') syncModelChip();
|
||||
if(typeof syncTerminalButton==='function') syncTerminalButton();
|
||||
if(typeof _syncHermesPanelSessionActions==='function') _syncHermesPanelSessionActions();
|
||||
@@ -3795,6 +3796,7 @@ function syncTopbar(){
|
||||
}
|
||||
}
|
||||
if(typeof syncAppTitlebar==='function') syncAppTitlebar();
|
||||
if(typeof _syncWorkspaceHeadingState==='function') _syncWorkspaceHeadingState();
|
||||
// If a profile switch just happened, apply its model rather than the session's stale value.
|
||||
// S._pendingProfileModel is set by switchToProfile() and cleared here after one application.
|
||||
const modelOverride=S._pendingProfileModel;
|
||||
@@ -6234,16 +6236,37 @@ function bindWorkspaceHeadingActions(){
|
||||
};
|
||||
heading.onclick=goRoot;
|
||||
heading.onkeydown=(e)=>{
|
||||
if(!(S.session&&S.session.workspace)) return;
|
||||
if(e.key==='Enter'||e.key===' '){
|
||||
e.preventDefault();
|
||||
goRoot();
|
||||
}
|
||||
};
|
||||
heading.oncontextmenu=(e)=>{
|
||||
if(!(S.session&&S.session.workspace)) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if(S.session&&S.session.workspace) _showWorkspaceRootContextMenu(e);
|
||||
_showWorkspaceRootContextMenu(e);
|
||||
};
|
||||
_syncWorkspaceHeadingState();
|
||||
}
|
||||
|
||||
function _syncWorkspaceHeadingState(){
|
||||
const heading=$('workspacePanelHeading');
|
||||
if(!heading) return;
|
||||
const enabled=!!(S.session&&S.session.workspace);
|
||||
heading.classList.toggle('workspace-panel-heading--enabled',enabled);
|
||||
if(enabled){
|
||||
heading.setAttribute('role','button');
|
||||
heading.setAttribute('tabindex','0');
|
||||
heading.setAttribute('aria-disabled','false');
|
||||
heading.title='Workspace root';
|
||||
} else {
|
||||
heading.removeAttribute('role');
|
||||
heading.removeAttribute('tabindex');
|
||||
heading.setAttribute('aria-disabled','true');
|
||||
heading.title=t('no_workspace');
|
||||
}
|
||||
}
|
||||
if(document.readyState==='loading') document.addEventListener('DOMContentLoaded',bindWorkspaceHeadingActions);
|
||||
else bindWorkspaceHeadingActions();
|
||||
|
||||
@@ -21,3 +21,23 @@ def test_workspace_heading_context_menu_exposes_root_reveal_and_copy_path():
|
||||
assert "path:'.'" in UI_JS.replace(" ", "")
|
||||
assert "copy_file_path" in UI_JS
|
||||
assert "reveal_in_finder" in UI_JS
|
||||
|
||||
|
||||
def test_workspace_heading_affordance_requires_workspace():
|
||||
"""The heading should only advertise button behavior when a workspace exists."""
|
||||
heading_line = next(line for line in INDEX_HTML.splitlines() if 'id="workspacePanelHeading"' in line)
|
||||
assert 'role="button"' not in heading_line
|
||||
assert 'tabindex="0"' not in heading_line
|
||||
assert "_syncWorkspaceHeadingState" in UI_JS
|
||||
assert "heading.classList.toggle('workspace-panel-heading--enabled',enabled)" in UI_JS
|
||||
assert "heading.setAttribute('role','button')" in UI_JS
|
||||
assert "heading.setAttribute('tabindex','0')" in UI_JS
|
||||
assert "heading.removeAttribute('role')" in UI_JS
|
||||
assert "heading.removeAttribute('tabindex')" in UI_JS
|
||||
assert "if(!(S.session&&S.session.workspace)) return;" in UI_JS
|
||||
assert "typeof _syncWorkspaceHeadingState==='function'" in UI_JS
|
||||
|
||||
context_idx = UI_JS.find("heading.oncontextmenu")
|
||||
guard_idx = UI_JS.find("if(!(S.session&&S.session.workspace)) return;", context_idx)
|
||||
prevent_idx = UI_JS.find("e.preventDefault()", context_idx)
|
||||
assert context_idx < guard_idx < prevent_idx
|
||||
|
||||
Reference in New Issue
Block a user