Skip to content

Add shortcuts for terminal close and chat with same Git state - #56

Merged
juliusmarminge merged 3 commits into
mainfrom
codething/48203257
Feb 16, 2026
Merged

juliusmarminge merged 3 commits into
mainfrom
codething/48203257

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Feb 16, 2026 •

Copy link
Copy Markdown
Member

Summary

  • add new keybinding commands: terminal.close and chat.newSameGitState in shared contracts
  • register default shortcuts for those commands (mod+w for terminal close with terminalFocus, mod+shift+n for new chat with same Git state)
  • wire UI handling so terminal.close closes the active terminal from ChatView
  • update sidebar new-chat flow to optionally preserve branch and worktreePath when using chat.newSameGitState
  • extend docs and tests across server, web, and contracts for the new commands and shortcut behavior

Testing

  • packages/contracts/src/keybindings.test.ts: validates terminal.close is accepted by schema parsing
  • apps/server/src/keybindings.test.ts: verifies default bindings include terminal.close and chat.newSameGitState
  • apps/web/src/keybindings.test.ts: verifies shortcut matching for terminal.close and chat.newSameGitState, including terminalFocus behavior
  • Not run: full project lint/test commands in this environment

Open with Devin

Summary by CodeRabbit

Release Notes

  • New Features

    • Added keyboard shortcut (Mod+W) to close the active terminal.
    • Added keyboard shortcut (Mod+Shift+N) to create a new chat thread with the current branch/worktree state.
    • Terminal and chat actions are now context-aware, responding intelligently based on terminal focus state.
  • UI/UX Improvements

    • Enhanced terminal close action visibility in the terminal drawer with clearer labeling and icon.

- Extend keybinding contracts/defaults with `terminal.close` and `chat.newSameGitState`
- Wire web handlers for `mod+w` to close focused terminal and `mod+shift+n` to open a thread preserving branch/worktree
- Update docs and keybinding tests across server, web, and contracts
@coderabbitai

coderabbitai Bot commented Feb 16, 2026 •

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Walkthrough

This pull request adds two new keybindings: a terminal close command (mod+w) that works when terminal focus is active, and a local chat thread command (mod+shift+n) that creates new threads while preserving the current git branch and worktree state. The changes update keybinding definitions, add context-aware shortcut detection helpers, and modify UI components to handle and display these new shortcuts.

Changes

Cohort / File(s) Summary
Keybinding Definitions
KEYBINDINGS.md, apps/server/src/keybindings.ts, packages/contracts/src/keybindings.ts
Added terminal.close (mod+w) with terminalFocus condition and chat.newLocal (mod+shift+n) with !terminalFocus condition. Updated terminal and chat command semantics to be gated by focus state. Extended keybinding schema to include new command types.
Keybinding Utilities & Tests
apps/web/src/keybindings.ts, apps/web/src/keybindings.test.ts, packages/contracts/src/keybindings.test.ts
Added isTerminalCloseShortcut() and isChatNewLocalShortcut() helper functions following existing patterns. Extended test coverage to validate new shortcuts across platform-specific key combinations and focus states.
Chat View Component
apps/web/src/components/ChatView.tsx
Added terminal close shortcut handling that verifies an active terminal exists and calls closeTerminal(). Introduced memoized closeShortcutLabel for display via ThreadTerminalDrawer prop. Extended keyboard handler dependencies.
Sidebar Component
apps/web/src/components/Sidebar.tsx
Updated handleNewThread() signature to accept optional branch and worktreePath parameters. Added keyboard handler for isChatNewLocalShortcut that creates new threads while preserving current thread's git state.
Terminal Drawer Component
apps/web/src/components/ThreadTerminalDrawer.tsx
Added closeShortcutLabel prop. Introduced computed closeTerminalActionLabel for consistent labeling. Replaced plain close button (×) with Popover-based control displaying close tooltip with shortcut hint. Updated per-terminal close labels to include shortcut information.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 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.
Title check ✅ Passed The title accurately describes the main changes: adding shortcuts for terminal close (mod+w) and a new chat command that preserves Git state (mod+shift+n for chat.newLocal).
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

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

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

@macroscopeapp

macroscopeapp Bot commented Feb 16, 2026 •

Copy link
Copy Markdown
Contributor

Add mod+w terminal close and gate chat thread creation shortcuts to preserve or clear Git context in server defaults and web UI

Update default keybindings to include terminal.close on mod+w under terminalFocus and add chat.new/chat.newLocal gated by !terminalFocus; wire terminal.close handling and shortcut labels in ChatView and ThreadTerminalDrawer; route chat.new to preserve active thread branch/worktree and chat.newLocal to create a local thread in Sidebar; extend keybinding schema and tests.

📍Where to Start

Start with the updated default bindings in keybindings.ts, then review shortcut handling in ChatView.tsx and thread creation logic in Sidebar.tsx.


Macroscope summarized 9075086.

@greptile-apps

greptile-apps Bot commented Feb 16, 2026

Copy link
Copy Markdown

Greptile Summary

Adds two new keybinding commands (terminal.close and chat.newSameGitState) with default shortcuts (mod+w and mod+shift+n), wires them into the web UI, and updates docs/tests across all three packages.

  • Extends the shared keybindingCommandSchema enum in packages/contracts with the two new commands
  • Registers default bindings in apps/server/src/keybindings.ts — terminal.close is gated by terminalFocus
  • Adds shortcut matcher functions (isTerminalCloseShortcut, isChatNewSameGitStateShortcut) in the web keybindings module
  • ChatView handles terminal.close by closing the active terminal via closeTerminal, with proper dependency array updates
  • Sidebar handles chat.newSameGitState by passing the active thread's branch and worktreePath to handleNewThread
  • KEYBINDINGS.md updated with new commands and defaults
  • Tests added/updated across contracts, server, and web packages
  • Note: mod+w default may conflict with browser tab-close in non-Electron contexts (see inline comment)

Confidence Score: 4/5

  • This PR is safe to merge with minor consideration around the mod+w browser shortcut conflict.
  • The changes follow established patterns consistently across all three packages (contracts, server, web). The terminal close logic is properly guarded by terminalFocus context and the store's closeThreadTerminal handles edge cases (last terminal gracefully resets to default). The new git-state-preserving thread creation is clean and well-integrated. The only concern is the mod+w default potentially conflicting with browser tab-close behavior outside Electron, which is a UX consideration rather than a correctness bug.
  • apps/web/src/components/ChatView.tsx — verify that mod+w shortcut interception works as expected in browser-served mode (not just Electron).

Important Files Changed

Filename Overview
packages/contracts/src/keybindings.ts Adds terminal.close and chat.newSameGitState to the keybinding command enum. Straightforward schema extension, no issues.
apps/server/src/keybindings.ts Adds default keybinding rules for terminal.close (mod+w with terminalFocus) and chat.newSameGitState (mod+shift+n). Consistent with existing patterns.
apps/web/src/keybindings.ts Adds isTerminalCloseShortcut and isChatNewSameGitStateShortcut helper functions following existing pattern of delegating to matchesCommandShortcut. No issues.
apps/web/src/components/ChatView.tsx Wires terminal.close shortcut to close the active terminal. Properly guards with terminalOpen check, adds activeTerminalId and closeTerminal to useEffect deps. Note: mod+w may conflict with browser tab-close behavior in non-Electron contexts.
apps/web/src/components/Sidebar.tsx Extends handleNewThread to accept optional branch/worktreePath. Adds chat.newSameGitState shortcut handler that preserves active thread's git state. Checks isChatNewSameGitStateShortcut before isChatNewShortcut (correct order since they use different keys).
apps/web/src/keybindings.test.ts Adds tests for terminal.close and chat.newSameGitState shortcut matching including terminalFocus context validation. Comprehensive coverage.
apps/server/src/keybindings.test.ts Verifies new default keybinding entries exist for terminal.close and chat.newSameGitState. Clean.
packages/contracts/src/keybindings.test.ts Adds schema parsing test for terminal.close command. Clean extension of existing tests.
KEYBINDINGS.md Documents new commands and their default keybindings. Accurate and consistent with code.

Flowchart

flowchart TD
    subgraph Contracts["packages/contracts"]
        Schema["keybindingCommandSchema\n+ terminal.close\n+ chat.newSameGitState"]
    end

    subgraph Server["apps/server"]
        Defaults["DEFAULT_KEYBINDINGS\nmod+w → terminal.close\nmod+shift+n → chat.newSameGitState"]
    end

    subgraph Web["apps/web"]
        Matchers["keybindings.ts\nisTerminalCloseShortcut()\nisChatNewSameGitStateShortcut()"]
        
        subgraph ChatView["ChatView.tsx"]
            KDHandler["keydown handler"]
            CloseCheck{"isTerminalCloseShortcut?"}
            CloseGuard{"terminalOpen?"}
            CloseAction["closeTerminal(activeTerminalId)"]
        end
        
        subgraph Sidebar["Sidebar.tsx"]
            SidebarHandler["keydown handler"]
            GitStateCheck{"isChatNewSameGitStateShortcut?"}
            NewThread["handleNewThread(projectId,\n{branch, worktreePath})"]
        end
    end

    Schema --> Defaults
    Defaults --> Matchers
    Matchers --> KDHandler
    KDHandler --> CloseCheck
    CloseCheck -->|Yes| CloseGuard
    CloseGuard -->|Yes| CloseAction
    Matchers --> SidebarHandler
    SidebarHandler --> GitStateCheck
    GitStateCheck -->|Yes| NewThread
Loading

Last reviewed commit: fb4afca

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

9 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

{ key: "mod+j", command: "terminal.toggle" },
{ key: "mod+d", command: "terminal.split", when: "terminalFocus" },
{ key: "mod+n", command: "terminal.new", when: "terminalFocus" },
{ key: "mod+w", command: "terminal.close", when: "terminalFocus" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

mod+w conflicts with browser tab-close

Cmd+W / Ctrl+W is the standard shortcut for closing a browser tab. While the terminalFocus guard limits when this fires, most browsers do not allow JavaScript to intercept and preventDefault() on Ctrl+W/Cmd+W — the tab will still close.

This should work fine in Electron (where the app controls key event routing), but in the browser-served mode the shortcut will effectively be unusable and may cause unexpected tab closures. If the app is meant to run in both contexts, consider choosing a different default binding for browser users (e.g. mod+shift+w).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/contracts/src/keybindings.test.ts (1)

17-23: Consider adding chat.newSameGitState parsing coverage for parity.

The new command is now part of the schema; adding a matching parse assertion keeps the contract tests symmetrical.

🧪 Suggested test addition
     const parsedClose = keybindingRuleSchema.parse({
       key: "mod+w",
       command: "terminal.close",
     });
     assert.strictEqual(parsedClose.command, "terminal.close");
+
+    const parsedSameGit = keybindingRuleSchema.parse({
+      key: "mod+shift+n",
+      command: "chat.newSameGitState",
+    });
+    assert.strictEqual(parsedSameGit.command, "chat.newSameGitState");

- Replace `chat.newSameGitState` with `chat.newLocal` across contracts, server/web defaults, docs, and tests
- Make `chat.new` preserve branch/worktree state while `chat.newLocal` creates a local thread
- Show terminal close shortcut labels in drawer action tooltips and tab close controls
@juliusmarminge
juliusmarminge merged commit 228862a into main Feb 16, 2026
2 of 3 checks passed
piero-dev25 added a commit to piero-dev25/devgame that referenced this pull request Aug 4, 2026
Two tasks in one commit because both edit ChatView.tsx and cannot be separated
by pathspec.

DIFF AS A DOCK PANEL (pingdotgg#56). Part B of our own frozen spec, deferred since Part A
shipped. The app had TWO tab systems on screen -- the dockview grid, and a
second tab set inside the single chat panel. Diff moves first because it already
resolved its own thread identity from the URL rather than taking it from
ChatView's scope; the other three take a dozen callbacks each and will need
rewriting.

The structural problem worth recording: ChatView is a DESCENDANT of the dock
(ChatDock -> ChatPanel -> ChatView), so the components that need to open a panel
cannot receive a ref to the thing containing them. Solved with a module-scope
handle registered on mount and cleared on unmount, matching the two singletons
already in that file. It warns loudly rather than no-opping silently when null.

THE MIGRATION IS PROVEN COMPLETE, NOT ASSERTED. A review found Cmd/Ctrl+D still
driving the old store -- and because the render case had been correctly removed
while the tab strip still knew how to draw a Diff tab, the shortcut produced a
VISIBLE TAB THAT RENDERED NOTHING. Third instance of "looks wired, does nothing"
in this repo.

The fix was not to patch two call sites. `"diff"` is gone from RightPanelKind
and RightPanelSurface, so the compiler enumerated every straggler: exactly six,
across two files, all genuinely dead. This is the template for Files, Terminal
and Browser -- move the surface, delete its kind, and completeness becomes a
build error rather than something a human eyeballed. The one place that must
still recognise the retired kind is the persistence migration, which casts past
the union with a comment explaining that handling data older than the current
type is its entire job.

Cmd+D also lost its toggle in the first pass -- open-and-focus with no close.
The lane flagged it rather than letting me find it. Not accepted: the keybinding
is named `diff.toggle`, the handler is named onToggleDiff, and "use the tab x"
defeats the point of a keyboard shortcut. Now a genuine toggle, with the CLOSE
half specifically mutation-proven, because a toggle that only ever opens is the
exact bug being fixed. The layout-persistence risk was checked rather than
assumed: `persist()` stamps knownPanelIds from the current catalog on every
save, so a close survives reload as "closed on purpose" instead of being
re-grafted.

Storage v7->8 strips persisted "diff" surfaces, and recomputes `isOpen` from
surviving surfaces -- without that, a user whose ONLY surface was diff resumed
into an empty right panel they never asked for. Invisible to the first test
because its fixture always had a second surface.

THREE.JS PLAY (pingdotgg#51). `autoOpenPreview` has been declared in contracts, collected
by the settings form, and persisted to disk since it was written -- and nothing
ever read it. A user could tick the box, save, and nothing happened. It now
opens the preview when a script with a previewUrl starts.

It waits for the port scanner to report a listener attributed BY PID to the
exact terminal the script ran in, then opens the CONFIGURED url rather than the
scanner's reported one -- those differ when a port gets bumped, and the user
configured what they configured. Guarded on desktop before anything starts, so
in a browser it genuinely does nothing rather than failing gracefully.

Its mutation proof is the right shape: deleting the open call while still
returning `opened: true` turns the test red with zero calls to the mock -- the
exact "looks wired, does nothing" shape this task exists to fix, reproduced
deliberately to prove the test catches it.

Full typecheck, npm test and fmt:check green. Live browser E2E is an OPEN GATE,
tracked for pingdotgg#55 -- the dock is half migrated, so a live pass today would measure
a transitional state.
maria-rcks pushed a commit to maria-rcks/t3libre that referenced this pull request Sep 21, 2026
…ndpoints (pingdotgg#56)

Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
i2cjak added a commit to i2cjak/Backplane that referenced this pull request Sep 29, 2026
Hub: a taken web port is tried again until it is free
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