* fix: address review findings
* fix: improve error handling in terminal e2e helper JSON parsing
* fix: remove unused catch parameter in terminal e2e helper
* fix: use nullish coalescing assignment for LANG default in PTY spawn
The object spread approach ({LANG: default, ...process.env}) produced
undefined on CI (Ubuntu/Node 22) despite working locally. Switch to
explicit ??= assignment after the spread for reliable cross-platform
behavior.
Move build resources to resources/build/, icon source files to
resources/icon-source/, scripts to config/scripts/, and patches
to config/patches/ for a cleaner top-level directory layout.
- Make the "+" new tab button a compact rounded icon button instead of
a full-height rectangle
- Increase titlebar height from 38px to 42px for better proportions
- Remove font-medium from active tabs to prevent layout shift when
switching between tabs
* chore: clean up repo root for faster README visibility
- Delete unused images (debug_orca.png, orca_3d.jpg, screenshot.png)
- Delete stale design docs from docs/
- Move tsconfig sub-configs, electron-builder config, and vitest config to config/
- Move file-drag.gif to docs/assets/ and design doc to docs/
- Update all path references in package.json, tsconfig.json, and moved configs
* fix: remove stale worktree dialog callback dependency
Adds a platform-aware context menu option to reveal files/folders in the
system file manager (Finder on macOS, File Explorer on Windows, Files on Linux).
- Move xterm FitAddon refresh() outside the resize conditional so
sub-cell-width container shrinks don't leave blank content
- Defer startup notification to window 'show' event so the macOS
permission dialog appears after the window is visible
- Add click handler on startup notification to open System Preferences
- Prevent GC of startup notification via activeNotifications set
- Fix lint-staged config: remove yaml/html/md from oxfmt pattern
since oxfmt only supports json and css beyond JS/TS
* feat: run setup script in split pane so main terminal stays interactive (#381)
Instead of running the worktree setup script in the main terminal pane
(blocking user interaction), queue a setup-split signal that TerminalPane
consumes on mount — creating the initial pane clean, then splitting right
and injecting the command into the new pane's PTY.
* fix: address review findings
* fix(terminal): run context menu Close Pane through request-close lifecycle
* feat: run setup script in split pane so main terminal stays interactive (#381)
Instead of running the worktree setup script in the main terminal pane
(blocking user interaction), queue a setup-split signal that TerminalPane
consumes on mount — creating the initial pane clean, then splitting right
and injecting the command into the new pane's PTY.
* fix: address review findings
* fix: address review findings
* fix: mock process.platform as darwin in system-denied test
The test exercises the macOS notification permission gate, which only
checks systemPreferences.getNotificationSettings on darwin. Without
mocking the platform, Linux CI takes the non-darwin path and the
assertion fails.
* chore: remove stale test files
* refactor: remove macOS notification permission checking
The systemPreferences.getNotificationSettings() check added friction
without meaningful benefit — macOS handles permission prompting natively
when a notification is posted, making the manual check redundant.
Removes getPermissionStatus(), the system-denied dispatch reason,
the IPC handler, the renderer permission-blocked banner, and all
related tests.
- Fix ZoomOverlay to fully unmount after fade-out so the fixed overlay
doesn't linger and interfere with Radix portal layering, click-outside
detection, or focus management (fixes broken file switching)
- Lower z-index from 9999 to z-50 to match app conventions
- Fix editor zoom percent to use actual base font size from settings
and computeEditorFontSize for clamping instead of hardcoded 14
- Extract useTerminalFontZoom to own file to keep keyboard-handlers
under the 300-line lint limit
* fix: address review findings
* fix: mock process.platform as darwin in system-denied test
The test exercises the macOS notification permission gate, which only
checks systemPreferences.getNotificationSettings on darwin. Without
mocking the platform, Linux CI takes the non-darwin path and the
assertion fails.
* chore: remove stale test files
Patch @xterm/addon-fit to refresh the full viewport and restore scroll
position after reflow, batch ResizeObserver through requestAnimationFrame
to coalesce rapid-fire reflows, and recover terminal rendering after
WebGL context loss.
* fix: address review findings
- Add try/catch to debounced serialization timer callback for consistency
with flushPendingSerialization (prevents crash if editor is destroyed
during the 300ms debounce window)
- Use TextEncoder.encodeInto() with pre-allocated buffer instead of
encode() to avoid allocating a new Uint8Array on every render
- Extract keyboard handler to rich-markdown-key-handler.ts to stay under
the 400-line file limit
* feat: improve markdown file typing speed
- Debounce ProseMirror getMarkdown() serialization (300ms) to eliminate
the dominant typing-speed bottleneck on large markdown files
- Add 300KB size limit that falls back to Monaco source mode for files
too large for ProseMirror's in-memory document tree
- Expose flushPendingEditorChange() bridge so autosave, quiesce, and
restart flows capture debounced edits without data loss
- Signal dirty state immediately via onDirtyStateHint so close prompts
and beforeunload guards don't miss edits during the debounce window
- Always render through TabGroupSplitLayout (even single groups) to prevent TerminalPane unmounts that kill PTY processes
- CSS Grid flat rendering keeps TabGroupPanels as stable keyed siblings across layout changes
- Use effectiveVisibleRef in pty-connection so split-but-visible terminals receive PTY output
- Eagerly initialize layout in createUnifiedTab before first split
- Keep layout leaf when collapsing to single group (prevent unmount)
- Prevent Monaco model disposal with keepCurrentModel so split editors share models safely
- Keep titlebar spacer visible in split mode for right sidebar toggle positioning
When switching between worktrees, the active terminal tab was reset to
the first tab because activeTabId was global. This adds a per-worktree
map (activeTabIdByWorktree) mirroring the existing pattern used for
editor files, so users return to the same terminal tab they left.
* WIP: Changes before auto-review fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add custom title support for terminal panes
Users can set a custom title on individual terminal panes via the
right-click context menu ("Set Title...") or by clicking an existing
title. Titles display as a minimal floating label at the top of each
pane with inline editing (Enter to save, Escape to cancel). An X
button on hover removes the title.
Titles persist across restarts via titlesByLeafId in
TerminalLayoutSnapshot, piggybacking on the existing session:set flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address review findings for pane title feature
- Guard rename input against double-submit on Enter/Escape + blur race
- Memoize persistLayoutSnapshot with useCallback to prevent keyboard
listener re-registration on every render
- Eagerly update paneTitlesRef in onPaneClosed so persistLayoutSnapshot
sees correct data before React flushes state
- Document portal rendering's implicit dependency on paneTitles state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: write pane titles in shutdown snapshot even when buffers are empty
captureBuffers had an early return when no terminal scrollback content
existed, which skipped writing titlesByLeafId to the layout snapshot.
This caused titles to be lost on restart for fresh or cleared panes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove design doc from PR
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add consistent gaps between worktree cards
Use TanStack Virtual's built-in `gap` option (8px) instead of padding
on the wrapper div. The virtualizer accounts for the gap directly in
its position calculations, guaranteeing consistent spacing between
cards regardless of estimation/measurement mismatches.
* test: update estimate tests for virtualizer gap change
* feat: auto-reveal active file in explorer on tab switch
Adds useFileExplorerAutoReveal hook that scrolls the file explorer to
show the active editor file when switching tabs, mirroring VS Code's
explorer.autoReveal behavior. Files already visible in the tree get
scrolled to directly; files with collapsed ancestors trigger the reveal
machinery with flash disabled to avoid visual noise.
* refactor: extract file explorer handlers to fix max-lines lint rule
Move handleClick, handleDoubleClick, and handleWheelCapture into a
dedicated useFileExplorerHandlers hook to bring FileExplorer.tsx under
the 400-line limit.
* feat: add unified TabsSlice and tab bar UX improvements
Introduce content-agnostic Tab/TabGroup model (Step 1 of unified tabs
migration). TabsSlice coexists alongside legacy TerminalSlice/EditorSlice
and provides unified CRUD, neighbor selection, preview replacement, pin
support, and session hydration from both legacy and unified formats.
Also fixes several tab bar UX issues:
- Tab bar always visible (even with 1 tab)
- New terminal tabs appear at end of tab bar, not index 0
- Closing last terminal with editor files open switches to editor view
instead of deactivating the worktree
- New terminal tabs receive focus immediately
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract tabs-helpers, tabs-hydration, and reconcile-order modules
Split tabs.ts (503 countable lines) and useTerminalTabs.ts (325 countable
lines) to stay under the max-lines lint limit without eslint-disable:
- tabs-helpers.ts: pure lookup/update functions + generic patchTab helper
- tabs-hydration.ts: session hydration for unified and legacy formats
- reconcile-order.ts: shared tab order reconciliation (was duplicated in
TabBar.tsx, useTerminalTabs unifiedTabs memo, handleNewTab, and
handleCloseTabsToRight)
Includes tests for all three extracted modules (29 new tests).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: move tab bar into the titlebar row
Render the TabBar inside the titlebar via a React portal, sharing the
same row as the "Orca" title. The left section of the titlebar matches
the sidebar width so tabs start exactly where the sidebar ends.
This saves ~36px of vertical space by eliminating the separate tab bar
row. Empty space after tabs remains window-draggable; individual tabs
and the "+" button opt out with -webkit-app-region: no-drag.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent middle-click paste when closing tabs
Close tabs on auxclick instead of mousedown for middle-click. Closing on
mousedown removed the tab element before mouseup fired, causing the
mouseup to fall through to the terminal canvas and trigger an X11
primary selection paste on Linux.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract terminal tab actions to fix lint errors
Memoize `tabs` and `worktreeFiles` to fix useMemo unstable dependency
warnings. Extract action callbacks (close, activate, create, etc.) into
terminal-tab-actions.ts to bring useTerminalTabs.ts under the 300-line
max-lines limit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>