feat(desktop): resizable VS Code-themed terminal pane + palette polish#42521
Merged
Conversation
Keep the right rail focused on file browsing while moving the persistent terminal into the chat column bottom slot, and make terminal colors follow the active light/dark mode instead of a fixed Solarized palette.
2 tasks
Contributor
🔎 Lint report:
|
- Move the terminal into a resizable pane (viewport-% widths) that shares <main>'s stacking context, so its drag handle no longer sits under the fixed terminal overlay; works on either rail side. - Restore +x on node-pty's spawn-helper before the first spawn to fix "posix_spawnp failed" on macOS prebuilds (real cause; drop the redundant shell-candidate retry loop). - Gate terminal open/fit/start on document.fonts.ready and strip leading blank rows (re-armed before the resize Ctrl-L redraw) so the prompt sits flush at the top with no starship add_newline gap. - Inherit the app editor-surface color as the terminal background. - Bind Ctrl+` (⌃` on macOS) to toggle the terminal; add a palette entry.
- Render each palette item's live binding as a <KbdGroup> hint via a new comboTokens() helper (mac shows ⌘/⌃/⌥/⇧, every other platform shows Ctrl/Alt/Shift — never a ⌘ on PC). - Default the terminal toggle to ⌘` / Ctrl+` (the ~ key) on both platforms. - Drop the hardcoded (⌘⏎) baked into the composer steer tooltip; render it platform-aware with formatCombo instead.
Full-screen apps (hermes --tui, vim) enable mouse reporting, so a plain drag can't select text and ⌘/Ctrl+L (add-selection-to-chat) had nothing to send. Enable macOptionClickForcesSelection so ⌥-drag on macOS (Shift elsewhere) forces a native selection over mouse-mode apps.
Set HERMES_DESKTOP_TERMINAL=1 on the terminal pane's shell env and surface it in build_environment_hints, so a hermes/--tui launched inside the pane knows it's next to the GUI chat and that ⌥/Shift-drag + ⌘/Ctrl+L sends a selection to the composer. Distinct from HERMES_DESKTOP (agent backend).
The toggle now ships as mod+` on both platforms, so the standard combo index handles it — the bespoke fallback (and its stale 'old default' comment) is dead weight.
A redraw-heavy TUI (spinners/clocks) outruns onSelectionChange, leaving the React selection state empty so the state-gated shortcut listener never attached and ⌘L no-op'd. Always listen and read xterm's live selection (with a native fallback) at press time; only swallow the key when there's text to send. Drops the now-redundant custom key handler.
Generalize the runtime-surface hint: fire for HERMES_DESKTOP (the backend powering the GUI chat) as well as HERMES_DESKTOP_TERMINAL (a hermes in the embedded terminal pane), so it's about being inside the desktop GUI, not about being a TUI. The terminal-pane selection note stays pane-specific.
…esktop-terminal-bottom-main
The in-app terminal buffer lives in the renderer (xterm), so expose it to the chat agent over the same blocking bridge clarify uses: read_terminal emits terminal.read.request, the renderer serializes the buffer (visible screen by default, or a start_line/count range against total_lines) and answers terminal.read.respond. Gated to the GUI via HERMES_DESKTOP. Also restores the flipped-layout titlebar inset fix (app-shell + desktop-controller) for terminal/preview rails at the window's left edge.
…ottom-main # Conflicts: # apps/desktop/src/app/hooks/use-keybinds.ts # apps/desktop/src/lib/keybinds/actions.ts # apps/desktop/src/lib/keybinds/combo.ts
The file rail lost its terminal icon, leaving ⌘` and the command palette as the only ways in. Add a one-click toggle to the statusbar's left cluster, mirroring the command-center item: it reads $terminalTakeover so it lights up while the pane is open and stays in sync with the hotkey, and is gated to chat view (the only place the pane can show).
The in-pane button claimed a focus/split fullscreen toggle ("Focus
terminal view" / "Return to split view", screen-full/normal icons), but
the terminal is just a resizable side pane — there's no fullscreen. The
button only mounts while the pane is open, so the focus branch was dead
and clicking it merely closed the terminal. Relabel to "Hide terminal"
with a close icon, drop the dead conditional and the now-unused takeover
read.
Relocate it from the left cluster to the right of the statusbar, just left of the client version item.
Prefer pwsh (7+) then Windows PowerShell 5.1 over cmd.exe, falling back to comspec only when neither is present. -NoLogo drops the startup banner so the prompt sits flush like the POSIX shells.
The resize sash only painted on hover, so the terminal/chat boundary was invisible at rest. Add an opt-in `divider` prop to Pane that paints a thin resting hairline on the resize edge (side-aware, so it tracks the rail when the layout flips) and enable it on the terminal pane.
Make shell selection a real resolver: an explicit override wins (HERMES_DESKTOP_SHELL on both platforms, $SHELL on POSIX), otherwise auto-detect the best installed shell — pwsh > Windows PowerShell 5.1 > cmd on Windows, zsh > bash > sh on POSIX. A shared shellSpecFor() picks the interactive flags by family, so an overridden bash/pwsh/cmd all launch correctly.
Setting term.options.theme updated colors for the DOM renderer but not the WebGL one, which caches glyph colors in a texture atlas — so already-drawn cells kept their old palette after a mode switch. Hold the WebglAddon in a ref and clear its atlas when the theme changes.
Adopt VS Code's exact default ANSI palette (the terminalColorRegistry defaults), enable minimumContrastRatio: 4.5 so foregrounds are clamped against the background the way the integrated terminal does, and key the light/dark choice off renderedMode (the painted surface) instead of resolvedMode so it can't invert. The canvas + inset paint the live skin surface (--ui-editor-surface-background) so the terminal blends with the app and follows light/dark, while the contrast clamp keeps colors crisp.
cmdk's default fuzzy scorer matched anything with the query letters scattered across an item, so e.g. "color" never narrowed to color entries. Add a substring filter: every typed word must literally appear in an item's value/keywords, keeping results tight and predictable.
The persistent-terminal overlay painted the static palette background (#1e1e1e/#ffffff), so the transparent header strip revealed a near-black slab above the surface-colored body. Paint the overlay with the live --ui-editor-surface-background so header and body read as one pane.
The canvas surface only re-resolved on light/dark change, so switching skins at the same mode left the WebGL canvas painted with the old tint until reload. Key the resolve off themeName too. Also trim the palette comments.
…ottom-main # Conflicts: # apps/desktop/src/app/desktop-controller.tsx # apps/desktop/src/app/shell/app-shell.tsx
Collaborator
Author
Terminal theming, matched to VS Code / Cursor's integrated terminalThe terminal now reads like VS Code/Cursor's integrated terminal instead of the old "cotton candy" palette:
This-PR scope also includes
Merge with
|
changman
pushed a commit
to changman/hermes-agent
that referenced
this pull request
Jun 10, 2026
NousResearch#42521) * refactor(desktop): dock terminal under chat and simplify file rail Keep the right rail focused on file browsing while moving the persistent terminal into the chat column bottom slot, and make terminal colors follow the active light/dark mode instead of a fixed Solarized palette. * fix(desktop): make the terminal a resizable, themed side pane - Move the terminal into a resizable pane (viewport-% widths) that shares <main>'s stacking context, so its drag handle no longer sits under the fixed terminal overlay; works on either rail side. - Restore +x on node-pty's spawn-helper before the first spawn to fix "posix_spawnp failed" on macOS prebuilds (real cause; drop the redundant shell-candidate retry loop). - Gate terminal open/fit/start on document.fonts.ready and strip leading blank rows (re-armed before the resize Ctrl-L redraw) so the prompt sits flush at the top with no starship add_newline gap. - Inherit the app editor-surface color as the terminal background. - Bind Ctrl+` (⌃` on macOS) to toggle the terminal; add a palette entry. * feat(desktop): show platform hotkey hints in the command palette - Render each palette item's live binding as a <KbdGroup> hint via a new comboTokens() helper (mac shows ⌘/⌃/⌥/⇧, every other platform shows Ctrl/Alt/Shift — never a ⌘ on PC). - Default the terminal toggle to ⌘` / Ctrl+` (the ~ key) on both platforms. - Drop the hardcoded (⌘⏎) baked into the composer steer tooltip; render it platform-aware with formatCombo instead. * fix(desktop): drop the active check on the command-palette terminal item * fix(desktop): remove active/check states from the command palette * fix(desktop): allow ⌥/Shift-drag selection over mouse-mode TUIs Full-screen apps (hermes --tui, vim) enable mouse reporting, so a plain drag can't select text and ⌘/Ctrl+L (add-selection-to-chat) had nothing to send. Enable macOptionClickForcesSelection so ⌥-drag on macOS (Shift elsewhere) forces a native selection over mouse-mode apps. * feat(desktop): tell the in-pane agent it's embedded in the GUI Set HERMES_DESKTOP_TERMINAL=1 on the terminal pane's shell env and surface it in build_environment_hints, so a hermes/--tui launched inside the pane knows it's next to the GUI chat and that ⌥/Shift-drag + ⌘/Ctrl+L sends a selection to the composer. Distinct from HERMES_DESKTOP (agent backend). * refactor(desktop): drop the redundant Ctrl+` terminal-toggle fallback The toggle now ships as mod+` on both platforms, so the standard combo index handles it — the bespoke fallback (and its stale 'old default' comment) is dead weight. * fix(desktop): read live terminal selection for ⌘/Ctrl+L A redraw-heavy TUI (spinners/clocks) outruns onSelectionChange, leaving the React selection state empty so the state-gated shortcut listener never attached and ⌘L no-op'd. Always listen and read xterm's live selection (with a native fallback) at press time; only swallow the key when there's text to send. Drops the now-redundant custom key handler. * feat(desktop): make any agent aware it's in the Hermes desktop GUI Generalize the runtime-surface hint: fire for HERMES_DESKTOP (the backend powering the GUI chat) as well as HERMES_DESKTOP_TERMINAL (a hermes in the embedded terminal pane), so it's about being inside the desktop GUI, not about being a TUI. The terminal-pane selection note stays pane-specific. * feat(desktop): give the GUI agent a read_terminal tool The in-app terminal buffer lives in the renderer (xterm), so expose it to the chat agent over the same blocking bridge clarify uses: read_terminal emits terminal.read.request, the renderer serializes the buffer (visible screen by default, or a start_line/count range against total_lines) and answers terminal.read.respond. Gated to the GUI via HERMES_DESKTOP. Also restores the flipped-layout titlebar inset fix (app-shell + desktop-controller) for terminal/preview rails at the window's left edge. * chore(desktop): trim read_terminal comments * feat(desktop): add a terminal toggle to the statusbar The file rail lost its terminal icon, leaving ⌘` and the command palette as the only ways in. Add a one-click toggle to the statusbar's left cluster, mirroring the command-center item: it reads $terminalTakeover so it lights up while the pane is open and stays in sync with the hotkey, and is gated to chat view (the only place the pane can show). * fix(desktop): relabel the terminal header button to what it does The in-pane button claimed a focus/split fullscreen toggle ("Focus terminal view" / "Return to split view", screen-full/normal icons), but the terminal is just a resizable side pane — there's no fullscreen. The button only mounts while the pane is open, so the focus branch was dead and clicking it merely closed the terminal. Relabel to "Hide terminal" with a close icon, drop the dead conditional and the now-unused takeover read. * fix(desktop): move the terminal toggle next to the version item Relocate it from the left cluster to the right of the statusbar, just left of the client version item. * feat(desktop): default the terminal to PowerShell on Windows Prefer pwsh (7+) then Windows PowerShell 5.1 over cmd.exe, falling back to comspec only when neither is present. -NoLogo drops the startup banner so the prompt sits flush like the POSIX shells. * feat(desktop): show a persistent divider on the terminal pane The resize sash only painted on hover, so the terminal/chat boundary was invisible at rest. Add an opt-in `divider` prop to Pane that paints a thin resting hairline on the resize edge (side-aware, so it tracks the rail when the layout flips) and enable it on the terminal pane. * refactor(desktop): resolve the terminal shell instead of hardcoding it Make shell selection a real resolver: an explicit override wins (HERMES_DESKTOP_SHELL on both platforms, $SHELL on POSIX), otherwise auto-detect the best installed shell — pwsh > Windows PowerShell 5.1 > cmd on Windows, zsh > bash > sh on POSIX. A shared shellSpecFor() picks the interactive flags by family, so an overridden bash/pwsh/cmd all launch correctly. * fix(desktop): repaint the terminal on light/dark switch Setting term.options.theme updated colors for the DOM renderer but not the WebGL one, which caches glyph colors in a texture atlas — so already-drawn cells kept their old palette after a mode switch. Hold the WebglAddon in a ref and clear its atlas when the theme changes. * fix(desktop): match the terminal palette to VS Code Light+/Dark+ Adopt VS Code's exact default ANSI palette (the terminalColorRegistry defaults), enable minimumContrastRatio: 4.5 so foregrounds are clamped against the background the way the integrated terminal does, and key the light/dark choice off renderedMode (the painted surface) instead of resolvedMode so it can't invert. The canvas + inset paint the live skin surface (--ui-editor-surface-background) so the terminal blends with the app and follows light/dark, while the contrast clamp keeps colors crisp. * fix(desktop): tighten command palette search to substring matching cmdk's default fuzzy scorer matched anything with the query letters scattered across an item, so e.g. "color" never narrowed to color entries. Add a substring filter: every typed word must literally appear in an item's value/keywords, keeping results tight and predictable. * fix(desktop): blend the terminal header into the skin surface The persistent-terminal overlay painted the static palette background (#1e1e1e/#ffffff), so the transparent header strip revealed a near-black slab above the surface-colored body. Paint the overlay with the live --ui-editor-surface-background so header and body read as one pane. * fix(desktop): re-resolve the terminal surface on skin switch The canvas surface only re-resolved on light/dark change, so switching skins at the same mode left the WebGL canvas painted with the old tint until reload. Key the resolve off themeName too. Also trim the palette comments. * chore(desktop): drop redundant terminal theming header comment
alt-glitch
pushed a commit
that referenced
this pull request
Jun 14, 2026
#42521) * refactor(desktop): dock terminal under chat and simplify file rail Keep the right rail focused on file browsing while moving the persistent terminal into the chat column bottom slot, and make terminal colors follow the active light/dark mode instead of a fixed Solarized palette. * fix(desktop): make the terminal a resizable, themed side pane - Move the terminal into a resizable pane (viewport-% widths) that shares <main>'s stacking context, so its drag handle no longer sits under the fixed terminal overlay; works on either rail side. - Restore +x on node-pty's spawn-helper before the first spawn to fix "posix_spawnp failed" on macOS prebuilds (real cause; drop the redundant shell-candidate retry loop). - Gate terminal open/fit/start on document.fonts.ready and strip leading blank rows (re-armed before the resize Ctrl-L redraw) so the prompt sits flush at the top with no starship add_newline gap. - Inherit the app editor-surface color as the terminal background. - Bind Ctrl+` (⌃` on macOS) to toggle the terminal; add a palette entry. * feat(desktop): show platform hotkey hints in the command palette - Render each palette item's live binding as a <KbdGroup> hint via a new comboTokens() helper (mac shows ⌘/⌃/⌥/⇧, every other platform shows Ctrl/Alt/Shift — never a ⌘ on PC). - Default the terminal toggle to ⌘` / Ctrl+` (the ~ key) on both platforms. - Drop the hardcoded (⌘⏎) baked into the composer steer tooltip; render it platform-aware with formatCombo instead. * fix(desktop): drop the active check on the command-palette terminal item * fix(desktop): remove active/check states from the command palette * fix(desktop): allow ⌥/Shift-drag selection over mouse-mode TUIs Full-screen apps (hermes --tui, vim) enable mouse reporting, so a plain drag can't select text and ⌘/Ctrl+L (add-selection-to-chat) had nothing to send. Enable macOptionClickForcesSelection so ⌥-drag on macOS (Shift elsewhere) forces a native selection over mouse-mode apps. * feat(desktop): tell the in-pane agent it's embedded in the GUI Set HERMES_DESKTOP_TERMINAL=1 on the terminal pane's shell env and surface it in build_environment_hints, so a hermes/--tui launched inside the pane knows it's next to the GUI chat and that ⌥/Shift-drag + ⌘/Ctrl+L sends a selection to the composer. Distinct from HERMES_DESKTOP (agent backend). * refactor(desktop): drop the redundant Ctrl+` terminal-toggle fallback The toggle now ships as mod+` on both platforms, so the standard combo index handles it — the bespoke fallback (and its stale 'old default' comment) is dead weight. * fix(desktop): read live terminal selection for ⌘/Ctrl+L A redraw-heavy TUI (spinners/clocks) outruns onSelectionChange, leaving the React selection state empty so the state-gated shortcut listener never attached and ⌘L no-op'd. Always listen and read xterm's live selection (with a native fallback) at press time; only swallow the key when there's text to send. Drops the now-redundant custom key handler. * feat(desktop): make any agent aware it's in the Hermes desktop GUI Generalize the runtime-surface hint: fire for HERMES_DESKTOP (the backend powering the GUI chat) as well as HERMES_DESKTOP_TERMINAL (a hermes in the embedded terminal pane), so it's about being inside the desktop GUI, not about being a TUI. The terminal-pane selection note stays pane-specific. * feat(desktop): give the GUI agent a read_terminal tool The in-app terminal buffer lives in the renderer (xterm), so expose it to the chat agent over the same blocking bridge clarify uses: read_terminal emits terminal.read.request, the renderer serializes the buffer (visible screen by default, or a start_line/count range against total_lines) and answers terminal.read.respond. Gated to the GUI via HERMES_DESKTOP. Also restores the flipped-layout titlebar inset fix (app-shell + desktop-controller) for terminal/preview rails at the window's left edge. * chore(desktop): trim read_terminal comments * feat(desktop): add a terminal toggle to the statusbar The file rail lost its terminal icon, leaving ⌘` and the command palette as the only ways in. Add a one-click toggle to the statusbar's left cluster, mirroring the command-center item: it reads $terminalTakeover so it lights up while the pane is open and stays in sync with the hotkey, and is gated to chat view (the only place the pane can show). * fix(desktop): relabel the terminal header button to what it does The in-pane button claimed a focus/split fullscreen toggle ("Focus terminal view" / "Return to split view", screen-full/normal icons), but the terminal is just a resizable side pane — there's no fullscreen. The button only mounts while the pane is open, so the focus branch was dead and clicking it merely closed the terminal. Relabel to "Hide terminal" with a close icon, drop the dead conditional and the now-unused takeover read. * fix(desktop): move the terminal toggle next to the version item Relocate it from the left cluster to the right of the statusbar, just left of the client version item. * feat(desktop): default the terminal to PowerShell on Windows Prefer pwsh (7+) then Windows PowerShell 5.1 over cmd.exe, falling back to comspec only when neither is present. -NoLogo drops the startup banner so the prompt sits flush like the POSIX shells. * feat(desktop): show a persistent divider on the terminal pane The resize sash only painted on hover, so the terminal/chat boundary was invisible at rest. Add an opt-in `divider` prop to Pane that paints a thin resting hairline on the resize edge (side-aware, so it tracks the rail when the layout flips) and enable it on the terminal pane. * refactor(desktop): resolve the terminal shell instead of hardcoding it Make shell selection a real resolver: an explicit override wins (HERMES_DESKTOP_SHELL on both platforms, $SHELL on POSIX), otherwise auto-detect the best installed shell — pwsh > Windows PowerShell 5.1 > cmd on Windows, zsh > bash > sh on POSIX. A shared shellSpecFor() picks the interactive flags by family, so an overridden bash/pwsh/cmd all launch correctly. * fix(desktop): repaint the terminal on light/dark switch Setting term.options.theme updated colors for the DOM renderer but not the WebGL one, which caches glyph colors in a texture atlas — so already-drawn cells kept their old palette after a mode switch. Hold the WebglAddon in a ref and clear its atlas when the theme changes. * fix(desktop): match the terminal palette to VS Code Light+/Dark+ Adopt VS Code's exact default ANSI palette (the terminalColorRegistry defaults), enable minimumContrastRatio: 4.5 so foregrounds are clamped against the background the way the integrated terminal does, and key the light/dark choice off renderedMode (the painted surface) instead of resolvedMode so it can't invert. The canvas + inset paint the live skin surface (--ui-editor-surface-background) so the terminal blends with the app and follows light/dark, while the contrast clamp keeps colors crisp. * fix(desktop): tighten command palette search to substring matching cmdk's default fuzzy scorer matched anything with the query letters scattered across an item, so e.g. "color" never narrowed to color entries. Add a substring filter: every typed word must literally appear in an item's value/keywords, keeping results tight and predictable. * fix(desktop): blend the terminal header into the skin surface The persistent-terminal overlay painted the static palette background (#1e1e1e/#ffffff), so the transparent header strip revealed a near-black slab above the surface-colored body. Paint the overlay with the live --ui-editor-surface-background so header and body read as one pane. * fix(desktop): re-resolve the terminal surface on skin switch The canvas surface only re-resolved on light/dark change, so switching skins at the same mode left the WebGL canvas painted with the old tint until reload. Key the resolve off themeName too. Also trim the palette comments. * chore(desktop): drop redundant terminal theming header comment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Docks the persistent terminal as its own resizable pane beside the chat, themed to match VS Code's integrated terminal, plus command-palette and shell-resolution polish.
Terminal pane
<main>'s stacking context so the drag handle paints above the fixed terminal overlay.posix_spawnp failedfix. node-pty'sspawn-helperprebuilt ships without+x; restore it lazily before the first spawn.document.fonts.readyand strip leading blank rows so nostarship add_newlinegap survives boot or resize.macOptionClickForcesSelection(⌥-drag macOS / Shift-drag else) + ⌘/Ctrl+L reads xterm's live selection at press time (native fallback), only swallowing the key when there's text to send.Terminal theming — matches VS Code Light+/Dark+
terminalColorRegistrydefaults verbatim, per theme type — replaces the pastel Tailwind palette.minimumContrastRatio: 4.5(VS Code's default) so foregrounds stay crisp on the surface instead of reading neon.--ui-editor-surface-background, re-resolved on mode and skin switch, so the terminal inherits the app surface and follows light/dark.renderedMode. Palette follows the painted surface (luminance-derived), not the clicked switch, so it can't invert against a skin that keeps a bright surface in dark mode. (ExposesrenderedModefrom the theme context.)Shell resolution
HERMES_DESKTOP_SHELL→$SHELL(POSIX) → autodetect: pwsh > PowerShell 5.1 > cmd on Windows, zsh > bash > sh on POSIX. PowerShell is now the Windows default instead of cmd.Command palette
<KbdGroup>caps viacomboTokens()— ⌘/⌃/⌥/⇧ on macOS, Ctrl/Alt/Shift elsewhere. Fixed the composer steer tooltip's hardcoded(⌘⏎).File rail
Hotkeys
Agent ↔ GUI awareness
build_environment_hints:HERMES_DESKTOPandHERMES_DESKTOP_TERMINALsurface a "running inside the Hermes desktop GUI" line; the terminal-pane case adds the ⌥/Shift-drag + ⌘/Ctrl+L selection note. Aread_terminaltool lets the GUI agent read the pane.Test plan
npm run --workspace apps/desktop type-checkeslintclean on all changedsrc/+electron/fileshermes --tui→ ⌘L into composer