Files
Brooklyn Nicholson d2b9a49e63 fix(desktop): guarantee an exit path for a fullscreened preview pane
A preview guest that HTML5-fullscreens itself owns all input: the host
renderer can't see into the out-of-process webview, the macOS-only Close
menu and HUD chords aren't reachable, and Wayland has no xdotool/wmctrl to
break out from a terminal — so a page that swallows Esc locks the display
(#97213).

Three layers, all routed before the guest sees the key or fully outside it:

- before-input-event on every webview guest: Esc exits the host window's
  fullscreen (gated on the fullscreen state so normal-mode panes keep Esc),
  Ctrl/Cmd+Shift+W closes the pane via the existing close-preview channel
- hermes://close-preview deep link handled in the main process: restores
  and focuses the window, exits fullscreen, closes the pane
- `hermes desktop --close-preview` CLI flag forwarded to the packaged exe,
  riding the single-instance argv so a second invocation unlocks the running
  app

The permission handlers keep auto-allowing 'fullscreen' — the exit paths
are the fix, and scoping the grant would just break video fullscreen.

Refs #97213
2026-09-29 18:48:37 -05:00

63 lines
3.1 KiB
Python

"""``hermes gui`` subcommand parser."""
from __future__ import annotations
from typing import Callable
def build_gui_parser(subparsers, *, cmd_gui: Callable) -> None:
"""Attach the ``gui`` subcommand to ``subparsers``."""
gui_parser = subparsers.add_parser(
"desktop", aliases=["gui"], help="Build and launch the native desktop app",
description="Launch the Hermes Electron desktop app. By default this installs "
"workspace Node dependencies, builds the current OS's unpacked "
"Electron app, then launches that packaged artifact.")
gui_parser.add_argument(
"--source", action="store_true",
help="Launch via `electron .` against apps/desktop/dist instead of the packaged app")
gui_parser.add_argument(
"--build-only", action="store_true",
help="Build the desktop app but do not launch it (used by the installer's --update flow)")
gui_parser.add_argument(
"--fake-boot", action="store_true",
help="Enable deterministic desktop boot delays for validating startup UI")
gui_parser.add_argument(
"--ignore-existing", action="store_true",
help="Skip the installed Hermes runtime (~/.hermes/hermes-agent) so no local "
"backend starts and Desktop offers connect or install instead. --hermes-root "
"and the bundled runtime still win; a runtime installed during this launch is used.")
gui_parser.add_argument(
"--hermes-root",
help="Override the Hermes source root used by Desktop (sets HERMES_DESKTOP_HERMES_ROOT)")
gui_parser.add_argument(
"--cwd",
help="Initial project directory for Desktop chat sessions (sets HERMES_DESKTOP_CWD)")
gui_parser.add_argument(
"--skip-build", action="store_true",
help="Skip npm install/package and launch the existing unpacked app from apps/desktop/release",
)
gui_parser.add_argument(
"--local", action="store_true",
help="Show the local-models UI in the desktop app (models pane, quickstart, picker rows)")
gui_parser.add_argument(
"--force-build", action="store_true",
help="Force a full rebuild even if the content stamp matches")
gui_parser.add_argument(
"--setup-tcc-identity", action="store_true",
help="macOS only: create/import a self-signed code-signing certificate "
"in the login keychain and point desktop.macos_signing_identity at "
"it, then re-sign the packaged app. Makes macOS TCC grants (Full "
"Disk Access, Accessibility, Files and Folders, microphone) survive "
"rebuilds with a certificate-anchored identity. Idempotent — safe "
"to re-run after updates.")
gui_parser.add_argument(
"--identity", default="Hermes Local Signing",
help="Certificate name to create/use for --setup-tcc-identity (default: Hermes Local Signing)",
)
gui_parser.add_argument(
"--close-preview",
action="store_true",
help="Close the preview pane and exit its fullscreen (out-of-band escape hatch when the pane captured all input)",
)
gui_parser.set_defaults(func=cmd_gui)