Skip to content

Commit 9deae1b

Browse files
committed
🤖 Persist runtime preference per project
- Add getRuntimeKey() to storage constants for project-scoped runtime persistence - Save runtime string to localStorage after successful workspace creation - Load saved runtime preference when opening NewWorkspaceModal - Update NewWorkspaceModal to accept projectPath prop - Update Storybook stories with projectPath Users can now set their preferred runtime (local vs SSH) once per project, and the modal will remember it for future workspace creations.
1 parent 5f200a6 commit 9deae1b

File tree

4 files changed

+48
-1
lines changed

4 files changed

+48
-1
lines changed

src/App.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import type { ThinkingLevel } from "./types/thinking";
2525
import type { RuntimeConfig } from "./types/runtime";
2626
import { CUSTOM_EVENTS } from "./constants/events";
2727
import { isWorkspaceForkSwitchEvent } from "./utils/workspaceFork";
28-
import { getThinkingLevelKey } from "./constants/storage";
28+
import { getThinkingLevelKey, getRuntimeKey } from "./constants/storage";
2929
import type { BranchListResult } from "./types/ipc";
3030
import { useTelemetry } from "./hooks/useTelemetry";
3131
import { parseRuntimeString } from "./utils/chatCommands";
@@ -268,6 +268,12 @@ function AppInner() {
268268
// Track workspace creation
269269
telemetry.workspaceCreated(newWorkspace.workspaceId);
270270
setSelectedWorkspace(newWorkspace);
271+
272+
// Save runtime preference for this project if provided
273+
if (runtime) {
274+
const runtimeKey = getRuntimeKey(workspaceModalProject);
275+
localStorage.setItem(runtimeKey, runtime);
276+
}
271277
}
272278
};
273279

@@ -669,6 +675,7 @@ function AppInner() {
669675
<NewWorkspaceModal
670676
isOpen={workspaceModalOpen}
671677
projectName={workspaceModalProjectName}
678+
projectPath={workspaceModalProject}
672679
branches={workspaceModalBranches}
673680
defaultTrunkBranch={workspaceModalDefaultTrunk}
674681
loadErrorMessage={workspaceModalLoadError}

src/components/NewWorkspaceModal.stories.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const meta = {
2121
control: "text",
2222
description: "Name of the project",
2323
},
24+
projectPath: {
25+
control: "text",
26+
description: "Path to the project",
27+
},
2428
branches: {
2529
control: "object",
2630
description: "List of available branches",
@@ -47,6 +51,7 @@ export const Default: Story = {
4751
args: {
4852
isOpen: true,
4953
projectName: "my-project",
54+
projectPath: "/path/to/my-project",
5055
branches: ["main", "develop", "feature/new-feature"],
5156
defaultTrunkBranch: "main",
5257
},
@@ -56,6 +61,7 @@ export const LongProjectName: Story = {
5661
args: {
5762
isOpen: true,
5863
projectName: "very-long-project-name-that-demonstrates-wrapping",
64+
projectPath: "/path/to/very-long-project-name-that-demonstrates-wrapping",
5965
branches: ["main", "develop"],
6066
defaultTrunkBranch: "main",
6167
},
@@ -65,6 +71,7 @@ export const NoBranches: Story = {
6571
args: {
6672
isOpen: true,
6773
projectName: "empty-project",
74+
projectPath: "/path/to/empty-project",
6875
branches: [],
6976
},
7077
};
@@ -73,6 +80,7 @@ export const ManyBranches: Story = {
7380
args: {
7481
isOpen: true,
7582
projectName: "active-project",
83+
projectPath: "/path/to/active-project",
7684
branches: [
7785
"main",
7886
"develop",
@@ -90,6 +98,7 @@ export const Closed: Story = {
9098
args: {
9199
isOpen: false,
92100
projectName: "my-project",
101+
projectPath: "/path/to/my-project",
93102
branches: ["main", "develop"],
94103
defaultTrunkBranch: "main",
95104
},

src/components/NewWorkspaceModal.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import React, { useEffect, useId, useState } from "react";
22
import { Modal, ModalInfo, ModalActions, CancelButton, PrimaryButton } from "./Modal";
33
import { TooltipWrapper, Tooltip } from "./Tooltip";
44
import { formatNewCommand } from "@/utils/chatCommands";
5+
import { getRuntimeKey } from "@/constants/storage";
56

67
interface NewWorkspaceModalProps {
78
isOpen: boolean;
89
projectName: string;
10+
projectPath: string;
911
branches: string[];
1012
defaultTrunkBranch?: string;
1113
loadErrorMessage?: string | null;
@@ -20,6 +22,7 @@ const formFieldClasses =
2022
const NewWorkspaceModal: React.FC<NewWorkspaceModalProps> = ({
2123
isOpen,
2224
projectName,
25+
projectPath,
2326
branches,
2427
defaultTrunkBranch,
2528
loadErrorMessage,
@@ -56,6 +59,25 @@ const NewWorkspaceModal: React.FC<NewWorkspaceModalProps> = ({
5659
});
5760
}, [branches, defaultTrunkBranch, hasBranches]);
5861

62+
// Load saved runtime preference when modal opens
63+
useEffect(() => {
64+
if (isOpen && projectPath) {
65+
const runtimeKey = getRuntimeKey(projectPath);
66+
const savedRuntime = localStorage.getItem(runtimeKey);
67+
if (savedRuntime) {
68+
// Parse the saved runtime string (format: "ssh <host>" or undefined for local)
69+
if (savedRuntime.startsWith("ssh ")) {
70+
const host = savedRuntime.substring(4).trim();
71+
setRuntimeMode("ssh");
72+
setSshHost(host);
73+
} else {
74+
setRuntimeMode("local");
75+
setSshHost("");
76+
}
77+
}
78+
}
79+
}, [isOpen, projectPath]);
80+
5981
const handleCancel = () => {
6082
setBranchName("");
6183
setTrunkBranch(defaultTrunkBranch ?? branches[0] ?? "");

src/constants/storage.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ export function getModeKey(workspaceId: string): string {
6363
return `mode:${workspaceId}`;
6464
}
6565

66+
/**
67+
* Get the localStorage key for the default runtime for a project
68+
* Stores the last successfully used runtime config when creating a workspace
69+
* Format: "runtime:{projectPath}"
70+
*/
71+
export function getRuntimeKey(projectPath: string): string {
72+
return `runtime:${projectPath}`;
73+
}
74+
6675
/**
6776
* Get the localStorage key for the 1M context preference (global)
6877
* Format: "use1MContext"

0 commit comments

Comments
 (0)