@runloop/rl-cli 0.0.3 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -29
- package/dist/cli.js +401 -92
- package/dist/commands/auth.js +12 -11
- package/dist/commands/blueprint/create.js +108 -0
- package/dist/commands/blueprint/get.js +37 -0
- package/dist/commands/blueprint/list.js +293 -225
- package/dist/commands/blueprint/logs.js +40 -0
- package/dist/commands/blueprint/preview.js +45 -0
- package/dist/commands/devbox/create.js +10 -9
- package/dist/commands/devbox/delete.js +8 -8
- package/dist/commands/devbox/download.js +49 -0
- package/dist/commands/devbox/exec.js +23 -13
- package/dist/commands/devbox/execAsync.js +43 -0
- package/dist/commands/devbox/get.js +37 -0
- package/dist/commands/devbox/getAsync.js +37 -0
- package/dist/commands/devbox/list.js +328 -190
- package/dist/commands/devbox/logs.js +40 -0
- package/dist/commands/devbox/read.js +49 -0
- package/dist/commands/devbox/resume.js +37 -0
- package/dist/commands/devbox/rsync.js +118 -0
- package/dist/commands/devbox/scp.js +122 -0
- package/dist/commands/devbox/shutdown.js +37 -0
- package/dist/commands/devbox/ssh.js +104 -0
- package/dist/commands/devbox/suspend.js +37 -0
- package/dist/commands/devbox/tunnel.js +120 -0
- package/dist/commands/devbox/upload.js +10 -10
- package/dist/commands/devbox/write.js +51 -0
- package/dist/commands/mcp-http.js +37 -0
- package/dist/commands/mcp-install.js +120 -0
- package/dist/commands/mcp.js +30 -0
- package/dist/commands/menu.js +20 -20
- package/dist/commands/object/delete.js +37 -0
- package/dist/commands/object/download.js +88 -0
- package/dist/commands/object/get.js +37 -0
- package/dist/commands/object/list.js +112 -0
- package/dist/commands/object/upload.js +130 -0
- package/dist/commands/snapshot/create.js +12 -11
- package/dist/commands/snapshot/delete.js +8 -8
- package/dist/commands/snapshot/list.js +56 -97
- package/dist/commands/snapshot/status.js +37 -0
- package/dist/components/ActionsPopup.js +16 -13
- package/dist/components/Banner.js +4 -4
- package/dist/components/Breadcrumb.js +55 -5
- package/dist/components/DetailView.js +7 -4
- package/dist/components/DevboxActionsMenu.js +315 -178
- package/dist/components/DevboxCard.js +15 -14
- package/dist/components/DevboxCreatePage.js +147 -113
- package/dist/components/DevboxDetailPage.js +180 -102
- package/dist/components/ErrorMessage.js +5 -4
- package/dist/components/Header.js +4 -3
- package/dist/components/MainMenu.js +34 -33
- package/dist/components/MetadataDisplay.js +17 -9
- package/dist/components/OperationsMenu.js +6 -5
- package/dist/components/ResourceActionsMenu.js +117 -0
- package/dist/components/ResourceListView.js +213 -0
- package/dist/components/Spinner.js +5 -4
- package/dist/components/StatusBadge.js +81 -31
- package/dist/components/SuccessMessage.js +4 -3
- package/dist/components/Table.example.js +53 -23
- package/dist/components/Table.js +19 -11
- package/dist/hooks/useCursorPagination.js +125 -0
- package/dist/mcp/server-http.js +416 -0
- package/dist/mcp/server.js +397 -0
- package/dist/utils/CommandExecutor.js +16 -12
- package/dist/utils/client.js +7 -7
- package/dist/utils/config.js +130 -4
- package/dist/utils/interactiveCommand.js +2 -2
- package/dist/utils/output.js +17 -17
- package/dist/utils/ssh.js +160 -0
- package/dist/utils/sshSession.js +16 -12
- package/dist/utils/theme.js +22 -0
- package/dist/utils/url.js +4 -4
- package/package.json +29 -4
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React from
|
|
3
|
-
import { Box, Text, useInput, useStdout } from
|
|
4
|
-
import figures from
|
|
5
|
-
import { Header } from
|
|
6
|
-
import { StatusBadge } from
|
|
7
|
-
import { MetadataDisplay } from
|
|
8
|
-
import { Breadcrumb } from
|
|
9
|
-
import { DevboxActionsMenu } from
|
|
10
|
-
import { getDevboxUrl } from
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Box, Text, useInput, useStdout } from "ink";
|
|
4
|
+
import figures from "figures";
|
|
5
|
+
import { Header } from "./Header.js";
|
|
6
|
+
import { StatusBadge } from "./StatusBadge.js";
|
|
7
|
+
import { MetadataDisplay } from "./MetadataDisplay.js";
|
|
8
|
+
import { Breadcrumb } from "./Breadcrumb.js";
|
|
9
|
+
import { DevboxActionsMenu } from "./DevboxActionsMenu.js";
|
|
10
|
+
import { getDevboxUrl } from "../utils/url.js";
|
|
11
|
+
import { colors } from "../utils/theme.js";
|
|
11
12
|
// Format time ago in a succinct way
|
|
12
13
|
const formatTimeAgo = (timestamp) => {
|
|
13
14
|
const seconds = Math.floor((Date.now() - timestamp) / 1000);
|
|
@@ -28,7 +29,7 @@ const formatTimeAgo = (timestamp) => {
|
|
|
28
29
|
const years = Math.floor(months / 12);
|
|
29
30
|
return `${years}y ago`;
|
|
30
31
|
};
|
|
31
|
-
export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest }) => {
|
|
32
|
+
export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest, }) => {
|
|
32
33
|
const { stdout } = useStdout();
|
|
33
34
|
const [showDetailedInfo, setShowDetailedInfo] = React.useState(false);
|
|
34
35
|
const [detailScroll, setDetailScroll] = React.useState(0);
|
|
@@ -36,37 +37,99 @@ export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest }
|
|
|
36
37
|
const [selectedOperation, setSelectedOperation] = React.useState(0);
|
|
37
38
|
const selectedDevbox = initialDevbox;
|
|
38
39
|
const allOperations = [
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
|
|
40
|
+
{
|
|
41
|
+
key: "logs",
|
|
42
|
+
label: "View Logs",
|
|
43
|
+
color: colors.info,
|
|
44
|
+
icon: figures.info,
|
|
45
|
+
shortcut: "l",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: "exec",
|
|
49
|
+
label: "Execute Command",
|
|
50
|
+
color: colors.success,
|
|
51
|
+
icon: figures.play,
|
|
52
|
+
shortcut: "e",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: "upload",
|
|
56
|
+
label: "Upload File",
|
|
57
|
+
color: colors.success,
|
|
58
|
+
icon: figures.arrowUp,
|
|
59
|
+
shortcut: "u",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
key: "snapshot",
|
|
63
|
+
label: "Create Snapshot",
|
|
64
|
+
color: colors.warning,
|
|
65
|
+
icon: figures.circleFilled,
|
|
66
|
+
shortcut: "n",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: "ssh",
|
|
70
|
+
label: "SSH onto the box",
|
|
71
|
+
color: colors.primary,
|
|
72
|
+
icon: figures.arrowRight,
|
|
73
|
+
shortcut: "s",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: "tunnel",
|
|
77
|
+
label: "Open Tunnel",
|
|
78
|
+
color: colors.secondary,
|
|
79
|
+
icon: figures.pointerSmall,
|
|
80
|
+
shortcut: "t",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
key: "suspend",
|
|
84
|
+
label: "Suspend Devbox",
|
|
85
|
+
color: colors.warning,
|
|
86
|
+
icon: figures.squareSmallFilled,
|
|
87
|
+
shortcut: "p",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
key: "resume",
|
|
91
|
+
label: "Resume Devbox",
|
|
92
|
+
color: colors.success,
|
|
93
|
+
icon: figures.play,
|
|
94
|
+
shortcut: "r",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
key: "delete",
|
|
98
|
+
label: "Shutdown Devbox",
|
|
99
|
+
color: colors.error,
|
|
100
|
+
icon: figures.cross,
|
|
101
|
+
shortcut: "d",
|
|
102
|
+
},
|
|
48
103
|
];
|
|
49
104
|
// Filter operations based on devbox status
|
|
50
|
-
const operations = selectedDevbox
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
105
|
+
const operations = selectedDevbox
|
|
106
|
+
? allOperations.filter((op) => {
|
|
107
|
+
const status = selectedDevbox.status;
|
|
108
|
+
// When suspended: logs and resume
|
|
109
|
+
if (status === "suspended") {
|
|
110
|
+
return op.key === "resume" || op.key === "logs";
|
|
111
|
+
}
|
|
112
|
+
// When not running (shutdown, failure, etc): only logs
|
|
113
|
+
if (status !== "running" &&
|
|
114
|
+
status !== "provisioning" &&
|
|
115
|
+
status !== "initializing") {
|
|
116
|
+
return op.key === "logs";
|
|
117
|
+
}
|
|
118
|
+
// When running: everything except resume
|
|
119
|
+
if (status === "running") {
|
|
120
|
+
return op.key !== "resume";
|
|
121
|
+
}
|
|
122
|
+
// Default for transitional states (provisioning, initializing)
|
|
123
|
+
return op.key === "logs" || op.key === "delete";
|
|
124
|
+
})
|
|
125
|
+
: allOperations;
|
|
67
126
|
// Memoize time-based values to prevent re-rendering on every tick
|
|
68
|
-
const formattedCreateTime = React.useMemo(() => selectedDevbox.create_time_ms
|
|
69
|
-
|
|
127
|
+
const formattedCreateTime = React.useMemo(() => selectedDevbox.create_time_ms
|
|
128
|
+
? new Date(selectedDevbox.create_time_ms).toLocaleString()
|
|
129
|
+
: "", [selectedDevbox.create_time_ms]);
|
|
130
|
+
const createTimeAgo = React.useMemo(() => selectedDevbox.create_time_ms
|
|
131
|
+
? formatTimeAgo(selectedDevbox.create_time_ms)
|
|
132
|
+
: "", [selectedDevbox.create_time_ms]);
|
|
70
133
|
useInput((input, key) => {
|
|
71
134
|
// Skip input handling when in actions view
|
|
72
135
|
if (showActions) {
|
|
@@ -74,15 +137,15 @@ export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest }
|
|
|
74
137
|
}
|
|
75
138
|
// Handle detailed info mode
|
|
76
139
|
if (showDetailedInfo) {
|
|
77
|
-
if (input ===
|
|
140
|
+
if (input === "q" || key.escape) {
|
|
78
141
|
setShowDetailedInfo(false);
|
|
79
142
|
setDetailScroll(0);
|
|
80
143
|
}
|
|
81
|
-
else if (input ===
|
|
144
|
+
else if (input === "j" || input === "s" || key.downArrow) {
|
|
82
145
|
// Scroll down in detailed info
|
|
83
146
|
setDetailScroll(detailScroll + 1);
|
|
84
147
|
}
|
|
85
|
-
else if (input ===
|
|
148
|
+
else if (input === "k" || input === "w" || key.upArrow) {
|
|
86
149
|
// Scroll up in detailed info
|
|
87
150
|
setDetailScroll(Math.max(0, detailScroll - 1));
|
|
88
151
|
}
|
|
@@ -97,11 +160,11 @@ export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest }
|
|
|
97
160
|
return;
|
|
98
161
|
}
|
|
99
162
|
// Main view input handling
|
|
100
|
-
if (input ===
|
|
163
|
+
if (input === "q" || key.escape) {
|
|
101
164
|
console.clear();
|
|
102
165
|
onBack();
|
|
103
166
|
}
|
|
104
|
-
else if (input ===
|
|
167
|
+
else if (input === "i") {
|
|
105
168
|
setShowDetailedInfo(true);
|
|
106
169
|
setDetailScroll(0);
|
|
107
170
|
}
|
|
@@ -111,30 +174,30 @@ export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest }
|
|
|
111
174
|
else if (key.downArrow && selectedOperation < operations.length - 1) {
|
|
112
175
|
setSelectedOperation(selectedOperation + 1);
|
|
113
176
|
}
|
|
114
|
-
else if (key.return || input ===
|
|
177
|
+
else if (key.return || input === "a") {
|
|
115
178
|
console.clear();
|
|
116
179
|
setShowActions(true);
|
|
117
180
|
}
|
|
118
181
|
else if (input) {
|
|
119
182
|
// Check if input matches any operation shortcut
|
|
120
|
-
const matchedOpIndex = operations.findIndex(op => op.shortcut === input);
|
|
183
|
+
const matchedOpIndex = operations.findIndex((op) => op.shortcut === input);
|
|
121
184
|
if (matchedOpIndex !== -1) {
|
|
122
185
|
setSelectedOperation(matchedOpIndex);
|
|
123
186
|
console.clear();
|
|
124
187
|
setShowActions(true);
|
|
125
188
|
}
|
|
126
189
|
}
|
|
127
|
-
if (input ===
|
|
190
|
+
if (input === "o") {
|
|
128
191
|
// Open in browser
|
|
129
192
|
const url = getDevboxUrl(selectedDevbox.id);
|
|
130
193
|
const openBrowser = async () => {
|
|
131
|
-
const { exec } = await import(
|
|
194
|
+
const { exec } = await import("child_process");
|
|
132
195
|
const platform = process.platform;
|
|
133
196
|
let openCommand;
|
|
134
|
-
if (platform ===
|
|
197
|
+
if (platform === "darwin") {
|
|
135
198
|
openCommand = `open "${url}"`;
|
|
136
199
|
}
|
|
137
|
-
else if (platform ===
|
|
200
|
+
else if (platform === "win32") {
|
|
138
201
|
openCommand = `start "${url}"`;
|
|
139
202
|
}
|
|
140
203
|
else {
|
|
@@ -153,124 +216,126 @@ export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest }
|
|
|
153
216
|
const lines = [];
|
|
154
217
|
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
155
218
|
// Core Information
|
|
156
|
-
lines.push(_jsx(Text, { color:
|
|
157
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
158
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
159
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
160
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
219
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Devbox Details" }, "core-title"));
|
|
220
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "ID: ", selectedDevbox.id] }, "core-id"));
|
|
221
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Name: ", selectedDevbox.name || "(none)"] }, "core-name"));
|
|
222
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Status: ", capitalize(selectedDevbox.status)] }, "core-status"));
|
|
223
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Created: ", new Date(selectedDevbox.create_time_ms).toLocaleString()] }, "core-created"));
|
|
161
224
|
if (selectedDevbox.end_time_ms) {
|
|
162
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
225
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Ended: ", new Date(selectedDevbox.end_time_ms).toLocaleString()] }, "core-ended"));
|
|
163
226
|
}
|
|
164
227
|
lines.push(_jsx(Text, { children: " " }, "core-space"));
|
|
165
228
|
// Capabilities
|
|
166
229
|
if (selectedDevbox.capabilities && selectedDevbox.capabilities.length > 0) {
|
|
167
|
-
lines.push(_jsx(Text, { color:
|
|
230
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Capabilities" }, "cap-title"));
|
|
168
231
|
selectedDevbox.capabilities.forEach((cap, idx) => {
|
|
169
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
232
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", figures.pointer, " ", cap] }, `cap-${idx}`));
|
|
170
233
|
});
|
|
171
234
|
lines.push(_jsx(Text, { children: " " }, "cap-space"));
|
|
172
235
|
}
|
|
173
236
|
// Launch Parameters
|
|
174
237
|
if (selectedDevbox.launch_parameters) {
|
|
175
|
-
lines.push(_jsx(Text, { color:
|
|
238
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Launch Parameters" }, "launch-title"));
|
|
176
239
|
const lp = selectedDevbox.launch_parameters;
|
|
177
240
|
if (lp.resource_size_request) {
|
|
178
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
241
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Resource Size Request: ", lp.resource_size_request] }, "launch-size-req"));
|
|
179
242
|
}
|
|
180
243
|
if (lp.architecture) {
|
|
181
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
244
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Architecture: ", lp.architecture] }, "launch-arch"));
|
|
182
245
|
}
|
|
183
246
|
if (lp.custom_cpu_cores) {
|
|
184
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
247
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "CPU Cores: ", lp.custom_cpu_cores] }, "launch-cpu"));
|
|
185
248
|
}
|
|
186
249
|
if (lp.custom_gb_memory) {
|
|
187
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
250
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Memory: ", lp.custom_gb_memory, "GB"] }, "launch-memory"));
|
|
188
251
|
}
|
|
189
252
|
if (lp.custom_disk_size) {
|
|
190
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
253
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Disk Size: ", lp.custom_disk_size, "GB"] }, "launch-disk"));
|
|
191
254
|
}
|
|
192
255
|
if (lp.keep_alive_time_seconds) {
|
|
193
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
256
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Keep Alive: ", lp.keep_alive_time_seconds, "s (", Math.floor(lp.keep_alive_time_seconds / 60), "m)"] }, "launch-keepalive"));
|
|
194
257
|
}
|
|
195
258
|
if (lp.after_idle) {
|
|
196
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
259
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "After Idle: ", lp.after_idle.on_idle, " after", " ", lp.after_idle.idle_time_seconds, "s"] }, "launch-afteridle"));
|
|
197
260
|
}
|
|
198
261
|
if (lp.available_ports && lp.available_ports.length > 0) {
|
|
199
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
262
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Available Ports: ", lp.available_ports.join(", ")] }, "launch-ports"));
|
|
200
263
|
}
|
|
201
264
|
if (lp.launch_commands && lp.launch_commands.length > 0) {
|
|
202
|
-
lines.push(
|
|
265
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Launch Commands:"] }, "launch-launch-cmds"));
|
|
203
266
|
lp.launch_commands.forEach((cmd, idx) => {
|
|
204
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
267
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", figures.pointer, " ", cmd] }, `launch-cmd-${idx}`));
|
|
205
268
|
});
|
|
206
269
|
}
|
|
207
270
|
if (lp.required_services && lp.required_services.length > 0) {
|
|
208
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
271
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Required Services: ", lp.required_services.join(", ")] }, "launch-services"));
|
|
209
272
|
}
|
|
210
273
|
if (lp.user_parameters) {
|
|
211
|
-
lines.push(
|
|
274
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "User Parameters:"] }, "launch-user"));
|
|
212
275
|
if (lp.user_parameters.username) {
|
|
213
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
276
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Username: ", lp.user_parameters.username] }, "user-name"));
|
|
214
277
|
}
|
|
215
278
|
if (lp.user_parameters.uid) {
|
|
216
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
279
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "UID: ", lp.user_parameters.uid] }, "user-uid"));
|
|
217
280
|
}
|
|
218
281
|
}
|
|
219
282
|
lines.push(_jsx(Text, { children: " " }, "launch-space"));
|
|
220
283
|
}
|
|
221
284
|
// Source
|
|
222
285
|
if (selectedDevbox.blueprint_id || selectedDevbox.snapshot_id) {
|
|
223
|
-
lines.push(_jsx(Text, { color:
|
|
286
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Source" }, "source-title"));
|
|
224
287
|
if (selectedDevbox.blueprint_id) {
|
|
225
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
288
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Blueprint: ", selectedDevbox.blueprint_id] }, "source-bp"));
|
|
226
289
|
}
|
|
227
290
|
if (selectedDevbox.snapshot_id) {
|
|
228
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
291
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Snapshot: ", selectedDevbox.snapshot_id] }, "source-snap"));
|
|
229
292
|
}
|
|
230
293
|
lines.push(_jsx(Text, { children: " " }, "source-space"));
|
|
231
294
|
}
|
|
232
295
|
// Initiator
|
|
233
296
|
if (selectedDevbox.initiator_type) {
|
|
234
|
-
lines.push(_jsx(Text, { color:
|
|
235
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
297
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Initiator" }, "init-title"));
|
|
298
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Type: ", selectedDevbox.initiator_type] }, "init-type"));
|
|
236
299
|
if (selectedDevbox.initiator_id) {
|
|
237
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
300
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "ID: ", selectedDevbox.initiator_id] }, "init-id"));
|
|
238
301
|
}
|
|
239
302
|
lines.push(_jsx(Text, { children: " " }, "init-space"));
|
|
240
303
|
}
|
|
241
304
|
// Status Details
|
|
242
305
|
if (selectedDevbox.failure_reason || selectedDevbox.shutdown_reason) {
|
|
243
|
-
lines.push(_jsx(Text, { color:
|
|
306
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Status Details" }, "status-title"));
|
|
244
307
|
if (selectedDevbox.failure_reason) {
|
|
245
|
-
lines.push(_jsxs(Text, { color:
|
|
308
|
+
lines.push(_jsxs(Text, { color: colors.error, dimColor: true, children: [" ", "Failure Reason: ", selectedDevbox.failure_reason] }, "status-fail"));
|
|
246
309
|
}
|
|
247
310
|
if (selectedDevbox.shutdown_reason) {
|
|
248
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
311
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", "Shutdown Reason: ", selectedDevbox.shutdown_reason] }, "status-shut"));
|
|
249
312
|
}
|
|
250
313
|
lines.push(_jsx(Text, { children: " " }, "status-space"));
|
|
251
314
|
}
|
|
252
315
|
// Metadata
|
|
253
|
-
if (selectedDevbox.metadata &&
|
|
254
|
-
|
|
316
|
+
if (selectedDevbox.metadata &&
|
|
317
|
+
Object.keys(selectedDevbox.metadata).length > 0) {
|
|
318
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Metadata" }, "meta-title"));
|
|
255
319
|
Object.entries(selectedDevbox.metadata).forEach(([key, value], idx) => {
|
|
256
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
320
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", key, ": ", value] }, `meta-${idx}`));
|
|
257
321
|
});
|
|
258
322
|
lines.push(_jsx(Text, { children: " " }, "meta-space"));
|
|
259
323
|
}
|
|
260
324
|
// State Transitions
|
|
261
|
-
if (selectedDevbox.state_transitions &&
|
|
262
|
-
|
|
325
|
+
if (selectedDevbox.state_transitions &&
|
|
326
|
+
selectedDevbox.state_transitions.length > 0) {
|
|
327
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "State History" }, "state-title"));
|
|
263
328
|
selectedDevbox.state_transitions.forEach((transition, idx) => {
|
|
264
|
-
const text = `${idx + 1}. ${capitalize(transition.status)}${transition.transition_time_ms ? ` at ${new Date(transition.transition_time_ms).toLocaleString()}` :
|
|
265
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
329
|
+
const text = `${idx + 1}. ${capitalize(transition.status)}${transition.transition_time_ms ? ` at ${new Date(transition.transition_time_ms).toLocaleString()}` : ""}`;
|
|
330
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", text] }, `state-${idx}`));
|
|
266
331
|
});
|
|
267
332
|
lines.push(_jsx(Text, { children: " " }, "state-space"));
|
|
268
333
|
}
|
|
269
334
|
// Raw JSON (full)
|
|
270
|
-
lines.push(_jsx(Text, { color:
|
|
271
|
-
const jsonLines = JSON.stringify(selectedDevbox, null, 2).split(
|
|
335
|
+
lines.push(_jsx(Text, { color: colors.warning, bold: true, children: "Raw JSON" }, "json-title"));
|
|
336
|
+
const jsonLines = JSON.stringify(selectedDevbox, null, 2).split("\n");
|
|
272
337
|
jsonLines.forEach((line, idx) => {
|
|
273
|
-
lines.push(_jsxs(Text, { dimColor: true, children: ["
|
|
338
|
+
lines.push(_jsxs(Text, { dimColor: true, children: [" ", line] }, `json-${idx}`));
|
|
274
339
|
});
|
|
275
340
|
return lines;
|
|
276
341
|
};
|
|
@@ -281,8 +346,8 @@ export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest }
|
|
|
281
346
|
setShowActions(false);
|
|
282
347
|
setSelectedOperation(0);
|
|
283
348
|
}, breadcrumbItems: [
|
|
284
|
-
{ label:
|
|
285
|
-
{ label: selectedDevbox.name || selectedDevbox.id }
|
|
349
|
+
{ label: "Devboxes" },
|
|
350
|
+
{ label: selectedDevbox.name || selectedDevbox.id },
|
|
286
351
|
], initialOperation: selectedOp?.key, skipOperationsMenu: true, onSSHRequest: onSSHRequest }));
|
|
287
352
|
}
|
|
288
353
|
// Detailed info mode - full screen
|
|
@@ -296,19 +361,32 @@ export const DevboxDetailPage = ({ devbox: initialDevbox, onBack, onSSHRequest }
|
|
|
296
361
|
const hasMore = actualScroll + viewportHeight < detailLines.length;
|
|
297
362
|
const hasLess = actualScroll > 0;
|
|
298
363
|
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
|
|
299
|
-
{ label:
|
|
364
|
+
{ label: "Devboxes" },
|
|
300
365
|
{ label: selectedDevbox.name || selectedDevbox.id },
|
|
301
|
-
{ label:
|
|
302
|
-
] }), _jsx(Header, { title: `${selectedDevbox.name || selectedDevbox.id} - Complete Information` }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsxs(Box, { marginBottom: 1, children: [_jsx(StatusBadge, { status: selectedDevbox.status }), _jsx(Text, { children: " " }), _jsx(Text, { color:
|
|
366
|
+
{ label: "Full Details", active: true },
|
|
367
|
+
] }), _jsx(Header, { title: `${selectedDevbox.name || selectedDevbox.id} - Complete Information` }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsxs(Box, { marginBottom: 1, children: [_jsx(StatusBadge, { status: selectedDevbox.status }), _jsx(Text, { children: " " }), _jsx(Text, { color: colors.textDim, dimColor: true, children: selectedDevbox.id })] }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, borderStyle: "round", borderColor: colors.border, paddingX: 2, paddingY: 1, children: [_jsx(Box, { flexDirection: "column", children: visibleLines }), hasLess && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.primary, children: [figures.arrowUp, " More above"] }) })), hasMore && (_jsx(Box, { marginTop: hasLess ? 0 : 1, children: _jsxs(Text, { color: colors.primary, children: [figures.arrowDown, " More below"] }) }))] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.textDim, dimColor: true, children: [figures.arrowUp, figures.arrowDown, " Scroll \u2022 [q or esc] Back to Details \u2022 Line", " ", actualScroll + 1, "-", Math.min(actualScroll + viewportHeight, detailLines.length), " of", " ", detailLines.length] }) })] }));
|
|
303
368
|
}
|
|
304
369
|
// Main detail view
|
|
305
370
|
const lp = selectedDevbox.launch_parameters;
|
|
306
|
-
const hasCapabilities = selectedDevbox.capabilities &&
|
|
371
|
+
const hasCapabilities = selectedDevbox.capabilities &&
|
|
372
|
+
selectedDevbox.capabilities.filter((c) => c !== "unknown").length >
|
|
373
|
+
0;
|
|
307
374
|
return (_jsxs(_Fragment, { children: [_jsx(Breadcrumb, { items: [
|
|
308
|
-
{ label:
|
|
309
|
-
{ label: selectedDevbox.name || selectedDevbox.id, active: true }
|
|
310
|
-
] }),
|
|
375
|
+
{ label: "Devboxes" },
|
|
376
|
+
{ label: selectedDevbox.name || selectedDevbox.id, active: true },
|
|
377
|
+
] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.primary, bold: true, children: selectedDevbox.name || selectedDevbox.id }), _jsx(Text, { children: " " }), _jsx(StatusBadge, { status: selectedDevbox.status }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "\u2022 ", selectedDevbox.id] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.textDim, dimColor: true, children: formattedCreateTime }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "(", createTimeAgo, ")"] })] }), uptime !== null && selectedDevbox.status === "running" && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.success, dimColor: true, children: ["Uptime:", " ", uptime < 60
|
|
378
|
+
? `${uptime}m`
|
|
379
|
+
: `${Math.floor(uptime / 60)}h ${uptime % 60}m`] }), lp?.keep_alive_time_seconds && (_jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "\u2022 Keep-alive: ", Math.floor(lp.keep_alive_time_seconds / 60), "m"] }))] }))] }), _jsxs(Box, { flexDirection: "row", gap: 1, marginBottom: 1, children: [(lp?.resource_size_request ||
|
|
380
|
+
lp?.custom_cpu_cores ||
|
|
381
|
+
lp?.custom_gb_memory ||
|
|
382
|
+
lp?.custom_disk_size ||
|
|
383
|
+
lp?.architecture) && (_jsxs(Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [_jsxs(Text, { color: colors.warning, bold: true, children: [figures.squareSmallFilled, " Resources"] }), _jsxs(Text, { dimColor: true, children: [lp?.resource_size_request && `${lp.resource_size_request}`, lp?.architecture && ` • ${lp.architecture}`, lp?.custom_cpu_cores && ` • ${lp.custom_cpu_cores}VCPU`, lp?.custom_gb_memory && ` • ${lp.custom_gb_memory}GB RAM`, lp?.custom_disk_size && ` • ${lp.custom_disk_size}GB DISC`] })] })), hasCapabilities && (_jsxs(Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [_jsxs(Text, { color: colors.info, bold: true, children: [figures.tick, " Capabilities"] }), _jsx(Text, { dimColor: true, children: selectedDevbox.capabilities
|
|
384
|
+
.filter((c) => c !== "unknown")
|
|
385
|
+
.join(", ") })] })), (selectedDevbox.blueprint_id || selectedDevbox.snapshot_id) && (_jsxs(Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [_jsxs(Text, { color: colors.secondary, bold: true, children: [figures.circleFilled, " Source"] }), _jsxs(Text, { dimColor: true, children: [selectedDevbox.blueprint_id &&
|
|
386
|
+
`BP: ${selectedDevbox.blueprint_id}`, selectedDevbox.snapshot_id &&
|
|
387
|
+
`Snap: ${selectedDevbox.snapshot_id}`] })] }))] }), selectedDevbox.metadata &&
|
|
388
|
+
Object.keys(selectedDevbox.metadata).length > 0 && (_jsx(Box, { marginBottom: 1, paddingX: 1, children: _jsx(MetadataDisplay, { metadata: selectedDevbox.metadata, showBorder: false }) })), selectedDevbox.failure_reason && (_jsxs(Box, { marginBottom: 1, paddingX: 1, children: [_jsxs(Text, { color: colors.error, bold: true, children: [figures.cross, " "] }), _jsx(Text, { color: colors.error, dimColor: true, children: selectedDevbox.failure_reason })] })), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: colors.primary, bold: true, children: [figures.play, " Actions"] }), _jsx(Box, { flexDirection: "column", children: operations.map((op, index) => {
|
|
311
389
|
const isSelected = index === selectedOperation;
|
|
312
|
-
return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ?
|
|
313
|
-
}) })] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color:
|
|
390
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? colors.primary : colors.textDim, children: [isSelected ? figures.pointer : " ", " "] }), _jsxs(Text, { color: isSelected ? op.color : colors.textDim, bold: isSelected, children: [op.icon, " ", op.label] }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "[", op.shortcut, "]"] })] }, op.key));
|
|
391
|
+
}) })] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.textDim, dimColor: true, children: [figures.arrowUp, figures.arrowDown, " Navigate \u2022 [Enter] Execute \u2022 [i] Full Details \u2022 [o] Browser \u2022 [q] Back"] }) })] }));
|
|
314
392
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Text } from
|
|
3
|
-
import figures from
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import figures from "figures";
|
|
4
|
+
import { colors } from "../utils/theme.js";
|
|
5
|
+
export const ErrorMessage = ({ message, error, }) => {
|
|
6
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: colors.error, bold: true, children: [figures.cross, " ", message] }) }), error && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: colors.textDim, dimColor: true, children: error.message }) }))] }));
|
|
6
7
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React from
|
|
3
|
-
import { Box, Text } from
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import { colors } from "../utils/theme.js";
|
|
4
5
|
export const Header = React.memo(({ title, subtitle }) => {
|
|
5
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { bold: true, color:
|
|
6
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { bold: true, color: colors.accent3, children: ["\u258C", title] }), subtitle && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: colors.textDim, dimColor: true, children: subtitle })] }))] }), _jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: colors.accent3, children: "─".repeat(title.length + 1) }) })] }));
|
|
6
7
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React from
|
|
3
|
-
import { Box, Text, useInput, useApp } from
|
|
4
|
-
import figures from
|
|
5
|
-
import { Banner } from
|
|
6
|
-
import { Breadcrumb } from
|
|
7
|
-
import { VERSION } from
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Box, Text, useInput, useApp } from "ink";
|
|
4
|
+
import figures from "figures";
|
|
5
|
+
import { Banner } from "./Banner.js";
|
|
6
|
+
import { Breadcrumb } from "./Breadcrumb.js";
|
|
7
|
+
import { VERSION } from "../cli.js";
|
|
8
|
+
import { colors } from "../utils/theme.js";
|
|
8
9
|
export const MainMenu = React.memo(({ onSelect }) => {
|
|
9
10
|
const { exit } = useApp();
|
|
10
11
|
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
|
@@ -12,25 +13,25 @@ export const MainMenu = React.memo(({ onSelect }) => {
|
|
|
12
13
|
const terminalHeight = React.useMemo(() => process.stdout.rows || 24, []);
|
|
13
14
|
const menuItems = React.useMemo(() => [
|
|
14
15
|
{
|
|
15
|
-
key:
|
|
16
|
-
label:
|
|
17
|
-
description:
|
|
18
|
-
icon:
|
|
19
|
-
color:
|
|
16
|
+
key: "devboxes",
|
|
17
|
+
label: "Devboxes",
|
|
18
|
+
description: "Manage cloud development environments",
|
|
19
|
+
icon: "◉",
|
|
20
|
+
color: colors.accent1,
|
|
20
21
|
},
|
|
21
22
|
{
|
|
22
|
-
key:
|
|
23
|
-
label:
|
|
24
|
-
description:
|
|
25
|
-
icon:
|
|
26
|
-
color:
|
|
23
|
+
key: "blueprints",
|
|
24
|
+
label: "Blueprints",
|
|
25
|
+
description: "Create and manage devbox templates",
|
|
26
|
+
icon: "▣",
|
|
27
|
+
color: colors.accent2,
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
|
-
key:
|
|
30
|
-
label:
|
|
31
|
-
description:
|
|
32
|
-
icon:
|
|
33
|
-
color:
|
|
30
|
+
key: "snapshots",
|
|
31
|
+
label: "Snapshots",
|
|
32
|
+
description: "Save and restore devbox states",
|
|
33
|
+
icon: "◈",
|
|
34
|
+
color: colors.accent3,
|
|
34
35
|
},
|
|
35
36
|
], []);
|
|
36
37
|
useInput((input, key) => {
|
|
@@ -46,26 +47,26 @@ export const MainMenu = React.memo(({ onSelect }) => {
|
|
|
46
47
|
else if (key.escape) {
|
|
47
48
|
exit();
|
|
48
49
|
}
|
|
49
|
-
else if (input ===
|
|
50
|
-
onSelect(
|
|
50
|
+
else if (input === "d" || input === "1") {
|
|
51
|
+
onSelect("devboxes");
|
|
51
52
|
}
|
|
52
|
-
else if (input ===
|
|
53
|
-
onSelect(
|
|
53
|
+
else if (input === "b" || input === "2") {
|
|
54
|
+
onSelect("blueprints");
|
|
54
55
|
}
|
|
55
|
-
else if (input ===
|
|
56
|
-
onSelect(
|
|
56
|
+
else if (input === "s" || input === "3") {
|
|
57
|
+
onSelect("snapshots");
|
|
57
58
|
}
|
|
58
59
|
});
|
|
59
60
|
// Use compact layout if terminal height is less than 20 lines (memoized)
|
|
60
61
|
const useCompactLayout = React.useMemo(() => terminalHeight < 20, [terminalHeight]);
|
|
61
62
|
if (useCompactLayout) {
|
|
62
|
-
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { paddingX: 2, marginBottom: 1, children: [_jsx(Text, { color:
|
|
63
|
+
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsxs(Box, { paddingX: 2, marginBottom: 1, children: [_jsx(Text, { color: colors.primary, bold: true, children: "RUNLOOP.ai" }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "\u2022 Cloud development environments \u2022 v", VERSION] })] }), _jsx(Box, { flexDirection: "column", paddingX: 2, children: menuItems.map((item, index) => {
|
|
63
64
|
const isSelected = index === selectedIndex;
|
|
64
|
-
return (_jsxs(Box, { marginBottom: 0, children: [_jsx(Text, { color: isSelected ? item.color :
|
|
65
|
-
}) }), _jsx(Box, { paddingX: 2, marginTop: 1, children: _jsxs(Text, { color:
|
|
65
|
+
return (_jsxs(Box, { marginBottom: 0, children: [_jsx(Text, { color: isSelected ? item.color : colors.textDim, children: isSelected ? figures.pointer : " " }), _jsx(Text, { children: " " }), _jsx(Text, { color: item.color, bold: true, children: item.icon }), _jsx(Text, { children: " " }), _jsx(Text, { color: isSelected ? item.color : colors.text, bold: isSelected, children: item.label }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "- ", item.description] }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: [" ", "[", index + 1, "]"] })] }, item.key));
|
|
66
|
+
}) }), _jsx(Box, { paddingX: 2, marginTop: 1, children: _jsxs(Text, { color: colors.textDim, dimColor: true, children: [figures.arrowUp, figures.arrowDown, " Navigate \u2022 [1-3] Quick select \u2022 [Enter] Select \u2022 [Esc] Quit"] }) })] }));
|
|
66
67
|
}
|
|
67
|
-
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsx(Breadcrumb, { items: [{ label:
|
|
68
|
+
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsx(Breadcrumb, { items: [{ label: "Home", active: true }], showVersionCheck: true }), _jsx(Box, { flexShrink: 0, children: _jsx(Banner, {}) }), _jsx(Box, { flexDirection: "column", paddingX: 2, flexShrink: 0, children: _jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: colors.textDim, dimColor: true, children: ["Cloud development environments for your team \u2022 v", VERSION] }) }) }), _jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, flexGrow: 1, children: [_jsx(Box, { paddingX: 1, flexShrink: 0, children: _jsx(Text, { color: colors.text, bold: true, children: "Select a resource:" }) }), menuItems.map((item, index) => {
|
|
68
69
|
const isSelected = index === selectedIndex;
|
|
69
|
-
return (_jsxs(Box, { paddingX: 2, paddingY: 0, borderStyle: isSelected ?
|
|
70
|
-
})] }), _jsx(Box, { paddingX: 2, children: _jsx(Box, { paddingX: 1, children: _jsxs(Text, { color:
|
|
70
|
+
return (_jsxs(Box, { paddingX: 2, paddingY: 0, borderStyle: isSelected ? "round" : "single", borderColor: isSelected ? item.color : colors.border, marginTop: index === 0 ? 1 : 0, flexShrink: 0, children: [_jsx(Text, { color: item.color, bold: true, children: item.icon }), _jsx(Text, { children: " " }), _jsx(Text, { color: isSelected ? item.color : colors.text, bold: isSelected, children: item.label }), _jsx(Text, { color: colors.textDim, children: " " }), _jsx(Text, { color: colors.textDim, dimColor: true, children: item.description }), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.textDim, dimColor: true, children: ["[", index + 1, "]"] })] }, item.key));
|
|
71
|
+
})] }), _jsx(Box, { paddingX: 2, flexShrink: 0, children: _jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: colors.textDim, dimColor: true, children: [figures.arrowUp, figures.arrowDown, " Navigate \u2022 [1-3] Quick select \u2022 [Enter] Select \u2022 [Esc] Quit"] }) }) })] }));
|
|
71
72
|
});
|