Skip to content

Commit 19c5382

Browse files
authored
feat(snapshot): snapshot list view actions now allows for devbox creation from snapshot (#15)
1 parent c0adcee commit 19c5382

2 files changed

Lines changed: 58 additions & 7 deletions

File tree

src/commands/snapshot/list.tsx

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { colors } from "../../utils/theme.js";
1717
import { useViewportHeight } from "../../hooks/useViewportHeight.js";
1818
import { useExitOnCtrlC } from "../../hooks/useExitOnCtrlC.js";
1919
import { useCursorPagination } from "../../hooks/useCursorPagination.js";
20+
import { DevboxCreatePage } from "../../components/DevboxCreatePage.js";
21+
import { useNavigation } from "../../store/navigationStore.js";
2022

2123
interface ListOptions {
2224
devbox?: string;
@@ -45,6 +47,7 @@ const ListSnapshotsUI = ({
4547
onExit?: () => void;
4648
}) => {
4749
const { exit: inkExit } = useApp();
50+
const { navigate } = useNavigation();
4851
const [selectedIndex, setSelectedIndex] = React.useState(0);
4952
const [showPopup, setShowPopup] = React.useState(false);
5053
const [selectedOperation, setSelectedOperation] = React.useState(0);
@@ -62,6 +65,7 @@ const ListSnapshotsUI = ({
6265
null,
6366
);
6467
const [operationLoading, setOperationLoading] = React.useState(false);
68+
const [showCreateDevbox, setShowCreateDevbox] = React.useState(false);
6569

6670
// Calculate overhead for viewport height
6771
const overhead = 13;
@@ -142,13 +146,19 @@ const ListSnapshotsUI = ({
142146
pageSize: PAGE_SIZE,
143147
getItemId: (snapshot: SnapshotListItem) => snapshot.id,
144148
pollInterval: 2000,
145-
pollingEnabled: !showPopup && !executingOperation,
149+
pollingEnabled: !showPopup && !executingOperation && !showCreateDevbox,
146150
deps: [devboxId, PAGE_SIZE],
147151
});
148152

149153
// Operations for snapshots
150154
const operations: Operation[] = React.useMemo(
151155
() => [
156+
{
157+
key: "create_devbox",
158+
label: "Create Devbox from Snapshot",
159+
color: colors.success,
160+
icon: figures.play,
161+
},
152162
{
153163
key: "delete",
154164
label: "Delete Snapshot",
@@ -259,6 +269,11 @@ const ListSnapshotsUI = ({
259269
return;
260270
}
261271

272+
// Handle create devbox view
273+
if (showCreateDevbox) {
274+
return;
275+
}
276+
262277
// Handle popup navigation
263278
if (showPopup) {
264279
if (key.upArrow && selectedOperation > 0) {
@@ -268,13 +283,24 @@ const ListSnapshotsUI = ({
268283
} else if (key.return) {
269284
setShowPopup(false);
270285
const operationKey = operations[selectedOperation].key;
271-
setSelectedSnapshot(selectedSnapshotItem);
272-
setExecutingOperation(operationKey);
273-
// Execute immediately after state update
274-
setTimeout(() => executeOperation(), 0);
286+
287+
if (operationKey === "create_devbox") {
288+
setSelectedSnapshot(selectedSnapshotItem);
289+
setShowCreateDevbox(true);
290+
} else {
291+
setSelectedSnapshot(selectedSnapshotItem);
292+
setExecutingOperation(operationKey);
293+
// Execute immediately after state update
294+
setTimeout(() => executeOperation(), 0);
295+
}
275296
} else if (key.escape || input === "q") {
276297
setShowPopup(false);
277298
setSelectedOperation(0);
299+
} else if (input === "c") {
300+
// Create devbox hotkey
301+
setShowPopup(false);
302+
setSelectedSnapshot(selectedSnapshotItem);
303+
setShowCreateDevbox(true);
278304
} else if (input === "d") {
279305
// Delete hotkey
280306
setShowPopup(false);
@@ -378,6 +404,24 @@ const ListSnapshotsUI = ({
378404
);
379405
}
380406

407+
// Create devbox screen
408+
if (showCreateDevbox && selectedSnapshot) {
409+
return (
410+
<DevboxCreatePage
411+
onBack={() => {
412+
setShowCreateDevbox(false);
413+
setSelectedSnapshot(null);
414+
}}
415+
onCreate={(devbox) => {
416+
setShowCreateDevbox(false);
417+
setSelectedSnapshot(null);
418+
navigate("devbox-detail", { devboxId: devbox.id });
419+
}}
420+
initialSnapshotId={selectedSnapshot.id}
421+
/>
422+
);
423+
}
424+
381425
// Loading state
382426
if (loading && snapshots.length === 0) {
383427
return (
@@ -503,7 +547,12 @@ const ListSnapshotsUI = ({
503547
label: op.label,
504548
color: op.color,
505549
icon: op.icon,
506-
shortcut: op.key === "delete" ? "d" : "",
550+
shortcut:
551+
op.key === "create_devbox"
552+
? "c"
553+
: op.key === "delete"
554+
? "d"
555+
: "",
507556
}))}
508557
selectedOperation={selectedOperation}
509558
onClose={() => setShowPopup(false)}

src/components/DevboxCreatePage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ interface DevboxCreatePageProps {
2020
onBack: () => void;
2121
onCreate?: (devbox: DevboxView) => void;
2222
initialBlueprintId?: string;
23+
initialSnapshotId?: string;
2324
}
2425

2526
type FormField =
@@ -60,6 +61,7 @@ export const DevboxCreatePage = ({
6061
onBack,
6162
onCreate,
6263
initialBlueprintId,
64+
initialSnapshotId,
6365
}: DevboxCreatePageProps) => {
6466
const [currentField, setCurrentField] = React.useState<FormField>("create");
6567
const [formData, setFormData] = React.useState<FormData>({
@@ -72,7 +74,7 @@ export const DevboxCreatePage = ({
7274
keep_alive: "3600",
7375
metadata: {},
7476
blueprint_id: initialBlueprintId || "",
75-
snapshot_id: "",
77+
snapshot_id: initialSnapshotId || "",
7678
});
7779
const [metadataKey, setMetadataKey] = React.useState("");
7880
const [metadataValue, setMetadataValue] = React.useState("");

0 commit comments

Comments
 (0)