Add live terminal preview to settings#2049
Open
twoTimesAgnew wants to merge 16 commits into
Open
Conversation
Per code review: the prompt() comment leaked a layout detail (360px), and def/render were collapsed into a single yellow span — losing the function- name color distinction the preview is meant to demonstrate. Strip ANSI in the marker test so future re-coloring doesn't break it.
…pacity Per code review: lead the JSDoc with the why (preview reuse) instead of narrating the merge steps, and add a test that pins zero-background-opacity behavior so a future truthy-only guard refactor doesn't silently drop it.
Implements Tasks 3-7 of the terminal-settings-preview plan: a live read-only xterm.js preview card (DOM renderer) that mutates font/cursor options directly, repaints the ANSI buffer on theme changes, manages the LigaturesAddon on toggle, and refits on ResizeObserver events.
Per code review: tabIndex/aria-hidden on the wrapper don't reach xterm's internal textarea — disableStdin does. And bg-black on the wrapper showed a stark black ring around light-mode previews; track the composed theme's background instead so the padding band always matches the canvas.
Per final review: the original Ghostty rationale comments inside applyTerminalAppearance were lost when the composition was extracted — restore them on each branch of composeActiveTerminalTheme. And the preview needs allowTransparency true when the user has set a sub-1 background opacity, otherwise xterm renders the rgba background fully opaque.
Three issues from initial visual testing: 1. Buffer accumulated on theme/settings changes. terminal.clear() keeps the row the cursor sits on, and PREVIEW_BUFFER ends mid-line on the prompt — so a follow-up clear+write left the trailing prompt fragment and appended the new buffer beneath it. Switch to terminal.reset() which restores cursor home and wipes the buffer. 2. Long lines wrapped because FitAddon shrunk cols to fit the 360px container. Drop FitAddon (and the resize observer that supported it), pin cols=50 / rows=15 in the constructor, and let the container clip horizontal overflow at large fonts. Lines never break mid-content now. 3. Add hover-to-preview to the font picker. FontAutocomplete fires a new onPreviewFontFamily callback as the user moves through dropdown options; TerminalPane lifts a previewFontFamily state and hands it to both Typography and Cursor previews so users see each font without committing the selection.
…yle, share with theme sections Three follow-ups from visual testing: 1. Trim preview content and pin cols=36 so PREVIEW_BUFFER fits in the ~312px-wide xterm canvas at the default 14px font. Larger fonts still extend past the right edge (clipped, not wrapped) — same behavior as before, just with the default size now on the right side of "fits". 2. Mirror cursorInactiveStyle to cursorStyle so the cursor shape always reflects the user's choice. xterm renders an outline cursor when unfocused by default, which masked shape changes in the preview because it's read-only and never gets focus. Blink still requires focus — same as a real terminal pane. 3. Replace TerminalThemePreview with TerminalSettingsPreview in the dark and light theme sections. Adds a themeOverride prop on the preview so each section can pin a specific theme regardless of the active app theme. Drops the static two-pane mock + palette grid in favor of the richer code/test/diff buffer the user prefers.
xterm only animates cursor blink when the terminal has focus, so the Cursor section's blink toggle had no visible effect unless the user clicked into the preview. Add an opt-in autoFocusOnCursorChange prop that focuses the preview whenever cursorStyle/Blink/Opacity changes — the user is interacting with cursor controls so a brief focus snap is expected. Skip the first run so opening Settings doesn't steal initial focus from elsewhere on the page. Only the Cursor preview opts in; Typography and theme previews keep natural focus behavior.
Drop the first-render guard so the cursor preview blinks immediately on mount, not just after a cursor setting change. Switch to focusing the xterm helper textarea directly with preventScroll: true so the page doesn't jump to the preview when Settings first opens — the preview can be below the fold and a scroll-into-view would be jarring.
Use terminal.focus() (which already uses preventScroll: true internally) and defer it via requestAnimationFrame. Focusing synchronously inside a React effect can land before xterm finishes wiring the textarea focus subscription, leaving _isFocused stale and the blink interval paused even though the textarea has DOM focus. One frame is enough for xterm's setup to complete.
…phy preview Cursor preview was high-friction (xterm cursor blink only animates on focus, and every workaround for that was either intrusive or fragile) and low-value — the bar/block/underline toggle is visually obvious from the controls themselves. Remove it. The Cursor section is back to a single-column layout. Add a Moon/Sun toggle to the Typography preview's card header so users can flip the preview between dark and light themes without changing the app theme. Toggle is hidden when themeOverride is set (Dark/Light theme sections already pin a specific theme). Initial mode follows the active app theme; flipping it doesn't change app settings.
The new xterm-based preview lost the visual feedback for terminal divider color/thickness and inactive pane opacity that the old TerminalThemePreview used to give. Bring back the same two-pane shape — real xterm on the left, configurable-width divider in the middle, small color-only stub on the right — so changing divider color, divider thickness, or inactive pane opacity has immediate visible feedback in the Dark/Light theme previews and the Typography preview. Switch the preview prop from themeOverride: ITheme to modeOverride: 'dark' | 'light' so the preview can derive both the theme and the matching divider color from a single resolveEffectiveTerminalAppearance call — keeps the dark/light variant rules in lockstep with live panes.
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
TerminalThemePreviewin the Dark/Light theme sections with the new xterm preview, restoring divider color, divider thickness, and inactive pane opacity feedback in the same xterm + divider + stub-pane shapecomposeActiveTerminalThemefromapplyTerminalAppearanceso live panes and the preview share one theme-composition path (color overrides, background opacity, cursor opacity)Screenshots
The Typography Preview lives in the right column of Settings → Terminal at ≥1280px window width and includes a Moon/Sun toggle in its card header.
Testing
pnpm lintpnpm typecheckpnpm test— 10 new unit tests (snapshot forPREVIEW_BUFFERplus 7 cases forcomposeActiveTerminalTheme)pnpm buildAI Review Report
Reviewed for spec compliance, refactor safety, React effect cleanup, and cross-platform behavior. Notable items:
composeActiveTerminalThemeextraction is behavior-preserving — traced thetheme/paneBackgroundflow through the refactoredapplyTerminalAppearance.terminal.reset()instead ofclear()for buffer rewrites —clear()keeps the cursor row, which caused a dangling-prompt duplication during font/theme swaps.Security Audit
No new IPC, network, file, or shell surface introduced. Preview is read-only via
disableStdin: trueplustabIndex={-1}/aria-hidden. The preview buffer is a fixed compile-time constant — no untrusted text reaches xterm. Theme composition reads existing user settings only.Notes
metaKeyor path-separator assumptions. The preview uses no PTY and no remote dependency, so it works the same in SSH-runtime sessions.