* fix(sidebar): indent worktree rows under their project header
Top-level worktree rows had no left padding, so their status dots sat
flush with the project folder icon. Indent each worktree group by one
LINEAGE_INDENT step so the dots nest under the folder icon, giving clear
parent-to-child visual hierarchy. Nested/lineage cards inherit the base
inset and keep their relative indentation.
* Avoid ungrouped workspace indentation
* review: cover grouped sidebar indentation
- reuse the WorktreeList render helper in the lineage test
- add the grouped project assertion that fails if the indent is reverted
- mock DropdownMenuSeparator for grouped project rendering
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
- feat(settings): restore divider + inactive pane stub in preview
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.
- feat(settings): drop cursor preview; add dark/light toggle to typography 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.
- fix(settings): defer cursor preview focus by one frame
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.
- fix(settings): blink cursor preview as soon as Settings opens
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.
- fix(settings): focus preview on cursor changes so blink demonstrates
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.
- fix(settings): fit preview at default font, mirror cursor inactive style, 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.
- fix(settings): preview lifecycle and font hover preview
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.
- fix(settings): preserve Ghostty context and honor opacity in preview
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.
- feat(settings): show live preview in Typography and Cursor sections
- fix(settings): make preview read-only via xterm and track theme bg
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.
- fix(settings): suppress false-positive exhaustive-deps warning
- feat(settings): add TerminalSettingsPreview component
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.
- fix(terminal): tighten composeActiveTerminalTheme docs + cover zero opacity
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.
- refactor(terminal): extract composeActiveTerminalTheme helper
- fix(settings): split def/render syntax colors and harden marker test
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.
- feat(settings): add terminal preview content module
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
* fix(shell-ready): honor ZDOTDIR without breaking zsh scoping
Fixes#1866
This reimplements PR #1737 (reverted in #1864) with a safer approach
that preserves normal zsh startup semantics.
**Core fix**: Discover ZDOTDIR by sourcing user ~/.zshenv in a subshell
instead of inside a wrapper function. This preserves top-level zsh
scoping for common patterns like `typeset -U path` that broke in the
original implementation.
**Shell safety improvements**:
- Use `printf '%s\n'` instead of `echo` for capturing ZDOTDIR (handles
special characters in paths safely)
- Subshell isolates early returns and side effects from wrapper
**Code quality**:
- Extract duplicated zsh wrapper template to `src/main/shell-templates.ts`
- Both local-pty and daemon paths now share identical wrapper logic
**Test coverage**:
- Add live zsh subprocess tests that spawn real zsh to verify:
- XDG ZDOTDIR discovery works
- `typeset -U path` in .zshrc preserves top-level scoping
- Early returns in .zshenv don't crash the wrapper
- Vanilla (non-XDG) configs fall back to HOME correctly
- Template structure tests validate subshell discovery logic
Before (broken):
```zsh
__orca_source_user_zshenv() {
source "$HOME/.zshenv" # typeset becomes function-scoped
}
```
After (fixed):
```zsh
_orca_discovered_zdotdir=$(
unset ZDOTDIR
[[ -f "$HOME/.zshenv" ]] && source "$HOME/.zshenv" 2>/dev/null
printf '%s\n' "${ZDOTDIR}"
)
export ORCA_ORIG_ZDOTDIR="${_orca_discovered_zdotdir:-${_orca_spawn_orig_zdotdir:-$HOME}}"
```
The subshell sources .zshenv at top-level (preserving normal scoping),
captures only the ZDOTDIR value, then exits. User rcfiles (.zshrc, etc.)
are still sourced at the wrapper's top level, so all scoping works normally.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* Add shell-script-literal test framework and improvements
Adds a new declarative test framework for shell-ready tests that uses
literal shell scripts (copy-pastable into terminals) with inline snapshots.
Framework features:
- Shell scripts as string literals with # Run: marker to split setup/test
- Direct script execution (no brittle parsing) via temp files
- Path normalization for reproducible snapshots (<HOME>, <WRAPPER_DIR>)
- Auto-detects shell from command, supports bash/zsh/sh
- Inline snapshot testing with vitest toMatchInlineSnapshot()
Code quality improvements:
- Extract escapeRegex to shared string-utils.ts (deduplicates 2 copies)
- Refactor shell-templates.ts for readability (condense comments, add structure)
- Pre-compile regex patterns to avoid hot-path allocation
- Fix path normalization to sort by length (prevent nested path corruption)
- Fix actualUserHome handling to skip empty values
All tests passing (64/64 shell-ready tests, 55/55 affected tests).
Files added:
- src/main/providers/__tests__/shell-ready-framework/shell-script-test.ts
- src/main/providers/__tests__/shell-ready-framework/README.md
- src/main/providers/__tests__/shell-ready-framework-example.test.ts
- src/shared/string-utils.ts
Files modified:
- src/main/shell-templates.ts (readability cleanup)
- src/main/codex/config-toml-trust.ts (use shared escapeRegex)
- src/main/daemon/shell-ready.test.ts (updated for new framework)
- src/main/providers/local-pty-shell-ready.test.ts (updated for new framework)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix(shell-ready): preserve zshenv semantics
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Original commits:
- Add multi-select copy paths in explorer
- Avoid explorer selection scans on hot paths
- feat: add multi-select support to explorer file tree
- fixup: resolve post-rebase cleanup after rebasing onto nwparker branch
- fix: preserve selected set and implement deterministic order delete
- Merge branch 'main' into feat/file-explorer-multi-select
- Merge branch 'main' into feat/file-explorer-multi-select
- fix: remove duplicate variable
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
* Add Command Code as a supported CLI agent
Register Command Code (https://commandcode.ai) across the agent
catalog, launch config, telemetry kind, agent-status icon record, and
settings search. Detection uses the full `command-code` binary rather
than the shorter `cmd` alias that `npm i -g command-code` also
installs — agent-process-recognition normalizes process names by
stripping `.exe`/`.cmd` extensions, so using `cmd` would collide with
Windows' built-in `cmd.exe` shell.
Closes#2083
* Complete Command Code agent registration
---------
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Adds OMP as a first-class TUI agent and keeps Pi/OMP overlay state isolated across local and relay PTY paths.\n\nValidated locally with focused Vitest coverage, typecheck, lint, git diff --check, and Electron manual verification using installed omp (omp/15.3.2).