| author | |
| committer | |
| log | bc22a71f10acf5d56ebc6f7b13966a7fc8fdc504 |
| tree | 94c0959b2e6bb3a028d2794a075b6bc3cbd933f7 |
| parent | c42d6a6a6489c89d56830cdba7fee752afef5c14 |
- 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 controls10 files changed, 76 insertions(+), 57 deletions(-)
KEYBINDINGS.md+3-3| ... | @@ -24,7 +24,7 @@ See the full schema for more details: [`packages/contracts/src/keybindings.ts`]( | ... | @@ -24,7 +24,7 @@ See the full schema for more details: [`packages/contracts/src/keybindings.ts`]( |
| 24 | { "key": "mod+n", "command": "terminal.new", "when": "terminalFocus" }, | 24 | { "key": "mod+n", "command": "terminal.new", "when": "terminalFocus" }, |
| 25 | { "key": "mod+w", "command": "terminal.close", "when": "terminalFocus" }, | 25 | { "key": "mod+w", "command": "terminal.close", "when": "terminalFocus" }, |
| 26 | { "key": "mod+shift+o", "command": "chat.new" }, | 26 | { "key": "mod+shift+o", "command": "chat.new" }, |
| 27 | { "key": "mod+shift+n", "command": "chat.newSameGitState" }, | 27 | { "key": "mod+shift+n", "command": "chat.newLocal" }, |
| 28 | { "key": "mod+o", "command": "editor.openFavorite" } | 28 | { "key": "mod+o", "command": "editor.openFavorite" } |
| 29 | ] | 29 | ] |
| 30 | ``` | 30 | ``` |
| ... | @@ -49,8 +49,8 @@ Invalid rules are ignored. Invalid config files are ignored. Warnings are logged | ... | @@ -49,8 +49,8 @@ Invalid rules are ignored. Invalid config files are ignored. Warnings are logged |
| 49 | - `terminal.split`: split terminal (in focused terminal context by default) | 49 | - `terminal.split`: split terminal (in focused terminal context by default) |
| 50 | - `terminal.new`: create new terminal (in focused terminal context by default) | 50 | - `terminal.new`: create new terminal (in focused terminal context by default) |
| 51 | - `terminal.close`: close/kill the focused terminal (in focused terminal context by default) | 51 | - `terminal.close`: close/kill the focused terminal (in focused terminal context by default) |
| 52 | - `chat.new`: create a new chat thread for the active project | 52 | - `chat.new`: create a new chat thread preserving the active thread's branch/worktree state |
| 53 | - `chat.newSameGitState`: create a new chat thread preserving the active thread's branch/worktree state | 53 | - `chat.newLocal`: create a new local chat thread for the active project (no worktree context) |
| 54 | - `editor.openFavorite`: open current project/worktree in the last-used editor | 54 | - `editor.openFavorite`: open current project/worktree in the last-used editor |
| 55 | 55 | ||
| 56 | ### Key Syntax | 56 | ### Key Syntax |
apps/server/src/keybindings.test.ts+2-2| ... | @@ -71,7 +71,7 @@ describe("server keybindings", () => { | ... | @@ -71,7 +71,7 @@ describe("server keybindings", () => { |
| 71 | ); | 71 | ); |
| 72 | }); | 72 | }); |
| 73 | 73 | ||
| 74 | it("defines defaults for terminal.close and chat.newSameGitState", () => { | 74 | it("defines defaults for terminal.close and chat.newLocal", () => { |
| 75 | assert.deepInclude(DEFAULT_KEYBINDINGS, { | 75 | assert.deepInclude(DEFAULT_KEYBINDINGS, { |
| 76 | key: "mod+w", | 76 | key: "mod+w", |
| 77 | command: "terminal.close", | 77 | command: "terminal.close", |
| ... | @@ -79,7 +79,7 @@ describe("server keybindings", () => { | ... | @@ -79,7 +79,7 @@ describe("server keybindings", () => { |
| 79 | }); | 79 | }); |
| 80 | assert.deepInclude(DEFAULT_KEYBINDINGS, { | 80 | assert.deepInclude(DEFAULT_KEYBINDINGS, { |
| 81 | key: "mod+shift+n", | 81 | key: "mod+shift+n", |
| 82 | command: "chat.newSameGitState", | 82 | command: "chat.newLocal", |
| 83 | }); | 83 | }); |
| 84 | }); | 84 | }); |
| 85 | }); | 85 | }); |
apps/server/src/keybindings.ts+1-1| ... | @@ -30,7 +30,7 @@ export const DEFAULT_KEYBINDINGS: ReadonlyArray<KeybindingRule> = [ | ... | @@ -30,7 +30,7 @@ export const DEFAULT_KEYBINDINGS: ReadonlyArray<KeybindingRule> = [ |
| 30 | { key: "mod+n", command: "terminal.new", when: "terminalFocus" }, | 30 | { key: "mod+n", command: "terminal.new", when: "terminalFocus" }, |
| 31 | { key: "mod+w", command: "terminal.close", when: "terminalFocus" }, | 31 | { key: "mod+w", command: "terminal.close", when: "terminalFocus" }, |
| 32 | { key: "mod+shift+o", command: "chat.new" }, | 32 | { key: "mod+shift+o", command: "chat.new" }, |
| 33 | { key: "mod+shift+n", command: "chat.newSameGitState" }, | 33 | { key: "mod+shift+n", command: "chat.newLocal" }, |
| 34 | { key: "mod+o", command: "editor.openFavorite" }, | 34 | { key: "mod+o", command: "editor.openFavorite" }, |
| 35 | ]; | 35 | ]; |
| 36 | 36 |
apps/web/src/components/ChatView.tsx+5| ... | @@ -279,6 +279,10 @@ export default function ChatView() { | ... | @@ -279,6 +279,10 @@ export default function ChatView() { |
| 279 | () => shortcutLabelForCommand(keybindings, "terminal.new"), | 279 | () => shortcutLabelForCommand(keybindings, "terminal.new"), |
| 280 | [keybindings], | 280 | [keybindings], |
| 281 | ); | 281 | ); |
| 282 | const closeTerminalShortcutLabel = useMemo( | ||
| 283 | () => shortcutLabelForCommand(keybindings, "terminal.close"), | ||
| 284 | [keybindings], | ||
| 285 | ); | ||
| 282 | 286 | ||
| 283 | const envLocked = Boolean( | 287 | const envLocked = Boolean( |
| 284 | activeThread && | 288 | activeThread && |
| ... | @@ -1464,6 +1468,7 @@ export default function ChatView() { | ... | @@ -1464,6 +1468,7 @@ export default function ChatView() { |
| 1464 | onNewTerminal={createNewTerminal} | 1468 | onNewTerminal={createNewTerminal} |
| 1465 | splitShortcutLabel={splitTerminalShortcutLabel ?? undefined} | 1469 | splitShortcutLabel={splitTerminalShortcutLabel ?? undefined} |
| 1466 | newShortcutLabel={newTerminalShortcutLabel ?? undefined} | 1470 | newShortcutLabel={newTerminalShortcutLabel ?? undefined} |
| 1471 | closeShortcutLabel={closeTerminalShortcutLabel ?? undefined} | ||
| 1467 | onActiveTerminalChange={activateTerminal} | 1472 | onActiveTerminalChange={activateTerminal} |
| 1468 | onCloseTerminal={closeTerminal} | 1473 | onCloseTerminal={closeTerminal} |
| 1469 | onHeightChange={(height) => | 1474 | onHeightChange={(height) => |
apps/web/src/components/Sidebar.tsx+7-7| ... | @@ -7,7 +7,7 @@ import { useTheme } from "../hooks/useTheme"; | ... | @@ -7,7 +7,7 @@ import { useTheme } from "../hooks/useTheme"; |
| 7 | import { DEFAULT_MODEL } from "../model-logic"; | 7 | import { DEFAULT_MODEL } from "../model-logic"; |
| 8 | import { derivePendingApprovals } from "../session-logic"; | 8 | import { derivePendingApprovals } from "../session-logic"; |
| 9 | import { useStore } from "../store"; | 9 | import { useStore } from "../store"; |
| 10 | import { isChatNewSameGitStateShortcut, isChatNewShortcut } from "../keybindings"; | 10 | import { isChatNewLocalShortcut, isChatNewShortcut } from "../keybindings"; |
| 11 | import { | 11 | import { |
| 12 | DEFAULT_THREAD_TERMINAL_HEIGHT, | 12 | DEFAULT_THREAD_TERMINAL_HEIGHT, |
| 13 | DEFAULT_THREAD_TERMINAL_ID, | 13 | DEFAULT_THREAD_TERMINAL_ID, |
| ... | @@ -388,14 +388,11 @@ export default function Sidebar() { | ... | @@ -388,14 +388,11 @@ export default function Sidebar() { |
| 388 | useEffect(() => { | 388 | useEffect(() => { |
| 389 | const onWindowKeyDown = (event: KeyboardEvent) => { | 389 | const onWindowKeyDown = (event: KeyboardEvent) => { |
| 390 | const activeThread = state.threads.find((t) => t.id === state.activeThreadId); | 390 | const activeThread = state.threads.find((t) => t.id === state.activeThreadId); |
| 391 | if (isChatNewSameGitStateShortcut(event, keybindings)) { | 391 | if (isChatNewLocalShortcut(event, keybindings)) { |
| 392 | const projectId = activeThread?.projectId ?? state.projects[0]?.id; | 392 | const projectId = activeThread?.projectId ?? state.projects[0]?.id; |
| 393 | if (!projectId) return; | 393 | if (!projectId) return; |
| 394 | event.preventDefault(); | 394 | event.preventDefault(); |
| 395 | handleNewThread(projectId, { | 395 | handleNewThread(projectId); |
| 396 | branch: activeThread?.branch ?? null, | ||
| 397 | worktreePath: activeThread?.worktreePath ?? null, | ||
| 398 | }); | ||
| 399 | return; | 396 | return; |
| 400 | } | 397 | } |
| 401 | 398 | ||
| ... | @@ -403,7 +400,10 @@ export default function Sidebar() { | ... | @@ -403,7 +400,10 @@ export default function Sidebar() { |
| 403 | const projectId = activeThread?.projectId ?? state.projects[0]?.id; | 400 | const projectId = activeThread?.projectId ?? state.projects[0]?.id; |
| 404 | if (!projectId) return; | 401 | if (!projectId) return; |
| 405 | event.preventDefault(); | 402 | event.preventDefault(); |
| 406 | handleNewThread(projectId); | 403 | handleNewThread(projectId, { |
| 404 | branch: activeThread?.branch ?? null, | ||
| 405 | worktreePath: activeThread?.worktreePath ?? null, | ||
| 406 | }); | ||
| 407 | }; | 407 | }; |
| 408 | 408 | ||
| 409 | window.addEventListener("keydown", onWindowKeyDown); | 409 | window.addEventListener("keydown", onWindowKeyDown); |
apps/web/src/components/ThreadTerminalDrawer.tsx+44-36| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | import { FitAddon } from "@xterm/addon-fit"; | 1 | import { FitAddon } from "@xterm/addon-fit"; |
| 2 | import { Plus, SquareSplitHorizontal, TerminalSquare, Trash2 } from "lucide-react"; | 2 | import { Plus, SquareSplitHorizontal, TerminalSquare, Trash2, XIcon } from "lucide-react"; |
| 3 | import { type NativeApi } from "@t3tools/contracts"; | 3 | import { type NativeApi } from "@t3tools/contracts"; |
| 4 | import { Terminal, type ITheme } from "@xterm/xterm"; | 4 | import { Terminal, type ITheme } from "@xterm/xterm"; |
| 5 | import { | 5 | import { |
| ... | @@ -18,10 +18,7 @@ import { | ... | @@ -18,10 +18,7 @@ import { |
| 18 | preferredTerminalEditor, | 18 | preferredTerminalEditor, |
| 19 | resolvePathLinkTarget, | 19 | resolvePathLinkTarget, |
| 20 | } from "../terminal-links"; | 20 | } from "../terminal-links"; |
| 21 | import { | 21 | import { isTerminalClearShortcut, terminalNavigationShortcutData } from "../keybindings"; |
| 22 | isTerminalClearShortcut, | ||
| 23 | terminalNavigationShortcutData, | ||
| 24 | } from "../keybindings"; | ||
| 25 | import { | 22 | import { |
| 26 | DEFAULT_THREAD_TERMINAL_HEIGHT, | 23 | DEFAULT_THREAD_TERMINAL_HEIGHT, |
| 27 | DEFAULT_THREAD_TERMINAL_ID, | 24 | DEFAULT_THREAD_TERMINAL_ID, |
| ... | @@ -161,10 +158,7 @@ function TerminalViewport({ | ... | @@ -161,10 +158,7 @@ function TerminalViewport({ |
| 161 | try { | 158 | try { |
| 162 | await api.terminal.write({ threadId, terminalId, data }); | 159 | await api.terminal.write({ threadId, terminalId, data }); |
| 163 | } catch (error) { | 160 | } catch (error) { |
| 164 | writeSystemMessage( | 161 | writeSystemMessage(activeTerminal, error instanceof Error ? error.message : fallbackError); |
| 165 | activeTerminal, | ||
| 166 | error instanceof Error ? error.message : fallbackError, | ||
| 167 | ); | ||
| 168 | } | 162 | } |
| 169 | }; | 163 | }; |
| 170 | 164 | ||
| ... | @@ -429,6 +423,7 @@ interface ThreadTerminalDrawerProps { | ... | @@ -429,6 +423,7 @@ interface ThreadTerminalDrawerProps { |
| 429 | onNewTerminal: () => void; | 423 | onNewTerminal: () => void; |
| 430 | splitShortcutLabel?: string | undefined; | 424 | splitShortcutLabel?: string | undefined; |
| 431 | newShortcutLabel?: string | undefined; | 425 | newShortcutLabel?: string | undefined; |
| 426 | closeShortcutLabel?: string | undefined; | ||
| 432 | onActiveTerminalChange: (terminalId: string) => void; | 427 | onActiveTerminalChange: (terminalId: string) => void; |
| 433 | onCloseTerminal: (terminalId: string) => void; | 428 | onCloseTerminal: (terminalId: string) => void; |
| 434 | onHeightChange: (height: number) => void; | 429 | onHeightChange: (height: number) => void; |
| ... | @@ -444,15 +439,9 @@ interface TerminalActionButtonProps { | ... | @@ -444,15 +439,9 @@ interface TerminalActionButtonProps { |
| 444 | function TerminalActionButton({ label, className, onClick, children }: TerminalActionButtonProps) { | 439 | function TerminalActionButton({ label, className, onClick, children }: TerminalActionButtonProps) { |
| 445 | return ( | 440 | return ( |
| 446 | <Popover> | 441 | <Popover> |
| 447 | <PopoverTrigger openOnHover | 442 | <PopoverTrigger |
| 448 | render={ | 443 | openOnHover |
| 449 | <button | 444 | render={<button type="button" className={className} onClick={onClick} aria-label={label} />} |
| 450 | type="button" | ||
| 451 | className={className} | ||
| 452 | onClick={onClick} | ||
| 453 | aria-label={label} | ||
| 454 | /> | ||
| 455 | } | ||
| 456 | > | 445 | > |
| 457 | {children} | 446 | {children} |
| 458 | </PopoverTrigger> | 447 | </PopoverTrigger> |
| ... | @@ -483,6 +472,7 @@ export default function ThreadTerminalDrawer({ | ... | @@ -483,6 +472,7 @@ export default function ThreadTerminalDrawer({ |
| 483 | onNewTerminal, | 472 | onNewTerminal, |
| 484 | splitShortcutLabel, | 473 | splitShortcutLabel, |
| 485 | newShortcutLabel, | 474 | newShortcutLabel, |
| 475 | closeShortcutLabel, | ||
| 486 | onActiveTerminalChange, | 476 | onActiveTerminalChange, |
| 487 | onCloseTerminal, | 477 | onCloseTerminal, |
| 488 | onHeightChange, | 478 | onHeightChange, |
| ... | @@ -598,6 +588,9 @@ export default function ThreadTerminalDrawer({ | ... | @@ -598,6 +588,9 @@ export default function ThreadTerminalDrawer({ |
| 598 | ), | 588 | ), |
| 599 | [normalizedTerminalIds], | 589 | [normalizedTerminalIds], |
| 600 | ); | 590 | ); |
| 591 | const closeTerminalActionLabel = closeShortcutLabel | ||
| 592 | ? `Close Terminal (${closeShortcutLabel})` | ||
| 593 | : "Close Terminal"; | ||
| 601 | 594 | ||
| 602 | useEffect(() => { | 595 | useEffect(() => { |
| 603 | onHeightChangeRef.current = onHeightChange; | 596 | onHeightChangeRef.current = onHeightChange; |
| ... | @@ -709,7 +702,9 @@ export default function ThreadTerminalDrawer({ | ... | @@ -709,7 +702,9 @@ export default function ThreadTerminalDrawer({ |
| 709 | <TerminalActionButton | 702 | <TerminalActionButton |
| 710 | className="p-1 text-foreground/90 transition-colors hover:bg-accent" | 703 | className="p-1 text-foreground/90 transition-colors hover:bg-accent" |
| 711 | onClick={onSplitTerminal} | 704 | onClick={onSplitTerminal} |
| 712 | label={splitShortcutLabel ? `Split Terminal (${splitShortcutLabel})` : "Split Terminal"} | 705 | label={ |
| 706 | splitShortcutLabel ? `Split Terminal (${splitShortcutLabel})` : "Split Terminal" | ||
| 707 | } | ||
| 713 | > | 708 | > |
| 714 | <SquareSplitHorizontal className="size-3.25" /> | 709 | <SquareSplitHorizontal className="size-3.25" /> |
| 715 | </TerminalActionButton> | 710 | </TerminalActionButton> |
| ... | @@ -725,7 +720,7 @@ export default function ThreadTerminalDrawer({ | ... | @@ -725,7 +720,7 @@ export default function ThreadTerminalDrawer({ |
| 725 | <TerminalActionButton | 720 | <TerminalActionButton |
| 726 | className="p-1 text-foreground/90 transition-colors hover:bg-accent" | 721 | className="p-1 text-foreground/90 transition-colors hover:bg-accent" |
| 727 | onClick={() => onCloseTerminal(resolvedActiveTerminalId)} | 722 | onClick={() => onCloseTerminal(resolvedActiveTerminalId)} |
| 728 | label="Close Terminal" | 723 | label={closeTerminalActionLabel} |
| 729 | > | 724 | > |
| 730 | <Trash2 className="size-3.25" /> | 725 | <Trash2 className="size-3.25" /> |
| 731 | </TerminalActionButton> | 726 | </TerminalActionButton> |
| ... | @@ -805,16 +800,14 @@ export default function ThreadTerminalDrawer({ | ... | @@ -805,16 +800,14 @@ export default function ThreadTerminalDrawer({ |
| 805 | <TerminalActionButton | 800 | <TerminalActionButton |
| 806 | className="inline-flex h-full items-center border-l border-border/70 px-1 text-foreground/90 transition-colors hover:bg-accent/70" | 801 | className="inline-flex h-full items-center border-l border-border/70 px-1 text-foreground/90 transition-colors hover:bg-accent/70" |
| 807 | onClick={onNewTerminal} | 802 | onClick={onNewTerminal} |
| 808 | label={ | 803 | label={newShortcutLabel ? `New Terminal (${newShortcutLabel})` : "New Terminal"} |
| 809 | newShortcutLabel ? `New Terminal (${newShortcutLabel})` : "New Terminal" | ||
| 810 | } | ||
| 811 | > | 804 | > |
| 812 | <Plus className="size-3.25" /> | 805 | <Plus className="size-3.25" /> |
| 813 | </TerminalActionButton> | 806 | </TerminalActionButton> |
| 814 | <TerminalActionButton | 807 | <TerminalActionButton |
| 815 | className="inline-flex h-full items-center border-l border-border/70 px-1 text-foreground/90 transition-colors hover:bg-accent/70" | 808 | className="inline-flex h-full items-center border-l border-border/70 px-1 text-foreground/90 transition-colors hover:bg-accent/70" |
| 816 | onClick={() => onCloseTerminal(resolvedActiveTerminalId)} | 809 | onClick={() => onCloseTerminal(resolvedActiveTerminalId)} |
| 817 | label="Close Terminal" | 810 | label={closeTerminalActionLabel} |
| 818 | > | 811 | > |
| 819 | <Trash2 className="size-3.25" /> | 812 | <Trash2 className="size-3.25" /> |
| 820 | </TerminalActionButton> | 813 | </TerminalActionButton> |
| ... | @@ -852,6 +845,9 @@ export default function ThreadTerminalDrawer({ | ... | @@ -852,6 +845,9 @@ export default function ThreadTerminalDrawer({ |
| 852 | > | 845 | > |
| 853 | {terminalGroup.terminalIds.map((terminalId) => { | 846 | {terminalGroup.terminalIds.map((terminalId) => { |
| 854 | const isActive = terminalId === resolvedActiveTerminalId; | 847 | const isActive = terminalId === resolvedActiveTerminalId; |
| 848 | const closeTerminalLabel = `Close ${ | ||
| 849 | terminalLabelById.get(terminalId) ?? "terminal" | ||
| 850 | }${isActive && closeShortcutLabel ? ` (${closeShortcutLabel})` : ""}`; | ||
| 855 | return ( | 851 | return ( |
| 856 | <div | 852 | <div |
| 857 | key={terminalId} | 853 | key={terminalId} |
| ... | @@ -875,18 +871,30 @@ export default function ThreadTerminalDrawer({ | ... | @@ -875,18 +871,30 @@ export default function ThreadTerminalDrawer({ |
| 875 | </span> | 871 | </span> |
| 876 | </button> | 872 | </button> |
| 877 | {normalizedTerminalIds.length > 1 && ( | 873 | {normalizedTerminalIds.length > 1 && ( |
| 878 | <button | 874 | <Popover> |
| 879 | type="button" | 875 | <PopoverTrigger |
| 880 | className="rounded px-1 text-xs font-medium leading-none text-muted-foreground opacity-0 transition hover:bg-accent hover:text-foreground group-hover:opacity-100" | 876 | openOnHover |
| 881 | onClick={(event) => { | 877 | render={ |
| 882 | event.stopPropagation(); | 878 | <button |
| 883 | onCloseTerminal(terminalId); | 879 | type="button" |
| 884 | }} | 880 | className="inline-flex size-3.5 items-center justify-center rounded text-xs font-medium leading-none text-muted-foreground opacity-0 transition hover:bg-accent hover:text-foreground group-hover:opacity-100" |
| 885 | aria-label={`Close ${terminalLabelById.get(terminalId) ?? "terminal"}`} | 881 | onClick={() => onCloseTerminal(terminalId)} |
| 886 | title={`Close ${terminalLabelById.get(terminalId) ?? "terminal"}`} | 882 | aria-label={closeTerminalLabel} |
| 887 | > | 883 | /> |
| 888 | × | 884 | } |
| 889 | </button> | 885 | > |
| 886 | <XIcon className="size-2.5" /> | ||
| 887 | </PopoverTrigger> | ||
| 888 | <PopoverPopup | ||
| 889 | tooltipStyle | ||
| 890 | side="bottom" | ||
| 891 | sideOffset={6} | ||
| 892 | align="center" | ||
| 893 | className="pointer-events-none select-none" | ||
| 894 | > | ||
| 895 | {closeTerminalLabel} | ||
| 896 | </PopoverPopup> | ||
| 897 | </Popover> | ||
| 890 | )} | 898 | )} |
| 891 | </div> | 899 | </div> |
| 892 | ); | 900 | ); |
apps/web/src/keybindings.test.ts+5-5| ... | @@ -7,9 +7,9 @@ import { | ... | @@ -7,9 +7,9 @@ import { |
| 7 | type ResolvedKeybindingsConfig, | 7 | type ResolvedKeybindingsConfig, |
| 8 | } from "@t3tools/contracts"; | 8 | } from "@t3tools/contracts"; |
| 9 | import { | 9 | import { |
| 10 | isChatNewSameGitStateShortcut, | ||
| 11 | formatShortcutLabel, | 10 | formatShortcutLabel, |
| 12 | isChatNewShortcut, | 11 | isChatNewShortcut, |
| 12 | isChatNewLocalShortcut, | ||
| 13 | isOpenFavoriteEditorShortcut, | 13 | isOpenFavoriteEditorShortcut, |
| 14 | isTerminalClearShortcut, | 14 | isTerminalClearShortcut, |
| 15 | isTerminalCloseShortcut, | 15 | isTerminalCloseShortcut, |
| ... | @@ -91,7 +91,7 @@ const DEFAULT_BINDINGS = compile([ | ... | @@ -91,7 +91,7 @@ const DEFAULT_BINDINGS = compile([ |
| 91 | whenAst: whenIdentifier("terminalFocus"), | 91 | whenAst: whenIdentifier("terminalFocus"), |
| 92 | }, | 92 | }, |
| 93 | { shortcut: modShortcut("o", { shiftKey: true }), command: "chat.new" }, | 93 | { shortcut: modShortcut("o", { shiftKey: true }), command: "chat.new" }, |
| 94 | { shortcut: modShortcut("n", { shiftKey: true }), command: "chat.newSameGitState" }, | 94 | { shortcut: modShortcut("n", { shiftKey: true }), command: "chat.newLocal" }, |
| 95 | { shortcut: modShortcut("o"), command: "editor.openFavorite" }, | 95 | { shortcut: modShortcut("o"), command: "editor.openFavorite" }, |
| 96 | ]); | 96 | ]); |
| 97 | 97 | ||
| ... | @@ -245,9 +245,9 @@ describe("chat/editor shortcuts", () => { | ... | @@ -245,9 +245,9 @@ describe("chat/editor shortcuts", () => { |
| 245 | ); | 245 | ); |
| 246 | }); | 246 | }); |
| 247 | 247 | ||
| 248 | it("matches chat.newSameGitState shortcut", () => { | 248 | it("matches chat.newLocal shortcut", () => { |
| 249 | assert.isTrue( | 249 | assert.isTrue( |
| 250 | isChatNewSameGitStateShortcut( | 250 | isChatNewLocalShortcut( |
| 251 | event({ key: "n", metaKey: true, shiftKey: true }), | 251 | event({ key: "n", metaKey: true, shiftKey: true }), |
| 252 | DEFAULT_BINDINGS, | 252 | DEFAULT_BINDINGS, |
| 253 | { | 253 | { |
| ... | @@ -256,7 +256,7 @@ describe("chat/editor shortcuts", () => { | ... | @@ -256,7 +256,7 @@ describe("chat/editor shortcuts", () => { |
| 256 | ), | 256 | ), |
| 257 | ); | 257 | ); |
| 258 | assert.isTrue( | 258 | assert.isTrue( |
| 259 | isChatNewSameGitStateShortcut( | 259 | isChatNewLocalShortcut( |
| 260 | event({ key: "n", ctrlKey: true, shiftKey: true }), | 260 | event({ key: "n", ctrlKey: true, shiftKey: true }), |
| 261 | DEFAULT_BINDINGS, | 261 | DEFAULT_BINDINGS, |
| 262 | { | 262 | { |
apps/web/src/keybindings.ts+2-2| ... | @@ -206,12 +206,12 @@ export function isChatNewShortcut( | ... | @@ -206,12 +206,12 @@ export function isChatNewShortcut( |
| 206 | return matchesCommandShortcut(event, keybindings, "chat.new", options); | 206 | return matchesCommandShortcut(event, keybindings, "chat.new", options); |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | export function isChatNewSameGitStateShortcut( | 209 | export function isChatNewLocalShortcut( |
| 210 | event: ShortcutEventLike, | 210 | event: ShortcutEventLike, |
| 211 | keybindings: ResolvedKeybindingsConfig, | 211 | keybindings: ResolvedKeybindingsConfig, |
| 212 | options?: ShortcutMatchOptions, | 212 | options?: ShortcutMatchOptions, |
| 213 | ): boolean { | 213 | ): boolean { |
| 214 | return matchesCommandShortcut(event, keybindings, "chat.newSameGitState", options); | 214 | return matchesCommandShortcut(event, keybindings, "chat.newLocal", options); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | export function isOpenFavoriteEditorShortcut( | 217 | export function isOpenFavoriteEditorShortcut( |
packages/contracts/src/keybindings.test.ts+6| ... | @@ -20,6 +20,12 @@ describe("keybindings contracts", () => { | ... | @@ -20,6 +20,12 @@ describe("keybindings contracts", () => { |
| 20 | command: "terminal.close", | 20 | command: "terminal.close", |
| 21 | }); | 21 | }); |
| 22 | assert.strictEqual(parsedClose.command, "terminal.close"); | 22 | assert.strictEqual(parsedClose.command, "terminal.close"); |
| 23 | |||
| 24 | const parsedLocal = keybindingRuleSchema.parse({ | ||
| 25 | key: "mod+shift+n", | ||
| 26 | command: "chat.newLocal", | ||
| 27 | }); | ||
| 28 | assert.strictEqual(parsedLocal.command, "chat.newLocal"); | ||
| 23 | }); | 29 | }); |
| 24 | 30 | ||
| 25 | it("rejects invalid command values", () => { | 31 | it("rejects invalid command values", () => { |
packages/contracts/src/keybindings.ts+1-1| ... | @@ -6,7 +6,7 @@ export const keybindingCommandSchema = z.enum([ | ... | @@ -6,7 +6,7 @@ export const keybindingCommandSchema = z.enum([ |
| 6 | "terminal.new", | 6 | "terminal.new", |
| 7 | "terminal.close", | 7 | "terminal.close", |
| 8 | "chat.new", | 8 | "chat.new", |
| 9 | "chat.newSameGitState", | 9 | "chat.newLocal", |
| 10 | "editor.openFavorite", | 10 | "editor.openFavorite", |
| 11 | ]); | 11 | ]); |
| 12 | 12 |