@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.
Files changed (73) hide show
  1. package/README.md +64 -29
  2. package/dist/cli.js +401 -92
  3. package/dist/commands/auth.js +12 -11
  4. package/dist/commands/blueprint/create.js +108 -0
  5. package/dist/commands/blueprint/get.js +37 -0
  6. package/dist/commands/blueprint/list.js +293 -225
  7. package/dist/commands/blueprint/logs.js +40 -0
  8. package/dist/commands/blueprint/preview.js +45 -0
  9. package/dist/commands/devbox/create.js +10 -9
  10. package/dist/commands/devbox/delete.js +8 -8
  11. package/dist/commands/devbox/download.js +49 -0
  12. package/dist/commands/devbox/exec.js +23 -13
  13. package/dist/commands/devbox/execAsync.js +43 -0
  14. package/dist/commands/devbox/get.js +37 -0
  15. package/dist/commands/devbox/getAsync.js +37 -0
  16. package/dist/commands/devbox/list.js +328 -190
  17. package/dist/commands/devbox/logs.js +40 -0
  18. package/dist/commands/devbox/read.js +49 -0
  19. package/dist/commands/devbox/resume.js +37 -0
  20. package/dist/commands/devbox/rsync.js +118 -0
  21. package/dist/commands/devbox/scp.js +122 -0
  22. package/dist/commands/devbox/shutdown.js +37 -0
  23. package/dist/commands/devbox/ssh.js +104 -0
  24. package/dist/commands/devbox/suspend.js +37 -0
  25. package/dist/commands/devbox/tunnel.js +120 -0
  26. package/dist/commands/devbox/upload.js +10 -10
  27. package/dist/commands/devbox/write.js +51 -0
  28. package/dist/commands/mcp-http.js +37 -0
  29. package/dist/commands/mcp-install.js +120 -0
  30. package/dist/commands/mcp.js +30 -0
  31. package/dist/commands/menu.js +20 -20
  32. package/dist/commands/object/delete.js +37 -0
  33. package/dist/commands/object/download.js +88 -0
  34. package/dist/commands/object/get.js +37 -0
  35. package/dist/commands/object/list.js +112 -0
  36. package/dist/commands/object/upload.js +130 -0
  37. package/dist/commands/snapshot/create.js +12 -11
  38. package/dist/commands/snapshot/delete.js +8 -8
  39. package/dist/commands/snapshot/list.js +56 -97
  40. package/dist/commands/snapshot/status.js +37 -0
  41. package/dist/components/ActionsPopup.js +16 -13
  42. package/dist/components/Banner.js +4 -4
  43. package/dist/components/Breadcrumb.js +55 -5
  44. package/dist/components/DetailView.js +7 -4
  45. package/dist/components/DevboxActionsMenu.js +315 -178
  46. package/dist/components/DevboxCard.js +15 -14
  47. package/dist/components/DevboxCreatePage.js +147 -113
  48. package/dist/components/DevboxDetailPage.js +180 -102
  49. package/dist/components/ErrorMessage.js +5 -4
  50. package/dist/components/Header.js +4 -3
  51. package/dist/components/MainMenu.js +34 -33
  52. package/dist/components/MetadataDisplay.js +17 -9
  53. package/dist/components/OperationsMenu.js +6 -5
  54. package/dist/components/ResourceActionsMenu.js +117 -0
  55. package/dist/components/ResourceListView.js +213 -0
  56. package/dist/components/Spinner.js +5 -4
  57. package/dist/components/StatusBadge.js +81 -31
  58. package/dist/components/SuccessMessage.js +4 -3
  59. package/dist/components/Table.example.js +53 -23
  60. package/dist/components/Table.js +19 -11
  61. package/dist/hooks/useCursorPagination.js +125 -0
  62. package/dist/mcp/server-http.js +416 -0
  63. package/dist/mcp/server.js +397 -0
  64. package/dist/utils/CommandExecutor.js +16 -12
  65. package/dist/utils/client.js +7 -7
  66. package/dist/utils/config.js +130 -4
  67. package/dist/utils/interactiveCommand.js +2 -2
  68. package/dist/utils/output.js +17 -17
  69. package/dist/utils/ssh.js +160 -0
  70. package/dist/utils/sshSession.js +16 -12
  71. package/dist/utils/theme.js +22 -0
  72. package/dist/utils/url.js +4 -4
  73. 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 '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';
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
- { key: 'logs', label: 'View Logs', color: 'blue', icon: figures.info, shortcut: 'l' },
40
- { key: 'exec', label: 'Execute Command', color: 'green', icon: figures.play, shortcut: 'e' },
41
- { key: 'upload', label: 'Upload File', color: 'green', icon: figures.arrowUp, shortcut: 'u' },
42
- { key: 'snapshot', label: 'Create Snapshot', color: 'yellow', icon: figures.circleFilled, shortcut: 'n' },
43
- { key: 'ssh', label: 'SSH onto the box', color: 'cyan', icon: figures.arrowRight, shortcut: 's' },
44
- { key: 'tunnel', label: 'Open Tunnel', color: 'magenta', icon: figures.pointerSmall, shortcut: 't' },
45
- { key: 'suspend', label: 'Suspend Devbox', color: 'yellow', icon: figures.squareSmallFilled, shortcut: 'p' },
46
- { key: 'resume', label: 'Resume Devbox', color: 'green', icon: figures.play, shortcut: 'r' },
47
- { key: 'delete', label: 'Shutdown Devbox', color: 'red', icon: figures.cross, shortcut: 'd' },
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 ? allOperations.filter(op => {
51
- const status = selectedDevbox.status;
52
- // When suspended: logs and resume
53
- if (status === 'suspended') {
54
- return op.key === 'resume' || op.key === 'logs';
55
- }
56
- // When not running (shutdown, failure, etc): only logs
57
- if (status !== 'running' && status !== 'provisioning' && status !== 'initializing') {
58
- return op.key === 'logs';
59
- }
60
- // When running: everything except resume
61
- if (status === 'running') {
62
- return op.key !== 'resume';
63
- }
64
- // Default for transitional states (provisioning, initializing)
65
- return op.key === 'logs' || op.key === 'delete';
66
- }) : allOperations;
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 ? new Date(selectedDevbox.create_time_ms).toLocaleString() : '', [selectedDevbox.create_time_ms]);
69
- const createTimeAgo = React.useMemo(() => selectedDevbox.create_time_ms ? formatTimeAgo(selectedDevbox.create_time_ms) : '', [selectedDevbox.create_time_ms]);
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 === 'q' || key.escape) {
140
+ if (input === "q" || key.escape) {
78
141
  setShowDetailedInfo(false);
79
142
  setDetailScroll(0);
80
143
  }
81
- else if (input === 'j' || input === 's' || key.downArrow) {
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 === 'k' || input === 'w' || key.upArrow) {
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 === 'q' || key.escape) {
163
+ if (input === "q" || key.escape) {
101
164
  console.clear();
102
165
  onBack();
103
166
  }
104
- else if (input === 'i') {
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 === 'a') {
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 === 'o') {
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('child_process');
194
+ const { exec } = await import("child_process");
132
195
  const platform = process.platform;
133
196
  let openCommand;
134
- if (platform === 'darwin') {
197
+ if (platform === "darwin") {
135
198
  openCommand = `open "${url}"`;
136
199
  }
137
- else if (platform === 'win32') {
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: "yellow", bold: true, children: "Devbox Details" }, "core-title"));
157
- lines.push(_jsxs(Text, { dimColor: true, children: [" ID: ", selectedDevbox.id] }, "core-id"));
158
- lines.push(_jsxs(Text, { dimColor: true, children: [" Name: ", selectedDevbox.name || '(none)'] }, "core-name"));
159
- lines.push(_jsxs(Text, { dimColor: true, children: [" Status: ", capitalize(selectedDevbox.status)] }, "core-status"));
160
- lines.push(_jsxs(Text, { dimColor: true, children: [" Created: ", new Date(selectedDevbox.create_time_ms).toLocaleString()] }, "core-created"));
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: [" Ended: ", new Date(selectedDevbox.end_time_ms).toLocaleString()] }, "core-ended"));
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: "yellow", bold: true, children: "Capabilities" }, "cap-title"));
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: [" ", figures.pointer, " ", cap] }, `cap-${idx}`));
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: "yellow", bold: true, children: "Launch Parameters" }, "launch-title"));
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: [" Resource Size Request: ", lp.resource_size_request] }, "launch-size-req"));
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: [" Architecture: ", lp.architecture] }, "launch-arch"));
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: [" CPU Cores: ", lp.custom_cpu_cores] }, "launch-cpu"));
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: [" Memory: ", lp.custom_gb_memory, "GB"] }, "launch-memory"));
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: [" Disk Size: ", lp.custom_disk_size, "GB"] }, "launch-disk"));
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: [" Keep Alive: ", lp.keep_alive_time_seconds, "s (", Math.floor(lp.keep_alive_time_seconds / 60), "m)"] }, "launch-keepalive"));
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: [" After Idle: ", lp.after_idle.on_idle, " after ", lp.after_idle.idle_time_seconds, "s"] }, "launch-afteridle"));
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: [" Available Ports: ", lp.available_ports.join(', ')] }, "launch-ports"));
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(_jsx(Text, { dimColor: true, children: " Launch Commands:" }, "launch-launch-cmds"));
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: [" ", figures.pointer, " ", cmd] }, `launch-cmd-${idx}`));
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: [" Required Services: ", lp.required_services.join(', ')] }, "launch-services"));
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(_jsx(Text, { dimColor: true, children: " User Parameters:" }, "launch-user"));
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: [" Username: ", lp.user_parameters.username] }, "user-name"));
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: [" UID: ", lp.user_parameters.uid] }, "user-uid"));
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: "yellow", bold: true, children: "Source" }, "source-title"));
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: [" Blueprint: ", selectedDevbox.blueprint_id] }, "source-bp"));
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: [" Snapshot: ", selectedDevbox.snapshot_id] }, "source-snap"));
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: "yellow", bold: true, children: "Initiator" }, "init-title"));
235
- lines.push(_jsxs(Text, { dimColor: true, children: [" Type: ", selectedDevbox.initiator_type] }, "init-type"));
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: [" ID: ", selectedDevbox.initiator_id] }, "init-id"));
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: "yellow", bold: true, children: "Status Details" }, "status-title"));
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: "red", dimColor: true, children: [" Failure Reason: ", selectedDevbox.failure_reason] }, "status-fail"));
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: [" Shutdown Reason: ", selectedDevbox.shutdown_reason] }, "status-shut"));
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 && Object.keys(selectedDevbox.metadata).length > 0) {
254
- lines.push(_jsx(Text, { color: "yellow", bold: true, children: "Metadata" }, "meta-title"));
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: [" ", key, ": ", value] }, `meta-${idx}`));
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 && selectedDevbox.state_transitions.length > 0) {
262
- lines.push(_jsx(Text, { color: "yellow", bold: true, children: "State History" }, "state-title"));
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: [" ", text] }, `state-${idx}`));
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: "yellow", bold: true, children: "Raw JSON" }, "json-title"));
271
- const jsonLines = JSON.stringify(selectedDevbox, null, 2).split('\n');
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: [" ", line] }, `json-${idx}`));
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: 'Devboxes' },
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: 'Devboxes' },
364
+ { label: "Devboxes" },
300
365
  { label: selectedDevbox.name || selectedDevbox.id },
301
- { label: 'Full Details', active: true }
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: "gray", dimColor: true, children: selectedDevbox.id })] }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, borderStyle: "round", borderColor: "gray", paddingX: 2, paddingY: 1, children: [_jsx(Box, { flexDirection: "column", children: visibleLines }), hasLess && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "cyan", children: [figures.arrowUp, " More above"] }) })), hasMore && (_jsx(Box, { marginTop: hasLess ? 0 : 1, children: _jsxs(Text, { color: "cyan", children: [figures.arrowDown, " More below"] }) }))] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", 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] }) })] }));
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 && selectedDevbox.capabilities.filter((c) => c !== 'unknown').length > 0;
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: 'Devboxes' },
309
- { label: selectedDevbox.name || selectedDevbox.id, active: true }
310
- ] }), _jsx(Header, { title: "Devbox Details" }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, paddingY: 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", bold: true, children: selectedDevbox.name || selectedDevbox.id }), _jsx(Text, { children: " " }), _jsx(StatusBadge, { status: selectedDevbox.status }), _jsxs(Text, { color: "gray", dimColor: true, children: [" \u2022 ", selectedDevbox.id] })] }), _jsxs(Box, { children: [_jsx(Text, { color: "gray", dimColor: true, children: formattedCreateTime }), _jsxs(Text, { color: "gray", dimColor: true, children: [" (", createTimeAgo, ")"] })] }), uptime !== null && selectedDevbox.status === 'running' && (_jsxs(Box, { children: [_jsxs(Text, { color: "green", dimColor: true, children: ["Uptime: ", uptime < 60 ? `${uptime}m` : `${Math.floor(uptime / 60)}h ${uptime % 60}m`] }), lp?.keep_alive_time_seconds && (_jsxs(Text, { color: "gray", dimColor: true, children: [" \u2022 Keep-alive: ", Math.floor(lp.keep_alive_time_seconds / 60), "m"] }))] }))] }), _jsxs(Box, { flexDirection: "row", gap: 1, children: [(lp?.resource_size_request || lp?.custom_cpu_cores || lp?.custom_gb_memory || lp?.custom_disk_size || lp?.architecture) && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, paddingY: 0, flexGrow: 1, children: [_jsxs(Text, { color: "yellow", 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", borderStyle: "round", borderColor: "blue", paddingX: 1, paddingY: 0, flexGrow: 1, children: [_jsxs(Text, { color: "blue", bold: true, children: [figures.tick, " Capabilities"] }), _jsx(Text, { dimColor: true, children: selectedDevbox.capabilities.filter((c) => c !== 'unknown').join(', ') })] })), (selectedDevbox.blueprint_id || selectedDevbox.snapshot_id) && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, paddingY: 0, flexGrow: 1, children: [_jsxs(Text, { color: "magenta", bold: true, children: [figures.circleFilled, " Source"] }), _jsxs(Text, { dimColor: true, children: [selectedDevbox.blueprint_id && `BP: ${selectedDevbox.blueprint_id}`, selectedDevbox.snapshot_id && `Snap: ${selectedDevbox.snapshot_id}`] })] }))] }), selectedDevbox.metadata && Object.keys(selectedDevbox.metadata).length > 0 && (_jsx(Box, { borderStyle: "round", borderColor: "green", paddingX: 1, paddingY: 0, children: _jsx(MetadataDisplay, { metadata: selectedDevbox.metadata, showBorder: false }) })), selectedDevbox.failure_reason && (_jsxs(Box, { borderStyle: "round", borderColor: "red", paddingX: 1, paddingY: 0, children: [_jsxs(Text, { color: "red", bold: true, children: [figures.cross, " "] }), _jsx(Text, { color: "red", dimColor: true, children: selectedDevbox.failure_reason })] })), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: "cyan", bold: true, children: [figures.play, " Actions"] }), _jsx(Box, { flexDirection: "column", children: operations.map((op, index) => {
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 ? 'cyan' : 'gray', children: [isSelected ? figures.pointer : ' ', " "] }), _jsxs(Text, { color: isSelected ? op.color : 'gray', bold: isSelected, children: [op.icon, " ", op.label] }), _jsxs(Text, { color: "gray", dimColor: true, children: [" [", op.shortcut, "]"] })] }, op.key));
313
- }) })] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", dimColor: true, children: [figures.arrowUp, figures.arrowDown, " Navigate \u2022 [Enter] Execute \u2022 [i] Full Details \u2022 [o] Browser \u2022 [q] Back"] }) })] }));
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 'ink';
3
- import figures from 'figures';
4
- export const ErrorMessage = ({ message, error }) => {
5
- return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "red", bold: true, children: [figures.cross, " ", message] }) }), error && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", dimColor: true, children: error.message }) }))] }));
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 'react';
3
- import { Box, Text } from 'ink';
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: "#0a4d3a", children: ["\u258C", title] }), subtitle && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: "gray", dimColor: true, children: subtitle })] }))] }), _jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: "#0a4d3a", children: ''.repeat(title.length + 1) }) })] }));
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 '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';
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: 'devboxes',
16
- label: 'Devboxes',
17
- description: 'Manage cloud development environments',
18
- icon: '',
19
- color: 'cyan',
16
+ key: "devboxes",
17
+ label: "Devboxes",
18
+ description: "Manage cloud development environments",
19
+ icon: "",
20
+ color: colors.accent1,
20
21
  },
21
22
  {
22
- key: 'blueprints',
23
- label: 'Blueprints',
24
- description: 'Create and manage devbox templates',
25
- icon: '',
26
- color: 'magenta',
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: 'snapshots',
30
- label: 'Snapshots',
31
- description: 'Save and restore devbox states',
32
- icon: '',
33
- color: 'green',
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 === 'd' || input === '1') {
50
- onSelect('devboxes');
50
+ else if (input === "d" || input === "1") {
51
+ onSelect("devboxes");
51
52
  }
52
- else if (input === 'b' || input === '2') {
53
- onSelect('blueprints');
53
+ else if (input === "b" || input === "2") {
54
+ onSelect("blueprints");
54
55
  }
55
- else if (input === 's' || input === '3') {
56
- onSelect('snapshots');
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: "cyan", bold: true, children: "RUNLOOP.ai" }), _jsxs(Text, { color: "gray", dimColor: true, children: [' ', "\u2022 Cloud development environments \u2022 v", VERSION] })] }), _jsx(Box, { flexDirection: "column", paddingX: 2, children: menuItems.map((item, index) => {
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 : 'gray', 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 : 'white', bold: isSelected, children: item.label }), _jsxs(Text, { color: "gray", dimColor: true, children: [' ', "- ", item.description] }), _jsxs(Text, { color: "gray", dimColor: true, children: [' ', "[", index + 1, "]"] })] }, item.key));
65
- }) }), _jsx(Box, { paddingX: 2, marginTop: 1, children: _jsxs(Text, { color: "gray", dimColor: true, children: [figures.arrowUp, figures.arrowDown, " Navigate \u2022 [1-3] Quick select \u2022 [Enter] Select \u2022 [Esc] Quit"] }) })] }));
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: 'Home', active: true }] }), _jsx(Box, { marginBottom: 1, children: _jsx(Banner, {}) }), _jsx(Box, { flexDirection: "column", paddingX: 2, marginBottom: 1, children: _jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: "gray", dimColor: true, children: ["Cloud development environments for your team \u2022 v", VERSION] }) }) }), _jsxs(Box, { flexDirection: "column", paddingX: 2, marginBottom: 1, marginTop: 1, children: [_jsx(Box, { marginBottom: 1, paddingX: 1, children: _jsx(Text, { color: "white", bold: true, children: "Select a resource:" }) }), menuItems.map((item, index) => {
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 ? 'round' : 'single', borderColor: isSelected ? item.color : 'gray', marginBottom: 0, children: [_jsx(Text, { color: item.color, bold: true, children: item.icon }), _jsx(Text, { children: " " }), _jsx(Text, { color: isSelected ? item.color : 'white', bold: isSelected, children: item.label }), _jsx(Text, { color: "gray", children: " " }), _jsx(Text, { color: "gray", dimColor: true, children: item.description }), _jsx(Text, { children: " " }), _jsxs(Text, { color: "gray", dimColor: true, children: ["[", index + 1, "]"] })] }, item.key));
70
- })] }), _jsx(Box, { paddingX: 2, children: _jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: "gray", dimColor: true, children: [figures.arrowUp, figures.arrowDown, " Navigate \u2022 [1-3] Quick select \u2022 [Enter] Select \u2022 [Esc] Quit"] }) }) })] }));
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
  });