fix(web): harden desktop webapp chat and mobile controls - #60829
Conversation
4e32b65 to
65acf34
Compare
|
Added a follow-up fix for Ctrl+V screenshot/image paste in the Desktop webapp variant. What changed:
Validation run locally:
|
|
Added a follow-up fix for the attachment picker UX. What changed:
Validation run locally:
|
|
Added a follow-up responsive-preview UX fix. What changed:
Validation run locally:
|
5551185 to
114ac55
Compare
|
Follow-up after Steven’s UX clarification: previews must stay docked inside the right-rail/grid, not pop out of the app layout. What changed:
Validation run in the clean PR worktree:
Visual QA:
Installed-source backport:
|
|
Related split-out work:
Steven's fork main now carries both focused branches as an integration branch, but upstream review can keep these PRs separate. |
114ac55 to
3d66eb0
Compare
|
Follow-up from live Desktop QA: fixed the preview rail so responsive presets size an inner clipped preview frame instead of widening the outer PaneShell track; added visible in-pane annotate/debug overlays so those controls no longer appear to do nothing; reduced the preview rail max so stale persisted desktop/ultrawide widths cannot keep consuming the chat area. Validated focused preview tests, typecheck, lint, and Desktop build. |
|
Follow-up from Desktop/Webapp browser-surface QA: added explicit in-pane browser chrome to the preview rail (Preview URL bar, reload, open externally, find-in-preview button and Ctrl/Cmd+F, Ctrl/Cmd+L, Ctrl/Cmd+R/F5 shortcuts). Also wired CodeMirror search into file/text edit mode so normal editor find works with Ctrl/Cmd+F. Validation: focused preview/editor tests, typecheck, eslint, production Desktop build, and privacy scan passed. |
|
Follow-up: implemented the right-rail file manager surface requested in Desktop/Webapp QA. What changed:
Validation:
Known test noise: CodeMirror still logs the jsdom getClientRects geometry warning in search tests, but tests and production build pass. |
|
Follow-up: fixed preview/code-editor rail containment for new-session and full-screen/non-chat views. What changed:
Validation:
Known test noise: CodeMirror jsdom getClientRects warnings still appear in editor-search tests; tests/build pass. |
|
Follow-up: fixed preview/code-editor auto-collapse when nothing is open. Research/backtrace:
What changed:
Validation:
|
|
Follow-up: audited and repaired the browser Webapp UX instead of only the Desktop rail. What I verified visually:
Issues found and fixed:
Docs/tests:
Validation:
Installed backport:
|
|
Follow-up from screenshot: fixed the “one container too many” preview trap without removing the responsive/free-size canvas feature. What changed:
Validation:
Design note: the larger Windows-like linked preview workspace / snapping / layering / minimization idea is good, but I kept this commit to the screenshot bug: default browser-style fit + preserve explicit responsive canvas mode. |
|
Follow-up preview-flow pass:
Validation:
Installed checkout was backported and rebuilt without restarting Hermes. |
|
Follow-up preview scaling pass:
Visual dogfood:
Validation:
Installed checkout was backported and rebuilt without restarting Hermes. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing the real dashboard refresh gap: current main still uses one localStorage attach token (web/src/pages/ChatPage.tsx:61-81) and directly sends sidecar frames without replay (hermes_cli/web_server.py:14693-14705).
Problems
web/src/lib/chat-identity.ts:84maps every no-resume route tonew. BecausegetPtyAttachToken()persists that scope in localStorage (:90-95), two new chats in one profile share an attach token/channel and can reattach or supersede each other's PTY. The tests cover stored sessions, not two no-resume tabs.apps/desktop/src/app/chat/right-rail/preview-pane.tsx:1099grants clipboard, microphone, and camera access to the iframe fallback without a sandbox. This is weaker than the Electron path'ssandbox=yesconfiguration at:1088.- Current main has TypeScriptified the Electron surface (
apps/desktop/electron/main.ts:65,hardening.ts:149); the PR's CJSfs-create.cjsimports./hardening.cjs, so that portion needs a port during salvage.
Suggested changes
- Use a tab-scoped, refresh-stable identity for no-resume chats and test two same-profile new tabs.
- Sandbox/minimize iframe permissions and cover the fallback policy.
- Port retained Electron work to the current TypeScript architecture.
Automated hermes-sweeper review.
| resumeSessionId: string | null | undefined, | ||
| profile: string | null | undefined | ||
| ): string { | ||
| const session = (resumeSessionId ?? '').trim() || 'new' |
There was a problem hiding this comment.
Every no-resume chat becomes the same default:new localStorage identity, so two /chat tabs in one profile reuse their attach token/channel and can reattach or supersede each other's PTY. Use a tab-scoped refresh-stable identity for this case and add a two-new-tab regression test.
| const iframe = document.createElement('iframe') as HTMLIFrameElement & PreviewWebview | ||
|
|
||
| iframe.className = 'h-full w-full border-0 bg-background' | ||
| iframe.setAttribute('allow', 'clipboard-read; clipboard-write; fullscreen; microphone; camera') |
There was a problem hiding this comment.
This browser fallback grants clipboard, microphone, and camera access without an iframe sandbox, weakening the sandboxed Electron-webview boundary above. Apply a minimal sandbox/Permissions Policy and only grant a capability when a user-mediated preview action requires it.
What
Hardens the Hermes dashboard/Desktop-as-webapp path for browser and VPN/PWA-style use while keeping this PR focused on the existing desktop/web chat surfaces:
<webview>to a browser-safe<iframe>when the dashboard runs outside Electron./api/pub→/api/eventsnow has a bounded per-channel replay buffer so tool/progress/thought frames emitted during a refresh gap are replayed to the reloaded sidebar.Model & toolsare larger and easier to tap at phone widths.PWA installability metadata was split into focused follow-up PR #60873 so this PR stays reviewable.
Related Issue
No single issue. Follow-up from dashboard/desktop-as-webapp QA around browser-mode desktop use, refresh-safe in-flight chat state, and mobile phone usability over VPN.
Related / overlap checked:
preview pane browser mode,dashboard chat refresh pty reattach,mobile dashboard chat controls, anddashboard streaming refresh; no exact duplicate found.Type of Change
Verification
Targeted automated verification on latest head
e1333a862ec8b79558f508143bda587d27a358ec:python -m py_compile hermes_cli/web_server.pyuv run --with pytest --with pytest-asyncio pytest -q tests/hermes_cli/test_web_server_pty_reconnect.py tests/test_pty_keepalive_ws.py— 7 passednpm --workspace web run test -- src/lib/chat-identity.test.ts— 4 passednpm --workspace web exec eslint -- src/pages/ChatPage.tsx src/lib/chat-identity.ts src/lib/chat-identity.test.ts --max-warnings=0npm --workspace web run typechecknpm --workspace web run buildnpm --workspace apps/desktop run test:ui -- src/app/chat/right-rail/preview-pane.test.tsx— 2 passednpm --workspace apps/desktop run typechecknpm --workspace apps/desktop run lint— 0 errors, 1 existing unrelated warning inapps/desktop/src/app/settings/model-settings.tsxnpm --workspace apps/desktop run buildgit diff --check origin/main...HEADpython scripts/check-windows-footguns.py $(git diff --name-only origin/main...HEAD)— no issuesManual/browser verification:
HERMES_HOME=[local Hermes home redacted],--host 127.0.0.1 --port 9132 --skip-build --no-open.GET http://127.0.0.1:9132/api/statusreturned 200.http://127.0.0.1:9132/chatin browser tooling.hermes.pty.token.chat.default:newandhermes.pty.channel.chat.default:newin localStorage./chat,/sessions,/models,/env,/systemat iPhone SE, iPhone 12, and iPad Mini widths had root horizontal overflow 0. The one console event per route was an expected unauthenticated auth probe returning 401, not a layout failure.Model & toolspanel are visible/usable at 320px.Checklist
Latest local model / gateway / VPN-mode verification (2026-07-08)
Additional user-facing smoke was run on the same WSL PC the user uses, without restarting the installed Hermes gateway service:
Hermes Agent v0.16.0 (2026.6.5).timeout 180 hermes chat -q 'Reply with exactly OK.'returned exit code 0 and the assistant responseOK.hermes-gateway.serviceactive/running under systemd user service, local PID 424; linger enabled.gateway.status.read_runtime_status()reportedgateway_state: running,exit_reason: null, and Telegramstate: connected.127.0.0.1:9134with realHERMES_HOME=[local Hermes home redacted];/api/statusreturnedgateway_running: true,gateway_state: running,gateway_pid: 424,gateway_mode: single, Telegram connected,auth_required: falsefor loopback local mode./chat; sidebar showedGateway Status: Running, model/tool rail showedgpt-5.5andlive, and the browser chat smoke sessionExact WEBOK Replywas present.This push also clarifies the three deployment shapes in docs: local dashboard/local Desktop, VPN/LAN-hosted dashboard, and Remote Desktop backend. It updates stale
--insecurewording to match current fail-closed behavior: non-loopback binds are treated as VPN/LAN/hosted mode and always require dashboard auth. The sensitive messaging gateway (hermes gateway) is distinct from Desktop's remote dashboard backend.Follow-up verification: local/VPN/remote backend wording
hermes chat -q "Reply with exactly OK."returnedOKwith exit code 0.hermes-gateway.serviceactive/running; Telegram platform connected.hermes gateway) as a separate service.npm run typecheckinwebsite/passes afternpm ci.npm run buildinwebsite/succeeds; existing broken-link/anchor warnings remain unrelated.npm --workspace apps/desktop run test:ui -- src/components/boot-failure-reauth.test.ts src/components/gateway-connecting-overlay.test.tsx src/lib/desktop-fs.test.tspassed (24 tests).npm --workspace apps/desktop run typecheckpassed.npm --workspace apps/desktop run lintpassed with one pre-existing warning inmodel-settings.tsx.npm --workspace apps/desktop run buildsucceeded.Current rebase status
Draft: current
mainincludes the Electron CommonJS-to-TypeScript migration. A merge rehearsal leaves four semantic conflicts, includingapps/desktop/electron/main.tsandweb/src/pages/ChatPage.tsx; this PR needs a dedicated behavior-preserving rebase before review. No conflict resolution has been force-pushed.