Skip to content

Add Arc-style keyboard shortcuts system#23

Merged
Kitenite merged 17 commits into
mainfrom
feat/keyboard-shortcuts
Nov 6, 2025
Merged

Add Arc-style keyboard shortcuts system#23
Kitenite merged 17 commits into
mainfrom
feat/keyboard-shortcuts

Conversation

@caffeinum
Copy link
Copy Markdown
Contributor

@caffeinum caffeinum commented Nov 6, 2025

Summary

  • Implement comprehensive keyboard shortcuts system inspired by Arc Browser
  • Add auto-focus and sidebar synchronization for all tab operations
  • Implement auto-close terminal tab when process exits

Keyboard Shortcuts

Workspace Management:

  • Cmd+Option+Left/Right - Switch between workspaces
  • Cmd+S - Toggle sidebar visibility

Tab Management:

  • Cmd+Option+Up/Down - Navigate between tabs (context-aware for groups)
  • Cmd+T - Create new terminal with auto-focus
  • Cmd+W - Close tab (handles split views properly)
  • Cmd+1-9 - Jump to tab by position (flattened numbering)

Split Views:

  • Cmd+D - Create horizontal split
  • Cmd+Shift+D - Create vertical split

Terminal:

  • Cmd+K - Clear terminal scrollback
  • Auto-close tab on exit command

Testing Checklist

Workspace Management

  • Cmd+Option+Left - Switch to previous workspace
  • Cmd+Option+Right - Switch to next workspace
  • Cmd+S - Toggle sidebar visibility

Tab Management

  • Cmd+Option+Up - Switch to previous tab (top-level)
  • Cmd+Option+Down - Switch to next tab (top-level)
  • Cmd+Option+Up/Down - Switch between terminals inside tab group
  • Cmd+T - Create new terminal with auto-focus and sidebar update
  • Cmd+W - Close top-level tab and update sidebar
  • Cmd+W - Close tab inside group and focus adjacent tab in same group
  • Cmd+W - Works when terminal is focused
  • Cmd+1-9 - Jump to tab by position (flattened numbering)

Split Views

  • Cmd+D - Create horizontal split with auto-focus and sidebar update
  • Cmd+Shift+D - Create vertical split with auto-focus and sidebar update

Terminal

  • Cmd+K - Clear terminal scrollback and screen
  • Type exit - Auto-close terminal tab and focus adjacent tab
  • Type exit - Auto-close tab in group and focus adjacent tab in same group

Technical Details

  • Centralized keyboard shortcuts infrastructure with type-safe handlers
  • Event capture phase to intercept shortcuts before terminal
  • Proper mosaic tree updates for split view cleanup
  • Context-aware navigation (respects tab groups)
  • Workspace state synchronization for sidebar updates

- auto-focus terminal when creating/switching tabs so users can type immediately
- fix cmd+w not updating sidebar by clearing selection and forcing re-render
- fix cmd+w not working when terminal focused by using event capture phase
- cmd+option+up/down now switches between terminals inside a tab group when focused on grouped terminal
- detects parentGroupTab and navigates group's children instead of top-level tabs
- makes keyboard navigation context-aware
- fix cmd+t to properly open new terminal by refreshing workspace before selection
- fix cmd+w sidebar update by reordering workspace refresh and tab selection
- fix cmd+1-9 to number flattened tabs including terminals inside groups
- pass selectedTabId down to determine which terminal should be focused
- only auto-focus terminal when isSelected is true
- prevents all terminals in group from trying to focus simultaneously
- match sidebar button behavior by selecting tab before workspace refresh
- prevents placeholder state from showing after creating new terminal
- handleTabSelect sets local state first, then workspace refresh updates data
- sidebar renders from workspaces array via WorkspaceCarousel
- need to call loadAllWorkspaces() to update sidebar after creating tab
- matches sidebar button behavior that calls onWorktreeCreated
- sidebar needs workspaces array refreshed to show deleted tab
- call loadAllWorkspaces() after deleting tab to update sidebar
… cleanup

- cmd+d/cmd+shift+d now auto-focus newly created terminal and update sidebar
- cmd+w inside tab group properly collapses split view by updating mosaic tree
- cmd+w intelligently selects adjacent tab within group or top-level
- add removeTabFromMosaicTree helper to handle split view cleanup
- terminal manager now emits terminal-exited event when pty process exits
- renderer listens for exit events and auto-closes the tab
- selects adjacent tab (respecting group context) if exited tab was focused
- updates mosaic tree for split views and refreshes sidebar
- silently removes background tabs without switching focus
@vercel
Copy link
Copy Markdown

vercel Bot commented Nov 6, 2025

@caffeinum is attempting to deploy a commit to the Personal team on Vercel, but is not a member of this team. To resolve this issue, you can:

  • Make your repository public. Collaboration is free for open source and public repositories.
  • Upgrade to pro and add @caffeinum as a member. A Pro subscription is required to access Vercel's collaborative features.
    • If you're the owner of the team, click here to upgrade and add @caffeinum as a member.
    • If you're the user who initiated this build request, click here to request access.
    • If you're already a member of the Personal team, make sure that your Vercel account is connected to your GitHub account.

To read more about collaboration on Vercel, click here.

- cmd+s now fully collapses/expands sidebar using ResizablePanel API
- add hover overlay on left edge to show sidebar when hidden
- cmd+d/shift+d inside tab groups now add to existing group instead of nesting
- fix split view not refreshing by clearing selection before workspace update
- add key props to TabGroup to force re-mount when mosaic tree changes
- ensure state synchronization with setTimeout for next tick updates
@caffeinum caffeinum requested a review from Kitenite November 6, 2025 05:16
@caffeinum
Copy link
Copy Markdown
Contributor Author

@Kitenite it's ready to review, it's a bit buggy with closing tabs inside split view, otherwise everything works

…tion

- Added null check for parentGroupTab.mosaicTree to prevent runtime errors when creating vertical splits
- Fixed tab selection to use selectedWorktreeId instead of savedWorktreeId for accurate worktree context
@Kitenite
Copy link
Copy Markdown
Collaborator

Kitenite commented Nov 6, 2025

@caffeinum 🔥

@Kitenite Kitenite merged commit f05ec3b into main Nov 6, 2025
2 of 6 checks passed
@Kitenite Kitenite deleted the feat/keyboard-shortcuts branch November 6, 2025 06:45
Kitenite added a commit that referenced this pull request Feb 11, 2026
Track per-session producer health. When a producer fires onError,
mark it unhealthy and route subsequent writes through direct
stream.append instead. A successful flush restores healthy status.
Prevents cascading failures when the producer is in a bad state.
Kitenite added a commit that referenced this pull request Feb 11, 2026
…rhaul (#1391)

* Reduce stream latency

* Smooth stream

* Smooth stream

* fix(streams): fail /generations/finish when producer had background errors

Track producer background errors per session. finishGeneration now
flushes, clears per-message seq state, and throws if any producer
errors occurred during the run. The finish route returns structured
error response (code: FINISH_FAILED) instead of silent success.

* refactor(streams): extract producer error helpers for clarity

Extract recordProducerError and drainProducerErrors as private
helpers. Simplifies the onError callback and finishGeneration
method, making the error lifecycle (record → drain → throw) explicit.

* fix(desktop): check res.ok for /generations/finish and send messageId

Non-2xx responses from finish are now logged with the response body.
The finish request now sends the messageId so the server can clear
per-message seq state.

* fix(streams): await producer flush and detach in deleteSession

deleteSession is now async and awaits producer.flush() then
producer.detach() before cleaning up session state. Prevents
returning 204 while queued chunks are still in flight.

* fix(streams): flush producer before reset control event

Ensures all queued chunks are durably written before the reset
event is appended, preventing reset from racing ahead of buffered
data. Also clears producer errors on reset.

* fix(streams): route all writes through producer for global ordering

Extract appendToStream helper that prefers the producer when
available, falling back to direct stream.append. writeChunk,
writeUserMessage, and writePresence all use this single write
path now. User messages and presence flush immediately for
durability while streaming chunks remain buffered.

* fix(desktop): add abort signal to chunk POSTs for fast interrupt

Pass the agent abort signal to streaming chunk fetch calls so that
interrupting an agent cancels in-flight chunk sends immediately.
AbortError is silently swallowed since it's the expected outcome.

* fix(desktop): emit error event when generation finish fails

If /generations/finish returns non-2xx or the network request fails,
emit an explicit error event so the UI shows a visible failure
instead of silently appearing done.

* fix(streams): guard session delete/reset with per-session mutex

Add a promise-chain based per-session lock so concurrent delete,
reset, and close operations serialize rather than race. Prevents
interleaved lifecycle transitions from corrupting session state.

* fix(streams): write user messages directly to stream for txid immediacy

Flush the producer first to preserve global ordering, then append
the user message directly to the stream. This avoids producer
queue latency that was causing txid timeout errors on the client
side (5s default timeout in stream-db awaitTxId).

* perf(desktop): remove /generations/start round trip

Generate messageId client-side with crypto.randomUUID() instead of
blocking on POST /generations/start. Eliminates a full HTTP round
trip before the first token can stream.

* perf: batch chunk sends to reduce per-chunk HTTP overhead

Add writeChunks method to protocol and POST /chunks/batch endpoint
that accepts an array of chunks in a single HTTP request.

On the desktop side, replace the sequential per-chunk POST chain
with a ChunkBatcher that coalesces chunks within a 5ms window
(or 50-chunk max) before sending as a batch. This reduces HTTP
round trips from N to ~N/batch_size during active streaming.

* Update docs

* perf(streams): tune producer lingerMs and add flush timeout

Reduce producer lingerMs from 5ms to 1ms since the desktop
ChunkBatcher already coalesces at 5ms — avoids double-buffering
latency. Add 10s timeout to flushSession so flush/finish cannot
hang indefinitely on a stuck producer.

* perf: skip Zod on batch endpoint, add bounded queue to ChunkBatcher

The /chunks/batch endpoint now does a lightweight array check
instead of full Zod schema validation on every chunk — this is
an authenticated internal path from the desktop client.

ChunkBatcher now has a maxBufferSize (default 2000) that drops
oldest chunks when the buffer exceeds the cap, preventing OOM
when the network or proxy is slower than the agent.

* Add retry with exponential backoff for batch sends (#21)

ChunkBatcher now retries failed sendBatch calls up to 3 times with
exponential backoff (50ms base). sendBatch callback throws on non-ok
responses so the retry logic can catch transient failures. AbortError
is rethrown immediately to respect cancellation.

* Add producer health tracking with sync fallback (#23, #24)

Track per-session producer health. When a producer fires onError,
mark it unhealthy and route subsequent writes through direct
stream.append instead. A successful flush restores healthy status.
Prevents cascading failures when the producer is in a bad state.

* Track active generation per session for single-writer enforcement (#25, #33)

Add startGeneration/getActiveGeneration/finishGeneration lifecycle to
protocol. Chunk routes auto-register the generation from the first
chunk if none is active. finishGeneration clears the active generation.
Reset and delete also clean up generation state.

* Add sessionId and messageId to all route responses (#34)

Include sessionId (and messageId where applicable) in both success
and error responses from chunk and session routes for tracing.

* Add structured error codes to all route responses (#32)

Every error response now includes a machine-readable `code` field
(SESSION_NOT_FOUND, WRITE_FAILED, FINISH_FAILED, INVALID_BODY, etc.)
for deterministic client error handling.

* Add structured error codes and sessionId to auth routes (#32, #34)

* Format: lint fixes across streams and desktop

* Update recommendations doc: mark completed items

30 of 51 items now done. Remaining items are larger architectural
changes (14, 29, 31), operational (16-18), and observability/test/
rollout work (35-50).

* Add perf

* Chunks

* Remove /generations/start endpoint (#29)

Generation is now auto-registered from the first chunk written.
Desktop generates messageId client-side, so this endpoint was
dead code. Replaced with chunksBatch in the discovery listing.

* Document terminal semantics convention (#31)

message-end chunk = UI signal (isLoading → false)
/generations/finish = server lifecycle cleanup (flush, seq clear, error drain)
Both are required and always sent in that order.

* Fix restore

* Refactor

* More splitting

* More split

* Fixed feedback

* Chunk

* Fixed comments

* Update CI env
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants