You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(ux): add Rename + Reveal-in-File-Manager + Copy-path to session three-dot menu, then to every context-menu surface that references a real on-disk path #1764
Add Rename, Reveal in File Manager, and Copy file path to the session row's three-dot menu, and extend the same Reveal/Copy-path pair to every other context-menu surface in the app where a real on-disk file or folder is referenced.
Reporter: Cygnus (Discord; please ping in replies) via the WebUI testers thread, May 6 2026.
Why — verbatim from Cygnus
Also, can renaming chats please be put in the three dot menu? Double-clicking often just opens the chat and I still really do not want that.
I also want to more vehemently say: there is never a point when I don't want to be able to rightclick on something I can see in Hermes that has a file path, and not be able to see:
"Reveal in Finder" (or "Open in File Manager" I guess, though that's confusing wording on the Mac app)
Copy file path
Sessions, profiles, workspaces, everything. Everywhere I see a reference to a file that lives on my computer. I want to have those options to go straight to it. Because I will anyway, but without those options it'll take 5 times as long and just annoy me.
I always want to be able to open stuff locally to be able to debug and check and edit stuff; I always want to be able to quickly get a file open in another program if I need to; quickly and easily.
It's one of the biggest strengths of having an app running locally on my own files. So I want to be able to actually do it efficiently, haha.
(May 7 2026, 00:06–00:07 UTC)
Current state
Three pieces of context that frame the gap:
Sidebar session rename is double-click-only.static/sessions.js:2412 exposes the rename via startRename() triggered from a manual double-click detector at line ~2594. There's no menu item — the three-dot menu (_openSessionActionMenu at sessions.js:1312) currently has Pin / Move to Project / Archive / Duplicate / Stop / Delete, but no Rename. Cygnus's complaint: when she double-clicks intending to rename, the first click registers as "open the chat" before the second click arrives, so she opens the conversation instead of entering rename mode. Putting Rename in the three-dot menu eliminates the timing-sensitive interaction entirely. (Same pattern as the recent bug/ux: project chips cannot be renamed or recolored after creation #1078 work that made project chips renameable from a menu instead of double-click.)
Reveal exists ONLY for the workspace file tree. The "Reveal in File Manager" item is wired up in exactly one place: static/ui.js:6143-6149 inside _showFileContextMenu, which only fires from the right workspace panel's file rows (static/ui.js:5958). Everywhere else — sidebar sessions, profile chips, project chips, Spaces (workspace registrations), tool/code execution outputs that include a path, attachment chips, Settings's profile/workspace/log paths — the right-click either falls through to the default browser menu or shows a different action menu without these items.
There is no Copy-path action anywhere. No call to navigator.clipboard.writeText(path) exists in the codebase tied to a context-menu item. The closest thing is Open in File Manager, which actually opens the OS file browser — slower than just copying the path string, and adds friction when the user wants to paste the path into a terminal, editor, or another tool.
What we should ship
Phase 1 — sidebar session rows (Cygnus's specific ask)
Extend _openSessionActionMenu in static/sessions.js to include three new items (gated like the existing ones — _isReadOnlySession skips Rename, externally-sourced sessions still have read-only menus where appropriate):
Rename → calls the existing startRename(s) closure with the same logic the double-click currently triggers. Just lifts the entry point out of the double-click into a guaranteed click target. No new backend.
Reveal in File Manager → POST to /api/file/reveal with session_id + the session's on-disk path. Sessions live at ~/.hermes/profiles/<profile>/sessions/<session_id>.json — the backend handler at api/routes.py:6610 (_handle_file_reveal) already does the OS-specific dispatch (open -R on Darwin, explorer.exe /select, on Windows, xdg-open on Linux). Backend needs a small extension to accept a "session reveal" semantic: either a new /api/session/reveal endpoint or a mode: 'session' field on the existing handler so callers don't need to know the on-disk filename shape.
Copy file path → resolves the absolute path on the server (so the user gets /Users/cygnus/.hermes/profiles/foo/sessions/<sid>.json, not a relative one) via a tiny new /api/session/path endpoint that returns {path: <abs>}. Frontend writes the result to the clipboard via navigator.clipboard.writeText and toasts confirmation. Why a backend round-trip rather than a hard-coded path: the session storage layout is profile-aware and the WebUI doesn't currently know the absolute root from the client side.
Phase 2 — every other context-menu surface
For each surface below, add the same Reveal-in-File-Manager + Copy-path pair (Rename only where applicable):
Surface
What gets revealed
Where the menu lives
Profile chip in Settings / topbar profile picker
~/.hermes/profiles/<name>/
static/panels.js profile picker
Workspace (Space) chip in sidebar/Settings
The registered workspace path
static/sessions.js:2085 (project chip ctxmenu — extend or sibling)
Project chip
The project's metadata file or the workspace root
static/sessions.js:2836_showProjectContextMenu
Workspace file tree row
already done at static/ui.js:6123_showFileContextMenu — add Copy path here (reveal already exists)
Tool/code-execution result with a file path (shell ls /tmp/foo.json output, write_file output, etc.)
The literal path string parsed from the output
renderer hook in static/ui.js near renderMd / tool card rendering
Attachment chips (uploaded files in chat)
The attachment's stored path on disk
static/ui.js attachment rendering
For tool-output paths the simplest implementation is a delegated right-click handler at the message container that detects when the click target is a <code>/<a> whose textContent looks like a path the server can resolve (/^/[^\s]+/.test(text) or /^[A-Za-z]:\\/.test(text) on Windows), and pops a tiny menu with Reveal + Copy-path against that absolute string. Worth gating behind a Settings toggle if it proves noisy.
Bonus bug surfaced while writing this
The existing reveal handler shows a toast with "Failed to reveal: not found" that drops the path entirely (Cygnus posted a screenshot — see Discord link below). The handler at api/routes.py:6610 returns bad(handler, "File not found", 404) when target.exists() is false, but the frontend toast at static/ui.js:6148 only surfaces err.message. Without the path the user can't tell which file the system expected — useful when the failure is e.g. a stale session row pointing at a deleted file. While we're in here, the toast should include the resolved server-side path.
M2/UX — not a blocker, but Cygnus's "everywhere I see a reference to a file" framing is a UX north-star ask: the WebUI runs locally on the user's filesystem and ought to feel native to it. Currently it doesn't. Phase 1 (session three-dot menu) is small enough to ship in a single PR; Phase 2 is a sweep, can land iteratively per surface.
Reporter
@CygnusIgnis via WebUI Discord testers thread, May 7 2026.
Summary
Add Rename, Reveal in File Manager, and Copy file path to the session row's three-dot menu, and extend the same Reveal/Copy-path pair to every other context-menu surface in the app where a real on-disk file or folder is referenced.
Reporter: Cygnus (Discord; please ping in replies) via the WebUI testers thread, May 6 2026.
Why — verbatim from Cygnus
Current state
Three pieces of context that frame the gap:
Sidebar session rename is double-click-only.
static/sessions.js:2412exposes the rename viastartRename()triggered from a manual double-click detector at line ~2594. There's no menu item — the three-dot menu (_openSessionActionMenuat sessions.js:1312) currently has Pin / Move to Project / Archive / Duplicate / Stop / Delete, but no Rename. Cygnus's complaint: when she double-clicks intending to rename, the first click registers as "open the chat" before the second click arrives, so she opens the conversation instead of entering rename mode. Putting Rename in the three-dot menu eliminates the timing-sensitive interaction entirely. (Same pattern as the recent bug/ux: project chips cannot be renamed or recolored after creation #1078 work that made project chips renameable from a menu instead of double-click.)Reveal exists ONLY for the workspace file tree. The "Reveal in File Manager" item is wired up in exactly one place:
static/ui.js:6143-6149inside_showFileContextMenu, which only fires from the right workspace panel's file rows (static/ui.js:5958). Everywhere else — sidebar sessions, profile chips, project chips, Spaces (workspace registrations), tool/code execution outputs that include a path, attachment chips, Settings's profile/workspace/log paths — the right-click either falls through to the default browser menu or shows a different action menu without these items.There is no Copy-path action anywhere. No call to
navigator.clipboard.writeText(path)exists in the codebase tied to a context-menu item. The closest thing isOpen in File Manager, which actually opens the OS file browser — slower than just copying the path string, and adds friction when the user wants to paste the path into a terminal, editor, or another tool.What we should ship
Phase 1 — sidebar session rows (Cygnus's specific ask)
Extend
_openSessionActionMenuinstatic/sessions.jsto include three new items (gated like the existing ones —_isReadOnlySessionskips Rename, externally-sourced sessions still have read-only menus where appropriate):startRename(s)closure with the same logic the double-click currently triggers. Just lifts the entry point out of the double-click into a guaranteed click target. No new backend./api/file/revealwithsession_id+ the session's on-disk path. Sessions live at~/.hermes/profiles/<profile>/sessions/<session_id>.json— the backend handler atapi/routes.py:6610(_handle_file_reveal) already does the OS-specific dispatch (open -Ron Darwin,explorer.exe /select,on Windows,xdg-openon Linux). Backend needs a small extension to accept a "session reveal" semantic: either a new/api/session/revealendpoint or amode: 'session'field on the existing handler so callers don't need to know the on-disk filename shape./Users/cygnus/.hermes/profiles/foo/sessions/<sid>.json, not a relative one) via a tiny new/api/session/pathendpoint that returns{path: <abs>}. Frontend writes the result to the clipboard vianavigator.clipboard.writeTextand toasts confirmation. Why a backend round-trip rather than a hard-coded path: the session storage layout is profile-aware and the WebUI doesn't currently know the absolute root from the client side.Phase 2 — every other context-menu surface
For each surface below, add the same Reveal-in-File-Manager + Copy-path pair (Rename only where applicable):
~/.hermes/profiles/<name>/static/panels.jsprofile pickerstatic/sessions.js:2085(project chip ctxmenu — extend or sibling)static/sessions.js:2836_showProjectContextMenustatic/ui.js:6123_showFileContextMenu— add Copy path here (reveal already exists)ls /tmp/foo.jsonoutput, write_file output, etc.)static/ui.jsnearrenderMd/ tool card renderingstatic/ui.jsattachment renderingFor tool-output paths the simplest implementation is a delegated right-click handler at the message container that detects when the click target is a
<code>/<a>whose textContent looks like a path the server can resolve (/^/[^\s]+/.test(text)or/^[A-Za-z]:\\/.test(text)on Windows), and pops a tiny menu with Reveal + Copy-path against that absolute string. Worth gating behind a Settings toggle if it proves noisy.Bonus bug surfaced while writing this
The existing reveal handler shows a toast with "Failed to reveal: not found" that drops the path entirely (Cygnus posted a screenshot — see Discord link below). The handler at
api/routes.py:6610returnsbad(handler, "File not found", 404)whentarget.exists()is false, but the frontend toast atstatic/ui.js:6148only surfaceserr.message. Without the path the user can't tell which file the system expected — useful when the failure is e.g. a stale session row pointing at a deleted file. While we're in here, the toast should include the resolved server-side path.Discord screenshot: https://cdn.discordapp.com/attachments/1497355276779786330/1501735049110880277/image.png
Severity
M2/UX — not a blocker, but Cygnus's "everywhere I see a reference to a file" framing is a UX north-star ask: the WebUI runs locally on the user's filesystem and ought to feel native to it. Currently it doesn't. Phase 1 (session three-dot menu) is small enough to ship in a single PR; Phase 2 is a sweep, can land iteratively per surface.
Reporter
@CygnusIgnis via WebUI Discord testers thread, May 7 2026.
Related