feat(session): add Ctrl+N new-session shortcut

This commit is contained in:
2026-08-31 17:41:33 +02:00
parent a4ee3eb7a8
commit e181b2ab12
2 changed files with 43 additions and 4 deletions
+23
View File
@@ -135,6 +135,29 @@ describe("App command progress and errors", () => {
expect(status).toHaveTextContent("Ready for your next prompt");
});
it("starts a session in selected directory with Ctrl+N", async () => {
mocks.invoke.mockImplementation((command: string) => {
if (command === "create_session_runtime")
return Promise.resolve({ runtime: { runtimeId: "runtime-new" } });
return Promise.resolve(uiState);
});
render(<App />);
const event = new KeyboardEvent("keydown", {
bubbles: true,
cancelable: true,
ctrlKey: true,
key: "n",
});
window.dispatchEvent(event);
expect(event.defaultPrevented).toBe(true);
await waitFor(() =>
expect(mocks.invoke).toHaveBeenCalledWith("create_session_runtime", {
worktreePath: "/repo",
}),
);
});
it("loads categorized snapshot selectors and derives providers from choices", async () => {
const user = userEvent.setup();
const loaded: UiState = {
+20 -4
View File
@@ -320,10 +320,7 @@ export default function App() {
followUpProvider: followUpModel?.provider ?? "",
followUpModel: followUpModel?.modelId ?? "",
defaultThinkingLevel: selectedQuickModel
? reconcileThinkingLevel(
selectedQuickModel,
current.defaultThinkingLevel,
)
? reconcileThinkingLevel(selectedQuickModel, current.defaultThinkingLevel)
: current.defaultThinkingLevel,
};
if (
@@ -890,6 +887,25 @@ export default function App() {
entry.kind === "opening" ||
entry.kind === "listing"),
);
useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => {
if (
event.defaultPrevented ||
event.repeat ||
!event.ctrlKey ||
event.metaKey ||
event.altKey ||
event.key.toLowerCase() !== "n" ||
sessionTabsBusy
)
return;
event.preventDefault();
void createRuntime();
};
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, [createRuntime, sessionTabsBusy]);
const closingRuntimeIds = new Set(
activeOperations
.filter((entry) => entry.kind === "closing" && entry.runtimeId)