* Add mobile diff review
* Wrap mobile review notes prompt
* Fix mobile review unreviewed navigation
* Clear review completion when a refreshed diff invalidates a reviewed file
mergeMobileDiffReviewState invalidates a file's reviewed flag when its
diff identity changes, but left completedAt set — inconsistent with
markUnreviewed. Drop completedAt on invalidation and add a regression
test.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* fix(terminal): emit zsh shell-ready marker even when a user zle-line-init hook fails
Orca's zsh wrapper registered its OSC-777 shell-ready marker via
`add-zle-hook-widget line-init`. zsh's azhw dispatcher aborts the whole
hook chain when an earlier hook returns non-zero, and oh-my-zsh's vi-mode
installs a raw `zle-line-init` that exits 1 when VI_MODE_SET_CURSOR is
unset. The marker never fired, so every queued startup command sat on the
daemon's 15s pre-ready timeout — agent quick-launches looked like they
just opened a bare terminal.
Own `zle-line-init` directly: emit the marker first, then chain to any
previously-installed widget (called as a plain function so $WIDGET stays
`zle-line-init` for downstream azhw dispatchers). The registration is
idempotent — a re-source keeps the originally captured widget instead of
clobbering the chain. Applied to both the daemon and local-PTY wrappers
via a shared template helper.
* test(shell-ready): skip global zsh rcs so CI compinit can't block the marker tests
The real-zsh node-pty tests spawned 'zsh -l'/'zsh -i', which on CI runners
sources /etc/zsh/* whose global compinit hits insecure (group-writable) fpath
dirs and blocks on an interactive 'insecure directories [y/n]?' prompt before
zle-line-init fires. The marker never appeared and the tests timed out. Spawn
with -o noglobalrcs so only our ZDOTDIR wrapper/fixtures load; the marker
contract is unaffected.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* Add keyboard shortcuts to launch agent tabs
Cmd+T opens a plain terminal tab, but launching an agent in a new tab was
mouse-only (tab bar + menu). This adds:
- tab.newAgent ("New agent tab"): launches the default agent in a new tab
in the active workspace. Default chord Cmd+Alt+T on macOS; unassigned on
Windows/Linux (AltGr / desktop-terminal chord conflicts), bindable in
Settings. Resolves the agent via the configured default when detected
and enabled, otherwise the shared auto-pick order; a 'blank' default
falls through to auto-pick since an explicit agent chord wants an agent.
- tab.newAgent.<id>: one bindable action per TUI agent so each enabled
agent can sit on its own chord. All ship unassigned. Settings →
Shortcuts shows them in a new "Agents" group, hiding disabled agents;
a per-agent chord launches its agent even when detection hasn't
confirmed the binary (explicit request fails visibly in the tab).
Launches go through launchAgentInNewTab (same path as the tab-bar quick
launch) with launch_source 'shortcut'. Agent display names move to a
shared module so the keybinding registry can title the generated actions
without importing renderer code. groupDefinitions moves out of
ShortcutsPane.tsx to keep it under the max-lines lint cap.
* Fix recording Option-composed shortcuts on macOS
On macOS, Option changes the produced character (Cmd+Alt+C reports
key 'ç'), so the shortcut recorder rejected Alt combos with 'Press a
key, not only a modifier'. Capture now falls back to the physical key
code when Alt is held, matching the existing matcher behavior.
* fix: ignore disabled agent shortcuts in conflict checks
Co-authored-by: Orca <help@stably.ai>
* fix: align mac option shortcut capture with matching
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* Fix markdown preview links opening in system browser instead of Orca
In the markdown preview, a plain (or Cmd/Ctrl) click on an http/https link
called window.api.shell.openUrl() directly, which always routes to
shell.openExternal (the OS default browser). It bypassed openHttpLink, so a
plain click could never open in the Orca built-in browser, regressing the
behavior from #987. The Cmd/Ctrl+Shift escape hatch was already correct.
Route both http click paths through openHttpLink via a new
resolveMarkdownPreviewHttpOpenOptions helper that centralizes the
cross-platform modifier decision (Cmd on Mac, Ctrl on Linux/Windows):
- plain / Cmd-click -> { worktreeId } -> Orca browser (per openLinksInApp),
falling back to the system browser when the setting is off, no worktree is
known, or a remote runtime is active.
- Cmd/Ctrl+Shift-click -> { forceSystemBrowser: true } -> system browser.
The remote/SSH case is handled inside openHttpLink (http stays on the client
system browser; file:// blocking is unchanged).
Adds a happy-dom interaction test that renders the real component and fires
real modifier clicks, plus full modifier-matrix coverage for the helper.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* review: share markdown preview link modifier logic
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
This allows simple clicks on the repository header to still reach the
inner collapse handler on pointerup. Additionally, body cursor and
user-select styles are only applied when actual dragging begins,
rather than when the session is armed.
- Pulls the Settings option out of the Help dropdown menu and renders it as a standalone button directly in the sidebar.
- Integrates the `useShortcutKeys` hook and `ShortcutKeyCombo` component to display the settings keyboard shortcut in the button's tooltip.
- Updates associated tests to verify the new button's placement, ARIA attributes, and tooltip content.
* fix(serve): persist active mobile-session tab so PTY output doesn't snap focus
In `orca serve` (headless host + remote/web client), running a command in a
terminal snapped the active tab back to the last-created tab. The renderer
reconcile forces the client's active tab to the host's reported active
(activeTabId / activeTabType / per-tab isActive), but `activateMobileSessionTab`
only called `focusTerminal` — a no-op for serve tabs no renderer has adopted —
and never persisted the chosen tab into the host's in-memory snapshot. So every
`onPtyData` -> `touchMobileSessionSnapshotsForPty` republish re-emitted the stale
"active = last tab" and the client snapped back on each command.
Persist the activation into the headless snapshot (activeTabId, activeTabType,
exclusive per-tab isActive, rebuilt tabGroups) after the focus dispatch, gated on
`!getAvailableAuthoritativeWindow() && isHeadlessMobileSessionPublication(...)` —
the same guard the hydrate path uses, so renderer-owned snapshots (including
`:headless-merge:`) stay authoritative. No-op when the tab is already active;
adds nothing to the `onPtyData` hot path.
Adds 3 regression tests covering: chosen tab survives a PTY republish, no version
bump on redundant activation, and no server-side persist when a renderer is
authoritative.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(serve): exclude :headless-merge: snapshots from headless active-tab persist
After a renderer detaches there is no authoritative window, so the
!getAvailableAuthoritativeWindow() guard alone let merged (:headless-merge:)
snapshots through this path. A merged snapshot still carries renderer-owned
tabs/groups, so a server-side active-tab rewrite could collapse that mixed
group state. Exclude merged epochs from the persist gate; only pure
`headless:` / `headless-hydrated:` publications are persisted server-side.
Adds a regression test: renderer-detach + `:headless-merge:` snapshot ->
the headless persist is suppressed (no snapshot mutation / event).
Addresses the CodeRabbit review on #5131.
* review: clarify headless active-tab persistence guard
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
Correct a minor grammatical issue in the English notice by adding
"the". Additionally, localize FolderWorkspaceComposerDialog and
ProjectOrderManualDefaultNotice in Spanish, Japanese, Korean, and
Chinese to replace the English fallback strings.
* Optimize CI checks caching and polling backoff behavior
- Prevent backing off the polling interval when no checks are reported
yet, keeping polling at the baseline rate to detect newly started jobs.
- Reduce empty checks cache TTL to 10 seconds to allow faster recovery
from a "no checks" state while preserving the 60-second TTL for active jobs.
- Avoid bypassing the GitHub CLI cache on tab entry, reserving forced
fetches only for explicit manual user refreshes.
- Update "No checks configured" copy to "No checks reported yet" to avoid
misleading users before their CI pipelines have started running.
- Ensure manual checks refreshes do not inherit in-flight automatic requests
which might be backed by cached CLI data.
- Lookup head-specific cache keys during PR refresh events to prevent
incorrectly deriving neutral check statuses.
* Clarify polling reset comment in ChecksPanel
Update the comment to accurately refer to the entry refresh rather
than a forced fetch establishing a fresh baseline.
* Constrain right sidebar primary button widths to prevent layout jumps
Introduce min-width constraints, truncation, and content-width layouts
for primary actions in the right sidebar and PR dialogs. This prevents
buttons from shifting layout dramatically when labels morph dynamically
as git and review state changes.
* Fix right sidebar split action overflow
Introduce min-width constraints, truncation, and content-width layouts
for primary actions in the right sidebar and PR dialogs. This prevents
buttons from shifting layout dramatically when labels morph dynamically
as git and review state changes.
* Persist commit message generation state across worktree switches
Introduce a new global store slice for commit message generation to prevent
in-flight requests from being orphaned or lost when the Source Control
sidebar is unmounted or when switching between worktrees.
- Key commit generation records by worktree ID/path in the Zustand store
- Hydrate completed messages back into the draft input when returning to
the originating worktree without overwriting active user edits
- Ensure the agent-selection dialog state remains bound to the target
worktree that initiated the generation
- Wait for the PR composer eligibility check before hydrating completed
PR field generations on sidebar remounts
* Route cancellations to starting runtime and prune stale worktree records
Ensure commit message and pull request generation cancellation requests
are always routed to the runtime environment that was selected when
the generation started, even if active settings change before
cancellation.
Additionally, prune commit message and pull request generation records
when worktrees are removed to prevent stale records from lingering in
the store.
* Support project reordering with groups and add manual-default notice
Refactors the project header drag-and-drop implementation to work correctly
with project groups by tracking ordering within buckets and updating project
group assignments on drop. Also introduces a dismissible sidebar education
card notifying upgraded profiles that manual project ordering is now the default.
* Split oversized translated UI modules
* Fix localized option test regressions