From 4b85ac103f3869d54ba02ef1fe09414a4a4d6626 Mon Sep 17 00:00:00 2001 From: Alexander Dines Date: Wed, 7 Jan 2026 18:19:38 -0800 Subject: [PATCH 1/2] cp dines --- src/commands/snapshot/list.tsx | 61 ++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/src/commands/snapshot/list.tsx b/src/commands/snapshot/list.tsx index 4184a003..32fdeb59 100644 --- a/src/commands/snapshot/list.tsx +++ b/src/commands/snapshot/list.tsx @@ -17,6 +17,8 @@ import { colors } from "../../utils/theme.js"; import { useViewportHeight } from "../../hooks/useViewportHeight.js"; import { useExitOnCtrlC } from "../../hooks/useExitOnCtrlC.js"; import { useCursorPagination } from "../../hooks/useCursorPagination.js"; +import { DevboxCreatePage } from "../../components/DevboxCreatePage.js"; +import { useNavigation } from "../../store/navigationStore.js"; interface ListOptions { devbox?: string; @@ -45,6 +47,7 @@ const ListSnapshotsUI = ({ onExit?: () => void; }) => { const { exit: inkExit } = useApp(); + const { navigate } = useNavigation(); const [selectedIndex, setSelectedIndex] = React.useState(0); const [showPopup, setShowPopup] = React.useState(false); const [selectedOperation, setSelectedOperation] = React.useState(0); @@ -62,6 +65,7 @@ const ListSnapshotsUI = ({ null, ); const [operationLoading, setOperationLoading] = React.useState(false); + const [showCreateDevbox, setShowCreateDevbox] = React.useState(false); // Calculate overhead for viewport height const overhead = 13; @@ -142,13 +146,19 @@ const ListSnapshotsUI = ({ pageSize: PAGE_SIZE, getItemId: (snapshot: SnapshotListItem) => snapshot.id, pollInterval: 2000, - pollingEnabled: !showPopup && !executingOperation, + pollingEnabled: !showPopup && !executingOperation && !showCreateDevbox, deps: [devboxId, PAGE_SIZE], }); // Operations for snapshots const operations: Operation[] = React.useMemo( () => [ + { + key: "create_devbox", + label: "Create Devbox from Snapshot", + color: colors.success, + icon: figures.play, + }, { key: "delete", label: "Delete Snapshot", @@ -259,6 +269,11 @@ const ListSnapshotsUI = ({ return; } + // Handle create devbox view + if (showCreateDevbox) { + return; + } + // Handle popup navigation if (showPopup) { if (key.upArrow && selectedOperation > 0) { @@ -268,13 +283,24 @@ const ListSnapshotsUI = ({ } else if (key.return) { setShowPopup(false); const operationKey = operations[selectedOperation].key; - setSelectedSnapshot(selectedSnapshotItem); - setExecutingOperation(operationKey); - // Execute immediately after state update - setTimeout(() => executeOperation(), 0); + + if (operationKey === "create_devbox") { + setSelectedSnapshot(selectedSnapshotItem); + setShowCreateDevbox(true); + } else { + setSelectedSnapshot(selectedSnapshotItem); + setExecutingOperation(operationKey); + // Execute immediately after state update + setTimeout(() => executeOperation(), 0); + } } else if (key.escape || input === "q") { setShowPopup(false); setSelectedOperation(0); + } else if (input === "c") { + // Create devbox hotkey + setShowPopup(false); + setSelectedSnapshot(selectedSnapshotItem); + setShowCreateDevbox(true); } else if (input === "d") { // Delete hotkey setShowPopup(false); @@ -378,6 +404,24 @@ const ListSnapshotsUI = ({ ); } + // Create devbox screen + if (showCreateDevbox && selectedSnapshot) { + return ( + { + setShowCreateDevbox(false); + setSelectedSnapshot(null); + }} + onCreate={(devbox) => { + setShowCreateDevbox(false); + setSelectedSnapshot(null); + navigate("devbox-detail", { devboxId: devbox.id }); + }} + initialSnapshotId={selectedSnapshot.id} + /> + ); + } + // Loading state if (loading && snapshots.length === 0) { return ( @@ -503,7 +547,12 @@ const ListSnapshotsUI = ({ label: op.label, color: op.color, icon: op.icon, - shortcut: op.key === "delete" ? "d" : "", + shortcut: + op.key === "create_devbox" + ? "c" + : op.key === "delete" + ? "d" + : "", }))} selectedOperation={selectedOperation} onClose={() => setShowPopup(false)} From f5590b4883b72ebdacea1eeb33fb2006968289a5 Mon Sep 17 00:00:00 2001 From: Alexander Dines Date: Wed, 7 Jan 2026 18:20:50 -0800 Subject: [PATCH 2/2] cp dines --- src/components/DevboxCreatePage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/DevboxCreatePage.tsx b/src/components/DevboxCreatePage.tsx index b5d7388b..3482803c 100644 --- a/src/components/DevboxCreatePage.tsx +++ b/src/components/DevboxCreatePage.tsx @@ -20,6 +20,7 @@ interface DevboxCreatePageProps { onBack: () => void; onCreate?: (devbox: DevboxView) => void; initialBlueprintId?: string; + initialSnapshotId?: string; } type FormField = @@ -60,6 +61,7 @@ export const DevboxCreatePage = ({ onBack, onCreate, initialBlueprintId, + initialSnapshotId, }: DevboxCreatePageProps) => { const [currentField, setCurrentField] = React.useState("create"); const [formData, setFormData] = React.useState({ @@ -72,7 +74,7 @@ export const DevboxCreatePage = ({ keep_alive: "3600", metadata: {}, blueprint_id: initialBlueprintId || "", - snapshot_id: "", + snapshot_id: initialSnapshotId || "", }); const [metadataKey, setMetadataKey] = React.useState(""); const [metadataValue, setMetadataValue] = React.useState("");