Skip to content

Add configurable keybindings via ~/.t3/keybindings.json#52

Merged
juliusmarminge merged 24 commits intomainfrom
codething/f54ac31e
Feb 15, 2026
Merged

Add configurable keybindings via ~/.t3/keybindings.json#52
juliusmarminge merged 24 commits intomainfrom
codething/f54ac31e

Conversation

@juliusmarminge
Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge commented Feb 15, 2026

Summary

  • Add server-side loading of ~/.t3/keybindings.json and return keybindings in server.getConfig.
  • Introduce shared contracts for server config and keybinding rule validation.
  • Expand terminal shortcut handling to support configurable bindings, when expressions, command precedence, and platform-aware label formatting.
  • Wire chat/terminal UI to fetch resolved keybindings, handle toggle/split/new shortcuts, and display shortcut labels in terminal controls.
  • Add user-facing documentation in KEYBINDINGS.md with syntax, commands, defaults, and precedence behavior.
  • Add/extend tests for config loading, invalid/malformed config handling, and shortcut resolution/matching behavior.

Testing

  • apps/server/src/wsServer.test.ts: verified serverGetConfig now includes keybindings, reads valid config from ~/.t3/keybindings.json, and warns/ignores invalid, unsupported, or malformed config.
  • apps/web/src/terminal-shortcuts.test.ts: verified default shortcuts, split/new when-gated behavior, override resolution, invalid config filtering, and shortcut label formatting.
  • Not run: full repository lint/test commands in this PR context.

Open with Devin

Summary by CodeRabbit

  • New Features

    • Customizable terminal keybindings (toggle, split, new) with server-resolved defaults, context-aware behavior, and UI labels that reflect current shortcuts.
  • Documentation

    • Added KEYBINDINGS.md covering config format, when-conditions, examples, precedence, warnings, and JSON schema rules.
  • Tests

    • Added server and client tests for loading, validation, fallback, warnings, label formatting, and shortcut handling.

- extend `server.getConfig` to return sanitized keybinding rules from `~/.t3/keybindings.json`
- apply resolved bindings in the web app for toggle/split/new terminal shortcuts and labels
- add contracts/docs and tests for config parsing, validation, and shortcut behavior
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Server now reads and validates ~/.t3/keybindings.json and returns resolved keybindings via server.getConfig; contracts add keybindings/server schemas; web resolves and merges bindings with defaults, formats labels, and applies terminal toggle/split/new shortcuts with when-expression context handling.

Changes

Cohort / File(s) Summary
Documentation
KEYBINDINGS.md
New docs describing keybindings JSON format, rule fields (key, command, when), syntax, semantics, defaults, precedence, and examples.
Server: config, validation & tests
apps/server/src/wsServer.ts, apps/server/src/wsServer.test.ts
Server reads ~/.t3/keybindings.json, validates via keybindingRuleSchema, sanitizes/merges with defaults, truncates to 256 entries, logs warnings for malformed/invalid data, and includes keybindings in serverGetConfig response; tests added for success and multiple failure cases.
Contracts / IPC
packages/contracts/src/keybindings.ts, packages/contracts/src/server.ts, packages/contracts/src/index.ts, packages/contracts/src/ipc.ts
Adds Zod schemas/types for keybindings and server config (KeybindingCommand, KeybindingRule, KeybindingsConfig, ServerConfig), re-exports keybindings and server, and extends NativeApi with server.getConfig(): Promise<ServerConfig>.
Client keybinding libraries & tests
apps/web/src/keybindings.ts, apps/web/src/keybindings.test.ts, apps/web/src/terminal-shortcuts.ts, apps/web/src/terminal-shortcuts.test.ts
Introduces client-side keybinding system: parsing/normalizing/merging/formatting shortcuts, when-expression parser/evaluator, platform/context-aware matching, resolver merging user+defaults, label formatting, and helpers (isTerminalToggle/Split/New/ClearShortcut, resolveTerminalKeybindings, shortcutLabelForCommand); tests added/updated.
Web UI integration
apps/web/src/components/ChatView.tsx, apps/web/src/components/ThreadTerminalDrawer.tsx
ChatView fetches server config and resolves keybindings; global keyboard handling updated to consider terminalFocus/terminalOpen and handle toggle/split/new shortcuts; ThreadTerminalDrawer accepts splitShortcutLabel and newShortcutLabel and displays labels.
Client WS API
apps/web/src/wsNativeApi.ts
Adds server.getConfig that forwards WS_METHODS.serverGetConfig via transport request.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Web Client
  participant WS as WS Server
  participant FS as Filesystem (~/.t3/keybindings.json)
  participant Defaults as Server Defaults
  Client->>WS: request serverGetConfig
  WS->>FS: read ~/.t3/keybindings.json
  alt file present & valid
    FS-->>WS: keybindings JSON
    WS->>WS: validate & sanitize via keybindingRuleSchema
    WS->>Defaults: merge with DEFAULT_KEYBINDINGS (later rules win)
    WS-->>Client: { cwd, keybindings }
  else malformed/invalid or missing
    FS-->>WS: malformed/invalid or not found
    WS->>WS: log warnings, ignore invalid entries, use defaults/truncated result
    WS-->>Client: { cwd, keybindings }
  end
  Client->>Client: resolveTerminalKeybindings(response.keybindings)
  Client->>Client: global keyboard handler matches events using platform & when-context
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main
Title check ✅ Passed The PR title accurately and concisely summarizes the main change: adding configurable terminal keybindings via a configuration file at ~/.t3/keybindings.json.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codething/f54ac31e

Comment @coderabbitai help to get the list of available commands and usage tips.

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp bot commented Feb 15, 2026

Add configurable keybindings from ~/.t3/keybindings.json and return resolved bindings in WS_METHODS.serverGetConfig with a 256-entry cap

Introduce server-side compilation and caching of default and user keybindings, extend the WebSocket server config response to include resolved keybindings, and update web clients to match and display shortcuts and evaluate when conditions. Add schemas in contracts for validation and documentation for configuration.

📍Where to Start

Start with the loader loadResolvedKeybindingsConfig and its integration in wsServer.createServer in apps/server/src/wsServer.ts, then review keybinding compilation in apps/server/src/keybindings.ts.


Macroscope summarized 59b1d5d.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Feb 15, 2026

Greptile Summary

This PR adds a configurable keybindings system that loads user overrides from ~/.t3/keybindings.json, merges them with defaults, resolves them into structured shortcut objects with parsed when-expression ASTs on the server, and delivers them to the client via server.getConfig. The client evaluates shortcuts with context-aware when-clause matching and last-wins precedence, supporting terminal toggle/split/new, chat.new, and editor.openFavorite commands.

  • Contracts (packages/contracts): New Zod schemas for keybinding rules, resolved shortcuts, when-node AST, and ServerConfig.
  • Server (apps/server): Keybinding parser, when-expression tokenizer/parser, config loader with graceful fallback on invalid/missing files, merge with defaults, and one-time startup caching.
  • Client (apps/web): Shortcut matching engine with platform-aware mod key resolution, when-clause evaluation, cross-command precedence, label formatting, and React Query integration.
  • UI wiring: ChatView handles terminal shortcuts with proper terminalFocus/terminalOpen context; Sidebar handles chat.new; ThreadTerminalDrawer displays shortcut labels in button tooltips; OpenInPicker uses configurable editor shortcut.
  • Tests: Comprehensive coverage across all three packages — schema validation, parsing, compilation, matching, precedence, label formatting, and server integration tests with filesystem mocking.
  • Documentation: KEYBINDINGS.md covers config format, key syntax, when-conditions, available commands, and precedence behavior.

Confidence Score: 4/5

  • This PR is well-structured and safe to merge with only minor style suggestions.
  • The implementation is clean with proper separation of concerns across contracts, server, and client. Keybinding parsing, when-expression evaluation, and precedence logic are well-tested. Server caches config at startup. Error handling gracefully falls back to defaults. The two style suggestions (regex robustness, staleTime) are non-blocking.
  • No files require special attention. Minor style suggestions on apps/web/src/lib/utils.ts and apps/web/src/lib/serverReactQuery.ts.

Important Files Changed

Filename Overview
packages/contracts/src/keybindings.ts New file: Zod schemas and TypeScript types for keybinding rules, shortcuts, when-node AST, and resolved keybindings. Clean, well-structured shared contracts.
packages/contracts/src/server.ts New file: ServerConfig schema with cwd and keybindings fields. Simple, correct contract for the server config endpoint.
packages/contracts/src/ipc.ts Adds server.getConfig method to NativeApi interface returning ServerConfig. Clean addition to the IPC contract.
apps/server/src/keybindings.ts New file: Server-side keybinding parsing, when-expression tokenizer/parser, config loading from ~/.t3/keybindings.json, and merge with defaults. Well-structured with proper error handling.
apps/server/src/wsServer.ts Loads keybindings config once at server startup (cached) and returns it in the serverGetConfig response. Clean integration.
apps/web/src/keybindings.ts New file: Client-side shortcut matching, when-clause evaluation, label formatting, and exported helper functions for each command. Clean design with last-wins precedence resolution.
apps/web/src/lib/serverReactQuery.ts New file: React Query options for server.getConfig. Properly guards against undefined API with enabled flag.
apps/web/src/lib/utils.ts Extracts isMacPlatform to shared utils and adds isWindowsPlatform. The isWindowsPlatform regex `/win
apps/web/src/wsNativeApi.ts Adds server.getConfig to the WebSocket native API implementation. One-line addition, correctly wired to the transport.
apps/web/src/components/ChatView.tsx Wires keybindings into ChatView: fetches config, handles toggle/split/new shortcuts with proper context, passes shortcut labels to terminal drawer. Properly uses event.stopPropagation() to prevent duplicate handling.
apps/web/src/components/Sidebar.tsx Replaces hardcoded chat.new shortcut detection with configurable isChatNewShortcut. Does not pass terminal context, which is acceptable since chat.new has no when clause by default.

Sequence Diagram

sequenceDiagram
    participant FS as ~/.t3/keybindings.json
    participant Server as WebSocket Server
    participant Client as React Client
    participant UI as ChatView / Sidebar / Terminal

    Note over Server: Server startup
    Server->>FS: readFileSync (once)
    FS-->>Server: JSON array of rules
    Server->>Server: Validate & compile rules<br/>(parse shortcuts, when ASTs)
    Server->>Server: Merge with defaults<br/>(custom overrides by command)
    Server->>Server: Cache resolved config

    Note over Client: Component mount
    Client->>Server: server.getConfig (WebSocket RPC)
    Server-->>Client: { cwd, keybindings: ResolvedKeybindingsConfig }
    Client->>Client: React Query caches config

    Note over UI: User presses key
    UI->>Client: KeyboardEvent
    Client->>Client: Build context<br/>(terminalFocus, terminalOpen)
    Client->>Client: resolveShortcutCommand()<br/>(iterate bindings last→first,<br/>match key + evaluate when AST)
    Client-->>UI: Execute matched command<br/>(toggle/split/new/chat.new/openEditor)
Loading

Last reviewed commit: 00d7519

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment thread apps/server/src/wsServer.ts
Comment thread apps/web/src/keybindings.ts Outdated
Comment thread apps/web/src/keybindings.test.ts
Comment thread apps/web/src/keybindings.ts Outdated
Comment thread apps/web/src/keybindings.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/web/src/terminal-shortcuts.ts`:
- Around line 302-367: normalizeConfiguredKeybinding currently accepts any when
string which can be malformed and thus silently override defaults; update
normalizeConfiguredKeybinding to validate the when expression and drop the
binding if invalid: after trimming when, attempt to validate it by calling
matchesWhenExpression(with a safe context from resolveContext(undefined) or a
minimal ShortcutMatchContext) inside a try/catch (or check the parser's
validation API) and return null for bindings whose when fails validation or
throws; this ensures resolveTerminalKeybindings will not treat malformed when
rules as overrides.
🧹 Nitpick comments (1)
apps/server/src/wsServer.ts (1)

73-119: Consider avoiding sync disk reads in the WS request path.
readFileSync + JSON.parse run on every server.getConfig call; memoizing the parsed config (and optionally refreshing on change/TTL) or moving the read to startup would keep the handler non-blocking under load.

As per coding guidelines, "apps/server/src/**/*.{ts,js}: Maintain predictable behavior under load and during failures (session restarts, reconnects, partial streams)".

Comment thread apps/web/src/keybindings.ts Outdated
- Rename `terminal-shortcuts.ts` and its test to `keybindings`
- Update ChatView and ThreadTerminalDrawer imports to use new module path
- Reuse `KeybindingRule` and `KeybindingsConfig` directly for resolved keybinding types
- Update normalization helper to accept `KeybindingRule` for consistent typing
Comment thread apps/web/src/keybindings.ts
- add `packages/contracts/src/keybindings.ts` with keybinding schemas and types
- import keybindings config schema from `server.ts`
- re-export keybindings contracts from `packages/contracts/src/index.ts`
- Move default keybinding resolution/override merging into `server.getConfig`
- Return server-resolved keybindings to the web client instead of resolving in UI
- Update docs and tests to reflect server-side defaults, overrides, and truncation behavior
- convert schema and server file references in `KEYBINDINGS.md` to clickable Markdown links
- add `serverConfigQueryOptions` in new `serverReactQuery` utility
- switch `ChatView` from manual `getConfig` effect/state to `useQuery`
- default to empty keybindings while config is unavailable
- add `chat.new` and `editor.openFavorite` to keybinding contracts and server defaults
- use shared keybinding matching in sidebar/chat view instead of hardcoded Cmd/Ctrl combos
- document new commands/defaults and extend server/web keybinding tests
- Evaluate shortcut matches by resolving the last matching key+when rule, regardless of command
- Add cross-command precedence tests and switch assertions to `vitest` `assert`
- Clarify precedence behavior in `KEYBINDINGS.md`
Comment thread apps/web/src/keybindings.ts Outdated
@juliusmarminge juliusmarminge changed the title Add configurable terminal keybindings via ~/.t3/keybindings.json Add configurable keybindings via ~/.t3/keybindings.json Feb 15, 2026
juliusmarminge and others added 2 commits February 15, 2026 13:20
Co-authored-by: codex <codex@users.noreply.github.com>
- add reusable `LruCache` utility with bounded eviction
- replace keybinding AST cache clear-all behavior with true LRU lookups
- add unit tests for eviction, recency updates, and capacity validation
Comment thread apps/server/src/wsServer.ts Outdated
- add server keybinding compiler for shortcuts and `when` AST parsing
- switch websocket/server config payload to resolved keybinding rules
- update web keybinding logic/types to consume resolved payload directly
- add/adjust contract, server, and web tests (including plus-key parsing) and docs
- Move `KeybindingWhenNode` into `packages/contracts/src/keybindings.ts`
- Remove deprecated `keybindingsWhen` module/export
- Update web keybinding tests to build `whenAst` directly in fixtures
Comment thread apps/server/src/wsServer.ts Outdated
- Replace `expect` assertions with `assert` in server and contracts keybinding tests
- Keep existing coverage while standardizing assertion style
- drop `key` and `when` from resolved keybinding rule schema/payload
- update server/web/contracts tests and keybindings docs for `whenAst`-only resolution
- delete unused web `LruCache` implementation and tests
Comment thread packages/contracts/src/keybindings.ts
Comment thread apps/server/src/wsServer.ts Outdated
- Move default keybindings, config parsing, validation, and merge logic out of `wsServer`
- Add `loadResolvedKeybindingsConfig(logger)` in `apps/server/src/keybindings.ts`
- Update server wiring docs to point to `server.getConfig` integration
- make resolved keybinding/shortcut schemas strict to reject unknown fields
- export server `DEFAULT_KEYBINDINGS` as source of truth and reuse it in ws tests
- simplify keybinding test fixtures and update docs to reference server defaults
@juliusmarminge
Copy link
Copy Markdown
Member Author

@greptileai review

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

21 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment thread apps/web/src/lib/utils.ts
Comment thread apps/web/src/lib/serverReactQuery.ts
- set server config query `staleTime` to `Infinity` to avoid unnecessary refetches
- narrow `isWindowsPlatform` matching to Windows-prefixed identifiers
- add unit tests for Windows and non-Windows platform cases
@juliusmarminge juliusmarminge merged commit 8dd1074 into main Feb 15, 2026
3 checks passed
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.

1 participant