Skip to content

Commit 711a5ea

Browse files
committed
cp dines
1 parent 98db800 commit 711a5ea

9 files changed

Lines changed: 755 additions & 270 deletions

File tree

src/commands/blueprint/list.tsx

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { useExitOnCtrlC } from "../../hooks/useExitOnCtrlC.js";
2424
import { useViewportHeight } from "../../hooks/useViewportHeight.js";
2525
import { useCursorPagination } from "../../hooks/useCursorPagination.js";
2626
import { useListSearch } from "../../hooks/useListSearch.js";
27+
import { openInBrowser } from "../../utils/browser.js";
2728
import {
2829
useInputHandler,
2930
type InputMode,
@@ -502,24 +503,10 @@ const ListBlueprintsUI = ({
502503
}
503504
}, [search, onBack, onExit, inkExit]);
504505

505-
const openInBrowser = React.useCallback(() => {
506+
const handleOpenInBrowser = React.useCallback(() => {
506507
const bp = blueprints[selectedIndex];
507508
if (!bp) return;
508-
const url = getBlueprintUrl(bp.id);
509-
const doOpen = async () => {
510-
const { exec } = await import("child_process");
511-
const platform = process.platform;
512-
let openCommand: string;
513-
if (platform === "darwin") {
514-
openCommand = `open "${url}"`;
515-
} else if (platform === "win32") {
516-
openCommand = `start "${url}"`;
517-
} else {
518-
openCommand = `xdg-open "${url}"`;
519-
}
520-
exec(openCommand);
521-
};
522-
doOpen();
509+
openInBrowser(getBlueprintUrl(bp.id));
523510
}, [blueprints, selectedIndex]);
524511

525512
// --- Declarative input modes ---
@@ -664,7 +651,7 @@ const ListBlueprintsUI = ({
664651
executeOperation(selectedBlueprintItem, "view_logs");
665652
}
666653
},
667-
o: openInBrowser,
654+
o: handleOpenInBrowser,
668655
"/": () => search.enterSearchMode(),
669656
escape: handleListEscape,
670657
},
@@ -690,7 +677,7 @@ const ListBlueprintsUI = ({
690677
blueprints.length,
691678
goToNextPage,
692679
goToPrevPage,
693-
openInBrowser,
680+
handleOpenInBrowser,
694681
handleListEscape,
695682
],
696683
);

src/commands/devbox/list.tsx

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { useViewportHeight } from "../../hooks/useViewportHeight.js";
2222
import { useExitOnCtrlC } from "../../hooks/useExitOnCtrlC.js";
2323
import { useCursorPagination } from "../../hooks/useCursorPagination.js";
2424
import { useListSearch } from "../../hooks/useListSearch.js";
25+
import { openInBrowser } from "../../utils/browser.js";
2526
import {
2627
useInputHandler,
2728
type InputMode,
@@ -467,23 +468,9 @@ const ListDevboxesUI = ({
467468
}
468469
}, [search, onBack, onExit, inkExit]);
469470

470-
const openInBrowser = React.useCallback(() => {
471+
const handleOpenInBrowser = React.useCallback(() => {
471472
if (!selectedDevbox) return;
472-
const url = getDevboxUrl(selectedDevbox.id);
473-
const doOpen = async () => {
474-
const { exec } = await import("child_process");
475-
const platform = process.platform;
476-
let openCommand: string;
477-
if (platform === "darwin") {
478-
openCommand = `open "${url}"`;
479-
} else if (platform === "win32") {
480-
openCommand = `start "${url}"`;
481-
} else {
482-
openCommand = `xdg-open "${url}"`;
483-
}
484-
exec(openCommand);
485-
};
486-
doOpen();
473+
openInBrowser(getDevboxUrl(selectedDevbox.id));
487474
}, [selectedDevbox]);
488475

489476
const goToNextPage = React.useCallback(() => {
@@ -575,7 +562,7 @@ const ListDevboxesUI = ({
575562
setSelectedOperation(0);
576563
},
577564
c: () => setShowCreate(true),
578-
o: openInBrowser,
565+
o: handleOpenInBrowser,
579566
"/": () => search.enterSearchMode(),
580567
escape: handleListEscape,
581568
},
@@ -596,7 +583,7 @@ const ListDevboxesUI = ({
596583
goToPrevPage,
597584
onNavigateToDetail,
598585
selectedDevbox,
599-
openInBrowser,
586+
handleOpenInBrowser,
600587
handleListEscape,
601588
],
602589
);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* DetailedInfoView - Full-screen scrollable detail view for resources.
3+
*
4+
* Extracted from ResourceDetailPage to reduce component size.
5+
* Displays all resource information in a scrollable, bordered container.
6+
*/
7+
import React from "react";
8+
import { Box, Text } from "ink";
9+
import figures from "figures";
10+
import { Header } from "./Header.js";
11+
import { StatusBadge } from "./StatusBadge.js";
12+
import { Breadcrumb } from "./Breadcrumb.js";
13+
import { colors } from "../utils/theme.js";
14+
15+
interface DetailedInfoViewProps {
16+
/** Lines of React elements to display */
17+
detailLines: React.ReactElement[];
18+
/** Current scroll offset */
19+
scrollOffset: number;
20+
/** Maximum visible lines */
21+
viewportHeight: number;
22+
/** Display name of the resource */
23+
displayName: string;
24+
/** Resource ID */
25+
resourceId: string;
26+
/** Resource status string */
27+
status: string;
28+
/** Resource type for breadcrumbs (e.g. "Devboxes") */
29+
resourceType: string;
30+
/** Optional breadcrumb prefix items */
31+
breadcrumbPrefix?: Array<{ label: string; active?: boolean }>;
32+
}
33+
34+
export function DetailedInfoView({
35+
detailLines,
36+
scrollOffset,
37+
viewportHeight,
38+
displayName,
39+
resourceId,
40+
status,
41+
resourceType,
42+
breadcrumbPrefix = [],
43+
}: DetailedInfoViewProps) {
44+
const maxScroll = Math.max(0, detailLines.length - viewportHeight);
45+
const actualScroll = Math.min(scrollOffset, maxScroll);
46+
const visibleLines = detailLines.slice(
47+
actualScroll,
48+
actualScroll + viewportHeight,
49+
);
50+
const hasMore = actualScroll + viewportHeight < detailLines.length;
51+
const hasLess = actualScroll > 0;
52+
53+
return (
54+
<>
55+
<Breadcrumb
56+
items={[
57+
...breadcrumbPrefix,
58+
{ label: resourceType },
59+
{ label: displayName },
60+
{ label: "Full Details", active: true },
61+
]}
62+
/>
63+
<Header title={`${displayName} - Complete Information`} />
64+
<Box flexDirection="column" marginBottom={1}>
65+
<Box marginBottom={1}>
66+
<StatusBadge status={status} />
67+
<Text> </Text>
68+
<Text color={colors.idColor}>{resourceId}</Text>
69+
</Box>
70+
</Box>
71+
72+
<Box
73+
flexDirection="column"
74+
marginTop={1}
75+
marginBottom={1}
76+
borderStyle="round"
77+
borderColor={colors.border}
78+
paddingX={2}
79+
paddingY={1}
80+
>
81+
<Box flexDirection="column">{visibleLines}</Box>
82+
</Box>
83+
84+
<Box marginTop={1}>
85+
<Text color={colors.textDim} dimColor>
86+
{figures.arrowUp}
87+
{figures.arrowDown} Scroll • Line {actualScroll + 1}-
88+
{Math.min(actualScroll + viewportHeight, detailLines.length)} of{" "}
89+
{detailLines.length}
90+
</Text>
91+
{hasLess && <Text color={colors.primary}> {figures.arrowUp}</Text>}
92+
{hasMore && <Text color={colors.primary}> {figures.arrowDown}</Text>}
93+
<Text color={colors.textDim} dimColor>
94+
{" "}
95+
• [q or esc] Back to Details
96+
</Text>
97+
</Box>
98+
</>
99+
);
100+
}

0 commit comments

Comments
 (0)