Commit Graph
1250 Commits
Author SHA1 Message Date
Brennan BensonandOrca 0ea27dfdd2 test(e2e): drive real composer flow for worktree creation (#1193)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 17:42:03 -07:00
github-actions[bot] 8f97e0009e release: v1.3.22-rc.6 v1.3.22-rc.6 2026-04-27 23:54:23 +00:00
Neil 1839a50c92 fix(new-workspace): unwrap getBaseRefDefault envelope in StartFromField (#1189) 2026-04-27 16:53:45 -07:00
Brennan BensonandOrca 3fe7162634 fix(sidebar): don't bump lastActivityAt when worktree is activated by click (#1169)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 15:53:52 -07:00
github-actions[bot] 821c9639fd release: 1.3.22-rc.5 [rc-slot:2026-04-27-15] v1.3.22-rc.5 2026-04-27 22:14:03 +00:00
github-actions[bot] d8beb057c9 release: v1.3.22-rc.4 v1.3.22-rc.4 2026-04-27 21:55:00 +00:00
Brennan BensonandOrca 3edb2f3ea2 fix(updater): surface user-initiated check failures when GitHub is down (#1181)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 14:50:39 -07:00
Kevin Acuña 7f6d42134d fix: resolve letter window shortcuts by layout-aware key (Dvorak/Colemak/AZERTY) (#1015) 2026-04-27 14:09:35 -07:00
github-actions[bot] 4d9816283a release: v1.3.22-rc.3 v1.3.22-rc.3 2026-04-27 21:05:44 +00:00
Brennan BensonandOrca 76139f88a9 fix: support upstream remote as base ref for fork workflows (#1186)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 13:51:19 -07:00
NeilandOrca 4ad1458fad fix(release): derive prerelease state from tag shape, not release flag (#1187)
Two interacting bugs produced v1.3.1-rc.4 today:

1. release-cut.yml used `gh release list --exclude-pre-releases` to
   find latest_stable. That filter checks the GitHub `isPrerelease`
   flag, which had been flipped to `false` on several RC releases
   (including v1.3.22-rc.2 and v1.3.1-rc.3). The query returned an
   RC tag, and bump() then silently mis-parsed `1.3.1-rc.3` via
   `Number("1-rc") = NaN` -> `(NaN||0)+1 = 1`, yielding base=1.3.1.

   Fix: filter by tag shape (no `-rc.`) in jq so an RC tag can never
   be treated as stable, regardless of metadata. Also strip any
   prerelease suffix in bump() and semver_gt() so the numeric math
   is robust even if a caller passes a dirty input.

2. release.yml's final publish step only ran `--draft=false`, leaving
   the `prerelease` flag at whatever electron-builder last wrote. When
   that flag ended up false on an RC, GitHub marked the RC as the
   "latest" release.

   Fix: re-assert `--prerelease=<derived from tag>` alongside
   `--draft=false` so the final state is a function of the tag name,
   not of any intermediate publisher behavior.

Co-authored-by: Orca <help@stably.ai>
2026-04-27 13:49:41 -07:00
github-actions[bot] 6b7d7d7cc0 release: v1.3.1-rc.4 v1.3.1-rc.4 2026-04-27 20:02:27 +00:00
Brennan BensonandOrca dd1ed7c697 fix(cli): record back/forward history for CLI-activated worktrees (#1166)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 12:43:02 -07:00
Jinwoo HongandOrca 75c6f25887 feat(linear): add create-issue button for feature parity with GitHub (#1185)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 12:28:09 -07:00
Jinwoo Hong ba0f263df1 Fix shell-ready zsh wrapper self loops (#1184) 2026-04-27 12:13:45 -07:00
Jinwoo Hong d8d2c0f033 Scope pending PTY spawns to terminal panes (#1182) 2026-04-27 11:55:56 -07:00
Jinwoo HongandOrca ead2368230 fix(tab-bar): dismiss new-tab menu when clicking browser webview (#1183)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 11:45:31 -07:00
JinjingandOrca 6978e6a4aa feat(cmd-j): show Recent Worktrees section + repo/branch composite search (#1180)
- Surface top 3 worktrees under a RECENT WORKTREES header on empty query
  when there are ≥4 worktrees, with a WORKTREES header for the rest
- Support "repo/branch" composite queries in the palette search, with
  highlight ranges on both segments
- Add placeholder hint for the new composite query

Co-authored-by: Orca <help@stably.ai>
2026-04-27 11:32:40 -07:00
github-actions[bot] 8cce94eb5a release: v1.3.1-rc.3 v1.3.1-rc.3 2026-04-27 18:32:06 +00:00
JinjingandOrca 6d2212c068 chore: remove pr-size-labeler workflow and config (#1177)
- chore: remove pr-size-labeler workflow and config

Co-authored-by: Orca <help@stably.ai>
2026-04-27 10:11:21 -07:00
github-actions[bot] a06b85078f release: v1.3.22-rc.2 v1.3.22-rc.2 2026-04-27 17:04:54 +00:00
JinjingandOrca 91b7068ab4 fix(sidebar): Cmd+Shift+Up/Down crosses Pinned/All boundary when collapsed (#1176)
Derive keyboard cycle order from an all-expanded layout so collapsed groups don't cause worktrees to be skipped, and uncollapse the All header on reveal when groupBy is 'none'.

Co-authored-by: Orca <help@stably.ai>
2026-04-27 09:59:29 -07:00
JinjingandOrca abd0ab9f49 fix(monaco): disable semantic validation in diff viewer (#1175)
- fix(monaco): disable semantic validation in diff viewer

Monaco's sandboxed TS worker cannot resolve cross-file imports, cascading
into a long tail of false semantic diagnostics beyond the previously-ignored
codes. Replace the growing ignore list with noSemanticValidation; keep
syntax validation for genuine parse errors.

Co-authored-by: Orca <help@stably.ai>
2026-04-27 09:58:41 -07:00
JinjingandOrca 2c2d970c14 fix(cmd-j-palette): increase selected row contrast (#1174)
- fix(cmd-j-palette): increase selected row contrast

Co-authored-by: Orca <help@stably.ai>
2026-04-27 09:57:08 -07:00
JinjingandOrca cfe247659a opt: disable GitHub attribution by default (#1173)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 09:53:33 -07:00
github-actions[bot] 830b78e0a7 release: 1.3.22-rc.1 [rc-slot:2026-04-27-03] v1.3.22-rc.1 2026-04-27 10:35:49 +00:00
github-actions[bot] 4af21e1793 release: v1.3.22-rc.0 v1.3.22-rc.0 2026-04-27 07:49:33 +00:00
NeilandOrca 5fe4fd4853 fix(pty): honor Windows shell selection on daemon path + shell-specific icons (#1164)
* fix(pty): honor Windows shell selection on the daemon path + shell-specific icons

The "+" menu picker and the Settings → Default Shell preference both set a
shellOverride, but the daemon-backed PTY path never forwarded it. Every
Windows terminal ended up as PowerShell (or cmd.exe via COMSPEC) no matter
what the user picked.

Fix:
- Thread shellOverride through daemon-pty-adapter → createOrAttach RPC →
  terminal-host → pty-subprocess, and actually resolve the correct launch
  args (chcp for CMD, $PROFILE dot-sourcing for PowerShell, /mnt/<drive>
  cwd translation for WSL) on the daemon path.
- Extract the Windows shell-args decision into a shared helper
  (resolveWindowsShellLaunchArgs) so LocalPtyProvider and the daemon
  spawner cannot drift again.
- In ipc/pty.ts, fall back to the persisted terminalWindowsShell setting
  when no per-tab override is sent, so the daemon path honors the user's
  Default Shell preference the same way LocalPtyProvider already did.

UI polish on the "+" dropdown and tab strip:
- Drop the "Default" tag next to the top entry (takes too much space).
- Rename "Command Prompt" → "CMD Prompt" to fit next to the Ctrl+T hint.
- Replace the generic terminal glyph with brand-style icons per shell
  (ShellIcon). Both the "+" menu and the per-tab strip use the same
  icon set so a WSL tab is visually distinct from a PowerShell tab.

Co-authored-by: Orca <help@stably.ai>

* fix(daemon): add oxlint-disable max-lines to daemon-server

CI counts 312 non-blank/non-comment lines for daemon-server.ts after the
shellOverride plumbing was added, exceeding the 300-line .ts override.
The file is a single RPC route table; splitting it would leak the host
reference across modules for no readability win, so add a scoped disable
with the rationale.

Co-authored-by: Orca <help@stably.ai>

* chore(lint): disable max-lines on pre-existing 312-line tabs-hydration test

Unrelated to the shell-selection fix, but surfaced on PR CI: the file is
right at oxlint's 300-line .ts ceiling; each case in the table is a
minimal fixture + assertion, so splitting it across files would scatter
closely related regression coverage for a single reducer.

Co-authored-by: Orca <help@stably.ai>

* chore(lint): disable max-lines on shared text-search module

The shared text-search module exceeds oxlint's 300-line .ts ceiling. It
is the single source of truth for rg arg construction, rg --json parsing,
git-grep submatch parsing, and relative-path normalization shared between
the local main process and the SSH relay. Re-splitting it would
re-introduce the maxBuffer divergence the design doc explicitly calls out.

Also reverts two speculative oxlint-disable directives on daemon-server
and tabs-hydration.test — those files were not the offender; CI's error
message elides the file path but running the lint locally against the
PR-merge commit pinpointed text-search.ts.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-04-27 00:47:15 -07:00
NeilandOrca aac395dbff fix(release-cut): anchor RC base to latest_stable + patch (#1163)
The rc branch previously derived the base version from the highest
git tag, which reopened an already-shipped series: after v1.3.21
stable was published, cutting an RC still produced v1.3.21-rc.N.
Anchor RCs to latest_stable + patch so a fresh RC after v1.3.21
correctly resolves to v1.3.22-rc.0. Minor/major RCs are cut by
running that stable kind first, which is a clearer workflow than
the old heuristic.

Co-authored-by: Orca <help@stably.ai>
2026-04-27 00:09:27 -07:00
Brennan BensonandOrca 8347b3099d test(opencode): add buildPtyEnv/clearPty round-trip coverage (#1162)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 00:07:19 -07:00
JinjingandOrca 099c4089a7 refactor(search): share rg/git-grep logic between main and relay (#1157)
Extracts rg and git-grep search logic into src/shared/text-search.ts so
the local main and SSH relay paths stop reinventing arg construction,
JSON parsing, submatch regex, and accumulator/truncation semantics.

Fixes silent truncation in the relay: searchWithRg used execFile with
a 50MB maxBuffer cap that rg --json easily exceeds on large repos,
dropping matches with no error surfaced to the user. The relay now
streams via spawn, matching the local path.

See docs/design/share-text-search.md for full rationale.

Co-authored-by: Orca <help@stably.ai>
2026-04-27 00:06:00 -07:00
JinjingandOrca afeac870b2 fix(search): keep include/exclude visible and prevent buried matches (#1161)
- Remove queryDetailsExpanded toggle; always show scope filters in the
  Search tab since it is a secondary destination used specifically for
  scoped search.
- Left-truncate pre-match text in MatchResultRow so the highlight stays
  visible at narrow sidebar widths instead of being pushed off the right
  edge. Mirrors VS Code's lcut behavior.

Co-authored-by: Orca <help@stably.ai>
2026-04-27 00:01:34 -07:00
Jinwoo HongandOrca 9e37f6c3c7 fix(daemon): checkpoint-based history persistence to reduce memory (#1139)
Co-authored-by: Orca <help@stably.ai>
2026-04-26 23:44:08 -07:00
github-actions[bot] 6c1b6f6f3c release: v1.3.21-rc.6 v1.3.21-rc.6 2026-04-27 06:24:28 +00:00
NeilandOrca 3a1d506e93 fix(tab-bar): inline Windows shell choices in the "+" menu (#1159)
Replace the flaky Radix submenu on the "+" New-tab dropdown with a flat
list of all available shells (PowerShell, Command Prompt, WSL when
present). The configured default shell is pinned to the top, labeled
"Default", and carries the Ctrl+T shortcut hint so users can see at a
glance which shell Ctrl+T will open.

Co-authored-by: Orca <help@stably.ai>
2026-04-26 23:23:42 -07:00
Brennan BensonandOrca c4b054fa39 fix(pty): tolerate trailing slash in ZDOTDIR self-loop guard (#1156)
Co-authored-by: Orca <help@stably.ai>
2026-04-26 23:02:48 -07:00
Brennan BensonandOrca ec907534fb fix(opencode): accept daemon-shaped sessionIds, hash to safe dir name (#1155)
Co-authored-by: Orca <help@stably.ai>
2026-04-26 22:49:08 -07:00
github-actions[bot] 07fb8ccc7c release: v1.3.21-rc.5 v1.3.21-rc.5 2026-04-27 05:45:36 +00:00
NeilandNeil Parker 98ba693481 feat(terminal): WSL shell support for Windows users (#1154)
Co-authored-by: Neil Parker <nwparker@anthropic.com>
2026-04-26 22:43:54 -07:00
3653b55f38 feat(language-detect): add Svelte Monarch grammar for .svelte syntax highlighting (#1150)
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-04-26 22:31:17 -07:00
cfc444242c fix(win32): resolve EPERM on userData writes, batch-file spawn failures, and native dep rebuild (#1152)
* chore: update .gitignore to include stackdump and .serena, enhance pre-commit script

* fix(win32): resolve EPERM on userData writes and batch-file spawn failures

Three Windows-specific issues prevented Orca from running correctly on
machines where Chromium resets the userData DACL during startup:

1. **EPERM on userData writes** — Chromium's BrowserWindow constructor calls
   SetNamedSecurityInfo on the userData folder with a Protected DACL.  When
   propagated to child directories the ACEs carry the Inherit-Only flag,
   meaning they apply to children-of-children but NOT to the directories
   themselves.  Any file write inside codex-runtime-home, agent-hooks, or
   similar subdirectories fails with EPERM.

   Fix: grant an explicit Full Control ACE (OI)(CI)(F) on userData and all
   existing children before BrowserWindow is created (icacls /T /C).
   Explicit ACEs survive future DACL propagation from the parent.  Per-write
   EPERM retries in fs-utils and installer-utils serve as the backstop for
   directories created after startup.

2. **Batch-file spawn failures** — resolveCodexCommand() can return a .cmd
   or .bat path (e.g. codex.cmd installed via npm).  Node's spawn() cannot
   execute batch scripts directly without shell:true, but shell:true with an
   args array triggers DEP0190 because args are concatenated rather than
   escaped.  Both service.ts and codex-fetcher.ts were affected.

   Fix: detect .cmd/.bat paths and route through cmd.exe /c explicitly,
   which is equivalent to what shell:true does internally but avoids the
   deprecation warning and arg-escaping hazard.

3. **Native dep rebuild failure** — electron-builder install-app-deps does
   not expose the ignoreModules option.  On Windows dev machines without the
   full VC++ / Python toolchain, cpu-features (an optional dep of ssh2) fails
   to build with node-gyp, aborting the entire postinstall step.

   Fix: replace electron-builder install-app-deps with a thin wrapper script
   (scripts/rebuild-native-deps.mjs) that calls @electron/rebuild's JS API
   directly with ignoreModules: ['cpu-features'] on Windows.  ssh2 detects
   the missing native module and falls back to pure-JS automatically.

Refactoring: extract shared win32-utils.ts with getIcaclsExePath(),
getCmdExePath(), isWindowsBatchScript(), isPermissionError(), grantDirAcl(),
and getSpawnArgsForWindows() to eliminate five instances of duplicated
SystemRoot path construction and two near-identical EPERM retry blocks.
Reduce startup icacls calls from three sequential blocking /T invocations
to one, removing up to 20 s of potential startup delay.

* fix(win32): address review feedback on ACL and spawn helpers

- Fall back to SID via `whoami /user` when `USERNAME` is unset so
  `grantDirAcl` works under services, CI, and hardened envs instead of
  silently no-op'ing.
- Use a 60s timeout for recursive `icacls /T` walks; the 10s cap could
  starve on large userData trees and silently fail the startup grant.
- Pass `windowsHide: true` to `icacls` and the cmd.exe-routed Codex
  spawns so no console window flashes in the packaged GUI app.
- Add `/d` to `cmd.exe /c` invocations to disable AutoRun registry
  commands — safer default for background spawns.
- Drop unused `createRequire`/`require` from rebuild-native-deps.mjs.
- Add `@electron/rebuild` as an explicit devDependency; relying on the
  electron-builder transitive was brittle under pnpm.
- Fix two misleading "Re-enable inheritance" comments that describe
  behavior opposite to what the code actually does (explicit ACL grant).
- Add unit tests for `isWindowsBatchScript`, `getSpawnArgsForWindows`,
  and `isPermissionError` to lock in Windows batch detection + cmd.exe
  routing.

Co-authored-by: Orca <help@stably.ai>

* fix(win32): unify PTY spawn through /d and document cmd.exe safety

- fetchViaPty now uses getCmdExePath() and /d /c, matching the rest of
  the codebase instead of hand-rolling 'cmd.exe' + ['/c', ...].
- getSpawnArgsForWindows gains a SAFETY note: when the .cmd/.bat branch
  is taken, cmd.exe re-parses the combined command line, so callers
  must only pass trusted/literal args.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-04-26 22:29:58 -07:00
github-actions[bot] 3b82e5a3cb release: v1.3.21 v1.3.21 2026-04-27 05:07:47 +00:00
Brennan Benson 0123b57e53 fix(pty): inject full host env on daemon path (#1148) 2026-04-26 22:05:42 -07:00
github-actions[bot] 09e9c2f8eb release: v1.3.21-rc.4 v1.3.21-rc.4 2026-04-27 04:47:05 +00:00
NeilandOrca 193c6ecab3 fix(linear): defer keychain decrypt, cache viewer, add Test connection (#1151)
Co-authored-by: Orca <help@stably.ai>
2026-04-26 21:45:38 -07:00
Brennan Benson 8b87acd72d feat(memory): add Memory & CPU status-bar popover (#1146) 2026-04-26 21:42:06 -07:00
github-actions[bot] 39d8a013f3 release: v1.3.21-rc.3 v1.3.21-rc.3 2026-04-27 03:52:04 +00:00
NeilandOrca 32c1651732 fix(terminal): stop scroll drift during resize (#1149)
* fix(terminal): stop terminal scroll from drifting upward during resize

xterm's terminal.resize() natively preserves viewportY across reflows (see
scroll-reflow.test.ts "reference: undisturbed"). The capture/restore dance
in the default safeFit path was fighting that — findLineByContent's 40-char
prefix match collides across duplicate scrollback lines (prompts, bullets,
borders) in long Claude sessions, so the viewport anchored to a slightly
wrong line each frame and the error compounded upward.

- Remove capture/restore from the default safeFit and fitAllPanesInternal
  paths. Just fitAddon.fit(), matching how Superset and VSCode handle this.
- Keep pendingSplitScrollState and pendingDragScrollState branches intact —
  split reparenting and divider drags still need those.
- Sidebar open/close is an instantaneous width change, so dispatch
  SYNC_FIT_PANES_EVENT from a useLayoutEffect and let the terminal fit
  synchronously pre-paint, eliminating the one-frame "old cols, new
  container width" flash users saw when fits were deferred to a
  ResizeObserver rAF.

Co-authored-by: Orca <help@stably.ai>

* fix(terminal): remove scroll-restore delay from in-pane divider drag

The divider drag was still using lockDragScroll/unlockDragScroll, which set
pendingDragScrollState on each affected pane so safeFit would
captureScrollState → fit → findLineByContent → scrollToLine on every frame.
findLineByContent scans the whole scrollback for a 40-char prefix match —
expensive on long Claude sessions and the cause of the drag feeling laggy.

Now that the previous commit established xterm's native viewportY
preservation is enough (see scroll-reflow.test.ts "reference: undisturbed"),
the divider doesn't need the snapshot either. Drop the lock calls from
onPointerDown/onPointerUp and the double-click equalize path; the default
safeFit path (plain fitAddon.fit()) handles it.

lockDragScroll / unlockDragScroll remain in the codebase for now — removing
them is a wider interface cleanup through pane-tree-ops and pane-drag-reorder
that can land separately.

Co-authored-by: Orca <help@stably.ai>

* refactor(pane-manager): drop unused drag-scroll lock machinery

The previous two commits showed xterm preserves viewportY natively across
resize, so safeFit's default path is just fit() and the divider no longer
sets pendingDragScrollState. That leaves a chain of dead plumbing:

- pane-drag-scroll.ts (lockDragScroll / unlockDragScroll) — nothing invokes
  these anymore. Delete the file.
- ManagedPaneInternal.pendingDragScrollState — no writers, no readers. Drop
  the field and the "null" initializer in createPaneDOM and test fixtures.
- pendingDragScrollState branches in safeFit and fitAllPanesInternal —
  unreachable. Remove them. fitAllPanesInternal collapses to a simple loop
  of safeFit calls.
- lockDragScroll / unlockDragScroll callback entries in TreeOpsCallbacks,
  DragReorderCallbacks, DividerCallbacks, and PaneManager's callback
  builders — drop.
- pane-lifecycle.ts WebGL context-loss handler: the three-branch
  pendingSplit / pendingDrag / default capture-restore dance collapses to a
  plain fit() + refresh(), relying on xterm's native viewportY preservation
  and scheduleSplitScrollRestore for the split-reparent case.
- restoreScrollState import in pane-lifecycle.ts — now unused.
- Stale "Why: we previously locked..." comment block in pane-divider —
  remove; the new code is self-explanatory and the motivation lives in the
  pane-tree-ops safeFit comment.

pendingSplitScrollState stays: it's still load-bearing in
pane-split-scroll.ts where it gates the early double-rAF restore during
wrapInSplit DOM reparenting.

Net: -123 lines. No behavior change — verified by the full renderer test
suite (1116/1116).

Co-authored-by: Orca <help@stably.ai>

* docs: tighten sidebar-toggle comment in App.tsx

The comment referenced 'restoring its scroll' but after the drag-scroll
lock cleanup the fit no longer restores anything — xterm preserves
viewportY natively. Reword to match actual behavior.

Co-authored-by: Orca <help@stably.ai>

* fix(terminal): preserve scroll when exiting expanded pane mode

Cmd+Enter enter/exit expanded mode (toggleExpandPane) runs refreshPaneSizes,
which still had the same content-hash capture/restore dance that the rest
of the resize paths were cleared of: captureScrollState → fit →
findLineByContent → scrollToLine. On long Claude sessions the 40-char
prefix match collides with duplicate scrollback lines (prompts, bullets,
borders) and jumps to the wrong line, leaving the viewport scrolled up
after exiting expanded mode.

xterm preserves viewportY natively across resize (see scroll-reflow.test.ts
"reference: undisturbed"), so a bare fit() is enough — matching every other
resize path in this codebase.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-04-26 20:51:00 -07:00
Jinwoo HongandOrca f11aacc795 feat(ssh): add port forwarding for remote workspaces (#1145)
Co-authored-by: Orca <help@stably.ai>
2026-04-26 19:21:26 -07:00
f5ad7aa249 feat(settings): import Ghostty config with preview, color overrides, opacity and blur (#1001)
* feat(settings): add Ghostty config import

Add safe one-shot Ghostty import with preview and success summary,
and probe documented Ghostty config paths before applying changes.

Refs #958

* chore(git): ignore atl artifacts

* feat(settings): map font-weight, cursor-blink and focus-follows-mouse from Ghostty

Adds three safe direct-mapping Ghostty keys that have clear equivalents
in GlobalSettings: font-weight, cursor-style-blink, and focus-follows-mouse.

Refs #958

* feat(settings): expand Ghostty import to support colors, opacity and option-as-alt

- Add TerminalColorOverrides type grouping 21 optional xterm ITheme fields
- Add terminalBackgroundOpacity, terminalPanePaddingColor, terminalPaddingBalance
  to GlobalSettings
- Extend parser to collect repeated keys as string[] (needed for palette lines)
- Map background-opacity, background, foreground, cursor-color,
  selection-background/foreground, palette (0-15), window-padding-color,
  window-padding-balance, macos-option-as-alt in mapper
- Merge terminalColorOverrides into xterm ITheme at theme resolution; apply
  opacity as rgba() with allowTransparency enabled
- Accept hex colors with or without leading # (Ghostty omits it)
- Fix preview diff to use deep equality for object values so already-applied
  color overrides no longer reappear on next import

* feat(settings): support background-blur-radius and window-padding-color extend in Ghostty import

- Map background-blur-radius > 0 to windowBackgroundBlur: true; apply
  vibrancy on macOS and backgroundMaterial acrylic on Windows at window
  creation (blur requires restart — no hot-reload IPC exists)
- Accept window-padding-color = extend/background as valid Ghostty values;
  both map to default Orca padding behavior (undefined field) instead of
  landing in unsupportedKeys
- Split mapper.test.ts into domain-scoped describes to stay under 300-line limit

* feat(settings): add Window section to Terminal settings panel

Expose terminalBackgroundOpacity, windowBackgroundBlur, terminalPaddingBalance,
terminalPanePaddingColor, and terminalColorOverrides in the settings UI so
imported Ghostty values can be viewed and changed manually.

- New TerminalWindowSection component (extracted from TerminalPane to stay
  under the 400-line limit)
- Collapsible color overrides sub-section with ColorField for all 21 xterm
  ITheme fields grouped as base, ANSI normal, and ANSI bright
- Reset button clears all color overrides at once
- Window blur toggle shows restart-required note (blur applies at window
  creation, no hot-reload IPC exists)
- Search entries added for all new controls

* feat(settings): expand Ghostty import with scrollback, padding, divider, cursor and word-chars keys

Map 9 additional Ghostty keys to Orca settings:

- split-divider-color → terminalDividerColorDark + terminalDividerColorLight
  (single value applies to both; Ghostty has no dark/light distinction)
- unfocused-split-opacity → terminalInactivePaneOpacity (direct float 0-1)
- scrollback-limit → terminalScrollbackLimit; applied to xterm scrollback option
- window-padding-x / window-padding-y → terminalPaddingX/Y; applied as CSS
  vars --pane-padding-x / --pane-padding-y in terminal.css
- cursor-text → terminalColorOverrides.cursorAccent (xterm ITheme field)
- bold-color → terminalColorOverrides.bold (persisted; xterm ITheme has no
  bold field yet — stored for future xterm upgrade)
- cursor-opacity → terminalCursorOpacity; blended into cursor rgba at theme
  resolution time
- selection-word-chars → terminalWordSeparator; applied to xterm wordSeparator
- mouse-hide-while-typing → terminalMouseHideWhileTyping field added; renderer
  application deferred (needs per-pane disposable + global mousemove listener)

* feat(settings): expose new Ghostty-imported settings in Terminal Settings UI

- Window section: scrollback limit, horizontal/vertical padding, hide mouse
  while typing toggle, cursor text and bold color in Color Overrides
- Cursor section: cursor opacity NumberField
- Advanced section: word separators text input
- Search entries added for all new controls
- terminalDividerColorDark/Light and terminalInactivePaneOpacity skipped —
  already present in Theme and Pane Styling sections respectively

* feat(terminal): implement mouse-hide-while-typing per pane

Register terminal.onData → cursor:none and mousemove → restore, scoped to
the pane container element. Uses the existing IDisposable per-pane pattern
(same as selectionDisposablesRef). Cleans up on pane close and effect teardown.

* refactor(settings): address ghostty import code review findings

- Centralize GhosttyImportPreview type in shared/types (remove duplicate from mapper)
- Fix parser to strip inline comments without breaking hex color values (#1a1a1a)
- Extract HEX_COLOR_RE to shared/color-validation to avoid duplication
- Remove redundant Number.isNaN checks after Number.isFinite (4 sites)
- Replace unsafe catch-all assignment with explicit font-family branch
- Migrate 280-line if-chain in mapGhosttyToOrca to FIELD_PARSERS registry
- Add human-readable setting labels in GhosttyImportModal via setting-labels map
- Add clarifying comment in index.ts re JSON.stringify undefined behavior

* fix(settings): harden ghostty import from judgment-day review

- Surface readFile errors in GhosttyImportPreview.error instead of
  showing misleading 'No config found' on permission denied
- Guard handleApply against double-apply when already applied
- Strip surrounding quotes from parsed config values (font-family)
- Return null from palette handler when all entries fail validation
- Inform user when background-blur-radius radius is not preserved
- Add valuesEqual key-order stability via stableStringify
- Normalize hex colors to #-prefixed format across all color mappers
- Reject blank values before numeric parsing (Number('') === 0 trap)
- Remove selection-word-chars mapping (inverted xterm semantics)
- Guard window-padding-x/y against negative integers
- Reactive mouse-hide-while-typing on existing panes when setting toggles
- Merge terminalColorOverrides on import instead of replacing

* fix(ghostty): drop broken imports, tighten parsing, prompt restart for blur

Review found three high-impact issues in the Ghostty import: scrollback-limit
semantics are inverted/rescaled (Ghostty is bytes with 0=unlimited, xterm is
rows with 0=disabled), and window-padding-color + window-padding-balance set
CSS custom properties (--pane-padding-color, --pane-padding-balance) that
have no consuming rule anywhere in the tree — so users confirming "changes"
to those keys would see nothing happen.

Because none of the three keys have a safe mapping today, drop them from the
import and remove the dead UI controls + GlobalSettings fields + CSS var
plumbing. The mapper now lists them as unsupportedKeys alongside the
existing window-decoration / keybind / custom-shader entries.

Other fixes in the same review:

- allowTransparency now clears when background-opacity returns to 1 (prior
  code only ever set it to true, leaving a stale flag with measurable render
  cost).
- background-blur-radius = 0 no longer emits a misleading "radius value not
  preserved" note (0 cleanly maps to blur=false with no radius to lose).
- Add a 1 MB size cap on the config read so a pathological or symlinked file
  cannot OOM the main process.
- Make handleApply async and surface IPC errors inline in the modal instead
  of flipping straight to "Import complete" on failure.
- Type settings.previewGhosttyImport as Promise<GhosttyImportPreview> in
  preload so shape drift is caught at compile time.
- Make stableStringify recursive so future nested settings round-trip
  cleanly through valuesEqual.
- Restrict window-padding-x/y and background-blur-radius to decimal ints;
  prior code accepted exponent notation (1e10 sails through Number.isInteger)
  and would have landed absurd values in the store.
- Window blur now shows a "Restart required" banner with a Restart now
  button when the setting differs from the mount-time snapshot, mirroring
  the ExperimentalPane daemon pattern. Blur only applies at BrowserWindow
  creation on macOS/Windows.

Co-authored-by: Orca <help@stably.ai>

* feat(settings): move Ghostty import trigger to Terminal section header

Per review feedback: the "Import from Ghostty" row was taking its own slot
in the Terminal settings list alongside real configuration sections. Move
the trigger into the Terminal section's header (upper-right corner) as a
headerAction, next to the Terminal heading — it's a one-shot action, not a
setting.

- SettingsSection gains an optional `headerAction` slot rendered to the
  right of the section title/description.
- The useGhosttyImport hook is lifted from TerminalPane into Settings.tsx
  so the section header button (owned by Settings.tsx) and the modal
  (still rendered inside TerminalPane) share one state instance.
- TerminalPane drops its own "Import" section + the TERMINAL_GHOSTTY_IMPORT
  search entry group is no longer referenced there.
- Button carries the official Ghostty mark as a 16x16 icon so it reads
  clearly as a cross-app import even before users parse the label.

useGhosttyImport now accepts `GlobalSettings | null` so the parent can call
it above the pre-load spinner guard without violating hook ordering; the
apply path no-ops until settings arrive. Related test file updated to
pass the new `ghostty` prop and to assert the trigger is *not* rendered
inside TerminalPane anymore.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-04-26 17:00:46 -07:00