@polterware/polter 0.0.0 → 0.1.0
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/dist/api.js +45 -0
- package/dist/chunk-EB76TTZL.js +0 -0
- package/dist/chunk-TWWRDI3Q.js +1488 -0
- package/dist/index.js +2899 -951
- package/dist/mcp.js +13965 -0
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -1,36 +1,102 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
allCommands,
|
|
4
|
+
applyActions,
|
|
5
|
+
commandExists,
|
|
6
|
+
executePipeline,
|
|
7
|
+
features,
|
|
8
|
+
findCommandByValue,
|
|
9
|
+
findNearestPackageRoot,
|
|
10
|
+
findPipelineByName,
|
|
11
|
+
getAllPipelines,
|
|
12
|
+
getCommandById,
|
|
13
|
+
getCommandValue,
|
|
14
|
+
getCurrentStatus,
|
|
15
|
+
getFeatureById,
|
|
16
|
+
getFlagsForTool,
|
|
17
|
+
getOrCreateProjectConfig,
|
|
18
|
+
getProjectConfigPath,
|
|
19
|
+
getToolInfo,
|
|
20
|
+
parsePolterYaml,
|
|
21
|
+
planChanges,
|
|
22
|
+
resolveToolCommand,
|
|
23
|
+
runCommand,
|
|
24
|
+
runSupabaseCommand,
|
|
25
|
+
savePipeline,
|
|
26
|
+
writeProjectConfig
|
|
27
|
+
} from "./chunk-TWWRDI3Q.js";
|
|
2
28
|
|
|
3
29
|
// src/index.tsx
|
|
4
|
-
import
|
|
30
|
+
import React19 from "react";
|
|
5
31
|
import { render } from "ink";
|
|
6
32
|
|
|
7
33
|
// src/app.tsx
|
|
8
|
-
import { Box as
|
|
34
|
+
import { Box as Box21, Text as Text23, useApp } from "ink";
|
|
9
35
|
|
|
10
36
|
// src/hooks/useNavigation.ts
|
|
11
37
|
import { useState, useCallback } from "react";
|
|
12
38
|
function useNavigation() {
|
|
13
|
-
const [
|
|
14
|
-
screen: "
|
|
15
|
-
|
|
16
|
-
|
|
39
|
+
const [stack, setStack] = useState([
|
|
40
|
+
{ screen: "home", params: {} }
|
|
41
|
+
]);
|
|
42
|
+
const current = stack[stack.length - 1];
|
|
17
43
|
const navigate = useCallback((screen, params) => {
|
|
18
|
-
|
|
19
|
-
screen,
|
|
20
|
-
params: params ?? {}
|
|
21
|
-
});
|
|
44
|
+
setStack((prev) => [...prev, { screen, params: params ?? {} }]);
|
|
22
45
|
}, []);
|
|
23
46
|
const goBack = useCallback(() => {
|
|
24
|
-
|
|
47
|
+
setStack((prev) => {
|
|
48
|
+
if (prev.length <= 1) return [{ screen: "home", params: {} }];
|
|
49
|
+
return prev.slice(0, -1);
|
|
50
|
+
});
|
|
51
|
+
}, []);
|
|
52
|
+
const goHome = useCallback(() => {
|
|
53
|
+
setStack([{ screen: "home", params: {} }]);
|
|
25
54
|
}, []);
|
|
26
|
-
return {
|
|
55
|
+
return {
|
|
56
|
+
screen: current.screen,
|
|
57
|
+
params: current.params,
|
|
58
|
+
navigate,
|
|
59
|
+
goBack,
|
|
60
|
+
goHome
|
|
61
|
+
};
|
|
27
62
|
}
|
|
28
63
|
|
|
29
|
-
// src/
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
64
|
+
// src/hooks/useTerminalDimensions.ts
|
|
65
|
+
import { useState as useState2, useEffect } from "react";
|
|
66
|
+
import { useStdout } from "ink";
|
|
67
|
+
function useTerminalDimensions() {
|
|
68
|
+
const { stdout } = useStdout();
|
|
69
|
+
const [dimensions, setDimensions] = useState2(() => ({
|
|
70
|
+
width: stdout?.columns ?? process.stdout.columns ?? 80,
|
|
71
|
+
height: stdout?.rows ?? process.stdout.rows ?? 24
|
|
72
|
+
}));
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
const onResize = () => {
|
|
75
|
+
setDimensions({
|
|
76
|
+
width: stdout?.columns ?? process.stdout.columns ?? 80,
|
|
77
|
+
height: stdout?.rows ?? process.stdout.rows ?? 24
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
process.stdout.on("resize", onResize);
|
|
81
|
+
return () => {
|
|
82
|
+
process.stdout.off("resize", onResize);
|
|
83
|
+
};
|
|
84
|
+
}, [stdout]);
|
|
85
|
+
return dimensions;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/hooks/useTerminalWidth.ts
|
|
89
|
+
function useTerminalWidth() {
|
|
90
|
+
return useTerminalDimensions().width;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/hooks/useTerminalHeight.ts
|
|
94
|
+
function useTerminalHeight() {
|
|
95
|
+
return useTerminalDimensions().height;
|
|
96
|
+
}
|
|
32
97
|
|
|
33
98
|
// src/components/GhostBanner.tsx
|
|
99
|
+
import React from "react";
|
|
34
100
|
import { Box, Text } from "ink";
|
|
35
101
|
|
|
36
102
|
// src/theme.ts
|
|
@@ -67,6 +133,33 @@ var colors = {
|
|
|
67
133
|
white: pc.white,
|
|
68
134
|
highlight: (s) => supabaseBg(pc.black(pc.bold(s)))
|
|
69
135
|
};
|
|
136
|
+
var symbols = {
|
|
137
|
+
pointer: "\u203A",
|
|
138
|
+
pointerActive: "\u276F",
|
|
139
|
+
check: "\u2713",
|
|
140
|
+
cross: "\u2717",
|
|
141
|
+
bullet: "\u25CF",
|
|
142
|
+
bulletEmpty: "\u25CB",
|
|
143
|
+
pin: "\u{1F4CC}",
|
|
144
|
+
ghost: "\u{1F47B}",
|
|
145
|
+
exit: "\u{1F6AA}",
|
|
146
|
+
edit: "\u270F\uFE0F",
|
|
147
|
+
gear: "\u2699\uFE0F",
|
|
148
|
+
back: "\u2190",
|
|
149
|
+
arrowRight: "\u2192",
|
|
150
|
+
separator: "\u2500",
|
|
151
|
+
cornerTL: "\u256D",
|
|
152
|
+
cornerTR: "\u256E",
|
|
153
|
+
cornerBL: "\u2570",
|
|
154
|
+
cornerBR: "\u256F",
|
|
155
|
+
vertical: "\u2502",
|
|
156
|
+
horizontal: "\u2500"
|
|
157
|
+
};
|
|
158
|
+
var panel = {
|
|
159
|
+
borderFocused: SUPABASE_HEX,
|
|
160
|
+
borderDim: "#555555",
|
|
161
|
+
sidebarWidth: (termWidth) => Math.max(20, Math.min(35, Math.floor(termWidth * 0.3)))
|
|
162
|
+
};
|
|
70
163
|
var ghost = {
|
|
71
164
|
art: [
|
|
72
165
|
" \u2584\u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584\u2584",
|
|
@@ -82,7 +175,84 @@ var ghost = {
|
|
|
82
175
|
|
|
83
176
|
// src/components/GhostBanner.tsx
|
|
84
177
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
85
|
-
function
|
|
178
|
+
var ToolStatusBadges = React.memo(function ToolStatusBadges2() {
|
|
179
|
+
const tools = ["supabase", "gh", "vercel"].map(getToolInfo);
|
|
180
|
+
return /* @__PURE__ */ jsx(Box, { gap: 1, children: tools.map((t) => /* @__PURE__ */ jsxs(
|
|
181
|
+
Text,
|
|
182
|
+
{
|
|
183
|
+
color: t.installed ? inkColors.accent : "red",
|
|
184
|
+
dimColor: !t.installed,
|
|
185
|
+
children: [
|
|
186
|
+
t.id,
|
|
187
|
+
":",
|
|
188
|
+
t.installed ? "ok" : "x"
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
t.id
|
|
192
|
+
)) });
|
|
193
|
+
});
|
|
194
|
+
function GhostBanner({ width = 80, compact = false }) {
|
|
195
|
+
if (compact) {
|
|
196
|
+
if (width < 60) {
|
|
197
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
198
|
+
/* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
|
|
199
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
200
|
+
" v",
|
|
201
|
+
VERSION,
|
|
202
|
+
" "
|
|
203
|
+
] }),
|
|
204
|
+
/* @__PURE__ */ jsx(ToolStatusBadges, {})
|
|
205
|
+
] });
|
|
206
|
+
}
|
|
207
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
|
|
208
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx(Text, { color: inkColors.accent, children: line }, i)) }),
|
|
209
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", justifyContent: "center", children: [
|
|
210
|
+
/* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
|
|
211
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
212
|
+
"v",
|
|
213
|
+
VERSION
|
|
214
|
+
] }),
|
|
215
|
+
/* @__PURE__ */ jsx(ToolStatusBadges, {})
|
|
216
|
+
] })
|
|
217
|
+
] });
|
|
218
|
+
}
|
|
219
|
+
if (width < 50) {
|
|
220
|
+
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
221
|
+
/* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
|
|
222
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
223
|
+
" v",
|
|
224
|
+
VERSION
|
|
225
|
+
] })
|
|
226
|
+
] });
|
|
227
|
+
}
|
|
228
|
+
if (width < 80) {
|
|
229
|
+
return /* @__PURE__ */ jsxs(
|
|
230
|
+
Box,
|
|
231
|
+
{
|
|
232
|
+
flexDirection: "column",
|
|
233
|
+
borderStyle: "single",
|
|
234
|
+
borderColor: inkColors.accent,
|
|
235
|
+
paddingX: 1,
|
|
236
|
+
marginBottom: 1,
|
|
237
|
+
children: [
|
|
238
|
+
/* @__PURE__ */ jsx(
|
|
239
|
+
Text,
|
|
240
|
+
{
|
|
241
|
+
backgroundColor: inkColors.accent,
|
|
242
|
+
color: inkColors.accentContrast,
|
|
243
|
+
bold: true,
|
|
244
|
+
children: " POLTER "
|
|
245
|
+
}
|
|
246
|
+
),
|
|
247
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
248
|
+
"Version: ",
|
|
249
|
+
VERSION
|
|
250
|
+
] }),
|
|
251
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Project & infrastructure orchestrator" })
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
}
|
|
86
256
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", alignItems: "flex-start", gap: 2, marginBottom: 1, children: [
|
|
87
257
|
/* @__PURE__ */ jsx(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx(Text, { color: inkColors.accent, children: line }, i)) }),
|
|
88
258
|
/* @__PURE__ */ jsxs(
|
|
@@ -106,16 +276,45 @@ function GhostBanner() {
|
|
|
106
276
|
"Version: ",
|
|
107
277
|
VERSION
|
|
108
278
|
] }),
|
|
109
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "
|
|
279
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Project & infrastructure orchestrator" })
|
|
110
280
|
]
|
|
111
281
|
}
|
|
112
282
|
)
|
|
113
283
|
] });
|
|
114
284
|
}
|
|
115
285
|
|
|
286
|
+
// src/screens/Home.tsx
|
|
287
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
|
|
288
|
+
import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
|
|
289
|
+
|
|
290
|
+
// src/components/TabBar.tsx
|
|
291
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
292
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
293
|
+
function TabBar({ tabs, activeIndex, width = 80 }) {
|
|
294
|
+
const narrow = width < 50;
|
|
295
|
+
const medium = width < 80;
|
|
296
|
+
return /* @__PURE__ */ jsx2(Box2, { gap: 1, children: tabs.map((tab, i) => {
|
|
297
|
+
const isActive = i === activeIndex;
|
|
298
|
+
const showLabel = narrow ? false : medium ? isActive : true;
|
|
299
|
+
const displayText = showLabel ? `${tab.icon} ${tab.label}` : tab.icon;
|
|
300
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
301
|
+
/* @__PURE__ */ jsx2(
|
|
302
|
+
Text2,
|
|
303
|
+
{
|
|
304
|
+
color: isActive ? inkColors.accent : void 0,
|
|
305
|
+
bold: isActive,
|
|
306
|
+
dimColor: !isActive,
|
|
307
|
+
children: displayText
|
|
308
|
+
}
|
|
309
|
+
),
|
|
310
|
+
isActive && !narrow && /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, children: "\u2550".repeat(displayText.length) })
|
|
311
|
+
] }, tab.id);
|
|
312
|
+
}) });
|
|
313
|
+
}
|
|
314
|
+
|
|
116
315
|
// src/components/SelectList.tsx
|
|
117
|
-
import { useEffect, useMemo, useState as
|
|
118
|
-
import { Box as
|
|
316
|
+
import { useEffect as useEffect2, useMemo, useState as useState3 } from "react";
|
|
317
|
+
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
119
318
|
|
|
120
319
|
// src/components/selectListSections.ts
|
|
121
320
|
function isHeader(item) {
|
|
@@ -193,16 +392,21 @@ function countBoxedSectionLines(sections) {
|
|
|
193
392
|
}
|
|
194
393
|
|
|
195
394
|
// src/components/SelectList.tsx
|
|
196
|
-
import { jsx as
|
|
395
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
197
396
|
function SelectList({
|
|
198
397
|
items,
|
|
199
398
|
onSelect,
|
|
200
399
|
onRightAction,
|
|
201
400
|
onCancel,
|
|
202
401
|
maxVisible = 16,
|
|
203
|
-
labelWidth
|
|
204
|
-
boxedSections = false
|
|
402
|
+
labelWidth: labelWidthProp,
|
|
403
|
+
boxedSections = false,
|
|
404
|
+
width = 80,
|
|
405
|
+
isInputActive = true,
|
|
406
|
+
arrowNavigation = false
|
|
205
407
|
}) {
|
|
408
|
+
const labelWidth = labelWidthProp ?? Math.max(16, Math.floor(width * 0.45));
|
|
409
|
+
const isNarrow = width < 50;
|
|
206
410
|
const isHeader2 = (item) => item.kind === "header";
|
|
207
411
|
const isSelectable = (item) => item.selectable ?? !isHeader2(item);
|
|
208
412
|
const isPinnedRow = (item) => item.section === "pinned-runs" || item.section === "pinned-commands";
|
|
@@ -215,8 +419,8 @@ function SelectList({
|
|
|
215
419
|
}, []),
|
|
216
420
|
[items]
|
|
217
421
|
);
|
|
218
|
-
const [selectedSelectableIndex, setSelectedSelectableIndex] =
|
|
219
|
-
|
|
422
|
+
const [selectedSelectableIndex, setSelectedSelectableIndex] = useState3(0);
|
|
423
|
+
useEffect2(() => {
|
|
220
424
|
if (selectableIndexes.length === 0) {
|
|
221
425
|
setSelectedSelectableIndex(0);
|
|
222
426
|
return;
|
|
@@ -250,104 +454,104 @@ function SelectList({
|
|
|
250
454
|
return next;
|
|
251
455
|
});
|
|
252
456
|
}
|
|
253
|
-
if (key.return) {
|
|
457
|
+
if (key.return || arrowNavigation && key.rightArrow) {
|
|
254
458
|
if (selectedItem) {
|
|
255
459
|
onSelect(selectedItem.value, selectedItem);
|
|
256
460
|
}
|
|
257
461
|
}
|
|
258
|
-
if (
|
|
462
|
+
if (arrowNavigation && key.leftArrow) {
|
|
463
|
+
if (onCancel) onCancel();
|
|
464
|
+
}
|
|
465
|
+
if ((input2 === "p" || input2 === "P") && onRightAction && selectedItem?.rightActionable) {
|
|
259
466
|
onRightAction(selectedItem);
|
|
260
467
|
}
|
|
261
468
|
if (key.escape && onCancel) {
|
|
262
469
|
onCancel();
|
|
263
470
|
}
|
|
264
|
-
});
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
471
|
+
}, { isActive: isInputActive });
|
|
472
|
+
const { windowStart, windowEnd, visibleItems, boxedLayout } = useMemo(() => {
|
|
473
|
+
let windowStartVal = 0;
|
|
474
|
+
if (items.length > maxVisible) {
|
|
475
|
+
const safeSelectedIndex = selectedItemIndex >= 0 ? selectedItemIndex : 0;
|
|
476
|
+
windowStartVal = Math.max(
|
|
477
|
+
0,
|
|
478
|
+
Math.min(
|
|
479
|
+
safeSelectedIndex - Math.floor(maxVisible / 2),
|
|
480
|
+
items.length - maxVisible
|
|
481
|
+
)
|
|
482
|
+
);
|
|
483
|
+
const firstVisible = items[windowStartVal];
|
|
484
|
+
if (windowStartVal > 0 && firstVisible && !isHeader2(firstVisible)) {
|
|
485
|
+
let previousHeaderIndex = -1;
|
|
486
|
+
for (let i = windowStartVal - 1; i >= 0; i--) {
|
|
487
|
+
if (isHeader2(items[i])) {
|
|
488
|
+
previousHeaderIndex = i;
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (previousHeaderIndex >= 0 && safeSelectedIndex - previousHeaderIndex < maxVisible) {
|
|
493
|
+
windowStartVal = previousHeaderIndex;
|
|
284
494
|
}
|
|
285
495
|
}
|
|
286
|
-
if (previousHeaderIndex >= 0 && safeSelectedIndex - previousHeaderIndex < maxVisible) {
|
|
287
|
-
start = previousHeaderIndex;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return start;
|
|
291
|
-
};
|
|
292
|
-
const getWindowRange = () => {
|
|
293
|
-
const initialStart = getWindowStart();
|
|
294
|
-
const initialEnd = Math.min(initialStart + maxVisible, items.length);
|
|
295
|
-
if (!boxedSections) {
|
|
296
|
-
return [initialStart, initialEnd];
|
|
297
496
|
}
|
|
298
|
-
|
|
497
|
+
const initialEnd = Math.min(windowStartVal + maxVisible, items.length);
|
|
498
|
+
let start = windowStartVal;
|
|
299
499
|
let end = initialEnd;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
500
|
+
if (boxedSections) {
|
|
501
|
+
while (start < end) {
|
|
502
|
+
const sections = buildBoxedSectionLayout(items, start, end);
|
|
503
|
+
if (countBoxedSectionLines(sections) <= maxVisible) {
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
if (selectedItemIndex >= 0 && start === selectedItemIndex && end === selectedItemIndex + 1) {
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
const canTrimStart = selectedItemIndex > start;
|
|
510
|
+
const canTrimEnd = selectedItemIndex < end - 1;
|
|
511
|
+
if (canTrimEnd && (!canTrimStart || end - selectedItemIndex >= selectedItemIndex - start)) {
|
|
512
|
+
end -= 1;
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (canTrimStart) {
|
|
516
|
+
start += 1;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
311
519
|
end -= 1;
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
if (canTrimStart) {
|
|
315
|
-
start += 1;
|
|
316
|
-
continue;
|
|
317
520
|
}
|
|
318
|
-
end -= 1;
|
|
319
521
|
}
|
|
320
|
-
return
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
522
|
+
return {
|
|
523
|
+
windowStart: start,
|
|
524
|
+
windowEnd: end,
|
|
525
|
+
visibleItems: items.slice(start, end),
|
|
526
|
+
boxedLayout: boxedSections ? buildBoxedSectionLayout(items, start, end) : []
|
|
527
|
+
};
|
|
528
|
+
}, [items, selectedItemIndex, maxVisible, boxedSections]);
|
|
325
529
|
const showScrollUp = windowStart > 0;
|
|
326
530
|
const showScrollDown = windowEnd < items.length;
|
|
327
531
|
const renderSelectableRow = (item, globalIdx) => {
|
|
328
532
|
const isSelected = globalIdx === selectedItemIndex;
|
|
329
|
-
return /* @__PURE__ */
|
|
330
|
-
/* @__PURE__ */
|
|
331
|
-
/* @__PURE__ */
|
|
332
|
-
|
|
533
|
+
return /* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
534
|
+
/* @__PURE__ */ jsx3(Text3, { color: isSelected ? inkColors.accent : void 0, children: isSelected ? "\u276F" : " " }),
|
|
535
|
+
/* @__PURE__ */ jsx3(Box3, { width: labelWidth, children: /* @__PURE__ */ jsxs3(
|
|
536
|
+
Text3,
|
|
333
537
|
{
|
|
334
538
|
color: isSelected ? inkColors.accent : isPinnedRow(item) ? "white" : void 0,
|
|
335
539
|
bold: isSelected || isPinnedRow(item),
|
|
540
|
+
wrap: "truncate",
|
|
336
541
|
children: [
|
|
337
542
|
item.icon ? `${item.icon} ` : "",
|
|
338
543
|
item.label
|
|
339
544
|
]
|
|
340
545
|
}
|
|
341
546
|
) }),
|
|
342
|
-
item.hint && /* @__PURE__ */
|
|
343
|
-
isSelected && item.rightActionable && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2192 pin/unpin" })
|
|
547
|
+
!isNarrow && item.hint && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: item.hint })
|
|
344
548
|
] }, item.id ?? `${item.value}-${globalIdx}`);
|
|
345
549
|
};
|
|
346
|
-
return /* @__PURE__ */
|
|
347
|
-
showScrollUp && /* @__PURE__ */
|
|
550
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
551
|
+
showScrollUp && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " \u2191 more" }),
|
|
348
552
|
boxedSections ? boxedLayout.map((section) => {
|
|
349
553
|
if (section.type === "heading") {
|
|
350
|
-
return /* @__PURE__ */
|
|
554
|
+
return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: section.label }) }, section.key);
|
|
351
555
|
}
|
|
352
556
|
const hasSelectedRow = section.rows.some(
|
|
353
557
|
(row) => row.globalIndex === selectedItemIndex
|
|
@@ -355,8 +559,8 @@ function SelectList({
|
|
|
355
559
|
const isPinnedSection = section.rows.some(
|
|
356
560
|
(row) => isPinnedRow(row.item)
|
|
357
561
|
);
|
|
358
|
-
return /* @__PURE__ */
|
|
359
|
-
|
|
562
|
+
return /* @__PURE__ */ jsxs3(
|
|
563
|
+
Box3,
|
|
360
564
|
{
|
|
361
565
|
flexDirection: "column",
|
|
362
566
|
borderStyle: "round",
|
|
@@ -364,8 +568,8 @@ function SelectList({
|
|
|
364
568
|
borderDimColor: !hasSelectedRow && !isPinnedSection,
|
|
365
569
|
paddingX: 1,
|
|
366
570
|
children: [
|
|
367
|
-
section.title && /* @__PURE__ */
|
|
368
|
-
/* @__PURE__ */
|
|
571
|
+
section.title && /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: section.title }),
|
|
572
|
+
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: section.rows.map(
|
|
369
573
|
(row) => renderSelectableRow(row.item, row.globalIndex)
|
|
370
574
|
) })
|
|
371
575
|
]
|
|
@@ -376,28 +580,28 @@ function SelectList({
|
|
|
376
580
|
const globalIdx = windowStart + i;
|
|
377
581
|
const selectable = isSelectable(item);
|
|
378
582
|
if (!selectable) {
|
|
379
|
-
return /* @__PURE__ */
|
|
380
|
-
|
|
583
|
+
return /* @__PURE__ */ jsx3(
|
|
584
|
+
Box3,
|
|
381
585
|
{
|
|
382
586
|
marginTop: i === 0 ? 0 : 1,
|
|
383
|
-
children: /* @__PURE__ */
|
|
587
|
+
children: /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: item.label })
|
|
384
588
|
},
|
|
385
589
|
item.id ?? `${item.value}-${globalIdx}`
|
|
386
590
|
);
|
|
387
591
|
}
|
|
388
592
|
return renderSelectableRow(item, globalIdx);
|
|
389
593
|
}),
|
|
390
|
-
showScrollDown && /* @__PURE__ */
|
|
594
|
+
showScrollDown && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " \u2193 more" })
|
|
391
595
|
] });
|
|
392
596
|
}
|
|
393
597
|
|
|
394
598
|
// src/components/StatusBar.tsx
|
|
395
|
-
import { Box as
|
|
396
|
-
import { jsx as
|
|
397
|
-
function StatusBar({ hint }) {
|
|
398
|
-
return /* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
400
|
-
/* @__PURE__ */
|
|
599
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
600
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
601
|
+
function StatusBar({ hint, width = 80 }) {
|
|
602
|
+
return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, justifyContent: "space-between", children: [
|
|
603
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: hint || "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back" }),
|
|
604
|
+
width >= 60 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
401
605
|
"polter v",
|
|
402
606
|
VERSION
|
|
403
607
|
] })
|
|
@@ -459,184 +663,53 @@ function isPinnedRun(runCommand2) {
|
|
|
459
663
|
return getPinnedRuns().includes(runCommand2);
|
|
460
664
|
}
|
|
461
665
|
|
|
462
|
-
// src/
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
{
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
hint: "Start local Supabase containers"
|
|
492
|
-
},
|
|
493
|
-
{ value: "stop", label: "stop", hint: "Stop local Supabase containers" },
|
|
494
|
-
{ value: "status", label: "status", hint: "Show container status" },
|
|
495
|
-
{ value: "db", label: "db", hint: "Manage Postgres databases" },
|
|
496
|
-
{
|
|
497
|
-
value: "migration",
|
|
498
|
-
label: "migration",
|
|
499
|
-
hint: "Manage migration scripts"
|
|
500
|
-
},
|
|
501
|
-
{ value: "seed", label: "seed", hint: "Seed from config.toml" },
|
|
502
|
-
{ value: "test", label: "test", hint: "Run tests on local stack" }
|
|
503
|
-
]
|
|
504
|
-
},
|
|
505
|
-
"inspect-generate": {
|
|
506
|
-
icon: "\u{1F50D}",
|
|
507
|
-
label: "Inspect & Generate",
|
|
508
|
-
description: "Tooling and introspection",
|
|
509
|
-
commands: [
|
|
510
|
-
{ value: "inspect", label: "inspect", hint: "Inspect project resources" },
|
|
511
|
-
{ value: "gen", label: "gen", hint: "Run code generation tools" },
|
|
512
|
-
{ value: "services", label: "services", hint: "Show service versions" },
|
|
513
|
-
{ value: "link", label: "link", hint: "Link to remote project" },
|
|
514
|
-
{ value: "unlink", label: "unlink", hint: "Unlink remote project" }
|
|
515
|
-
]
|
|
516
|
-
},
|
|
517
|
-
cloud: {
|
|
518
|
-
icon: "\u2601\uFE0F",
|
|
519
|
-
label: "Cloud Management",
|
|
520
|
-
description: "Core cloud resources",
|
|
521
|
-
commands: [
|
|
522
|
-
{
|
|
523
|
-
value: "projects",
|
|
524
|
-
label: "projects",
|
|
525
|
-
hint: "Manage Supabase projects"
|
|
526
|
-
},
|
|
527
|
-
{ value: "functions", label: "functions", hint: "Manage Edge Functions" },
|
|
528
|
-
{ value: "secrets", label: "secrets", hint: "Manage project secrets" },
|
|
529
|
-
{
|
|
530
|
-
value: "config",
|
|
531
|
-
label: "config",
|
|
532
|
-
hint: "Manage project configuration"
|
|
533
|
-
},
|
|
534
|
-
{ value: "storage", label: "storage", hint: "Manage Storage objects" }
|
|
535
|
-
]
|
|
536
|
-
},
|
|
537
|
-
networking: {
|
|
538
|
-
icon: "\u{1F310}",
|
|
539
|
-
label: "Networking & Security",
|
|
540
|
-
description: "Network and security config",
|
|
541
|
-
commands: [
|
|
542
|
-
{ value: "domains", label: "domains", hint: "Manage custom domains" },
|
|
543
|
-
{
|
|
544
|
-
value: "ssl-enforcement",
|
|
545
|
-
label: "ssl-enforcement",
|
|
546
|
-
hint: "Manage SSL config"
|
|
547
|
-
},
|
|
548
|
-
{
|
|
549
|
-
value: "network-bans",
|
|
550
|
-
label: "network-bans",
|
|
551
|
-
hint: "Manage network bans"
|
|
552
|
-
},
|
|
553
|
-
{
|
|
554
|
-
value: "network-restrictions",
|
|
555
|
-
label: "network-restrictions",
|
|
556
|
-
hint: "Manage network restrictions"
|
|
557
|
-
},
|
|
558
|
-
{
|
|
559
|
-
value: "vanity-subdomains",
|
|
560
|
-
label: "vanity-subdomains",
|
|
561
|
-
hint: "Manage vanity subdomains"
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
value: "encryption",
|
|
565
|
-
label: "encryption",
|
|
566
|
-
hint: "Manage encryption keys"
|
|
567
|
-
}
|
|
568
|
-
]
|
|
569
|
-
},
|
|
570
|
-
"org-auth": {
|
|
571
|
-
icon: "\u{1F465}",
|
|
572
|
-
label: "Organizations & Admin",
|
|
573
|
-
description: "Team management and admin tools",
|
|
574
|
-
commands: [
|
|
575
|
-
{ value: "orgs", label: "orgs", hint: "Manage organizations" },
|
|
576
|
-
{ value: "sso", label: "sso", hint: "Manage Single Sign-On" },
|
|
577
|
-
{ value: "branches", label: "branches", hint: "Manage preview branches" },
|
|
578
|
-
{ value: "backups", label: "backups", hint: "Manage physical backups" },
|
|
579
|
-
{ value: "snippets", label: "snippets", hint: "Manage SQL snippets" },
|
|
580
|
-
{
|
|
581
|
-
value: "postgres-config",
|
|
582
|
-
label: "postgres-config",
|
|
583
|
-
hint: "Manage Postgres config"
|
|
584
|
-
}
|
|
585
|
-
]
|
|
586
|
-
},
|
|
587
|
-
utilities: {
|
|
588
|
-
icon: "\u2699\uFE0F",
|
|
589
|
-
label: "Utilities",
|
|
590
|
-
description: "Help and shell completions",
|
|
591
|
-
commands: [
|
|
592
|
-
{
|
|
593
|
-
value: "completion",
|
|
594
|
-
label: "completion",
|
|
595
|
-
hint: "Generate shell completion script"
|
|
596
|
-
},
|
|
597
|
-
{ value: "help", label: "help", hint: "Help about any command" }
|
|
598
|
-
]
|
|
666
|
+
// src/screens/homeModel.ts
|
|
667
|
+
function getFeatures() {
|
|
668
|
+
return features;
|
|
669
|
+
}
|
|
670
|
+
function buildPinnedOnlyItems(pinnedCommands, pinnedRuns) {
|
|
671
|
+
const items = [];
|
|
672
|
+
if (pinnedCommands.length > 0) {
|
|
673
|
+
items.push({
|
|
674
|
+
id: "pinned-commands-header",
|
|
675
|
+
value: "__pinned_commands_header__",
|
|
676
|
+
label: "\u{1F4CC} Commands",
|
|
677
|
+
kind: "header",
|
|
678
|
+
selectable: false
|
|
679
|
+
});
|
|
680
|
+
for (const command of pinnedCommands) {
|
|
681
|
+
const cmdDef = findCommandByValue(command);
|
|
682
|
+
const toolHint = cmdDef ? cmdDef.tool : "supabase";
|
|
683
|
+
const labelHint = cmdDef?.hint;
|
|
684
|
+
items.push({
|
|
685
|
+
id: `command:${command}`,
|
|
686
|
+
value: command,
|
|
687
|
+
label: command,
|
|
688
|
+
hint: [toolHint, labelHint].filter(Boolean).join(" \xB7 "),
|
|
689
|
+
icon: "\u{1F4CC}",
|
|
690
|
+
kind: "command",
|
|
691
|
+
rightActionable: true,
|
|
692
|
+
section: "pinned-commands"
|
|
693
|
+
});
|
|
694
|
+
}
|
|
599
695
|
}
|
|
600
|
-
};
|
|
601
|
-
|
|
602
|
-
// src/screens/mainMenuModel.ts
|
|
603
|
-
var commandCatalog = Object.entries(categories).flatMap(
|
|
604
|
-
([, category]) => category.commands.map((command) => ({
|
|
605
|
-
categoryIcon: category.icon,
|
|
606
|
-
categoryLabel: category.label,
|
|
607
|
-
value: command.value,
|
|
608
|
-
hint: command.hint
|
|
609
|
-
}))
|
|
610
|
-
);
|
|
611
|
-
var commandInfoByValue = new Map(
|
|
612
|
-
commandCatalog.map((entry) => [entry.value, entry])
|
|
613
|
-
);
|
|
614
|
-
function getCommandInfo(commandValue) {
|
|
615
|
-
return commandInfoByValue.get(commandValue);
|
|
616
|
-
}
|
|
617
|
-
function buildMainMenuItems({
|
|
618
|
-
pinnedCommands,
|
|
619
|
-
pinnedRuns
|
|
620
|
-
}) {
|
|
621
|
-
const pinnedCommandSet = new Set(pinnedCommands);
|
|
622
|
-
const nextItems = [];
|
|
623
696
|
if (pinnedRuns.length > 0) {
|
|
624
|
-
|
|
625
|
-
id: "
|
|
626
|
-
value: "
|
|
627
|
-
label: "\u25B6
|
|
697
|
+
items.push({
|
|
698
|
+
id: "pinned-runs-header",
|
|
699
|
+
value: "__pinned_runs_header__",
|
|
700
|
+
label: "\u25B6 Pipelines",
|
|
628
701
|
kind: "header",
|
|
629
702
|
selectable: false
|
|
630
703
|
});
|
|
631
704
|
for (const runCommand2 of pinnedRuns) {
|
|
632
705
|
const baseCommand = runCommand2.split(" ").filter(Boolean)[0] ?? "";
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
|
|
706
|
+
const cmdDef = findCommandByValue(baseCommand);
|
|
707
|
+
const toolHint = cmdDef ? cmdDef.tool : "supabase";
|
|
708
|
+
items.push({
|
|
636
709
|
id: `run:${runCommand2}`,
|
|
637
710
|
value: runCommand2,
|
|
638
711
|
label: runCommand2,
|
|
639
|
-
hint:
|
|
712
|
+
hint: toolHint,
|
|
640
713
|
icon: "\u25B6",
|
|
641
714
|
kind: "run",
|
|
642
715
|
rightActionable: true,
|
|
@@ -644,115 +717,122 @@ function buildMainMenuItems({
|
|
|
644
717
|
});
|
|
645
718
|
}
|
|
646
719
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
720
|
+
return items;
|
|
721
|
+
}
|
|
722
|
+
function buildHomeItems({
|
|
723
|
+
activeFeature,
|
|
724
|
+
pinnedCommands,
|
|
725
|
+
pinnedRuns,
|
|
726
|
+
showPinnedSection = true
|
|
727
|
+
}) {
|
|
728
|
+
const items = [];
|
|
729
|
+
if (showPinnedSection && (pinnedRuns.length > 0 || pinnedCommands.length > 0)) {
|
|
730
|
+
items.push({
|
|
731
|
+
id: "section-pinned",
|
|
732
|
+
value: "__section_pinned__",
|
|
733
|
+
label: "\u{1F4CC} Pinned",
|
|
652
734
|
kind: "header",
|
|
653
735
|
selectable: false
|
|
654
736
|
});
|
|
655
|
-
|
|
656
|
-
const info = getCommandInfo(command);
|
|
657
|
-
const infoHint = info ? `${info.categoryIcon} ${info.categoryLabel}${info.hint ? ` \xB7 ${info.hint}` : ""}` : "Pinned command";
|
|
658
|
-
nextItems.push({
|
|
659
|
-
id: `command:${command}`,
|
|
660
|
-
value: command,
|
|
661
|
-
label: command,
|
|
662
|
-
hint: infoHint,
|
|
663
|
-
icon: "\u{1F4CC}",
|
|
664
|
-
kind: "command",
|
|
665
|
-
rightActionable: true,
|
|
666
|
-
section: "pinned-commands"
|
|
667
|
-
});
|
|
668
|
-
}
|
|
737
|
+
items.push(...buildPinnedOnlyItems(pinnedCommands, pinnedRuns));
|
|
669
738
|
}
|
|
670
|
-
|
|
671
|
-
id: "section-
|
|
672
|
-
value: "
|
|
673
|
-
label:
|
|
739
|
+
items.push({
|
|
740
|
+
id: "section-commands",
|
|
741
|
+
value: "__section_commands__",
|
|
742
|
+
label: `\u{1F4C2} ${activeFeature.label} Commands`,
|
|
674
743
|
kind: "header",
|
|
675
744
|
selectable: false
|
|
676
745
|
});
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
746
|
+
const toolOrder = { supabase: 0, vercel: 1, gh: 2, pulumi: 3 };
|
|
747
|
+
const toolIcons = { supabase: "\u{1F7E2}", vercel: "\u26AA", gh: "\u{1F535}", pulumi: "\u{1F7E3}" };
|
|
748
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
749
|
+
for (const cmd of activeFeature.commands) {
|
|
750
|
+
const existing = grouped.get(cmd.tool) ?? [];
|
|
751
|
+
existing.push(cmd);
|
|
752
|
+
grouped.set(cmd.tool, existing);
|
|
753
|
+
}
|
|
754
|
+
const sortedTools = [...grouped.keys()].sort(
|
|
755
|
+
(a, b) => (toolOrder[a] ?? 99) - (toolOrder[b] ?? 99)
|
|
756
|
+
);
|
|
757
|
+
for (const tool of sortedTools) {
|
|
758
|
+
const cmds = grouped.get(tool);
|
|
759
|
+
const icon = toolIcons[tool] ?? "\u26AA";
|
|
760
|
+
items.push({
|
|
761
|
+
id: `tool-header-${tool}`,
|
|
762
|
+
value: `__tool_header_${tool}__`,
|
|
763
|
+
label: `${icon} ${tool}`,
|
|
682
764
|
kind: "header",
|
|
683
765
|
selectable: false
|
|
684
766
|
});
|
|
685
|
-
for (const
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
767
|
+
for (const cmd of cmds) {
|
|
768
|
+
const value = getCommandValue(cmd);
|
|
769
|
+
const isPinned = pinnedCommands.includes(value);
|
|
770
|
+
items.push({
|
|
771
|
+
id: `cmd-${cmd.id}`,
|
|
772
|
+
value,
|
|
773
|
+
label: cmd.label,
|
|
774
|
+
hint: [cmd.hint, isPinned ? "pinned" : void 0].filter(Boolean).join(" \xB7 "),
|
|
692
775
|
kind: "command",
|
|
693
776
|
rightActionable: true,
|
|
694
|
-
section:
|
|
695
|
-
groupLabel:
|
|
777
|
+
section: activeFeature.id,
|
|
778
|
+
groupLabel: tool
|
|
696
779
|
});
|
|
697
780
|
}
|
|
698
781
|
}
|
|
699
|
-
|
|
700
|
-
id: "section-actions",
|
|
701
|
-
value: "__section_actions__",
|
|
702
|
-
label: "\u26A1 Actions",
|
|
703
|
-
kind: "header",
|
|
704
|
-
selectable: false
|
|
705
|
-
});
|
|
706
|
-
nextItems.push({
|
|
707
|
-
id: "action-custom",
|
|
708
|
-
value: "__action_custom__",
|
|
709
|
-
label: "Custom Command",
|
|
710
|
-
hint: "Free-form args or check version",
|
|
711
|
-
icon: "\u270F\uFE0F",
|
|
712
|
-
kind: "action"
|
|
713
|
-
});
|
|
714
|
-
nextItems.push({
|
|
715
|
-
id: "action-update",
|
|
716
|
-
value: "__action_update__",
|
|
717
|
-
label: "Update Polter",
|
|
718
|
-
hint: "Update the current repo install or the global install",
|
|
719
|
-
icon: "\u2B06\uFE0F",
|
|
720
|
-
kind: "action"
|
|
721
|
-
});
|
|
722
|
-
nextItems.push({
|
|
723
|
-
id: "action-exit",
|
|
724
|
-
value: "__action_exit__",
|
|
725
|
-
label: "Exit",
|
|
726
|
-
icon: "\u{1F6AA}",
|
|
727
|
-
kind: "action"
|
|
728
|
-
});
|
|
729
|
-
return nextItems;
|
|
782
|
+
return items;
|
|
730
783
|
}
|
|
731
784
|
|
|
732
|
-
// src/screens/
|
|
733
|
-
import { jsx as
|
|
734
|
-
function
|
|
785
|
+
// src/screens/Home.tsx
|
|
786
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
787
|
+
function Home({
|
|
735
788
|
onNavigate,
|
|
736
|
-
onExit
|
|
789
|
+
onExit,
|
|
790
|
+
width = 80,
|
|
791
|
+
height = 24
|
|
737
792
|
}) {
|
|
738
|
-
const
|
|
793
|
+
const allFeatures = useMemo2(() => getFeatures(), []);
|
|
794
|
+
const [activeTabIndex, setActiveTabIndex] = useState4(0);
|
|
795
|
+
const [pinnedCommands, setPinnedCommands2] = useState4(
|
|
739
796
|
() => getPinnedCommands()
|
|
740
797
|
);
|
|
741
|
-
const [pinnedRuns, setPinnedRuns2] =
|
|
798
|
+
const [pinnedRuns, setPinnedRuns2] = useState4(
|
|
742
799
|
() => getPinnedRuns()
|
|
743
800
|
);
|
|
744
|
-
const [pinFeedback, setPinFeedback] =
|
|
745
|
-
|
|
801
|
+
const [pinFeedback, setPinFeedback] = useState4();
|
|
802
|
+
const activeFeature = allFeatures[activeTabIndex];
|
|
803
|
+
useEffect3(() => {
|
|
746
804
|
if (!pinFeedback) return;
|
|
747
805
|
const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
|
|
748
806
|
return () => clearTimeout(timeout);
|
|
749
807
|
}, [pinFeedback]);
|
|
808
|
+
useInput2((_input, key) => {
|
|
809
|
+
if (key.leftArrow && !key.meta) {
|
|
810
|
+
setActiveTabIndex(
|
|
811
|
+
(prev) => prev > 0 ? prev - 1 : allFeatures.length - 1
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
if (key.rightArrow && !key.meta) {
|
|
815
|
+
setActiveTabIndex(
|
|
816
|
+
(prev) => prev < allFeatures.length - 1 ? prev + 1 : 0
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
if (key.tab) {
|
|
820
|
+
setActiveTabIndex(
|
|
821
|
+
(prev) => prev < allFeatures.length - 1 ? prev + 1 : 0
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
const tabs = useMemo2(
|
|
826
|
+
() => allFeatures.map((f) => ({ id: f.id, icon: f.icon, label: f.label })),
|
|
827
|
+
[allFeatures]
|
|
828
|
+
);
|
|
750
829
|
const items = useMemo2(
|
|
751
|
-
() =>
|
|
830
|
+
() => buildHomeItems({
|
|
831
|
+
activeFeature,
|
|
752
832
|
pinnedCommands,
|
|
753
833
|
pinnedRuns
|
|
754
834
|
}),
|
|
755
|
-
[pinnedCommands, pinnedRuns]
|
|
835
|
+
[activeFeature, pinnedCommands, pinnedRuns]
|
|
756
836
|
);
|
|
757
837
|
const pinnedCommandSet = useMemo2(
|
|
758
838
|
() => new Set(pinnedCommands),
|
|
@@ -766,78 +846,98 @@ function MainMenu({
|
|
|
766
846
|
const handleSelect = (value, item) => {
|
|
767
847
|
if (!item) return;
|
|
768
848
|
if (item.kind === "command") {
|
|
769
|
-
|
|
849
|
+
const cmdDef = findCommandByValue(value);
|
|
850
|
+
if (cmdDef) {
|
|
851
|
+
onNavigate("command-args", {
|
|
852
|
+
command: value,
|
|
853
|
+
commandId: cmdDef.id,
|
|
854
|
+
tool: cmdDef.tool
|
|
855
|
+
});
|
|
856
|
+
} else {
|
|
857
|
+
onNavigate("command-args", { command: value, tool: "supabase" });
|
|
858
|
+
}
|
|
770
859
|
return;
|
|
771
860
|
}
|
|
772
861
|
if (item.kind === "run") {
|
|
773
862
|
const args = value.split(" ").filter(Boolean);
|
|
774
863
|
if (args.length > 0) {
|
|
775
|
-
|
|
864
|
+
const basePart = args[0] ?? "";
|
|
865
|
+
const cmdDef = findCommandByValue(basePart);
|
|
866
|
+
const tool = cmdDef?.tool ?? "supabase";
|
|
867
|
+
onNavigate("confirm-execute", { args, tool });
|
|
776
868
|
}
|
|
777
869
|
return;
|
|
778
870
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
871
|
+
switch (value) {
|
|
872
|
+
case "__action_custom__":
|
|
873
|
+
onNavigate("custom-command");
|
|
874
|
+
break;
|
|
875
|
+
case "__action_pipelines__":
|
|
876
|
+
onNavigate("pipeline-list");
|
|
877
|
+
break;
|
|
878
|
+
case "__action_tools__":
|
|
879
|
+
onNavigate("tool-status");
|
|
880
|
+
break;
|
|
881
|
+
case "__action_config__":
|
|
882
|
+
onNavigate("project-config");
|
|
883
|
+
break;
|
|
884
|
+
case "__action_update__":
|
|
885
|
+
onNavigate("self-update");
|
|
886
|
+
break;
|
|
887
|
+
case "__action_exit__":
|
|
888
|
+
onExit();
|
|
889
|
+
break;
|
|
789
890
|
}
|
|
790
891
|
};
|
|
791
892
|
const handleRightAction = (item) => {
|
|
792
893
|
if (item.kind === "command") {
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
togglePinnedCommand(command);
|
|
894
|
+
const wasPinned = pinnedCommandSet.has(item.value);
|
|
895
|
+
togglePinnedCommand(item.value);
|
|
796
896
|
refreshPins();
|
|
797
897
|
setPinFeedback(
|
|
798
|
-
wasPinned ? `Unpinned "${
|
|
898
|
+
wasPinned ? `Unpinned "${item.value}"` : `Pinned "${item.value}"`
|
|
799
899
|
);
|
|
800
900
|
return;
|
|
801
901
|
}
|
|
802
902
|
if (item.kind === "run") {
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
togglePinnedRun(runCommand2);
|
|
903
|
+
const wasPinned = pinnedRunSet.has(item.value);
|
|
904
|
+
togglePinnedRun(item.value);
|
|
806
905
|
refreshPins();
|
|
807
906
|
setPinFeedback(
|
|
808
|
-
wasPinned ? `Unpinned
|
|
907
|
+
wasPinned ? `Unpinned run "${item.value}"` : `Pinned run "${item.value}"`
|
|
809
908
|
);
|
|
810
909
|
}
|
|
811
910
|
};
|
|
812
|
-
return /* @__PURE__ */
|
|
813
|
-
/* @__PURE__ */
|
|
814
|
-
/* @__PURE__ */
|
|
815
|
-
pinFeedback && /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: inkColors.accent, children: [
|
|
911
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
912
|
+
/* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsx5(TabBar, { tabs, activeIndex: activeTabIndex, width }) }),
|
|
913
|
+
pinFeedback && /* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsxs5(Text5, { color: inkColors.accent, children: [
|
|
816
914
|
"\u2713 ",
|
|
817
915
|
pinFeedback
|
|
818
916
|
] }) }),
|
|
819
|
-
/* @__PURE__ */
|
|
917
|
+
/* @__PURE__ */ jsx5(
|
|
820
918
|
SelectList,
|
|
821
919
|
{
|
|
822
920
|
items,
|
|
823
921
|
onSelect: handleSelect,
|
|
824
922
|
onRightAction: handleRightAction,
|
|
825
|
-
boxedSections: true
|
|
923
|
+
boxedSections: true,
|
|
924
|
+
width,
|
|
925
|
+
maxVisible: Math.max(8, height - 14)
|
|
826
926
|
}
|
|
827
927
|
),
|
|
828
|
-
/* @__PURE__ */
|
|
928
|
+
/* @__PURE__ */ jsx5(StatusBar, { hint: "\u2190\u2192 tab \xB7 \u2191\u2193 navigate \xB7 Enter select \xB7 p pin", width })
|
|
829
929
|
] });
|
|
830
930
|
}
|
|
831
931
|
|
|
832
932
|
// src/screens/CommandArgs.tsx
|
|
833
|
-
import { useEffect as
|
|
834
|
-
import { Box as
|
|
933
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useState as useState6 } from "react";
|
|
934
|
+
import { Box as Box7, Text as Text8 } from "ink";
|
|
835
935
|
|
|
836
936
|
// src/components/TextPrompt.tsx
|
|
837
|
-
import { useState as
|
|
838
|
-
import { Box as
|
|
937
|
+
import { useState as useState5 } from "react";
|
|
938
|
+
import { Box as Box6, Text as Text6, useInput as useInput3 } from "ink";
|
|
839
939
|
import TextInputComponent from "ink-text-input";
|
|
840
|
-
import { jsx as
|
|
940
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
841
941
|
function TextPrompt({
|
|
842
942
|
label,
|
|
843
943
|
placeholder,
|
|
@@ -845,9 +945,9 @@ function TextPrompt({
|
|
|
845
945
|
onCancel,
|
|
846
946
|
validate
|
|
847
947
|
}) {
|
|
848
|
-
const [value, setValue] =
|
|
849
|
-
const [error, setError] =
|
|
850
|
-
|
|
948
|
+
const [value, setValue] = useState5("");
|
|
949
|
+
const [error, setError] = useState5();
|
|
950
|
+
useInput3((_input, key) => {
|
|
851
951
|
if (key.escape && onCancel) {
|
|
852
952
|
onCancel();
|
|
853
953
|
}
|
|
@@ -863,14 +963,14 @@ function TextPrompt({
|
|
|
863
963
|
setError(void 0);
|
|
864
964
|
onSubmit(val);
|
|
865
965
|
};
|
|
866
|
-
return /* @__PURE__ */
|
|
867
|
-
/* @__PURE__ */
|
|
868
|
-
/* @__PURE__ */
|
|
869
|
-
/* @__PURE__ */
|
|
966
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
967
|
+
/* @__PURE__ */ jsxs6(Box6, { gap: 1, children: [
|
|
968
|
+
/* @__PURE__ */ jsx6(Text6, { color: inkColors.accent, bold: true, children: "?" }),
|
|
969
|
+
/* @__PURE__ */ jsx6(Text6, { children: label })
|
|
870
970
|
] }),
|
|
871
|
-
/* @__PURE__ */
|
|
872
|
-
/* @__PURE__ */
|
|
873
|
-
/* @__PURE__ */
|
|
971
|
+
/* @__PURE__ */ jsxs6(Box6, { gap: 1, marginLeft: 2, children: [
|
|
972
|
+
/* @__PURE__ */ jsx6(Text6, { color: inkColors.accent, children: "\u276F" }),
|
|
973
|
+
/* @__PURE__ */ jsx6(
|
|
874
974
|
TextInputComponent,
|
|
875
975
|
{
|
|
876
976
|
value,
|
|
@@ -883,68 +983,30 @@ function TextPrompt({
|
|
|
883
983
|
}
|
|
884
984
|
)
|
|
885
985
|
] }),
|
|
886
|
-
error && /* @__PURE__ */
|
|
986
|
+
error && /* @__PURE__ */ jsx6(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsxs6(Text6, { color: "red", children: [
|
|
887
987
|
"\u2717 ",
|
|
888
988
|
error
|
|
889
989
|
] }) })
|
|
890
990
|
] });
|
|
891
991
|
}
|
|
892
992
|
|
|
893
|
-
// src/
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
{ value: "list", label: "list", hint: "List local migrations", args: ["list"] },
|
|
908
|
-
{ value: "repair", label: "repair", hint: "Repair migration history", args: ["repair"] },
|
|
909
|
-
{ value: "fetch", label: "fetch", hint: "Fetch migration history", args: ["fetch"] }
|
|
910
|
-
],
|
|
911
|
-
projects: [
|
|
912
|
-
{ value: "list", label: "list", hint: "List projects", args: ["list"] },
|
|
913
|
-
{ value: "create", label: "create", hint: "Create project", args: ["create"] },
|
|
914
|
-
{ value: "delete", label: "delete", hint: "Delete project", args: ["delete"] }
|
|
915
|
-
],
|
|
916
|
-
functions: [
|
|
917
|
-
{ value: "list", label: "list", hint: "List functions", args: ["list"] },
|
|
918
|
-
{ value: "new", label: "new <name>", hint: "Create a function", args: ["new"] },
|
|
919
|
-
{ value: "serve", label: "serve", hint: "Serve functions locally", args: ["serve"] },
|
|
920
|
-
{ value: "deploy", label: "deploy <name>", hint: "Deploy function", args: ["deploy"] },
|
|
921
|
-
{ value: "delete", label: "delete <name>", hint: "Delete function", args: ["delete"] }
|
|
922
|
-
],
|
|
923
|
-
storage: [
|
|
924
|
-
{ value: "ls", label: "ls", hint: "List storage objects", args: ["ls"] },
|
|
925
|
-
{ value: "cp", label: "cp", hint: "Copy storage object", args: ["cp"] },
|
|
926
|
-
{ value: "mv", label: "mv", hint: "Move storage object", args: ["mv"] },
|
|
927
|
-
{ value: "rm", label: "rm", hint: "Remove storage object", args: ["rm"] }
|
|
928
|
-
],
|
|
929
|
-
seed: [
|
|
930
|
-
{ value: "run", label: "run", hint: "Run configured seed file", args: ["run"] }
|
|
931
|
-
],
|
|
932
|
-
orgs: [
|
|
933
|
-
{ value: "list", label: "list", hint: "List organizations", args: ["list"] },
|
|
934
|
-
{ value: "create", label: "create", hint: "Create organization", args: ["create"] }
|
|
935
|
-
],
|
|
936
|
-
snippets: [
|
|
937
|
-
{ value: "list", label: "list", hint: "List SQL snippets", args: ["list"] },
|
|
938
|
-
{ value: "create", label: "create", hint: "Create SQL snippet", args: ["create"] },
|
|
939
|
-
{ value: "delete", label: "delete", hint: "Delete SQL snippet", args: ["delete"] }
|
|
940
|
-
],
|
|
941
|
-
backups: [
|
|
942
|
-
{ value: "list", label: "list", hint: "List backups", args: ["list"] },
|
|
943
|
-
{ value: "download", label: "download", hint: "Download backup", args: ["download"] }
|
|
944
|
-
]
|
|
993
|
+
// src/components/ToolBadge.tsx
|
|
994
|
+
import { Text as Text7 } from "ink";
|
|
995
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
996
|
+
var toolColors = {
|
|
997
|
+
supabase: "#3ECF8E",
|
|
998
|
+
gh: "#58A6FF",
|
|
999
|
+
vercel: "#FFFFFF",
|
|
1000
|
+
pulumi: "#8B5CF6"
|
|
1001
|
+
};
|
|
1002
|
+
var toolLabels = {
|
|
1003
|
+
supabase: "supabase",
|
|
1004
|
+
gh: "github",
|
|
1005
|
+
vercel: "vercel",
|
|
1006
|
+
pulumi: "pulumi"
|
|
945
1007
|
};
|
|
946
|
-
function
|
|
947
|
-
return
|
|
1008
|
+
function ToolBadge({ tool }) {
|
|
1009
|
+
return /* @__PURE__ */ jsx7(Text7, { color: toolColors[tool], dimColor: true, children: toolLabels[tool] });
|
|
948
1010
|
}
|
|
949
1011
|
|
|
950
1012
|
// src/screens/commandArgsModel.ts
|
|
@@ -1000,7 +1062,7 @@ function buildCommandArgItems({
|
|
|
1000
1062
|
value: "__run_base__",
|
|
1001
1063
|
label: "Run without additional args",
|
|
1002
1064
|
hint: [
|
|
1003
|
-
|
|
1065
|
+
`${command}`,
|
|
1004
1066
|
pinnedRunSet.has(baseRunCommand) ? "pinned run" : void 0
|
|
1005
1067
|
].filter(Boolean).join(" \xB7 "),
|
|
1006
1068
|
kind: "action",
|
|
@@ -1021,55 +1083,71 @@ function buildCommandArgItems({
|
|
|
1021
1083
|
}
|
|
1022
1084
|
|
|
1023
1085
|
// src/screens/CommandArgs.tsx
|
|
1024
|
-
import { jsx as
|
|
1086
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1025
1087
|
function CommandArgs({
|
|
1026
1088
|
command,
|
|
1089
|
+
tool,
|
|
1027
1090
|
onNavigate,
|
|
1028
|
-
onBack
|
|
1091
|
+
onBack,
|
|
1092
|
+
width = 80,
|
|
1093
|
+
panelMode = false,
|
|
1094
|
+
isInputActive = true
|
|
1029
1095
|
}) {
|
|
1030
|
-
const
|
|
1031
|
-
const
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1096
|
+
const cmdDef = useMemo3(() => findCommandByValue(command), [command]);
|
|
1097
|
+
const resolvedTool = tool ?? cmdDef?.tool ?? "supabase";
|
|
1098
|
+
const suggestions = useMemo3(
|
|
1099
|
+
() => cmdDef?.suggestedArgs ?? [],
|
|
1100
|
+
[cmdDef]
|
|
1101
|
+
);
|
|
1102
|
+
const [pinnedRuns, setPinnedRuns2] = useState6(() => getPinnedRuns());
|
|
1103
|
+
const [pinFeedback, setPinFeedback] = useState6();
|
|
1104
|
+
const [phase, setPhase] = useState6(
|
|
1034
1105
|
suggestions.length > 0 ? "select" : "custom"
|
|
1035
1106
|
);
|
|
1036
|
-
|
|
1107
|
+
useEffect4(() => {
|
|
1037
1108
|
setPhase(suggestions.length > 0 ? "select" : "custom");
|
|
1038
1109
|
}, [command, suggestions.length]);
|
|
1039
|
-
|
|
1110
|
+
useEffect4(() => {
|
|
1040
1111
|
if (!pinFeedback) return;
|
|
1041
1112
|
const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
|
|
1042
1113
|
return () => clearTimeout(timeout);
|
|
1043
1114
|
}, [pinFeedback]);
|
|
1044
1115
|
const navigateWithExtraArgs = (extraArgs) => {
|
|
1116
|
+
const baseArgs = cmdDef ? cmdDef.base : [command];
|
|
1045
1117
|
onNavigate("flag-selection", {
|
|
1046
|
-
args: [
|
|
1047
|
-
command
|
|
1118
|
+
args: [...baseArgs, ...extraArgs],
|
|
1119
|
+
command,
|
|
1120
|
+
tool: resolvedTool
|
|
1048
1121
|
});
|
|
1049
1122
|
};
|
|
1050
1123
|
if (!command) {
|
|
1051
|
-
return /* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1053
|
-
/* @__PURE__ */
|
|
1124
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1125
|
+
/* @__PURE__ */ jsx8(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text8, { color: "red", children: "Command not provided." }) }),
|
|
1126
|
+
/* @__PURE__ */ jsx8(
|
|
1054
1127
|
SelectList,
|
|
1055
1128
|
{
|
|
1056
1129
|
items: [{ value: "__back__", label: "\u2190 Back to menu" }],
|
|
1057
1130
|
onSelect: onBack,
|
|
1058
|
-
onCancel: onBack
|
|
1131
|
+
onCancel: onBack,
|
|
1132
|
+
width,
|
|
1133
|
+
isInputActive,
|
|
1134
|
+
arrowNavigation: panelMode
|
|
1059
1135
|
}
|
|
1060
1136
|
)
|
|
1061
1137
|
] });
|
|
1062
1138
|
}
|
|
1063
1139
|
if (phase === "custom") {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
/* @__PURE__ */
|
|
1140
|
+
const toolLabel = resolvedTool === "supabase" ? "supabase" : resolvedTool;
|
|
1141
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1142
|
+
/* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, gap: 1, children: [
|
|
1143
|
+
/* @__PURE__ */ jsx8(Text8, { color: inkColors.accent, bold: true, children: "Command" }),
|
|
1144
|
+
/* @__PURE__ */ jsx8(Text8, { children: command }),
|
|
1145
|
+
/* @__PURE__ */ jsx8(ToolBadge, { tool: resolvedTool })
|
|
1068
1146
|
] }),
|
|
1069
|
-
/* @__PURE__ */
|
|
1147
|
+
/* @__PURE__ */ jsx8(
|
|
1070
1148
|
TextPrompt,
|
|
1071
1149
|
{
|
|
1072
|
-
label: `Additional args for
|
|
1150
|
+
label: `Additional args for ${toolLabel} ${command}?`,
|
|
1073
1151
|
placeholder: "e.g. list, pull, push (Enter to skip)",
|
|
1074
1152
|
onSubmit: (extra) => {
|
|
1075
1153
|
const extraArgs = extra.trim().split(" ").filter(Boolean);
|
|
@@ -1084,27 +1162,34 @@ function CommandArgs({
|
|
|
1084
1162
|
}
|
|
1085
1163
|
}
|
|
1086
1164
|
),
|
|
1087
|
-
/* @__PURE__ */
|
|
1165
|
+
!panelMode && /* @__PURE__ */ jsx8(StatusBar, { hint: "Type args \xB7 Enter to continue \xB7 Esc to go back", width })
|
|
1088
1166
|
] });
|
|
1089
1167
|
}
|
|
1168
|
+
const legacySuggestions = suggestions.map((s) => ({
|
|
1169
|
+
value: s.value,
|
|
1170
|
+
label: s.label,
|
|
1171
|
+
hint: s.hint,
|
|
1172
|
+
args: s.args
|
|
1173
|
+
}));
|
|
1090
1174
|
const items = useMemo3(
|
|
1091
1175
|
() => buildCommandArgItems({
|
|
1092
1176
|
command,
|
|
1093
|
-
suggestions,
|
|
1177
|
+
suggestions: legacySuggestions,
|
|
1094
1178
|
pinnedRuns
|
|
1095
1179
|
}),
|
|
1096
|
-
[command,
|
|
1180
|
+
[command, legacySuggestions, pinnedRuns]
|
|
1097
1181
|
);
|
|
1098
|
-
return /* @__PURE__ */
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
/* @__PURE__ */
|
|
1101
|
-
/* @__PURE__ */
|
|
1182
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1183
|
+
/* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, gap: 1, children: [
|
|
1184
|
+
/* @__PURE__ */ jsx8(Text8, { color: inkColors.accent, bold: true, children: "Command" }),
|
|
1185
|
+
/* @__PURE__ */ jsx8(Text8, { children: command }),
|
|
1186
|
+
/* @__PURE__ */ jsx8(ToolBadge, { tool: resolvedTool })
|
|
1102
1187
|
] }),
|
|
1103
|
-
pinFeedback && /* @__PURE__ */
|
|
1188
|
+
pinFeedback && /* @__PURE__ */ jsx8(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsxs7(Text8, { color: inkColors.accent, children: [
|
|
1104
1189
|
"\u2713 ",
|
|
1105
1190
|
pinFeedback
|
|
1106
1191
|
] }) }),
|
|
1107
|
-
/* @__PURE__ */
|
|
1192
|
+
/* @__PURE__ */ jsx8(
|
|
1108
1193
|
SelectList,
|
|
1109
1194
|
{
|
|
1110
1195
|
items,
|
|
@@ -1125,7 +1210,7 @@ function CommandArgs({
|
|
|
1125
1210
|
const runCommand2 = getRunCommandFromArgsSelection(
|
|
1126
1211
|
command,
|
|
1127
1212
|
value,
|
|
1128
|
-
|
|
1213
|
+
legacySuggestions
|
|
1129
1214
|
);
|
|
1130
1215
|
if (runCommand2) {
|
|
1131
1216
|
navigateWithExtraArgs(
|
|
@@ -1139,7 +1224,7 @@ function CommandArgs({
|
|
|
1139
1224
|
const runCommand2 = getRunCommandFromArgsSelection(
|
|
1140
1225
|
command,
|
|
1141
1226
|
item.value,
|
|
1142
|
-
|
|
1227
|
+
legacySuggestions
|
|
1143
1228
|
);
|
|
1144
1229
|
if (!runCommand2) {
|
|
1145
1230
|
return;
|
|
@@ -1152,56 +1237,103 @@ function CommandArgs({
|
|
|
1152
1237
|
);
|
|
1153
1238
|
},
|
|
1154
1239
|
onCancel: onBack,
|
|
1155
|
-
boxedSections: true
|
|
1240
|
+
boxedSections: true,
|
|
1241
|
+
width,
|
|
1242
|
+
isInputActive,
|
|
1243
|
+
arrowNavigation: panelMode
|
|
1156
1244
|
}
|
|
1157
1245
|
),
|
|
1158
|
-
/* @__PURE__ */
|
|
1246
|
+
!panelMode && /* @__PURE__ */ jsx8(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 p pin \xB7 Esc back", width })
|
|
1159
1247
|
] });
|
|
1160
1248
|
}
|
|
1161
1249
|
|
|
1162
1250
|
// src/screens/CustomCommand.tsx
|
|
1163
|
-
import {
|
|
1164
|
-
import {
|
|
1251
|
+
import { useState as useState7 } from "react";
|
|
1252
|
+
import { Box as Box8, Text as Text9 } from "ink";
|
|
1253
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1165
1254
|
function CustomCommand({
|
|
1166
1255
|
onNavigate,
|
|
1167
|
-
onBack
|
|
1256
|
+
onBack,
|
|
1257
|
+
width = 80,
|
|
1258
|
+
panelMode = false,
|
|
1259
|
+
isInputActive = true
|
|
1168
1260
|
}) {
|
|
1169
|
-
|
|
1170
|
-
|
|
1261
|
+
const [phase, setPhase] = useState7("tool-select");
|
|
1262
|
+
const [selectedTool, setSelectedTool] = useState7("supabase");
|
|
1263
|
+
if (phase === "tool-select") {
|
|
1264
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
1265
|
+
/* @__PURE__ */ jsx9(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { bold: true, color: inkColors.accent, children: "\u270F\uFE0F Custom Command" }) }),
|
|
1266
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Select tool:" }),
|
|
1267
|
+
/* @__PURE__ */ jsx9(
|
|
1268
|
+
SelectList,
|
|
1269
|
+
{
|
|
1270
|
+
items: [
|
|
1271
|
+
{ value: "supabase", label: "Supabase CLI", hint: "supabase ..." },
|
|
1272
|
+
{ value: "gh", label: "GitHub CLI", hint: "gh ..." },
|
|
1273
|
+
{ value: "vercel", label: "Vercel CLI", hint: "vercel ..." },
|
|
1274
|
+
{ value: "__back__", label: "\u2190 Back" }
|
|
1275
|
+
],
|
|
1276
|
+
onSelect: (value) => {
|
|
1277
|
+
if (value === "__back__") {
|
|
1278
|
+
onBack();
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
setSelectedTool(value);
|
|
1282
|
+
setPhase("input");
|
|
1283
|
+
},
|
|
1284
|
+
onCancel: onBack,
|
|
1285
|
+
width,
|
|
1286
|
+
isInputActive,
|
|
1287
|
+
arrowNavigation: panelMode
|
|
1288
|
+
}
|
|
1289
|
+
),
|
|
1290
|
+
!panelMode && /* @__PURE__ */ jsx9(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
|
|
1291
|
+
] });
|
|
1292
|
+
}
|
|
1293
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
1294
|
+
/* @__PURE__ */ jsxs8(Box8, { marginBottom: 1, gap: 1, children: [
|
|
1295
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: inkColors.accent, children: "\u270F\uFE0F Custom Command" }),
|
|
1296
|
+
/* @__PURE__ */ jsxs8(Text9, { dimColor: true, children: [
|
|
1297
|
+
"(",
|
|
1298
|
+
selectedTool,
|
|
1299
|
+
")"
|
|
1300
|
+
] })
|
|
1301
|
+
] }),
|
|
1302
|
+
/* @__PURE__ */ jsx9(
|
|
1171
1303
|
TextPrompt,
|
|
1172
1304
|
{
|
|
1173
|
-
label:
|
|
1174
|
-
placeholder:
|
|
1305
|
+
label: `Enter your ${selectedTool} command/flags:`,
|
|
1306
|
+
placeholder: `e.g. ${selectedTool === "supabase" ? "-v, status -o json, db pull" : selectedTool === "gh" ? "pr list, issue create" : "deploy, env ls"}`,
|
|
1175
1307
|
validate: (val) => {
|
|
1176
1308
|
if (!val || !val.trim()) return "Please enter a command";
|
|
1177
1309
|
return void 0;
|
|
1178
1310
|
},
|
|
1179
1311
|
onSubmit: (value) => {
|
|
1180
1312
|
const args = value.split(" ").filter(Boolean);
|
|
1181
|
-
onNavigate("flag-selection", { args });
|
|
1313
|
+
onNavigate("flag-selection", { args, tool: selectedTool });
|
|
1182
1314
|
},
|
|
1183
|
-
onCancel:
|
|
1315
|
+
onCancel: () => setPhase("tool-select")
|
|
1184
1316
|
}
|
|
1185
1317
|
),
|
|
1186
|
-
/* @__PURE__ */
|
|
1318
|
+
!panelMode && /* @__PURE__ */ jsx9(StatusBar, { hint: "Type a command \xB7 Enter to continue \xB7 Esc to go back", width })
|
|
1187
1319
|
] });
|
|
1188
1320
|
}
|
|
1189
1321
|
|
|
1190
1322
|
// src/screens/FlagSelection.tsx
|
|
1191
|
-
import { Box as
|
|
1323
|
+
import { Box as Box10 } from "ink";
|
|
1192
1324
|
|
|
1193
1325
|
// src/components/FlagToggle.tsx
|
|
1194
|
-
import { useState as
|
|
1195
|
-
import { Box as
|
|
1196
|
-
import { jsx as
|
|
1326
|
+
import { useState as useState8 } from "react";
|
|
1327
|
+
import { Box as Box9, Text as Text10, useInput as useInput4 } from "ink";
|
|
1328
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1197
1329
|
function FlagToggle({
|
|
1198
1330
|
flags,
|
|
1199
1331
|
onSubmit,
|
|
1200
1332
|
onCancel
|
|
1201
1333
|
}) {
|
|
1202
|
-
const [cursor, setCursor] =
|
|
1203
|
-
const [selected, setSelected] =
|
|
1204
|
-
|
|
1334
|
+
const [cursor, setCursor] = useState8(0);
|
|
1335
|
+
const [selected, setSelected] = useState8(/* @__PURE__ */ new Set());
|
|
1336
|
+
useInput4((input2, key) => {
|
|
1205
1337
|
if (key.upArrow || input2 === "k") {
|
|
1206
1338
|
setCursor((prev) => prev > 0 ? prev - 1 : flags.length - 1);
|
|
1207
1339
|
}
|
|
@@ -1227,106 +1359,99 @@ function FlagToggle({
|
|
|
1227
1359
|
onCancel();
|
|
1228
1360
|
}
|
|
1229
1361
|
});
|
|
1230
|
-
return /* @__PURE__ */
|
|
1231
|
-
/* @__PURE__ */
|
|
1232
|
-
/* @__PURE__ */
|
|
1233
|
-
/* @__PURE__ */
|
|
1362
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
1363
|
+
/* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, children: [
|
|
1364
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: inkColors.accent, children: "\u2691 Global Flags" }),
|
|
1365
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " (Space to toggle, Enter to confirm)" })
|
|
1234
1366
|
] }),
|
|
1235
1367
|
flags.map((flag, i) => {
|
|
1236
1368
|
const isActive = cursor === i;
|
|
1237
1369
|
const isChecked = selected.has(flag.value);
|
|
1238
|
-
return /* @__PURE__ */
|
|
1239
|
-
/* @__PURE__ */
|
|
1240
|
-
/* @__PURE__ */
|
|
1241
|
-
|
|
1370
|
+
return /* @__PURE__ */ jsxs9(Box9, { gap: 1, children: [
|
|
1371
|
+
/* @__PURE__ */ jsx10(Text10, { color: isActive ? inkColors.accent : void 0, children: isActive ? "\u276F" : " " }),
|
|
1372
|
+
/* @__PURE__ */ jsx10(
|
|
1373
|
+
Text10,
|
|
1242
1374
|
{
|
|
1243
1375
|
color: isChecked || isActive ? inkColors.accent : void 0,
|
|
1244
1376
|
bold: isChecked,
|
|
1245
1377
|
children: isChecked ? "\u25C9" : "\u25CB"
|
|
1246
1378
|
}
|
|
1247
1379
|
),
|
|
1248
|
-
/* @__PURE__ */
|
|
1249
|
-
|
|
1380
|
+
/* @__PURE__ */ jsx10(
|
|
1381
|
+
Text10,
|
|
1250
1382
|
{
|
|
1251
1383
|
color: isActive ? inkColors.accent : void 0,
|
|
1252
1384
|
bold: isActive,
|
|
1253
1385
|
children: flag.label
|
|
1254
1386
|
}
|
|
1255
1387
|
),
|
|
1256
|
-
flag.hint && /* @__PURE__ */
|
|
1388
|
+
flag.hint && /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: flag.hint })
|
|
1257
1389
|
] }, flag.value);
|
|
1258
1390
|
}),
|
|
1259
|
-
/* @__PURE__ */
|
|
1391
|
+
/* @__PURE__ */ jsx10(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: selected.size > 0 ? `${selected.size} flag${selected.size > 1 ? "s" : ""} selected` : "No flags selected (Enter to skip)" }) })
|
|
1260
1392
|
] });
|
|
1261
1393
|
}
|
|
1262
1394
|
|
|
1263
|
-
// src/data/flags.ts
|
|
1264
|
-
var globalFlags = [
|
|
1265
|
-
{ value: "--debug", label: "--debug", hint: "Output debug logs to stderr" },
|
|
1266
|
-
{ value: "--yes", label: "--yes", hint: "Answer yes to all prompts" },
|
|
1267
|
-
{
|
|
1268
|
-
value: "--create-ticket",
|
|
1269
|
-
label: "--create-ticket",
|
|
1270
|
-
hint: "Create support ticket on error"
|
|
1271
|
-
},
|
|
1272
|
-
{
|
|
1273
|
-
value: "--experimental",
|
|
1274
|
-
label: "--experimental",
|
|
1275
|
-
hint: "Enable experimental features"
|
|
1276
|
-
}
|
|
1277
|
-
];
|
|
1278
|
-
|
|
1279
1395
|
// src/screens/FlagSelection.tsx
|
|
1280
|
-
import { jsx as
|
|
1396
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1281
1397
|
function FlagSelection({
|
|
1282
1398
|
args,
|
|
1399
|
+
tool = "supabase",
|
|
1283
1400
|
onNavigate,
|
|
1284
|
-
onBack
|
|
1401
|
+
onBack,
|
|
1402
|
+
width = 80,
|
|
1403
|
+
panelMode = false,
|
|
1404
|
+
isInputActive = true
|
|
1285
1405
|
}) {
|
|
1286
|
-
|
|
1287
|
-
|
|
1406
|
+
const flags = getFlagsForTool(tool);
|
|
1407
|
+
if (flags.length === 0) {
|
|
1408
|
+
onNavigate("confirm-execute", { args, tool });
|
|
1409
|
+
return /* @__PURE__ */ jsx11(Box10, {});
|
|
1410
|
+
}
|
|
1411
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
1412
|
+
/* @__PURE__ */ jsx11(
|
|
1288
1413
|
FlagToggle,
|
|
1289
1414
|
{
|
|
1290
|
-
flags
|
|
1415
|
+
flags,
|
|
1291
1416
|
onSubmit: (selectedFlags) => {
|
|
1292
1417
|
const finalArgs = selectedFlags.length > 0 ? [...args, ...selectedFlags] : args;
|
|
1293
|
-
onNavigate("confirm-execute", { args: finalArgs });
|
|
1418
|
+
onNavigate("confirm-execute", { args: finalArgs, tool });
|
|
1294
1419
|
},
|
|
1295
1420
|
onCancel: onBack
|
|
1296
1421
|
}
|
|
1297
1422
|
),
|
|
1298
|
-
/* @__PURE__ */
|
|
1423
|
+
!panelMode && /* @__PURE__ */ jsx11(StatusBar, { hint: "Space toggle \xB7 Enter confirm \xB7 Esc back", width })
|
|
1299
1424
|
] });
|
|
1300
1425
|
}
|
|
1301
1426
|
|
|
1302
1427
|
// src/screens/CommandExecution.tsx
|
|
1303
|
-
import { useState as
|
|
1304
|
-
import { Box as Box12, Text as
|
|
1428
|
+
import { useState as useState10, useEffect as useEffect5 } from "react";
|
|
1429
|
+
import { Box as Box12, Text as Text14 } from "ink";
|
|
1305
1430
|
|
|
1306
1431
|
// src/components/Spinner.tsx
|
|
1307
|
-
import { Text as
|
|
1432
|
+
import { Text as Text11 } from "ink";
|
|
1308
1433
|
import InkSpinner from "ink-spinner";
|
|
1309
|
-
import { jsx as
|
|
1434
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1310
1435
|
function Spinner({
|
|
1311
1436
|
label = "Running...",
|
|
1312
1437
|
color = inkColors.accent
|
|
1313
1438
|
}) {
|
|
1314
|
-
return /* @__PURE__ */
|
|
1315
|
-
/* @__PURE__ */
|
|
1439
|
+
return /* @__PURE__ */ jsxs11(Text11, { children: [
|
|
1440
|
+
/* @__PURE__ */ jsx12(Text11, { color, children: /* @__PURE__ */ jsx12(InkSpinner, { type: "dots" }) }),
|
|
1316
1441
|
" ",
|
|
1317
|
-
/* @__PURE__ */
|
|
1442
|
+
/* @__PURE__ */ jsx12(Text11, { children: label })
|
|
1318
1443
|
] });
|
|
1319
1444
|
}
|
|
1320
1445
|
|
|
1321
1446
|
// src/components/ConfirmPrompt.tsx
|
|
1322
|
-
import { Box as
|
|
1323
|
-
import { jsx as
|
|
1447
|
+
import { Box as Box11, Text as Text12, useInput as useInput5 } from "ink";
|
|
1448
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1324
1449
|
function ConfirmPrompt({
|
|
1325
1450
|
message,
|
|
1326
1451
|
defaultValue = true,
|
|
1327
1452
|
onConfirm
|
|
1328
1453
|
}) {
|
|
1329
|
-
|
|
1454
|
+
useInput5((input2) => {
|
|
1330
1455
|
if (input2 === "y" || input2 === "Y") {
|
|
1331
1456
|
onConfirm(true);
|
|
1332
1457
|
} else if (input2 === "n" || input2 === "N") {
|
|
@@ -1335,26 +1460,27 @@ function ConfirmPrompt({
|
|
|
1335
1460
|
onConfirm(defaultValue);
|
|
1336
1461
|
}
|
|
1337
1462
|
});
|
|
1338
|
-
return /* @__PURE__ */
|
|
1339
|
-
/* @__PURE__ */
|
|
1340
|
-
/* @__PURE__ */
|
|
1341
|
-
/* @__PURE__ */
|
|
1463
|
+
return /* @__PURE__ */ jsxs12(Box11, { gap: 1, children: [
|
|
1464
|
+
/* @__PURE__ */ jsx13(Text12, { color: inkColors.accent, bold: true, children: "?" }),
|
|
1465
|
+
/* @__PURE__ */ jsx13(Text12, { children: message }),
|
|
1466
|
+
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: defaultValue ? "(Y/n)" : "(y/N)" })
|
|
1342
1467
|
] });
|
|
1343
1468
|
}
|
|
1344
1469
|
|
|
1345
1470
|
// src/components/Divider.tsx
|
|
1346
|
-
import { Text as
|
|
1347
|
-
import { jsx as
|
|
1471
|
+
import { Text as Text13 } from "ink";
|
|
1472
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1348
1473
|
function Divider({
|
|
1349
1474
|
label,
|
|
1350
|
-
width
|
|
1475
|
+
width
|
|
1351
1476
|
}) {
|
|
1477
|
+
const effectiveWidth = width ?? (process.stdout.columns ?? 50);
|
|
1352
1478
|
if (label) {
|
|
1353
1479
|
const labelLen = label.length + 2;
|
|
1354
|
-
const sideLen = Math.max(2, Math.floor((
|
|
1480
|
+
const sideLen = Math.max(2, Math.floor((effectiveWidth - labelLen) / 2));
|
|
1355
1481
|
const left = "\u2500".repeat(sideLen);
|
|
1356
1482
|
const right = "\u2500".repeat(sideLen);
|
|
1357
|
-
return /* @__PURE__ */
|
|
1483
|
+
return /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
1358
1484
|
left,
|
|
1359
1485
|
" ",
|
|
1360
1486
|
label,
|
|
@@ -1362,112 +1488,30 @@ function Divider({
|
|
|
1362
1488
|
right
|
|
1363
1489
|
] });
|
|
1364
1490
|
}
|
|
1365
|
-
return /* @__PURE__ */
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
// src/hooks/useCommand.ts
|
|
1369
|
-
import { useState as useState7, useCallback as useCallback2 } from "react";
|
|
1370
|
-
|
|
1371
|
-
// src/lib/runner.ts
|
|
1372
|
-
import { spawn } from "child_process";
|
|
1373
|
-
import { existsSync } from "fs";
|
|
1374
|
-
import { delimiter, dirname, join, resolve } from "path";
|
|
1375
|
-
function getSupabaseBinaryCandidates() {
|
|
1376
|
-
if (process.platform === "win32") {
|
|
1377
|
-
return ["supabase.cmd", "supabase.exe", "supabase"];
|
|
1378
|
-
}
|
|
1379
|
-
return ["supabase"];
|
|
1380
|
-
}
|
|
1381
|
-
function hasLocalSupabaseBinary(binDir) {
|
|
1382
|
-
return getSupabaseBinaryCandidates().some(
|
|
1383
|
-
(candidate) => existsSync(join(binDir, candidate))
|
|
1384
|
-
);
|
|
1385
|
-
}
|
|
1386
|
-
function getPathEnvKey(env) {
|
|
1387
|
-
return Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
1388
|
-
}
|
|
1389
|
-
function findLocalSupabaseBinDir(startDir = process.cwd()) {
|
|
1390
|
-
let currentDir = resolve(startDir);
|
|
1391
|
-
while (true) {
|
|
1392
|
-
const binDir = join(currentDir, "node_modules", ".bin");
|
|
1393
|
-
if (hasLocalSupabaseBinary(binDir)) {
|
|
1394
|
-
return binDir;
|
|
1395
|
-
}
|
|
1396
|
-
const parentDir = dirname(currentDir);
|
|
1397
|
-
if (parentDir === currentDir) {
|
|
1398
|
-
return void 0;
|
|
1399
|
-
}
|
|
1400
|
-
currentDir = parentDir;
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
function resolveSupabaseCommand(startDir = process.cwd(), env = process.env) {
|
|
1404
|
-
const localBinDir = findLocalSupabaseBinDir(startDir);
|
|
1405
|
-
if (!localBinDir) {
|
|
1406
|
-
return {
|
|
1407
|
-
command: "supabase",
|
|
1408
|
-
env: { ...env },
|
|
1409
|
-
source: "path"
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1412
|
-
const pathKey = getPathEnvKey(env);
|
|
1413
|
-
const currentPath = env[pathKey];
|
|
1414
|
-
return {
|
|
1415
|
-
command: "supabase",
|
|
1416
|
-
env: {
|
|
1417
|
-
...env,
|
|
1418
|
-
[pathKey]: currentPath ? `${localBinDir}${delimiter}${currentPath}` : localBinDir
|
|
1419
|
-
},
|
|
1420
|
-
source: "repository",
|
|
1421
|
-
localBinDir
|
|
1422
|
-
};
|
|
1423
|
-
}
|
|
1424
|
-
async function runCommand(execution, args, cwd = process.cwd()) {
|
|
1425
|
-
return new Promise((resolve4) => {
|
|
1426
|
-
let stdout = "";
|
|
1427
|
-
let stderr = "";
|
|
1428
|
-
const resolvedExecution = typeof execution === "string" ? { command: execution } : execution;
|
|
1429
|
-
const child = spawn(resolvedExecution.command, args, {
|
|
1430
|
-
cwd,
|
|
1431
|
-
env: resolvedExecution.env,
|
|
1432
|
-
shell: true,
|
|
1433
|
-
stdio: ["inherit", "pipe", "pipe"]
|
|
1434
|
-
});
|
|
1435
|
-
child.stdout?.on("data", (data) => {
|
|
1436
|
-
const text = data.toString();
|
|
1437
|
-
stdout += text;
|
|
1438
|
-
process.stdout.write(text);
|
|
1439
|
-
});
|
|
1440
|
-
child.stderr?.on("data", (data) => {
|
|
1441
|
-
const text = data.toString();
|
|
1442
|
-
stderr += text;
|
|
1443
|
-
process.stderr.write(text);
|
|
1444
|
-
});
|
|
1445
|
-
child.on("error", (err) => {
|
|
1446
|
-
resolve4({
|
|
1447
|
-
exitCode: null,
|
|
1448
|
-
signal: null,
|
|
1449
|
-
stdout,
|
|
1450
|
-
stderr,
|
|
1451
|
-
spawnError: err.message
|
|
1452
|
-
});
|
|
1453
|
-
});
|
|
1454
|
-
child.on("exit", (code, signal) => {
|
|
1455
|
-
resolve4({ exitCode: code, signal, stdout, stderr });
|
|
1456
|
-
});
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
async function runSupabaseCommand(args, cwd = process.cwd()) {
|
|
1460
|
-
return runCommand(resolveSupabaseCommand(cwd), args, cwd);
|
|
1491
|
+
return /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: "\u2500".repeat(effectiveWidth) });
|
|
1461
1492
|
}
|
|
1462
1493
|
|
|
1463
1494
|
// src/hooks/useCommand.ts
|
|
1495
|
+
import { useState as useState9, useCallback as useCallback2 } from "react";
|
|
1464
1496
|
function useCommand(execution = "supabase", cwd = process.cwd()) {
|
|
1465
|
-
const [status, setStatus] =
|
|
1466
|
-
const [result, setResult] =
|
|
1497
|
+
const [status, setStatus] = useState9("idle");
|
|
1498
|
+
const [result, setResult] = useState9(null);
|
|
1467
1499
|
const run = useCallback2(async (args) => {
|
|
1468
1500
|
setStatus("running");
|
|
1469
1501
|
setResult(null);
|
|
1470
|
-
|
|
1502
|
+
let resolvedExecution;
|
|
1503
|
+
if (typeof execution === "string") {
|
|
1504
|
+
const toolIds2 = ["supabase", "gh", "vercel"];
|
|
1505
|
+
if (toolIds2.includes(execution)) {
|
|
1506
|
+
const resolved = resolveToolCommand(execution, cwd);
|
|
1507
|
+
resolvedExecution = { command: resolved.command, env: resolved.env };
|
|
1508
|
+
} else {
|
|
1509
|
+
resolvedExecution = execution;
|
|
1510
|
+
}
|
|
1511
|
+
} else {
|
|
1512
|
+
resolvedExecution = execution;
|
|
1513
|
+
}
|
|
1514
|
+
const res = await runCommand(resolvedExecution, args, cwd);
|
|
1471
1515
|
setResult(res);
|
|
1472
1516
|
if (res.spawnError || res.exitCode !== null && res.exitCode !== 0) {
|
|
1473
1517
|
setStatus("error");
|
|
@@ -1484,43 +1528,41 @@ function useCommand(execution = "supabase", cwd = process.cwd()) {
|
|
|
1484
1528
|
}
|
|
1485
1529
|
|
|
1486
1530
|
// src/lib/clipboard.ts
|
|
1487
|
-
import { spawn
|
|
1488
|
-
async function openInBrowser(url) {
|
|
1489
|
-
return new Promise((resolve4) => {
|
|
1490
|
-
const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "${url}"` : `xdg-open "${url}"`;
|
|
1491
|
-
exec(cmd, () => resolve4());
|
|
1492
|
-
});
|
|
1493
|
-
}
|
|
1531
|
+
import { spawn, exec } from "child_process";
|
|
1494
1532
|
async function copyToClipboard(text) {
|
|
1495
|
-
return new Promise((
|
|
1533
|
+
return new Promise((resolve2) => {
|
|
1496
1534
|
const cmd = process.platform === "darwin" ? "pbcopy" : process.platform === "win32" ? "clip" : "xclip -selection clipboard";
|
|
1497
|
-
const child =
|
|
1535
|
+
const child = spawn(cmd, [], { shell: true });
|
|
1498
1536
|
child.stdin?.write(text);
|
|
1499
1537
|
child.stdin?.end();
|
|
1500
|
-
child.on("exit", () =>
|
|
1501
|
-
child.on("error", () =>
|
|
1538
|
+
child.on("exit", () => resolve2());
|
|
1539
|
+
child.on("error", () => resolve2());
|
|
1502
1540
|
});
|
|
1503
1541
|
}
|
|
1504
1542
|
|
|
1505
1543
|
// src/screens/CommandExecution.tsx
|
|
1506
|
-
import { jsx as
|
|
1544
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1507
1545
|
function CommandExecution({
|
|
1508
1546
|
args: initialArgs,
|
|
1547
|
+
tool = "supabase",
|
|
1509
1548
|
onBack,
|
|
1510
|
-
onExit
|
|
1549
|
+
onExit,
|
|
1550
|
+
width = 80,
|
|
1551
|
+
panelMode = false,
|
|
1552
|
+
isInputActive = true
|
|
1511
1553
|
}) {
|
|
1512
|
-
const [phase, setPhase] =
|
|
1513
|
-
const [currentArgs, setCurrentArgs] =
|
|
1514
|
-
const [pinMessage, setPinMessage] =
|
|
1515
|
-
const { status, result, run, reset } = useCommand();
|
|
1516
|
-
const cmdDisplay =
|
|
1554
|
+
const [phase, setPhase] = useState10("confirm");
|
|
1555
|
+
const [currentArgs, setCurrentArgs] = useState10(initialArgs);
|
|
1556
|
+
const [pinMessage, setPinMessage] = useState10();
|
|
1557
|
+
const { status, result, run, reset } = useCommand(tool);
|
|
1558
|
+
const cmdDisplay = `${tool} ${currentArgs.join(" ")}`;
|
|
1517
1559
|
const runCommand2 = currentArgs.join(" ");
|
|
1518
|
-
|
|
1560
|
+
useEffect5(() => {
|
|
1519
1561
|
if (phase === "running" && status === "idle") {
|
|
1520
1562
|
run(currentArgs);
|
|
1521
1563
|
}
|
|
1522
1564
|
}, [phase, status, run, currentArgs]);
|
|
1523
|
-
|
|
1565
|
+
useEffect5(() => {
|
|
1524
1566
|
if (phase === "running" && status === "success") {
|
|
1525
1567
|
if (isPinnedRun(runCommand2)) {
|
|
1526
1568
|
setPhase("success");
|
|
@@ -1533,7 +1575,7 @@ function CommandExecution({
|
|
|
1533
1575
|
}
|
|
1534
1576
|
}, [phase, runCommand2, status]);
|
|
1535
1577
|
if (phase === "confirm") {
|
|
1536
|
-
return /* @__PURE__ */
|
|
1578
|
+
return /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx15(
|
|
1537
1579
|
ConfirmPrompt,
|
|
1538
1580
|
{
|
|
1539
1581
|
message: `Execute ${cmdDisplay}?`,
|
|
@@ -1549,25 +1591,26 @@ function CommandExecution({
|
|
|
1549
1591
|
) });
|
|
1550
1592
|
}
|
|
1551
1593
|
if (phase === "running") {
|
|
1552
|
-
return /* @__PURE__ */
|
|
1553
|
-
/* @__PURE__ */
|
|
1554
|
-
/* @__PURE__ */
|
|
1555
|
-
/* @__PURE__ */
|
|
1556
|
-
/* @__PURE__ */
|
|
1557
|
-
/* @__PURE__ */
|
|
1594
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
1595
|
+
/* @__PURE__ */ jsx15(Divider, { width }),
|
|
1596
|
+
/* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
|
|
1597
|
+
/* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u25B6" }),
|
|
1598
|
+
/* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "Running:" }),
|
|
1599
|
+
/* @__PURE__ */ jsx15(Text14, { children: cmdDisplay }),
|
|
1600
|
+
/* @__PURE__ */ jsx15(ToolBadge, { tool })
|
|
1558
1601
|
] }),
|
|
1559
|
-
/* @__PURE__ */
|
|
1560
|
-
/* @__PURE__ */
|
|
1602
|
+
/* @__PURE__ */ jsx15(Divider, { width }),
|
|
1603
|
+
/* @__PURE__ */ jsx15(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx15(Spinner, { label: `Executing ${cmdDisplay}...` }) })
|
|
1561
1604
|
] });
|
|
1562
1605
|
}
|
|
1563
1606
|
if (phase === "success-pin-offer") {
|
|
1564
|
-
return /* @__PURE__ */
|
|
1565
|
-
/* @__PURE__ */
|
|
1566
|
-
/* @__PURE__ */
|
|
1567
|
-
/* @__PURE__ */
|
|
1568
|
-
/* @__PURE__ */
|
|
1607
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
1608
|
+
/* @__PURE__ */ jsx15(Divider, { width }),
|
|
1609
|
+
/* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
|
|
1610
|
+
/* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u2713" }),
|
|
1611
|
+
/* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
|
|
1569
1612
|
] }),
|
|
1570
|
-
/* @__PURE__ */
|
|
1613
|
+
/* @__PURE__ */ jsx15(
|
|
1571
1614
|
ConfirmPrompt,
|
|
1572
1615
|
{
|
|
1573
1616
|
message: "Pin this exact command?",
|
|
@@ -1584,19 +1627,22 @@ function CommandExecution({
|
|
|
1584
1627
|
] });
|
|
1585
1628
|
}
|
|
1586
1629
|
if (phase === "success") {
|
|
1587
|
-
return /* @__PURE__ */
|
|
1588
|
-
/* @__PURE__ */
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__PURE__ */
|
|
1591
|
-
/* @__PURE__ */
|
|
1630
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
1631
|
+
/* @__PURE__ */ jsx15(Divider, { width }),
|
|
1632
|
+
/* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
|
|
1633
|
+
/* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u2713" }),
|
|
1634
|
+
/* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
|
|
1592
1635
|
] }),
|
|
1593
|
-
pinMessage && /* @__PURE__ */
|
|
1594
|
-
/* @__PURE__ */
|
|
1636
|
+
pinMessage && /* @__PURE__ */ jsx15(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, children: pinMessage }) }),
|
|
1637
|
+
/* @__PURE__ */ jsx15(
|
|
1595
1638
|
SelectList,
|
|
1596
1639
|
{
|
|
1597
1640
|
items: [{ value: "__back__", label: "\u2190 Back to menu" }],
|
|
1598
1641
|
onSelect: onBack,
|
|
1599
|
-
onCancel: onBack
|
|
1642
|
+
onCancel: onBack,
|
|
1643
|
+
width,
|
|
1644
|
+
isInputActive,
|
|
1645
|
+
arrowNavigation: panelMode
|
|
1600
1646
|
}
|
|
1601
1647
|
)
|
|
1602
1648
|
] });
|
|
@@ -1614,11 +1660,6 @@ function CommandExecution({
|
|
|
1614
1660
|
}
|
|
1615
1661
|
}
|
|
1616
1662
|
errorItems.push(
|
|
1617
|
-
{
|
|
1618
|
-
value: "docs",
|
|
1619
|
-
label: "\u{1F4D6} Open Supabase CLI docs",
|
|
1620
|
-
hint: "Opens in browser"
|
|
1621
|
-
},
|
|
1622
1663
|
{
|
|
1623
1664
|
value: "copy",
|
|
1624
1665
|
label: "\u{1F4CB} Copy command to clipboard"
|
|
@@ -1626,44 +1667,42 @@ function CommandExecution({
|
|
|
1626
1667
|
{ value: "menu", label: "\u2190 Return to main menu" },
|
|
1627
1668
|
{ value: "exit", label: "\u{1F6AA} Exit Polter" }
|
|
1628
1669
|
);
|
|
1629
|
-
return /* @__PURE__ */
|
|
1630
|
-
/* @__PURE__ */
|
|
1631
|
-
result?.spawnError ? /* @__PURE__ */
|
|
1632
|
-
/* @__PURE__ */
|
|
1633
|
-
/* @__PURE__ */
|
|
1634
|
-
/* @__PURE__ */
|
|
1670
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
1671
|
+
/* @__PURE__ */ jsx15(Divider, { width }),
|
|
1672
|
+
result?.spawnError ? /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginY: 1, children: [
|
|
1673
|
+
/* @__PURE__ */ jsxs14(Box12, { gap: 1, children: [
|
|
1674
|
+
/* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: "\u2717" }),
|
|
1675
|
+
/* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: "Failed to start command" })
|
|
1635
1676
|
] }),
|
|
1636
|
-
/* @__PURE__ */
|
|
1637
|
-
/* @__PURE__ */
|
|
1638
|
-
/* @__PURE__ */
|
|
1677
|
+
/* @__PURE__ */ jsxs14(Box12, { marginLeft: 2, marginTop: 1, children: [
|
|
1678
|
+
/* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "Error: " }),
|
|
1679
|
+
/* @__PURE__ */ jsx15(Text14, { color: "red", children: result.spawnError })
|
|
1639
1680
|
] }),
|
|
1640
|
-
(result.spawnError.includes("ENOENT") || result.spawnError.includes("not found")) && /* @__PURE__ */
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
/* @__PURE__ */
|
|
1650
|
-
/* @__PURE__ */
|
|
1651
|
-
/* @__PURE__ */
|
|
1652
|
-
/* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: String(result?.exitCode) }),
|
|
1653
|
-
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ")" })
|
|
1681
|
+
(result.spawnError.includes("ENOENT") || result.spawnError.includes("not found")) && /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsxs14(Text14, { color: inkColors.accent, bold: true, children: [
|
|
1682
|
+
"\u{1F4A1} ",
|
|
1683
|
+
tool,
|
|
1684
|
+
" CLI not found in this repository or PATH"
|
|
1685
|
+
] }) })
|
|
1686
|
+
] }) : /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginY: 1, children: [
|
|
1687
|
+
/* @__PURE__ */ jsxs14(Box12, { gap: 1, children: [
|
|
1688
|
+
/* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: "\u2717" }),
|
|
1689
|
+
/* @__PURE__ */ jsx15(Text14, { color: "red", children: "Command failed " }),
|
|
1690
|
+
/* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "(exit code " }),
|
|
1691
|
+
/* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: String(result?.exitCode) }),
|
|
1692
|
+
/* @__PURE__ */ jsx15(Text14, { dimColor: true, children: ")" })
|
|
1654
1693
|
] }),
|
|
1655
|
-
/* @__PURE__ */
|
|
1656
|
-
/* @__PURE__ */
|
|
1657
|
-
/* @__PURE__ */
|
|
1694
|
+
/* @__PURE__ */ jsxs14(Box12, { marginLeft: 2, marginTop: 1, children: [
|
|
1695
|
+
/* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "Command: " }),
|
|
1696
|
+
/* @__PURE__ */ jsx15(Text14, { children: cmdDisplay })
|
|
1658
1697
|
] }),
|
|
1659
|
-
!hasDebug && /* @__PURE__ */
|
|
1660
|
-
/* @__PURE__ */
|
|
1661
|
-
/* @__PURE__ */
|
|
1662
|
-
/* @__PURE__ */
|
|
1698
|
+
!hasDebug && /* @__PURE__ */ jsxs14(Box12, { marginLeft: 2, marginTop: 1, gap: 1, children: [
|
|
1699
|
+
/* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "\u{1F4A1} Tip: retry with" }),
|
|
1700
|
+
/* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, children: "--debug" }),
|
|
1701
|
+
/* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "to see detailed logs" })
|
|
1663
1702
|
] })
|
|
1664
1703
|
] }),
|
|
1665
|
-
/* @__PURE__ */
|
|
1666
|
-
/* @__PURE__ */
|
|
1704
|
+
/* @__PURE__ */ jsx15(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx15(Text14, { bold: true, children: "What would you like to do?" }) }),
|
|
1705
|
+
/* @__PURE__ */ jsx15(
|
|
1667
1706
|
SelectList,
|
|
1668
1707
|
{
|
|
1669
1708
|
items: errorItems,
|
|
@@ -1682,9 +1721,6 @@ function CommandExecution({
|
|
|
1682
1721
|
setPhase("running");
|
|
1683
1722
|
break;
|
|
1684
1723
|
}
|
|
1685
|
-
case "docs":
|
|
1686
|
-
await openInBrowser("https://supabase.com/docs/guides/cli");
|
|
1687
|
-
break;
|
|
1688
1724
|
case "copy":
|
|
1689
1725
|
await copyToClipboard(cmdDisplay);
|
|
1690
1726
|
break;
|
|
@@ -1696,36 +1732,20 @@ function CommandExecution({
|
|
|
1696
1732
|
break;
|
|
1697
1733
|
}
|
|
1698
1734
|
},
|
|
1699
|
-
onCancel: onBack
|
|
1735
|
+
onCancel: onBack,
|
|
1736
|
+
width,
|
|
1737
|
+
isInputActive,
|
|
1738
|
+
arrowNavigation: panelMode
|
|
1700
1739
|
}
|
|
1701
1740
|
),
|
|
1702
|
-
/* @__PURE__ */
|
|
1741
|
+
!panelMode && /* @__PURE__ */ jsx15(StatusBar, { width })
|
|
1703
1742
|
] });
|
|
1704
1743
|
}
|
|
1705
1744
|
|
|
1706
1745
|
// src/screens/SelfUpdate.tsx
|
|
1707
|
-
import { useEffect as
|
|
1708
|
-
import { Box as Box13, Text as
|
|
1709
|
-
|
|
1710
|
-
// src/lib/packageRoot.ts
|
|
1711
|
-
import { existsSync as existsSync2 } from "fs";
|
|
1712
|
-
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
1713
|
-
function findNearestPackageRoot(startDir = process.cwd()) {
|
|
1714
|
-
let currentDir = resolve2(startDir);
|
|
1715
|
-
while (true) {
|
|
1716
|
-
if (existsSync2(join2(currentDir, "package.json"))) {
|
|
1717
|
-
return currentDir;
|
|
1718
|
-
}
|
|
1719
|
-
const parentDir = dirname2(currentDir);
|
|
1720
|
-
if (parentDir === currentDir) {
|
|
1721
|
-
return void 0;
|
|
1722
|
-
}
|
|
1723
|
-
currentDir = parentDir;
|
|
1724
|
-
}
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
// src/screens/SelfUpdate.tsx
|
|
1728
|
-
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1746
|
+
import { useEffect as useEffect6, useState as useState11 } from "react";
|
|
1747
|
+
import { Box as Box13, Text as Text15 } from "ink";
|
|
1748
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1729
1749
|
var packageName = "@polterware/polter";
|
|
1730
1750
|
var globalUpdateArgs = ["install", "-g", `${packageName}@latest`];
|
|
1731
1751
|
var repositoryUpdateArgs = ["install", "-D", `${packageName}@latest`];
|
|
@@ -1734,25 +1754,28 @@ function getUpdateArgs(target) {
|
|
|
1734
1754
|
}
|
|
1735
1755
|
function SelfUpdate({
|
|
1736
1756
|
onBack,
|
|
1737
|
-
onExit
|
|
1757
|
+
onExit,
|
|
1758
|
+
width = 80,
|
|
1759
|
+
panelMode = false,
|
|
1760
|
+
isInputActive = true
|
|
1738
1761
|
}) {
|
|
1739
1762
|
const repositoryRoot = findNearestPackageRoot();
|
|
1740
|
-
const [target, setTarget] =
|
|
1763
|
+
const [target, setTarget] = useState11(
|
|
1741
1764
|
repositoryRoot ? "repository" : "global"
|
|
1742
1765
|
);
|
|
1743
|
-
const [phase, setPhase] =
|
|
1766
|
+
const [phase, setPhase] = useState11(
|
|
1744
1767
|
repositoryRoot ? "target" : "confirm"
|
|
1745
1768
|
);
|
|
1746
1769
|
const updateArgs = getUpdateArgs(target);
|
|
1747
1770
|
const updateDisplay = `npm ${updateArgs.join(" ")}`;
|
|
1748
1771
|
const updateCwd = target === "repository" && repositoryRoot ? repositoryRoot : process.cwd();
|
|
1749
1772
|
const { status, result, run, reset } = useCommand("npm", updateCwd);
|
|
1750
|
-
|
|
1773
|
+
useEffect6(() => {
|
|
1751
1774
|
if (phase === "running" && status === "idle") {
|
|
1752
1775
|
run(updateArgs);
|
|
1753
1776
|
}
|
|
1754
1777
|
}, [phase, run, status, updateArgs]);
|
|
1755
|
-
|
|
1778
|
+
useEffect6(() => {
|
|
1756
1779
|
if (phase === "running" && status === "success") {
|
|
1757
1780
|
setPhase("success");
|
|
1758
1781
|
}
|
|
@@ -1761,9 +1784,9 @@ function SelfUpdate({
|
|
|
1761
1784
|
}
|
|
1762
1785
|
}, [phase, status]);
|
|
1763
1786
|
if (phase === "target") {
|
|
1764
|
-
return /* @__PURE__ */
|
|
1765
|
-
/* @__PURE__ */
|
|
1766
|
-
/* @__PURE__ */
|
|
1787
|
+
return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
|
|
1788
|
+
/* @__PURE__ */ jsx16(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { bold: true, children: "Choose where to update Polter." }) }),
|
|
1789
|
+
/* @__PURE__ */ jsx16(
|
|
1767
1790
|
SelectList,
|
|
1768
1791
|
{
|
|
1769
1792
|
items: [
|
|
@@ -1788,18 +1811,21 @@ function SelfUpdate({
|
|
|
1788
1811
|
reset();
|
|
1789
1812
|
setPhase("confirm");
|
|
1790
1813
|
},
|
|
1791
|
-
onCancel: onBack
|
|
1814
|
+
onCancel: onBack,
|
|
1815
|
+
width,
|
|
1816
|
+
isInputActive,
|
|
1817
|
+
arrowNavigation: panelMode
|
|
1792
1818
|
}
|
|
1793
1819
|
),
|
|
1794
|
-
repositoryRoot && /* @__PURE__ */
|
|
1820
|
+
repositoryRoot && /* @__PURE__ */ jsx16(Box13, { marginTop: 1, marginLeft: 2, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
1795
1821
|
"Repository root: ",
|
|
1796
1822
|
repositoryRoot
|
|
1797
1823
|
] }) })
|
|
1798
1824
|
] });
|
|
1799
1825
|
}
|
|
1800
1826
|
if (phase === "confirm") {
|
|
1801
|
-
return /* @__PURE__ */
|
|
1802
|
-
/* @__PURE__ */
|
|
1827
|
+
return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
|
|
1828
|
+
/* @__PURE__ */ jsx16(
|
|
1803
1829
|
ConfirmPrompt,
|
|
1804
1830
|
{
|
|
1805
1831
|
message: `Run ${updateDisplay}?`,
|
|
@@ -1818,9 +1844,9 @@ function SelfUpdate({
|
|
|
1818
1844
|
}
|
|
1819
1845
|
}
|
|
1820
1846
|
),
|
|
1821
|
-
/* @__PURE__ */
|
|
1822
|
-
/* @__PURE__ */
|
|
1823
|
-
target === "repository" && repositoryRoot && /* @__PURE__ */
|
|
1847
|
+
/* @__PURE__ */ jsxs15(Box13, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [
|
|
1848
|
+
/* @__PURE__ */ jsx16(Text15, { dimColor: true, children: target === "repository" ? "This updates the dependency in the current repository." : "This updates the global npm install." }),
|
|
1849
|
+
target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
1824
1850
|
"Run location: ",
|
|
1825
1851
|
repositoryRoot
|
|
1826
1852
|
] })
|
|
@@ -1828,32 +1854,32 @@ function SelfUpdate({
|
|
|
1828
1854
|
] });
|
|
1829
1855
|
}
|
|
1830
1856
|
if (phase === "running") {
|
|
1831
|
-
return /* @__PURE__ */
|
|
1832
|
-
/* @__PURE__ */
|
|
1833
|
-
/* @__PURE__ */
|
|
1834
|
-
/* @__PURE__ */
|
|
1835
|
-
/* @__PURE__ */
|
|
1836
|
-
/* @__PURE__ */
|
|
1857
|
+
return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
|
|
1858
|
+
/* @__PURE__ */ jsx16(Divider, { width }),
|
|
1859
|
+
/* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
|
|
1860
|
+
/* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "\u25B6" }),
|
|
1861
|
+
/* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Running:" }),
|
|
1862
|
+
/* @__PURE__ */ jsx16(Text15, { children: updateDisplay })
|
|
1837
1863
|
] }),
|
|
1838
|
-
/* @__PURE__ */
|
|
1839
|
-
/* @__PURE__ */
|
|
1864
|
+
/* @__PURE__ */ jsx16(Divider, { width }),
|
|
1865
|
+
/* @__PURE__ */ jsx16(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx16(Spinner, { label: "Updating Polter..." }) })
|
|
1840
1866
|
] });
|
|
1841
1867
|
}
|
|
1842
1868
|
if (phase === "success") {
|
|
1843
|
-
return /* @__PURE__ */
|
|
1844
|
-
/* @__PURE__ */
|
|
1845
|
-
/* @__PURE__ */
|
|
1846
|
-
/* @__PURE__ */
|
|
1847
|
-
/* @__PURE__ */
|
|
1869
|
+
return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
|
|
1870
|
+
/* @__PURE__ */ jsx16(Divider, { width }),
|
|
1871
|
+
/* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
|
|
1872
|
+
/* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "\u2713" }),
|
|
1873
|
+
/* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "Update completed successfully!" })
|
|
1848
1874
|
] }),
|
|
1849
|
-
/* @__PURE__ */
|
|
1850
|
-
/* @__PURE__ */
|
|
1851
|
-
target === "repository" && repositoryRoot && /* @__PURE__ */
|
|
1875
|
+
/* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
|
|
1876
|
+
/* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Restart Polter to use the latest version." }),
|
|
1877
|
+
target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
1852
1878
|
"Repository updated in: ",
|
|
1853
1879
|
repositoryRoot
|
|
1854
1880
|
] })
|
|
1855
1881
|
] }),
|
|
1856
|
-
/* @__PURE__ */
|
|
1882
|
+
/* @__PURE__ */ jsx16(
|
|
1857
1883
|
SelectList,
|
|
1858
1884
|
{
|
|
1859
1885
|
items: [
|
|
@@ -1867,130 +1893,1915 @@ function SelfUpdate({
|
|
|
1867
1893
|
}
|
|
1868
1894
|
onBack();
|
|
1869
1895
|
},
|
|
1896
|
+
onCancel: onBack,
|
|
1897
|
+
width,
|
|
1898
|
+
isInputActive,
|
|
1899
|
+
arrowNavigation: panelMode
|
|
1900
|
+
}
|
|
1901
|
+
)
|
|
1902
|
+
] });
|
|
1903
|
+
}
|
|
1904
|
+
return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
|
|
1905
|
+
/* @__PURE__ */ jsx16(Divider, { width }),
|
|
1906
|
+
result?.spawnError ? /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
|
|
1907
|
+
/* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
|
|
1908
|
+
/* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: "\u2717" }),
|
|
1909
|
+
/* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: "Failed to start update" })
|
|
1910
|
+
] }),
|
|
1911
|
+
/* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, children: [
|
|
1912
|
+
/* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Error: " }),
|
|
1913
|
+
/* @__PURE__ */ jsx16(Text15, { color: "red", children: result.spawnError })
|
|
1914
|
+
] })
|
|
1915
|
+
] }) : /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
|
|
1916
|
+
/* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
|
|
1917
|
+
/* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: "\u2717" }),
|
|
1918
|
+
/* @__PURE__ */ jsx16(Text15, { color: "red", children: "Update failed " }),
|
|
1919
|
+
/* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "(exit code " }),
|
|
1920
|
+
/* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: String(result?.exitCode) }),
|
|
1921
|
+
/* @__PURE__ */ jsx16(Text15, { dimColor: true, children: ")" })
|
|
1922
|
+
] }),
|
|
1923
|
+
/* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, children: [
|
|
1924
|
+
/* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Command: " }),
|
|
1925
|
+
/* @__PURE__ */ jsx16(Text15, { children: updateDisplay })
|
|
1926
|
+
] })
|
|
1927
|
+
] }),
|
|
1928
|
+
/* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
|
|
1929
|
+
/* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Manual fallback:" }),
|
|
1930
|
+
/* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, children: updateDisplay }),
|
|
1931
|
+
target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
1932
|
+
"Run location: ",
|
|
1933
|
+
repositoryRoot
|
|
1934
|
+
] })
|
|
1935
|
+
] }),
|
|
1936
|
+
/* @__PURE__ */ jsx16(Box13, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { bold: true, children: "What would you like to do?" }) }),
|
|
1937
|
+
/* @__PURE__ */ jsx16(
|
|
1938
|
+
SelectList,
|
|
1939
|
+
{
|
|
1940
|
+
items: [
|
|
1941
|
+
{ value: "retry", label: "\u{1F504} Retry update" },
|
|
1942
|
+
...repositoryRoot ? [{ value: "target", label: "\u2194 Choose update target" }] : [],
|
|
1943
|
+
{ value: "menu", label: "\u2190 Return to main menu" },
|
|
1944
|
+
{ value: "exit", label: "\u{1F6AA} Exit Polter" }
|
|
1945
|
+
],
|
|
1946
|
+
onSelect: (value) => {
|
|
1947
|
+
switch (value) {
|
|
1948
|
+
case "retry":
|
|
1949
|
+
reset();
|
|
1950
|
+
setPhase("running");
|
|
1951
|
+
break;
|
|
1952
|
+
case "target":
|
|
1953
|
+
reset();
|
|
1954
|
+
setPhase("target");
|
|
1955
|
+
break;
|
|
1956
|
+
case "menu":
|
|
1957
|
+
onBack();
|
|
1958
|
+
break;
|
|
1959
|
+
case "exit":
|
|
1960
|
+
onExit();
|
|
1961
|
+
break;
|
|
1962
|
+
}
|
|
1963
|
+
},
|
|
1964
|
+
onCancel: onBack,
|
|
1965
|
+
width,
|
|
1966
|
+
isInputActive,
|
|
1967
|
+
arrowNavigation: panelMode
|
|
1968
|
+
}
|
|
1969
|
+
),
|
|
1970
|
+
!panelMode && /* @__PURE__ */ jsx16(StatusBar, { width })
|
|
1971
|
+
] });
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
// src/screens/ToolStatus.tsx
|
|
1975
|
+
import { useMemo as useMemo4 } from "react";
|
|
1976
|
+
import { Box as Box14, Text as Text16 } from "ink";
|
|
1977
|
+
import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1978
|
+
var toolIds = ["supabase", "gh", "vercel", "pulumi"];
|
|
1979
|
+
function ToolStatus({ onBack, width = 80, panelMode = false, isInputActive = true }) {
|
|
1980
|
+
const tools = useMemo4(() => toolIds.map(getToolInfo), []);
|
|
1981
|
+
return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
|
|
1982
|
+
/* @__PURE__ */ jsx17(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text16, { bold: true, color: inkColors.accent, children: "\u{1F527} Tool Status" }) }),
|
|
1983
|
+
tools.map((tool) => /* @__PURE__ */ jsxs16(Box14, { gap: 1, marginLeft: 2, children: [
|
|
1984
|
+
/* @__PURE__ */ jsx17(Text16, { color: tool.installed ? inkColors.accent : "red", children: tool.installed ? "\u2713" : "\u2717" }),
|
|
1985
|
+
/* @__PURE__ */ jsx17(Box14, { width: 16, children: /* @__PURE__ */ jsx17(Text16, { bold: true, children: tool.label }) }),
|
|
1986
|
+
/* @__PURE__ */ jsx17(Text16, { dimColor: true, children: tool.installed ? tool.version ?? "installed" : "not found" })
|
|
1987
|
+
] }, tool.id)),
|
|
1988
|
+
/* @__PURE__ */ jsx17(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx17(
|
|
1989
|
+
SelectList,
|
|
1990
|
+
{
|
|
1991
|
+
items: [{ value: "__back__", label: "\u2190 Back" }],
|
|
1992
|
+
onSelect: onBack,
|
|
1993
|
+
onCancel: onBack,
|
|
1994
|
+
width,
|
|
1995
|
+
isInputActive,
|
|
1996
|
+
arrowNavigation: panelMode
|
|
1997
|
+
}
|
|
1998
|
+
) }),
|
|
1999
|
+
!panelMode && /* @__PURE__ */ jsx17(StatusBar, { hint: "Enter to go back", width })
|
|
2000
|
+
] });
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// src/screens/ProjectConfig.tsx
|
|
2004
|
+
import { useMemo as useMemo5, useState as useState13 } from "react";
|
|
2005
|
+
import { Box as Box15, Text as Text17 } from "ink";
|
|
2006
|
+
|
|
2007
|
+
// src/hooks/useEditor.ts
|
|
2008
|
+
import { useState as useState12, useCallback as useCallback3 } from "react";
|
|
2009
|
+
import { useStdin } from "ink";
|
|
2010
|
+
|
|
2011
|
+
// src/lib/editor.ts
|
|
2012
|
+
import { spawnSync, spawn as spawn2 } from "child_process";
|
|
2013
|
+
import { basename } from "path";
|
|
2014
|
+
var TERMINAL_EDITORS = /* @__PURE__ */ new Set([
|
|
2015
|
+
"vi",
|
|
2016
|
+
"vim",
|
|
2017
|
+
"nvim",
|
|
2018
|
+
"nano",
|
|
2019
|
+
"pico",
|
|
2020
|
+
"emacs",
|
|
2021
|
+
"micro",
|
|
2022
|
+
"helix",
|
|
2023
|
+
"hx",
|
|
2024
|
+
"joe",
|
|
2025
|
+
"mcedit"
|
|
2026
|
+
]);
|
|
2027
|
+
function resolveEditor() {
|
|
2028
|
+
const raw = process.env["VISUAL"] || process.env["EDITOR"];
|
|
2029
|
+
if (raw) {
|
|
2030
|
+
const parts = raw.trim().split(/\s+/);
|
|
2031
|
+
return { command: parts[0], args: parts.slice(1) };
|
|
2032
|
+
}
|
|
2033
|
+
const fallback = process.platform === "win32" ? "notepad" : "nano";
|
|
2034
|
+
return { command: fallback, args: [] };
|
|
2035
|
+
}
|
|
2036
|
+
function isTerminalEditor(cmd) {
|
|
2037
|
+
return TERMINAL_EDITORS.has(basename(cmd));
|
|
2038
|
+
}
|
|
2039
|
+
function openInEditor(filePath) {
|
|
2040
|
+
const editor = resolveEditor();
|
|
2041
|
+
const terminal = isTerminalEditor(editor.command);
|
|
2042
|
+
if (terminal) {
|
|
2043
|
+
const result = spawnSync(editor.command, [...editor.args, filePath], {
|
|
2044
|
+
stdio: "inherit"
|
|
2045
|
+
});
|
|
2046
|
+
return { exitCode: result.status, isTerminal: true };
|
|
2047
|
+
}
|
|
2048
|
+
const child = spawn2(editor.command, [...editor.args, filePath], {
|
|
2049
|
+
detached: true,
|
|
2050
|
+
stdio: "ignore"
|
|
2051
|
+
});
|
|
2052
|
+
child.unref();
|
|
2053
|
+
return { exitCode: 0, isTerminal: false };
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
// src/hooks/useEditor.ts
|
|
2057
|
+
function useEditor() {
|
|
2058
|
+
const { setRawMode } = useStdin();
|
|
2059
|
+
const [isEditing, setIsEditing] = useState12(false);
|
|
2060
|
+
const openEditor = useCallback3(async (filePath) => {
|
|
2061
|
+
const editor = resolveEditor();
|
|
2062
|
+
const terminal = isTerminalEditor(editor.command);
|
|
2063
|
+
setIsEditing(true);
|
|
2064
|
+
if (terminal) {
|
|
2065
|
+
setRawMode(false);
|
|
2066
|
+
}
|
|
2067
|
+
try {
|
|
2068
|
+
openInEditor(filePath);
|
|
2069
|
+
} finally {
|
|
2070
|
+
if (terminal) {
|
|
2071
|
+
setRawMode(true);
|
|
2072
|
+
}
|
|
2073
|
+
setIsEditing(false);
|
|
2074
|
+
}
|
|
2075
|
+
}, [setRawMode]);
|
|
2076
|
+
return { openEditor, isEditing };
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// src/screens/ProjectConfig.tsx
|
|
2080
|
+
import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2081
|
+
function ProjectConfig({
|
|
2082
|
+
onBack,
|
|
2083
|
+
width = 80,
|
|
2084
|
+
panelMode = false,
|
|
2085
|
+
isInputActive = true
|
|
2086
|
+
}) {
|
|
2087
|
+
const configPath = useMemo5(() => getProjectConfigPath(), []);
|
|
2088
|
+
const [config2, setConfig] = useState13(() => getOrCreateProjectConfig());
|
|
2089
|
+
const [phase, setPhase] = useState13("overview");
|
|
2090
|
+
const [feedback, setFeedback] = useState13();
|
|
2091
|
+
const { openEditor, isEditing } = useEditor();
|
|
2092
|
+
if (!configPath) {
|
|
2093
|
+
return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
|
|
2094
|
+
/* @__PURE__ */ jsx18(Text17, { color: "red", children: "No package.json found. Run from a project directory." }),
|
|
2095
|
+
/* @__PURE__ */ jsx18(
|
|
2096
|
+
SelectList,
|
|
2097
|
+
{
|
|
2098
|
+
items: [{ value: "__back__", label: "\u2190 Back" }],
|
|
2099
|
+
onSelect: onBack,
|
|
2100
|
+
onCancel: onBack,
|
|
2101
|
+
width,
|
|
2102
|
+
isInputActive,
|
|
2103
|
+
arrowNavigation: panelMode
|
|
2104
|
+
}
|
|
2105
|
+
)
|
|
2106
|
+
] });
|
|
2107
|
+
}
|
|
2108
|
+
if (phase === "edit-supabase-ref") {
|
|
2109
|
+
return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
|
|
2110
|
+
TextPrompt,
|
|
2111
|
+
{
|
|
2112
|
+
label: "Supabase project ref:",
|
|
2113
|
+
placeholder: "e.g. abcdefghijklmnop",
|
|
2114
|
+
onSubmit: (val) => {
|
|
2115
|
+
const updated = {
|
|
2116
|
+
...config2,
|
|
2117
|
+
tools: {
|
|
2118
|
+
...config2.tools,
|
|
2119
|
+
supabase: { ...config2.tools.supabase, projectRef: val.trim() || void 0 }
|
|
2120
|
+
}
|
|
2121
|
+
};
|
|
2122
|
+
writeProjectConfig(updated);
|
|
2123
|
+
setConfig(updated);
|
|
2124
|
+
setFeedback("Supabase project ref updated");
|
|
2125
|
+
setPhase("overview");
|
|
2126
|
+
},
|
|
2127
|
+
onCancel: () => setPhase("overview")
|
|
2128
|
+
}
|
|
2129
|
+
) });
|
|
2130
|
+
}
|
|
2131
|
+
if (phase === "edit-vercel-id") {
|
|
2132
|
+
return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
|
|
2133
|
+
TextPrompt,
|
|
2134
|
+
{
|
|
2135
|
+
label: "Vercel project ID:",
|
|
2136
|
+
placeholder: "e.g. prj_xxxx",
|
|
2137
|
+
onSubmit: (val) => {
|
|
2138
|
+
const updated = {
|
|
2139
|
+
...config2,
|
|
2140
|
+
tools: {
|
|
2141
|
+
...config2.tools,
|
|
2142
|
+
vercel: { ...config2.tools.vercel, projectId: val.trim() || void 0 }
|
|
2143
|
+
}
|
|
2144
|
+
};
|
|
2145
|
+
writeProjectConfig(updated);
|
|
2146
|
+
setConfig(updated);
|
|
2147
|
+
setFeedback("Vercel project ID updated");
|
|
2148
|
+
setPhase("overview");
|
|
2149
|
+
},
|
|
2150
|
+
onCancel: () => setPhase("overview")
|
|
2151
|
+
}
|
|
2152
|
+
) });
|
|
2153
|
+
}
|
|
2154
|
+
if (phase === "edit-gh-repo") {
|
|
2155
|
+
return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
|
|
2156
|
+
TextPrompt,
|
|
2157
|
+
{
|
|
2158
|
+
label: "GitHub repo (owner/name):",
|
|
2159
|
+
placeholder: "e.g. myorg/my-app",
|
|
2160
|
+
onSubmit: (val) => {
|
|
2161
|
+
const updated = {
|
|
2162
|
+
...config2,
|
|
2163
|
+
tools: {
|
|
2164
|
+
...config2.tools,
|
|
2165
|
+
gh: { ...config2.tools.gh, repo: val.trim() || void 0 }
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
writeProjectConfig(updated);
|
|
2169
|
+
setConfig(updated);
|
|
2170
|
+
setFeedback("GitHub repo updated");
|
|
2171
|
+
setPhase("overview");
|
|
2172
|
+
},
|
|
2173
|
+
onCancel: () => setPhase("overview")
|
|
2174
|
+
}
|
|
2175
|
+
) });
|
|
2176
|
+
}
|
|
2177
|
+
return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
|
|
2178
|
+
/* @__PURE__ */ jsx18(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx18(Text17, { bold: true, color: inkColors.accent, children: "\u2699\uFE0F Project Config" }) }),
|
|
2179
|
+
/* @__PURE__ */ jsxs17(Box15, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
|
|
2180
|
+
/* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
2181
|
+
"Path: ",
|
|
2182
|
+
configPath.file
|
|
2183
|
+
] }),
|
|
2184
|
+
/* @__PURE__ */ jsxs17(Text17, { children: [
|
|
2185
|
+
"Supabase ref: ",
|
|
2186
|
+
config2.tools.supabase?.projectRef ?? /* @__PURE__ */ jsx18(Text17, { dimColor: true, children: "not set" })
|
|
2187
|
+
] }),
|
|
2188
|
+
/* @__PURE__ */ jsxs17(Text17, { children: [
|
|
2189
|
+
"Vercel ID: ",
|
|
2190
|
+
config2.tools.vercel?.projectId ?? /* @__PURE__ */ jsx18(Text17, { dimColor: true, children: "not set" })
|
|
2191
|
+
] }),
|
|
2192
|
+
/* @__PURE__ */ jsxs17(Text17, { children: [
|
|
2193
|
+
"GitHub repo: ",
|
|
2194
|
+
config2.tools.gh?.repo ?? /* @__PURE__ */ jsx18(Text17, { dimColor: true, children: "not set" })
|
|
2195
|
+
] })
|
|
2196
|
+
] }),
|
|
2197
|
+
feedback && /* @__PURE__ */ jsx18(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs17(Text17, { color: inkColors.accent, children: [
|
|
2198
|
+
"\u2713 ",
|
|
2199
|
+
feedback
|
|
2200
|
+
] }) }),
|
|
2201
|
+
/* @__PURE__ */ jsx18(
|
|
2202
|
+
SelectList,
|
|
2203
|
+
{
|
|
2204
|
+
items: [
|
|
2205
|
+
{ value: "supabase", label: "Set Supabase project ref" },
|
|
2206
|
+
{ value: "vercel", label: "Set Vercel project ID" },
|
|
2207
|
+
{ value: "gh", label: "Set GitHub repo" },
|
|
2208
|
+
{ value: "editor", label: "Open config in editor" },
|
|
2209
|
+
{ value: "__back__", label: "\u2190 Back" }
|
|
2210
|
+
],
|
|
2211
|
+
onSelect: (value) => {
|
|
2212
|
+
switch (value) {
|
|
2213
|
+
case "supabase":
|
|
2214
|
+
setPhase("edit-supabase-ref");
|
|
2215
|
+
break;
|
|
2216
|
+
case "vercel":
|
|
2217
|
+
setPhase("edit-vercel-id");
|
|
2218
|
+
break;
|
|
2219
|
+
case "gh":
|
|
2220
|
+
setPhase("edit-gh-repo");
|
|
2221
|
+
break;
|
|
2222
|
+
case "editor":
|
|
2223
|
+
openEditor(configPath.file).then(() => {
|
|
2224
|
+
try {
|
|
2225
|
+
const reloaded = getOrCreateProjectConfig();
|
|
2226
|
+
setConfig(reloaded);
|
|
2227
|
+
setFeedback("Config reloaded from file");
|
|
2228
|
+
} catch {
|
|
2229
|
+
setFeedback("Warning: could not parse config after editing");
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
break;
|
|
2233
|
+
default:
|
|
2234
|
+
onBack();
|
|
2235
|
+
}
|
|
2236
|
+
},
|
|
2237
|
+
onCancel: onBack,
|
|
2238
|
+
width,
|
|
2239
|
+
isInputActive,
|
|
2240
|
+
arrowNavigation: panelMode
|
|
2241
|
+
}
|
|
2242
|
+
),
|
|
2243
|
+
!panelMode && /* @__PURE__ */ jsx18(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
|
|
2244
|
+
] });
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
// src/screens/PipelineList.tsx
|
|
2248
|
+
import { useMemo as useMemo6 } from "react";
|
|
2249
|
+
import { Box as Box16, Text as Text18 } from "ink";
|
|
2250
|
+
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2251
|
+
function PipelineList({
|
|
2252
|
+
onNavigate,
|
|
2253
|
+
onBack,
|
|
2254
|
+
width = 80,
|
|
2255
|
+
panelMode = false,
|
|
2256
|
+
isInputActive = true
|
|
2257
|
+
}) {
|
|
2258
|
+
const pipelines = useMemo6(() => getAllPipelines(), []);
|
|
2259
|
+
const items = useMemo6(() => {
|
|
2260
|
+
const list = [];
|
|
2261
|
+
if (pipelines.length === 0) {
|
|
2262
|
+
list.push({
|
|
2263
|
+
id: "empty-hint",
|
|
2264
|
+
value: "__empty__",
|
|
2265
|
+
label: "No pipelines yet",
|
|
2266
|
+
kind: "header",
|
|
2267
|
+
selectable: false
|
|
2268
|
+
});
|
|
2269
|
+
} else {
|
|
2270
|
+
list.push({
|
|
2271
|
+
id: "section-pipelines",
|
|
2272
|
+
value: "__section__",
|
|
2273
|
+
label: "\u{1F517} Saved Pipelines",
|
|
2274
|
+
kind: "header",
|
|
2275
|
+
selectable: false
|
|
2276
|
+
});
|
|
2277
|
+
for (const pipeline of pipelines) {
|
|
2278
|
+
list.push({
|
|
2279
|
+
id: `pipeline:${pipeline.id}`,
|
|
2280
|
+
value: pipeline.id,
|
|
2281
|
+
label: pipeline.name,
|
|
2282
|
+
hint: `${pipeline.steps.length} steps \xB7 ${pipeline.source}${pipeline.description ? ` \xB7 ${pipeline.description}` : ""}`,
|
|
2283
|
+
kind: "action"
|
|
2284
|
+
});
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
list.push({
|
|
2288
|
+
id: "section-actions",
|
|
2289
|
+
value: "__section_actions__",
|
|
2290
|
+
label: "\u26A1 Actions",
|
|
2291
|
+
kind: "header",
|
|
2292
|
+
selectable: false
|
|
2293
|
+
});
|
|
2294
|
+
list.push({
|
|
2295
|
+
id: "action-new",
|
|
2296
|
+
value: "__new__",
|
|
2297
|
+
label: "Create Pipeline",
|
|
2298
|
+
hint: "Build a new command sequence",
|
|
2299
|
+
icon: "\u2795",
|
|
2300
|
+
kind: "action"
|
|
2301
|
+
});
|
|
2302
|
+
list.push({
|
|
2303
|
+
id: "action-back",
|
|
2304
|
+
value: "__back__",
|
|
2305
|
+
label: "\u2190 Back",
|
|
2306
|
+
kind: "action"
|
|
2307
|
+
});
|
|
2308
|
+
return list;
|
|
2309
|
+
}, [pipelines]);
|
|
2310
|
+
return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", children: [
|
|
2311
|
+
/* @__PURE__ */ jsx19(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx19(Text18, { bold: true, color: inkColors.accent, children: "\u{1F517} Pipelines" }) }),
|
|
2312
|
+
/* @__PURE__ */ jsx19(
|
|
2313
|
+
SelectList,
|
|
2314
|
+
{
|
|
2315
|
+
items,
|
|
2316
|
+
onSelect: (value) => {
|
|
2317
|
+
if (value === "__back__") {
|
|
2318
|
+
onBack();
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
if (value === "__new__") {
|
|
2322
|
+
onNavigate("pipeline-builder");
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
onNavigate("pipeline-execution", { pipelineId: value });
|
|
2326
|
+
},
|
|
2327
|
+
onCancel: onBack,
|
|
2328
|
+
width,
|
|
2329
|
+
isInputActive,
|
|
2330
|
+
arrowNavigation: panelMode
|
|
2331
|
+
}
|
|
2332
|
+
),
|
|
2333
|
+
!panelMode && /* @__PURE__ */ jsx19(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
|
|
2334
|
+
] });
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
// src/screens/PipelineBuilder.tsx
|
|
2338
|
+
import { useState as useState14 } from "react";
|
|
2339
|
+
import { Box as Box17, Text as Text19 } from "ink";
|
|
2340
|
+
import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2341
|
+
var stepCounter = 0;
|
|
2342
|
+
function nextStepId() {
|
|
2343
|
+
stepCounter += 1;
|
|
2344
|
+
return `step-${Date.now()}-${stepCounter}`;
|
|
2345
|
+
}
|
|
2346
|
+
function PipelineBuilder({
|
|
2347
|
+
onBack,
|
|
2348
|
+
width = 80,
|
|
2349
|
+
height = 24,
|
|
2350
|
+
panelMode = false,
|
|
2351
|
+
isInputActive = true
|
|
2352
|
+
}) {
|
|
2353
|
+
const [phase, setPhase] = useState14("name");
|
|
2354
|
+
const [name, setName] = useState14("");
|
|
2355
|
+
const [steps, setSteps] = useState14([]);
|
|
2356
|
+
if (phase === "name") {
|
|
2357
|
+
return /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", children: [
|
|
2358
|
+
/* @__PURE__ */ jsx20(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsx20(Text19, { bold: true, color: inkColors.accent, children: "\u{1F517} New Pipeline" }) }),
|
|
2359
|
+
/* @__PURE__ */ jsx20(
|
|
2360
|
+
TextPrompt,
|
|
2361
|
+
{
|
|
2362
|
+
label: "Pipeline name:",
|
|
2363
|
+
placeholder: "e.g. deploy-all",
|
|
2364
|
+
validate: (val) => {
|
|
2365
|
+
if (!val?.trim()) return "Name is required";
|
|
2366
|
+
return void 0;
|
|
2367
|
+
},
|
|
2368
|
+
onSubmit: (val) => {
|
|
2369
|
+
setName(val.trim());
|
|
2370
|
+
setPhase("add-step");
|
|
2371
|
+
},
|
|
1870
2372
|
onCancel: onBack
|
|
1871
2373
|
}
|
|
2374
|
+
),
|
|
2375
|
+
!panelMode && /* @__PURE__ */ jsx20(StatusBar, { hint: "Type name \xB7 Enter to continue \xB7 Esc cancel", width })
|
|
2376
|
+
] });
|
|
2377
|
+
}
|
|
2378
|
+
if (phase === "add-step") {
|
|
2379
|
+
const commandItems = [
|
|
2380
|
+
{
|
|
2381
|
+
id: "section-commands",
|
|
2382
|
+
value: "__section__",
|
|
2383
|
+
label: "Available Commands",
|
|
2384
|
+
kind: "header",
|
|
2385
|
+
selectable: false
|
|
2386
|
+
},
|
|
2387
|
+
...allCommands.map((cmd) => ({
|
|
2388
|
+
id: cmd.id,
|
|
2389
|
+
value: cmd.id,
|
|
2390
|
+
label: `${cmd.tool}: ${cmd.label}`,
|
|
2391
|
+
hint: cmd.hint,
|
|
2392
|
+
kind: "command"
|
|
2393
|
+
})),
|
|
2394
|
+
{
|
|
2395
|
+
id: "section-done",
|
|
2396
|
+
value: "__section_done__",
|
|
2397
|
+
label: "\u26A1 Actions",
|
|
2398
|
+
kind: "header",
|
|
2399
|
+
selectable: false
|
|
2400
|
+
},
|
|
2401
|
+
{
|
|
2402
|
+
id: "action-done",
|
|
2403
|
+
value: "__done__",
|
|
2404
|
+
label: steps.length > 0 ? "\u2713 Done - save pipeline" : "\u2190 Cancel",
|
|
2405
|
+
kind: "action"
|
|
2406
|
+
}
|
|
2407
|
+
];
|
|
2408
|
+
return /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", children: [
|
|
2409
|
+
/* @__PURE__ */ jsxs19(Box17, { marginBottom: 1, gap: 1, children: [
|
|
2410
|
+
/* @__PURE__ */ jsxs19(Text19, { bold: true, color: inkColors.accent, children: [
|
|
2411
|
+
"\u{1F517} ",
|
|
2412
|
+
name
|
|
2413
|
+
] }),
|
|
2414
|
+
/* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
|
|
2415
|
+
"(",
|
|
2416
|
+
steps.length,
|
|
2417
|
+
" steps)"
|
|
2418
|
+
] })
|
|
2419
|
+
] }),
|
|
2420
|
+
steps.length > 0 && /* @__PURE__ */ jsx20(Box17, { flexDirection: "column", marginBottom: 1, children: steps.map((step, i) => /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
|
|
2421
|
+
i + 1,
|
|
2422
|
+
". ",
|
|
2423
|
+
step.commandId,
|
|
2424
|
+
step.args.length > 0 ? ` ${step.args.join(" ")}` : ""
|
|
2425
|
+
] }, step.id)) }),
|
|
2426
|
+
/* @__PURE__ */ jsx20(Text19, { dimColor: true, children: "Select a command to add as step:" }),
|
|
2427
|
+
/* @__PURE__ */ jsx20(
|
|
2428
|
+
SelectList,
|
|
2429
|
+
{
|
|
2430
|
+
items: commandItems,
|
|
2431
|
+
onSelect: (value) => {
|
|
2432
|
+
if (value === "__done__") {
|
|
2433
|
+
if (steps.length > 0) {
|
|
2434
|
+
setPhase("review");
|
|
2435
|
+
} else {
|
|
2436
|
+
onBack();
|
|
2437
|
+
}
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
setSteps((prev) => [
|
|
2441
|
+
...prev,
|
|
2442
|
+
{
|
|
2443
|
+
id: nextStepId(),
|
|
2444
|
+
commandId: value,
|
|
2445
|
+
args: [],
|
|
2446
|
+
flags: [],
|
|
2447
|
+
continueOnError: false
|
|
2448
|
+
}
|
|
2449
|
+
]);
|
|
2450
|
+
},
|
|
2451
|
+
onCancel: () => {
|
|
2452
|
+
if (steps.length > 0) {
|
|
2453
|
+
setPhase("review");
|
|
2454
|
+
} else {
|
|
2455
|
+
onBack();
|
|
2456
|
+
}
|
|
2457
|
+
},
|
|
2458
|
+
maxVisible: Math.max(8, height - 14),
|
|
2459
|
+
width,
|
|
2460
|
+
isInputActive,
|
|
2461
|
+
arrowNavigation: panelMode
|
|
2462
|
+
}
|
|
2463
|
+
),
|
|
2464
|
+
!panelMode && /* @__PURE__ */ jsx20(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter add step \xB7 Esc done", width })
|
|
2465
|
+
] });
|
|
2466
|
+
}
|
|
2467
|
+
const reviewItems = [
|
|
2468
|
+
{
|
|
2469
|
+
id: "action-save",
|
|
2470
|
+
value: "__save__",
|
|
2471
|
+
label: "\u2713 Save pipeline",
|
|
2472
|
+
kind: "action"
|
|
2473
|
+
},
|
|
2474
|
+
{
|
|
2475
|
+
id: "action-add-more",
|
|
2476
|
+
value: "__add__",
|
|
2477
|
+
label: "\u2795 Add more steps",
|
|
2478
|
+
kind: "action"
|
|
2479
|
+
},
|
|
2480
|
+
{
|
|
2481
|
+
id: "action-cancel",
|
|
2482
|
+
value: "__cancel__",
|
|
2483
|
+
label: "\u2190 Cancel",
|
|
2484
|
+
kind: "action"
|
|
2485
|
+
}
|
|
2486
|
+
];
|
|
2487
|
+
return /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", children: [
|
|
2488
|
+
/* @__PURE__ */ jsx20(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text19, { bold: true, color: inkColors.accent, children: [
|
|
2489
|
+
"\u{1F517} Review: ",
|
|
2490
|
+
name
|
|
2491
|
+
] }) }),
|
|
2492
|
+
/* @__PURE__ */ jsx20(Box17, { flexDirection: "column", marginBottom: 1, children: steps.map((step, i) => /* @__PURE__ */ jsxs19(Text19, { children: [
|
|
2493
|
+
i + 1,
|
|
2494
|
+
". ",
|
|
2495
|
+
step.commandId,
|
|
2496
|
+
step.args.length > 0 ? ` ${step.args.join(" ")}` : ""
|
|
2497
|
+
] }, step.id)) }),
|
|
2498
|
+
/* @__PURE__ */ jsx20(
|
|
2499
|
+
SelectList,
|
|
2500
|
+
{
|
|
2501
|
+
items: reviewItems,
|
|
2502
|
+
onSelect: (value) => {
|
|
2503
|
+
if (value === "__save__") {
|
|
2504
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2505
|
+
const pipeline = {
|
|
2506
|
+
id: `pipeline-${Date.now()}`,
|
|
2507
|
+
name,
|
|
2508
|
+
steps,
|
|
2509
|
+
createdAt: now,
|
|
2510
|
+
updatedAt: now
|
|
2511
|
+
};
|
|
2512
|
+
savePipeline(pipeline, "project");
|
|
2513
|
+
onBack();
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
if (value === "__add__") {
|
|
2517
|
+
setPhase("add-step");
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
onBack();
|
|
2521
|
+
},
|
|
2522
|
+
onCancel: onBack,
|
|
2523
|
+
width,
|
|
2524
|
+
isInputActive,
|
|
2525
|
+
arrowNavigation: panelMode
|
|
2526
|
+
}
|
|
2527
|
+
),
|
|
2528
|
+
!panelMode && /* @__PURE__ */ jsx20(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
|
|
2529
|
+
] });
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
// src/screens/PipelineExecution.tsx
|
|
2533
|
+
import { useState as useState15, useEffect as useEffect7, useMemo as useMemo7 } from "react";
|
|
2534
|
+
import { Box as Box20, Text as Text22 } from "ink";
|
|
2535
|
+
|
|
2536
|
+
// src/components/StepIndicator.tsx
|
|
2537
|
+
import { Box as Box18, Text as Text20 } from "ink";
|
|
2538
|
+
import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2539
|
+
var statusIcons = {
|
|
2540
|
+
pending: "\u25CB",
|
|
2541
|
+
running: "",
|
|
2542
|
+
success: "\u2713",
|
|
2543
|
+
error: "\u2717",
|
|
2544
|
+
skipped: "\u2298"
|
|
2545
|
+
};
|
|
2546
|
+
var statusColors = {
|
|
2547
|
+
pending: void 0,
|
|
2548
|
+
running: inkColors.accent,
|
|
2549
|
+
success: inkColors.accent,
|
|
2550
|
+
error: "red",
|
|
2551
|
+
skipped: void 0
|
|
2552
|
+
};
|
|
2553
|
+
function StepIndicator({
|
|
2554
|
+
label,
|
|
2555
|
+
status,
|
|
2556
|
+
index
|
|
2557
|
+
}) {
|
|
2558
|
+
return /* @__PURE__ */ jsxs20(Box18, { gap: 1, children: [
|
|
2559
|
+
/* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
|
|
2560
|
+
String(index + 1).padStart(2, " "),
|
|
2561
|
+
"."
|
|
2562
|
+
] }),
|
|
2563
|
+
status === "running" ? /* @__PURE__ */ jsx21(Spinner, { label: "" }) : /* @__PURE__ */ jsx21(
|
|
2564
|
+
Text20,
|
|
2565
|
+
{
|
|
2566
|
+
color: statusColors[status],
|
|
2567
|
+
dimColor: status === "pending" || status === "skipped",
|
|
2568
|
+
children: statusIcons[status]
|
|
2569
|
+
}
|
|
2570
|
+
),
|
|
2571
|
+
/* @__PURE__ */ jsx21(
|
|
2572
|
+
Text20,
|
|
2573
|
+
{
|
|
2574
|
+
color: statusColors[status],
|
|
2575
|
+
dimColor: status === "pending" || status === "skipped",
|
|
2576
|
+
bold: status === "running",
|
|
2577
|
+
children: label
|
|
2578
|
+
}
|
|
2579
|
+
)
|
|
2580
|
+
] });
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
// src/components/PipelineProgress.tsx
|
|
2584
|
+
import { Box as Box19, Text as Text21 } from "ink";
|
|
2585
|
+
import { jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2586
|
+
function PipelineProgressBar({
|
|
2587
|
+
total,
|
|
2588
|
+
completed,
|
|
2589
|
+
errors,
|
|
2590
|
+
width = 80
|
|
2591
|
+
}) {
|
|
2592
|
+
const barWidth = Math.max(10, width - 20);
|
|
2593
|
+
const filledCount = total > 0 ? Math.round(completed / total * barWidth) : 0;
|
|
2594
|
+
const filled = "\u2588".repeat(filledCount);
|
|
2595
|
+
const empty = "\u2591".repeat(barWidth - filledCount);
|
|
2596
|
+
return /* @__PURE__ */ jsxs21(Box19, { gap: 1, children: [
|
|
2597
|
+
/* @__PURE__ */ jsxs21(Text21, { color: errors > 0 ? "red" : inkColors.accent, children: [
|
|
2598
|
+
"[",
|
|
2599
|
+
filled,
|
|
2600
|
+
empty,
|
|
2601
|
+
"]"
|
|
2602
|
+
] }),
|
|
2603
|
+
/* @__PURE__ */ jsxs21(Text21, { dimColor: true, children: [
|
|
2604
|
+
completed,
|
|
2605
|
+
"/",
|
|
2606
|
+
total,
|
|
2607
|
+
errors > 0 ? ` (${errors} failed)` : ""
|
|
2608
|
+
] })
|
|
2609
|
+
] });
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
// src/screens/PipelineExecution.tsx
|
|
2613
|
+
import { Fragment, jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2614
|
+
function PipelineExecution({
|
|
2615
|
+
pipelineId,
|
|
2616
|
+
onBack,
|
|
2617
|
+
onExit,
|
|
2618
|
+
width = 80,
|
|
2619
|
+
panelMode = false,
|
|
2620
|
+
isInputActive = true
|
|
2621
|
+
}) {
|
|
2622
|
+
const pipeline = useMemo7(
|
|
2623
|
+
() => getAllPipelines().find((p) => p.id === pipelineId),
|
|
2624
|
+
[pipelineId]
|
|
2625
|
+
);
|
|
2626
|
+
const [phase, setPhase] = useState15("running");
|
|
2627
|
+
const [progress, setProgress] = useState15(null);
|
|
2628
|
+
const [results, setResults] = useState15([]);
|
|
2629
|
+
useEffect7(() => {
|
|
2630
|
+
if (!pipeline) return;
|
|
2631
|
+
executePipeline(pipeline, (p) => {
|
|
2632
|
+
setProgress({ ...p });
|
|
2633
|
+
if (p.done) {
|
|
2634
|
+
setResults(p.stepResults);
|
|
2635
|
+
setPhase("done");
|
|
2636
|
+
}
|
|
2637
|
+
}).catch(() => {
|
|
2638
|
+
setPhase("done");
|
|
2639
|
+
});
|
|
2640
|
+
}, [pipeline]);
|
|
2641
|
+
if (!pipeline) {
|
|
2642
|
+
return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
|
|
2643
|
+
/* @__PURE__ */ jsxs22(Text22, { color: "red", children: [
|
|
2644
|
+
"Pipeline not found: ",
|
|
2645
|
+
pipelineId
|
|
2646
|
+
] }),
|
|
2647
|
+
/* @__PURE__ */ jsx22(
|
|
2648
|
+
SelectList,
|
|
2649
|
+
{
|
|
2650
|
+
items: [{ value: "__back__", label: "\u2190 Back" }],
|
|
2651
|
+
onSelect: onBack,
|
|
2652
|
+
onCancel: onBack,
|
|
2653
|
+
width,
|
|
2654
|
+
isInputActive,
|
|
2655
|
+
arrowNavigation: panelMode
|
|
2656
|
+
}
|
|
1872
2657
|
)
|
|
1873
2658
|
] });
|
|
1874
2659
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
/* @__PURE__ */
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
2660
|
+
const stepResults = progress?.stepResults ?? [];
|
|
2661
|
+
const completedCount = stepResults.filter(
|
|
2662
|
+
(s) => s.status === "success" || s.status === "error" || s.status === "skipped"
|
|
2663
|
+
).length;
|
|
2664
|
+
const errorCount = stepResults.filter((s) => s.status === "error").length;
|
|
2665
|
+
function getStepLabel(step) {
|
|
2666
|
+
const cmdDef = getCommandById(step.commandId);
|
|
2667
|
+
const base = cmdDef ? `${cmdDef.tool}: ${cmdDef.label}` : step.commandId;
|
|
2668
|
+
return step.args.length > 0 ? `${base} ${step.args.join(" ")}` : base;
|
|
2669
|
+
}
|
|
2670
|
+
return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
|
|
2671
|
+
/* @__PURE__ */ jsxs22(Box20, { marginBottom: 1, gap: 1, children: [
|
|
2672
|
+
/* @__PURE__ */ jsxs22(Text22, { bold: true, color: inkColors.accent, children: [
|
|
2673
|
+
"\u{1F517} ",
|
|
2674
|
+
pipeline.name
|
|
2675
|
+
] }),
|
|
2676
|
+
/* @__PURE__ */ jsx22(Text22, { dimColor: true, children: phase === "running" ? "Running..." : "Complete" })
|
|
2677
|
+
] }),
|
|
2678
|
+
/* @__PURE__ */ jsx22(
|
|
2679
|
+
PipelineProgressBar,
|
|
2680
|
+
{
|
|
2681
|
+
total: pipeline.steps.length,
|
|
2682
|
+
completed: completedCount,
|
|
2683
|
+
errors: errorCount,
|
|
2684
|
+
width
|
|
2685
|
+
}
|
|
2686
|
+
),
|
|
2687
|
+
/* @__PURE__ */ jsx22(Divider, { width }),
|
|
2688
|
+
/* @__PURE__ */ jsx22(Box20, { flexDirection: "column", marginY: 1, children: pipeline.steps.map((step, i) => /* @__PURE__ */ jsx22(
|
|
2689
|
+
StepIndicator,
|
|
2690
|
+
{
|
|
2691
|
+
label: step.label ?? getStepLabel(step),
|
|
2692
|
+
status: stepResults[i]?.status ?? "pending",
|
|
2693
|
+
index: i
|
|
2694
|
+
},
|
|
2695
|
+
step.id
|
|
2696
|
+
)) }),
|
|
2697
|
+
phase === "done" && /* @__PURE__ */ jsxs22(Fragment, { children: [
|
|
2698
|
+
/* @__PURE__ */ jsx22(Divider, { width }),
|
|
2699
|
+
/* @__PURE__ */ jsx22(Box20, { marginY: 1, children: /* @__PURE__ */ jsx22(
|
|
2700
|
+
Text22,
|
|
2701
|
+
{
|
|
2702
|
+
color: errorCount > 0 ? "red" : inkColors.accent,
|
|
2703
|
+
bold: true,
|
|
2704
|
+
children: errorCount > 0 ? `Pipeline completed with ${errorCount} error(s)` : "Pipeline completed successfully!"
|
|
2705
|
+
}
|
|
2706
|
+
) }),
|
|
2707
|
+
/* @__PURE__ */ jsx22(
|
|
2708
|
+
SelectList,
|
|
2709
|
+
{
|
|
2710
|
+
items: [
|
|
2711
|
+
{ value: "__back__", label: "\u2190 Back" },
|
|
2712
|
+
{ value: "__exit__", label: "\u{1F6AA} Exit" }
|
|
2713
|
+
],
|
|
2714
|
+
onSelect: (value) => {
|
|
2715
|
+
if (value === "__exit__") {
|
|
2716
|
+
onExit();
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
onBack();
|
|
2720
|
+
},
|
|
2721
|
+
onCancel: onBack,
|
|
2722
|
+
width,
|
|
2723
|
+
isInputActive,
|
|
2724
|
+
arrowNavigation: panelMode
|
|
2725
|
+
}
|
|
2726
|
+
)
|
|
2727
|
+
] }),
|
|
2728
|
+
!panelMode && /* @__PURE__ */ jsx22(
|
|
2729
|
+
StatusBar,
|
|
2730
|
+
{
|
|
2731
|
+
hint: phase === "running" ? "Running pipeline..." : "Enter select \xB7 Esc back",
|
|
2732
|
+
width
|
|
2733
|
+
}
|
|
2734
|
+
)
|
|
2735
|
+
] });
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
// src/app.tsx
|
|
2739
|
+
import { jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2740
|
+
function AppClassic() {
|
|
2741
|
+
const { screen, params, navigate, goBack, goHome } = useNavigation();
|
|
2742
|
+
const { exit } = useApp();
|
|
2743
|
+
const width = useTerminalWidth();
|
|
2744
|
+
const height = useTerminalHeight();
|
|
2745
|
+
const handleExit = () => {
|
|
2746
|
+
process.stdout.write(
|
|
2747
|
+
"\n" + colors.dim("Thank you for using ") + colors.primaryBold("Polter") + colors.dim("!") + "\n\n"
|
|
2748
|
+
);
|
|
2749
|
+
exit();
|
|
2750
|
+
};
|
|
2751
|
+
const renderScreen = () => {
|
|
2752
|
+
switch (screen) {
|
|
2753
|
+
case "home":
|
|
2754
|
+
return /* @__PURE__ */ jsx23(Home, { onNavigate: navigate, onExit: handleExit, width, height });
|
|
2755
|
+
case "command-args":
|
|
2756
|
+
return /* @__PURE__ */ jsx23(
|
|
2757
|
+
CommandArgs,
|
|
2758
|
+
{
|
|
2759
|
+
command: params.command ?? "",
|
|
2760
|
+
tool: params.tool,
|
|
2761
|
+
onNavigate: navigate,
|
|
2762
|
+
onBack: goBack,
|
|
2763
|
+
width
|
|
2764
|
+
}
|
|
2765
|
+
);
|
|
2766
|
+
case "custom-command":
|
|
2767
|
+
return /* @__PURE__ */ jsx23(CustomCommand, { onNavigate: navigate, onBack: goBack, width });
|
|
2768
|
+
case "flag-selection":
|
|
2769
|
+
return /* @__PURE__ */ jsx23(
|
|
2770
|
+
FlagSelection,
|
|
2771
|
+
{
|
|
2772
|
+
args: params.args ?? [],
|
|
2773
|
+
tool: params.tool,
|
|
2774
|
+
onNavigate: navigate,
|
|
2775
|
+
onBack: goBack,
|
|
2776
|
+
width
|
|
2777
|
+
}
|
|
2778
|
+
);
|
|
2779
|
+
case "confirm-execute":
|
|
2780
|
+
case "command-execution":
|
|
2781
|
+
return /* @__PURE__ */ jsx23(
|
|
2782
|
+
CommandExecution,
|
|
2783
|
+
{
|
|
2784
|
+
args: params.args ?? [],
|
|
2785
|
+
tool: params.tool,
|
|
2786
|
+
onBack: goBack,
|
|
2787
|
+
onExit: handleExit,
|
|
2788
|
+
width
|
|
2789
|
+
}
|
|
2790
|
+
);
|
|
2791
|
+
case "self-update":
|
|
2792
|
+
return /* @__PURE__ */ jsx23(SelfUpdate, { onBack: goBack, onExit: handleExit, width });
|
|
2793
|
+
case "tool-status":
|
|
2794
|
+
return /* @__PURE__ */ jsx23(ToolStatus, { onBack: goBack, width });
|
|
2795
|
+
case "project-config":
|
|
2796
|
+
return /* @__PURE__ */ jsx23(ProjectConfig, { onBack: goBack, width });
|
|
2797
|
+
case "pipeline-list":
|
|
2798
|
+
return /* @__PURE__ */ jsx23(PipelineList, { onNavigate: navigate, onBack: goBack, width });
|
|
2799
|
+
case "pipeline-builder":
|
|
2800
|
+
return /* @__PURE__ */ jsx23(PipelineBuilder, { onBack: goBack, width, height });
|
|
2801
|
+
case "pipeline-execution":
|
|
2802
|
+
return /* @__PURE__ */ jsx23(
|
|
2803
|
+
PipelineExecution,
|
|
2804
|
+
{
|
|
2805
|
+
pipelineId: params.pipelineId ?? "",
|
|
2806
|
+
onBack: goBack,
|
|
2807
|
+
onExit: handleExit,
|
|
2808
|
+
width
|
|
2809
|
+
}
|
|
2810
|
+
);
|
|
2811
|
+
default:
|
|
2812
|
+
return /* @__PURE__ */ jsx23(Box21, { children: /* @__PURE__ */ jsxs23(Text23, { color: "red", children: [
|
|
2813
|
+
"Unknown screen: ",
|
|
2814
|
+
screen
|
|
2815
|
+
] }) });
|
|
2816
|
+
}
|
|
2817
|
+
};
|
|
2818
|
+
return /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", children: [
|
|
2819
|
+
/* @__PURE__ */ jsx23(GhostBanner, { width }),
|
|
2820
|
+
renderScreen()
|
|
2821
|
+
] });
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
// src/appPanel.tsx
|
|
2825
|
+
import React18 from "react";
|
|
2826
|
+
import { Box as Box29, Text as Text30, useApp as useApp2, useInput as useInput7 } from "ink";
|
|
2827
|
+
|
|
2828
|
+
// src/hooks/useFullscreen.ts
|
|
2829
|
+
import { useEffect as useEffect8 } from "react";
|
|
2830
|
+
var ENTER_ALT_SCREEN = "\x1B[?1049h";
|
|
2831
|
+
var LEAVE_ALT_SCREEN = "\x1B[?1049l";
|
|
2832
|
+
var HIDE_CURSOR = "\x1B[?25l";
|
|
2833
|
+
var SHOW_CURSOR = "\x1B[?25h";
|
|
2834
|
+
function useFullscreen() {
|
|
2835
|
+
useEffect8(() => {
|
|
2836
|
+
process.stdout.write(ENTER_ALT_SCREEN + HIDE_CURSOR);
|
|
2837
|
+
return () => {
|
|
2838
|
+
process.stdout.write(SHOW_CURSOR + LEAVE_ALT_SCREEN);
|
|
2839
|
+
};
|
|
2840
|
+
}, []);
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
// src/hooks/usePanelNavigation.ts
|
|
2844
|
+
import { useState as useState16, useCallback as useCallback4 } from "react";
|
|
2845
|
+
function usePanelNavigation() {
|
|
2846
|
+
const [state, setState] = useState16({
|
|
2847
|
+
view: "feature",
|
|
2848
|
+
featureId: "database",
|
|
2849
|
+
innerScreen: "home",
|
|
2850
|
+
innerParams: {},
|
|
2851
|
+
innerStack: []
|
|
2852
|
+
});
|
|
2853
|
+
const selectSidebarItem = useCallback4((itemId) => {
|
|
2854
|
+
const featureIds = [
|
|
2855
|
+
"database",
|
|
2856
|
+
"functions",
|
|
2857
|
+
"deploy",
|
|
2858
|
+
"repo",
|
|
2859
|
+
"cicd",
|
|
2860
|
+
"auth-storage",
|
|
2861
|
+
"networking",
|
|
2862
|
+
"infrastructure",
|
|
2863
|
+
"setup"
|
|
2864
|
+
];
|
|
2865
|
+
if (featureIds.includes(itemId)) {
|
|
2866
|
+
setState({
|
|
2867
|
+
view: "feature",
|
|
2868
|
+
featureId: itemId,
|
|
2869
|
+
innerScreen: "home",
|
|
2870
|
+
innerParams: {},
|
|
2871
|
+
innerStack: []
|
|
2872
|
+
});
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2875
|
+
const viewMap = {
|
|
2876
|
+
pinned: "pinned",
|
|
2877
|
+
"custom-command": "custom-command",
|
|
2878
|
+
pipelines: "pipelines",
|
|
2879
|
+
"tool-status": "tool-status",
|
|
2880
|
+
config: "config",
|
|
2881
|
+
"self-update": "self-update"
|
|
2882
|
+
};
|
|
2883
|
+
const view = viewMap[itemId];
|
|
2884
|
+
if (view) {
|
|
2885
|
+
setState({
|
|
2886
|
+
view,
|
|
2887
|
+
featureId: state.featureId,
|
|
2888
|
+
innerScreen: "home",
|
|
2889
|
+
innerParams: {},
|
|
2890
|
+
innerStack: []
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
}, [state.featureId]);
|
|
2894
|
+
const navigateInner = useCallback4((screen, params) => {
|
|
2895
|
+
setState((prev) => ({
|
|
2896
|
+
...prev,
|
|
2897
|
+
innerStack: [...prev.innerStack, { screen: prev.innerScreen, params: prev.innerParams }],
|
|
2898
|
+
innerScreen: screen,
|
|
2899
|
+
innerParams: params ?? {}
|
|
2900
|
+
}));
|
|
2901
|
+
}, []);
|
|
2902
|
+
const goBackInner = useCallback4(() => {
|
|
2903
|
+
setState((prev) => {
|
|
2904
|
+
if (prev.innerStack.length === 0) {
|
|
2905
|
+
return { ...prev, innerScreen: "home", innerParams: {} };
|
|
2906
|
+
}
|
|
2907
|
+
const newStack = [...prev.innerStack];
|
|
2908
|
+
const last = newStack.pop();
|
|
2909
|
+
return {
|
|
2910
|
+
...prev,
|
|
2911
|
+
innerStack: newStack,
|
|
2912
|
+
innerScreen: last.screen,
|
|
2913
|
+
innerParams: last.params
|
|
2914
|
+
};
|
|
2915
|
+
});
|
|
2916
|
+
}, []);
|
|
2917
|
+
const goHomeInner = useCallback4(() => {
|
|
2918
|
+
setState((prev) => ({
|
|
2919
|
+
...prev,
|
|
2920
|
+
innerScreen: "home",
|
|
2921
|
+
innerParams: {},
|
|
2922
|
+
innerStack: []
|
|
2923
|
+
}));
|
|
2924
|
+
}, []);
|
|
2925
|
+
return {
|
|
2926
|
+
...state,
|
|
2927
|
+
selectSidebarItem,
|
|
2928
|
+
navigateInner,
|
|
2929
|
+
goBackInner,
|
|
2930
|
+
goHomeInner
|
|
2931
|
+
};
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
// src/hooks/usePanelFocus.ts
|
|
2935
|
+
import { useState as useState17, useCallback as useCallback5 } from "react";
|
|
2936
|
+
function usePanelFocus() {
|
|
2937
|
+
const [focused, setFocused] = useState17("sidebar");
|
|
2938
|
+
const toggleFocus = useCallback5(() => {
|
|
2939
|
+
setFocused((prev) => prev === "sidebar" ? "main" : "sidebar");
|
|
2940
|
+
}, []);
|
|
2941
|
+
const focusSidebar = useCallback5(() => setFocused("sidebar"), []);
|
|
2942
|
+
const focusMain = useCallback5(() => setFocused("main"), []);
|
|
2943
|
+
return {
|
|
2944
|
+
focused,
|
|
2945
|
+
isSidebarFocused: focused === "sidebar",
|
|
2946
|
+
isMainFocused: focused === "main",
|
|
2947
|
+
toggleFocus,
|
|
2948
|
+
focusSidebar,
|
|
2949
|
+
focusMain
|
|
2950
|
+
};
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// src/hooks/useSidebarItems.ts
|
|
2954
|
+
import { useMemo as useMemo8 } from "react";
|
|
2955
|
+
function useSidebarItems(hasPins = false) {
|
|
2956
|
+
return useMemo8(() => {
|
|
2957
|
+
const items = [];
|
|
2958
|
+
if (hasPins) {
|
|
2959
|
+
items.push({ id: "pinned", label: "Pinned", icon: "\u{1F4CC}", type: "action" });
|
|
2960
|
+
items.push({ id: "__sep_pinned__", label: "---", icon: "", type: "separator" });
|
|
2961
|
+
}
|
|
2962
|
+
for (const feature of features) {
|
|
2963
|
+
items.push({
|
|
2964
|
+
id: feature.id,
|
|
2965
|
+
label: feature.label,
|
|
2966
|
+
icon: feature.icon,
|
|
2967
|
+
type: "feature"
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
items.push({ id: "__sep__", label: "---", icon: "", type: "separator" });
|
|
2971
|
+
items.push({ id: "custom-command", label: "Custom Cmd", icon: "\u270F\uFE0F", type: "action" });
|
|
2972
|
+
items.push({ id: "pipelines", label: "Pipelines", icon: "\u{1F517}", type: "action" });
|
|
2973
|
+
items.push({ id: "tool-status", label: "Tool Status", icon: "\u{1F527}", type: "action" });
|
|
2974
|
+
items.push({ id: "config", label: "Config", icon: "\u2699\uFE0F", type: "action" });
|
|
2975
|
+
items.push({ id: "self-update", label: "Update", icon: "\u2B06\uFE0F", type: "action" });
|
|
2976
|
+
items.push({ id: "exit", label: "Exit", icon: "\u{1F6AA}", type: "action" });
|
|
2977
|
+
return items;
|
|
2978
|
+
}, [hasPins]);
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
// src/hooks/useModal.ts
|
|
2982
|
+
import { useState as useState18, useCallback as useCallback6 } from "react";
|
|
2983
|
+
function useModal() {
|
|
2984
|
+
const [state, setState] = useState18(null);
|
|
2985
|
+
const openModal = useCallback6((content, title) => {
|
|
2986
|
+
setState({ content, title });
|
|
2987
|
+
}, []);
|
|
2988
|
+
const closeModal = useCallback6(() => {
|
|
2989
|
+
setState(null);
|
|
2990
|
+
}, []);
|
|
2991
|
+
return {
|
|
2992
|
+
isOpen: state !== null,
|
|
2993
|
+
modalContent: state?.content ?? null,
|
|
2994
|
+
modalTitle: state?.title ?? "",
|
|
2995
|
+
openModal,
|
|
2996
|
+
closeModal
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
// src/components/PanelLayout.tsx
|
|
3001
|
+
import { Box as Box22 } from "ink";
|
|
3002
|
+
import { jsx as jsx24, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3003
|
+
function PanelLayout({
|
|
3004
|
+
header,
|
|
3005
|
+
footer,
|
|
3006
|
+
sidebar,
|
|
3007
|
+
main: main2,
|
|
3008
|
+
width,
|
|
3009
|
+
height,
|
|
3010
|
+
bannerHeight,
|
|
3011
|
+
singlePanel = false
|
|
3012
|
+
}) {
|
|
3013
|
+
const footerHeight = 1;
|
|
3014
|
+
const contentHeight = Math.max(5, height - bannerHeight - footerHeight);
|
|
3015
|
+
const sidebarWidth = singlePanel ? 0 : panel.sidebarWidth(width);
|
|
3016
|
+
const mainWidth = singlePanel ? width : width - sidebarWidth;
|
|
3017
|
+
return /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", width, height, children: [
|
|
3018
|
+
header,
|
|
3019
|
+
/* @__PURE__ */ jsxs24(Box22, { flexDirection: "row", height: contentHeight, children: [
|
|
3020
|
+
!singlePanel && /* @__PURE__ */ jsx24(Box22, { width: sidebarWidth, height: contentHeight, children: sidebar }),
|
|
3021
|
+
/* @__PURE__ */ jsx24(Box22, { width: mainWidth, height: contentHeight, children: main2 })
|
|
3022
|
+
] }),
|
|
3023
|
+
/* @__PURE__ */ jsx24(Box22, { height: footerHeight, children: footer })
|
|
3024
|
+
] });
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
// src/components/Panel.tsx
|
|
3028
|
+
import { Box as Box23, Text as Text24 } from "ink";
|
|
3029
|
+
import { jsx as jsx25, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3030
|
+
function Panel({
|
|
3031
|
+
id,
|
|
3032
|
+
title,
|
|
3033
|
+
width,
|
|
3034
|
+
height,
|
|
3035
|
+
focused = false,
|
|
3036
|
+
children
|
|
3037
|
+
}) {
|
|
3038
|
+
const borderColor = focused ? panel.borderFocused : panel.borderDim;
|
|
3039
|
+
return /* @__PURE__ */ jsxs25(
|
|
3040
|
+
Box23,
|
|
3041
|
+
{
|
|
3042
|
+
flexDirection: "column",
|
|
3043
|
+
width,
|
|
3044
|
+
height,
|
|
3045
|
+
borderStyle: "round",
|
|
3046
|
+
borderColor,
|
|
3047
|
+
overflow: "hidden",
|
|
3048
|
+
children: [
|
|
3049
|
+
title && /* @__PURE__ */ jsx25(Box23, { marginBottom: 0, children: /* @__PURE__ */ jsxs25(Text24, { color: focused ? inkColors.accent : void 0, bold: focused, dimColor: !focused, children: [
|
|
3050
|
+
" ",
|
|
3051
|
+
title,
|
|
3052
|
+
" "
|
|
3053
|
+
] }) }),
|
|
3054
|
+
/* @__PURE__ */ jsx25(Box23, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
|
|
3055
|
+
]
|
|
3056
|
+
}
|
|
3057
|
+
);
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
// src/components/Sidebar.tsx
|
|
3061
|
+
import { useEffect as useEffect9, useMemo as useMemo9, useState as useState19 } from "react";
|
|
3062
|
+
import { Box as Box24, Text as Text25, useInput as useInput6 } from "ink";
|
|
3063
|
+
import { jsx as jsx26, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
3064
|
+
function Sidebar({
|
|
3065
|
+
items,
|
|
3066
|
+
selectedId,
|
|
3067
|
+
isFocused,
|
|
3068
|
+
height,
|
|
3069
|
+
onSelect,
|
|
3070
|
+
onHighlight
|
|
3071
|
+
}) {
|
|
3072
|
+
const selectableItems = useMemo9(
|
|
3073
|
+
() => items.filter((item) => item.type !== "separator"),
|
|
3074
|
+
[items]
|
|
3075
|
+
);
|
|
3076
|
+
const selectedIdx = selectableItems.findIndex((item) => item.id === selectedId);
|
|
3077
|
+
const [cursorIdx, setCursorIdx] = useState19(Math.max(0, selectedIdx));
|
|
3078
|
+
useEffect9(() => {
|
|
3079
|
+
const idx = selectableItems.findIndex((item) => item.id === selectedId);
|
|
3080
|
+
if (idx >= 0) setCursorIdx(idx);
|
|
3081
|
+
}, [selectedId, selectableItems]);
|
|
3082
|
+
useInput6(
|
|
3083
|
+
(input2, key) => {
|
|
3084
|
+
if (key.upArrow || input2 === "k") {
|
|
3085
|
+
setCursorIdx((prev) => {
|
|
3086
|
+
const next = prev > 0 ? prev - 1 : selectableItems.length - 1;
|
|
3087
|
+
const item = selectableItems[next];
|
|
3088
|
+
if (item && onHighlight) onHighlight(item.id);
|
|
3089
|
+
return next;
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
3092
|
+
if (key.downArrow || input2 === "j") {
|
|
3093
|
+
setCursorIdx((prev) => {
|
|
3094
|
+
const next = prev < selectableItems.length - 1 ? prev + 1 : 0;
|
|
3095
|
+
const item = selectableItems[next];
|
|
3096
|
+
if (item && onHighlight) onHighlight(item.id);
|
|
3097
|
+
return next;
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
if (key.return || key.rightArrow) {
|
|
3101
|
+
const item = selectableItems[cursorIdx];
|
|
3102
|
+
if (item) {
|
|
3103
|
+
onSelect(item.id);
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
},
|
|
3107
|
+
{ isActive: isFocused }
|
|
3108
|
+
);
|
|
3109
|
+
let selectableIdx = 0;
|
|
3110
|
+
return /* @__PURE__ */ jsx26(Box24, { flexDirection: "column", paddingX: 1, children: items.map((item) => {
|
|
3111
|
+
if (item.type === "separator") {
|
|
3112
|
+
return /* @__PURE__ */ jsx26(Box24, { children: /* @__PURE__ */ jsx26(Text25, { dimColor: true, children: symbols.horizontal.repeat(10) }) }, item.id);
|
|
3113
|
+
}
|
|
3114
|
+
const thisIdx = selectableIdx;
|
|
3115
|
+
selectableIdx++;
|
|
3116
|
+
const isCursor = isFocused && thisIdx === cursorIdx;
|
|
3117
|
+
const isActive = item.id === selectedId;
|
|
3118
|
+
return /* @__PURE__ */ jsx26(Box24, { gap: 0, children: /* @__PURE__ */ jsxs26(
|
|
3119
|
+
Text25,
|
|
3120
|
+
{
|
|
3121
|
+
color: isCursor ? inkColors.accent : isActive ? inkColors.accent : void 0,
|
|
3122
|
+
bold: isCursor || isActive,
|
|
3123
|
+
dimColor: !isCursor && !isActive,
|
|
3124
|
+
children: [
|
|
3125
|
+
isCursor ? `${symbols.pointerActive} ` : " ",
|
|
3126
|
+
item.icon,
|
|
3127
|
+
" ",
|
|
3128
|
+
item.label
|
|
3129
|
+
]
|
|
3130
|
+
}
|
|
3131
|
+
) }, item.id);
|
|
3132
|
+
}) });
|
|
3133
|
+
}
|
|
3134
|
+
|
|
3135
|
+
// src/components/PanelFooter.tsx
|
|
3136
|
+
import { Box as Box25, Text as Text26 } from "ink";
|
|
3137
|
+
import { jsx as jsx27, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
3138
|
+
function PanelFooter({ hints, width }) {
|
|
3139
|
+
return /* @__PURE__ */ jsx27(Box25, { width, children: /* @__PURE__ */ jsx27(Box25, { gap: 1, children: hints.map((hint) => /* @__PURE__ */ jsxs27(Box25, { gap: 0, children: [
|
|
3140
|
+
/* @__PURE__ */ jsx27(Text26, { color: inkColors.accent, bold: true, children: hint.key }),
|
|
3141
|
+
/* @__PURE__ */ jsxs27(Text26, { dimColor: true, children: [
|
|
3142
|
+
":",
|
|
3143
|
+
hint.action
|
|
3144
|
+
] })
|
|
3145
|
+
] }, hint.key)) }) });
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3148
|
+
// src/components/Modal.tsx
|
|
3149
|
+
import { Box as Box26, Text as Text27 } from "ink";
|
|
3150
|
+
import { jsx as jsx28, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3151
|
+
function Modal({
|
|
3152
|
+
title,
|
|
3153
|
+
width,
|
|
3154
|
+
height,
|
|
3155
|
+
children
|
|
3156
|
+
}) {
|
|
3157
|
+
const modalWidth = Math.min(width - 4, 60);
|
|
3158
|
+
const modalHeight = Math.min(height - 4, 20);
|
|
3159
|
+
const padX = Math.max(0, Math.floor((width - modalWidth) / 2));
|
|
3160
|
+
const padY = Math.max(0, Math.floor((height - modalHeight) / 2));
|
|
3161
|
+
return /* @__PURE__ */ jsxs28(Box26, { flexDirection: "column", width, height, children: [
|
|
3162
|
+
padY > 0 && /* @__PURE__ */ jsx28(Box26, { height: padY }),
|
|
3163
|
+
/* @__PURE__ */ jsx28(Box26, { marginLeft: padX, children: /* @__PURE__ */ jsxs28(
|
|
3164
|
+
Box26,
|
|
3165
|
+
{
|
|
3166
|
+
flexDirection: "column",
|
|
3167
|
+
width: modalWidth,
|
|
3168
|
+
height: modalHeight,
|
|
3169
|
+
borderStyle: "double",
|
|
3170
|
+
borderColor: inkColors.accent,
|
|
3171
|
+
paddingX: 1,
|
|
3172
|
+
children: [
|
|
3173
|
+
/* @__PURE__ */ jsx28(Box26, { marginBottom: 1, children: /* @__PURE__ */ jsx28(Text27, { color: inkColors.accent, bold: true, children: title }) }),
|
|
3174
|
+
/* @__PURE__ */ jsx28(Box26, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
|
|
3175
|
+
]
|
|
3176
|
+
}
|
|
3177
|
+
) })
|
|
3178
|
+
] });
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
// src/components/FeatureCommands.tsx
|
|
3182
|
+
import { useEffect as useEffect10, useMemo as useMemo10, useState as useState20 } from "react";
|
|
3183
|
+
import { Box as Box27, Text as Text28 } from "ink";
|
|
3184
|
+
import { jsx as jsx29, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3185
|
+
function FeatureCommands({
|
|
3186
|
+
feature,
|
|
3187
|
+
onNavigate,
|
|
3188
|
+
onExit,
|
|
3189
|
+
onBack,
|
|
3190
|
+
onPinsChanged,
|
|
3191
|
+
width = 80,
|
|
3192
|
+
height = 24,
|
|
3193
|
+
isInputActive = true
|
|
3194
|
+
}) {
|
|
3195
|
+
const [pinnedCommands, setPinnedCommands2] = useState20(() => getPinnedCommands());
|
|
3196
|
+
const [pinnedRuns, setPinnedRuns2] = useState20(() => getPinnedRuns());
|
|
3197
|
+
const [pinFeedback, setPinFeedback] = useState20();
|
|
3198
|
+
useEffect10(() => {
|
|
3199
|
+
if (!pinFeedback) return;
|
|
3200
|
+
const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
|
|
3201
|
+
return () => clearTimeout(timeout);
|
|
3202
|
+
}, [pinFeedback]);
|
|
3203
|
+
const items = useMemo10(
|
|
3204
|
+
() => buildHomeItems({
|
|
3205
|
+
activeFeature: feature,
|
|
3206
|
+
pinnedCommands,
|
|
3207
|
+
pinnedRuns,
|
|
3208
|
+
showPinnedSection: false
|
|
3209
|
+
}),
|
|
3210
|
+
[feature, pinnedCommands, pinnedRuns]
|
|
3211
|
+
);
|
|
3212
|
+
const pinnedCommandSet = useMemo10(() => new Set(pinnedCommands), [pinnedCommands]);
|
|
3213
|
+
const pinnedRunSet = useMemo10(() => new Set(pinnedRuns), [pinnedRuns]);
|
|
3214
|
+
const refreshPins = () => {
|
|
3215
|
+
setPinnedCommands2(getPinnedCommands());
|
|
3216
|
+
setPinnedRuns2(getPinnedRuns());
|
|
3217
|
+
};
|
|
3218
|
+
const handleSelect = (value, item) => {
|
|
3219
|
+
if (!item) return;
|
|
3220
|
+
if (item.kind === "command") {
|
|
3221
|
+
const cmdDef = findCommandByValue(value);
|
|
3222
|
+
if (cmdDef) {
|
|
3223
|
+
onNavigate("command-args", {
|
|
3224
|
+
command: value,
|
|
3225
|
+
commandId: cmdDef.id,
|
|
3226
|
+
tool: cmdDef.tool
|
|
3227
|
+
});
|
|
3228
|
+
} else {
|
|
3229
|
+
onNavigate("command-args", { command: value, tool: "supabase" });
|
|
3230
|
+
}
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
if (item.kind === "run") {
|
|
3234
|
+
const args = value.split(" ").filter(Boolean);
|
|
3235
|
+
if (args.length > 0) {
|
|
3236
|
+
const basePart = args[0] ?? "";
|
|
3237
|
+
const cmdDef = findCommandByValue(basePart);
|
|
3238
|
+
const tool = cmdDef?.tool ?? "supabase";
|
|
3239
|
+
onNavigate("confirm-execute", { args, tool });
|
|
3240
|
+
}
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3243
|
+
};
|
|
3244
|
+
const handleRightAction = (item) => {
|
|
3245
|
+
if (item.kind === "command") {
|
|
3246
|
+
const wasPinned = pinnedCommandSet.has(item.value);
|
|
3247
|
+
togglePinnedCommand(item.value);
|
|
3248
|
+
refreshPins();
|
|
3249
|
+
onPinsChanged?.();
|
|
3250
|
+
setPinFeedback(
|
|
3251
|
+
wasPinned ? `Unpinned "${item.value}"` : `Pinned "${item.value}"`
|
|
3252
|
+
);
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
if (item.kind === "run") {
|
|
3256
|
+
const wasPinned = pinnedRunSet.has(item.value);
|
|
3257
|
+
togglePinnedRun(item.value);
|
|
3258
|
+
refreshPins();
|
|
3259
|
+
onPinsChanged?.();
|
|
3260
|
+
setPinFeedback(
|
|
3261
|
+
wasPinned ? `Unpinned run "${item.value}"` : `Pinned run "${item.value}"`
|
|
3262
|
+
);
|
|
3263
|
+
}
|
|
3264
|
+
};
|
|
3265
|
+
return /* @__PURE__ */ jsxs29(Box27, { flexDirection: "column", paddingX: 1, children: [
|
|
3266
|
+
/* @__PURE__ */ jsx29(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsxs29(Text28, { color: inkColors.accent, bold: true, children: [
|
|
3267
|
+
feature.icon,
|
|
3268
|
+
" ",
|
|
3269
|
+
feature.label
|
|
3270
|
+
] }) }),
|
|
3271
|
+
pinFeedback && /* @__PURE__ */ jsx29(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsxs29(Text28, { color: inkColors.accent, children: [
|
|
3272
|
+
"\u2713 ",
|
|
3273
|
+
pinFeedback
|
|
3274
|
+
] }) }),
|
|
3275
|
+
/* @__PURE__ */ jsx29(
|
|
3276
|
+
SelectList,
|
|
3277
|
+
{
|
|
3278
|
+
items,
|
|
3279
|
+
onSelect: handleSelect,
|
|
3280
|
+
onRightAction: handleRightAction,
|
|
3281
|
+
boxedSections: true,
|
|
3282
|
+
width: Math.max(20, width - 4),
|
|
3283
|
+
maxVisible: Math.max(6, height - 6),
|
|
3284
|
+
onCancel: onBack,
|
|
3285
|
+
isInputActive,
|
|
3286
|
+
arrowNavigation: true
|
|
3287
|
+
}
|
|
3288
|
+
)
|
|
3289
|
+
] });
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
// src/components/PinnedCommands.tsx
|
|
3293
|
+
import { useEffect as useEffect11, useMemo as useMemo11, useState as useState21 } from "react";
|
|
3294
|
+
import { Box as Box28, Text as Text29 } from "ink";
|
|
3295
|
+
import { jsx as jsx30, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3296
|
+
function PinnedCommands({
|
|
3297
|
+
onNavigate,
|
|
3298
|
+
onBack,
|
|
3299
|
+
onPinsChanged,
|
|
3300
|
+
width = 80,
|
|
3301
|
+
height = 24,
|
|
3302
|
+
isInputActive = true
|
|
3303
|
+
}) {
|
|
3304
|
+
const [pinnedCommands, setPinnedCommands2] = useState21(() => getPinnedCommands());
|
|
3305
|
+
const [pinnedRuns, setPinnedRuns2] = useState21(() => getPinnedRuns());
|
|
3306
|
+
const [pinFeedback, setPinFeedback] = useState21();
|
|
3307
|
+
useEffect11(() => {
|
|
3308
|
+
if (!pinFeedback) return;
|
|
3309
|
+
const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
|
|
3310
|
+
return () => clearTimeout(timeout);
|
|
3311
|
+
}, [pinFeedback]);
|
|
3312
|
+
const items = useMemo11(
|
|
3313
|
+
() => buildPinnedOnlyItems(pinnedCommands, pinnedRuns),
|
|
3314
|
+
[pinnedCommands, pinnedRuns]
|
|
3315
|
+
);
|
|
3316
|
+
const pinnedCommandSet = useMemo11(() => new Set(pinnedCommands), [pinnedCommands]);
|
|
3317
|
+
const pinnedRunSet = useMemo11(() => new Set(pinnedRuns), [pinnedRuns]);
|
|
3318
|
+
const refreshPins = () => {
|
|
3319
|
+
setPinnedCommands2(getPinnedCommands());
|
|
3320
|
+
setPinnedRuns2(getPinnedRuns());
|
|
3321
|
+
};
|
|
3322
|
+
const handleSelect = (value, item) => {
|
|
3323
|
+
if (!item) return;
|
|
3324
|
+
if (item.kind === "command") {
|
|
3325
|
+
const cmdDef = findCommandByValue(value);
|
|
3326
|
+
if (cmdDef) {
|
|
3327
|
+
onNavigate("command-args", {
|
|
3328
|
+
command: value,
|
|
3329
|
+
commandId: cmdDef.id,
|
|
3330
|
+
tool: cmdDef.tool
|
|
3331
|
+
});
|
|
3332
|
+
} else {
|
|
3333
|
+
onNavigate("command-args", { command: value, tool: "supabase" });
|
|
3334
|
+
}
|
|
3335
|
+
return;
|
|
3336
|
+
}
|
|
3337
|
+
if (item.kind === "run") {
|
|
3338
|
+
const args = value.split(" ").filter(Boolean);
|
|
3339
|
+
if (args.length > 0) {
|
|
3340
|
+
const basePart = args[0] ?? "";
|
|
3341
|
+
const cmdDef = findCommandByValue(basePart);
|
|
3342
|
+
const tool = cmdDef?.tool ?? "supabase";
|
|
3343
|
+
onNavigate("confirm-execute", { args, tool });
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
};
|
|
3347
|
+
const handleRightAction = (item) => {
|
|
3348
|
+
if (item.kind === "command") {
|
|
3349
|
+
const wasPinned = pinnedCommandSet.has(item.value);
|
|
3350
|
+
togglePinnedCommand(item.value);
|
|
3351
|
+
refreshPins();
|
|
3352
|
+
onPinsChanged?.();
|
|
3353
|
+
setPinFeedback(
|
|
3354
|
+
wasPinned ? `Unpinned "${item.value}"` : `Pinned "${item.value}"`
|
|
3355
|
+
);
|
|
3356
|
+
return;
|
|
3357
|
+
}
|
|
3358
|
+
if (item.kind === "run") {
|
|
3359
|
+
const wasPinned = pinnedRunSet.has(item.value);
|
|
3360
|
+
togglePinnedRun(item.value);
|
|
3361
|
+
refreshPins();
|
|
3362
|
+
onPinsChanged?.();
|
|
3363
|
+
setPinFeedback(
|
|
3364
|
+
wasPinned ? `Unpinned run "${item.value}"` : `Pinned run "${item.value}"`
|
|
3365
|
+
);
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
if (items.length === 0) {
|
|
3369
|
+
return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", paddingX: 1, children: [
|
|
3370
|
+
/* @__PURE__ */ jsx30(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsx30(Text29, { color: inkColors.accent, bold: true, children: "\u{1F4CC} Pinned" }) }),
|
|
3371
|
+
/* @__PURE__ */ jsx30(Text29, { color: "gray", children: "No pinned items. Press p on any command to pin it." })
|
|
3372
|
+
] });
|
|
3373
|
+
}
|
|
3374
|
+
return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", paddingX: 1, children: [
|
|
3375
|
+
/* @__PURE__ */ jsx30(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsx30(Text29, { color: inkColors.accent, bold: true, children: "\u{1F4CC} Pinned" }) }),
|
|
3376
|
+
pinFeedback && /* @__PURE__ */ jsx30(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsxs30(Text29, { color: inkColors.accent, children: [
|
|
3377
|
+
"\u2713 ",
|
|
3378
|
+
pinFeedback
|
|
3379
|
+
] }) }),
|
|
3380
|
+
/* @__PURE__ */ jsx30(
|
|
1909
3381
|
SelectList,
|
|
1910
3382
|
{
|
|
1911
|
-
items
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
reset();
|
|
1921
|
-
setPhase("running");
|
|
1922
|
-
break;
|
|
1923
|
-
case "target":
|
|
1924
|
-
reset();
|
|
1925
|
-
setPhase("target");
|
|
1926
|
-
break;
|
|
1927
|
-
case "menu":
|
|
1928
|
-
onBack();
|
|
1929
|
-
break;
|
|
1930
|
-
case "exit":
|
|
1931
|
-
onExit();
|
|
1932
|
-
break;
|
|
1933
|
-
}
|
|
1934
|
-
},
|
|
1935
|
-
onCancel: onBack
|
|
3383
|
+
items,
|
|
3384
|
+
onSelect: handleSelect,
|
|
3385
|
+
onRightAction: handleRightAction,
|
|
3386
|
+
boxedSections: true,
|
|
3387
|
+
width: Math.max(20, width - 4),
|
|
3388
|
+
maxVisible: Math.max(6, height - 6),
|
|
3389
|
+
onCancel: onBack,
|
|
3390
|
+
isInputActive,
|
|
3391
|
+
arrowNavigation: true
|
|
1936
3392
|
}
|
|
1937
|
-
)
|
|
1938
|
-
/* @__PURE__ */ jsx14(StatusBar, {})
|
|
3393
|
+
)
|
|
1939
3394
|
] });
|
|
1940
3395
|
}
|
|
1941
3396
|
|
|
1942
|
-
// src/
|
|
1943
|
-
import { jsx as
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
3397
|
+
// src/appPanel.tsx
|
|
3398
|
+
import { jsx as jsx31, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3399
|
+
var FOOTER_HINTS = [
|
|
3400
|
+
{ key: "\u2190\u2192", action: "panels" },
|
|
3401
|
+
{ key: "j/k", action: "nav" },
|
|
3402
|
+
{ key: "Enter", action: "select" },
|
|
3403
|
+
{ key: "Esc", action: "back" },
|
|
3404
|
+
{ key: "q", action: "quit" },
|
|
3405
|
+
{ key: "p", action: "pin" },
|
|
3406
|
+
{ key: "?", action: "help" }
|
|
3407
|
+
];
|
|
3408
|
+
function AppPanel() {
|
|
3409
|
+
useFullscreen();
|
|
3410
|
+
const { width, height } = useTerminalDimensions();
|
|
3411
|
+
const { exit } = useApp2();
|
|
3412
|
+
const nav = usePanelNavigation();
|
|
3413
|
+
const focus = usePanelFocus();
|
|
3414
|
+
const [hasPins, setHasPins] = React18.useState(
|
|
3415
|
+
() => getPinnedCommands().length > 0 || getPinnedRuns().length > 0
|
|
3416
|
+
);
|
|
3417
|
+
const sidebarItems = useSidebarItems(hasPins);
|
|
3418
|
+
const modal = useModal();
|
|
3419
|
+
const refreshPins = React18.useCallback(() => {
|
|
3420
|
+
const newHasPins = getPinnedCommands().length > 0 || getPinnedRuns().length > 0;
|
|
3421
|
+
setHasPins(newHasPins);
|
|
3422
|
+
if (!newHasPins && nav.view === "pinned") {
|
|
3423
|
+
nav.selectSidebarItem("database");
|
|
3424
|
+
}
|
|
3425
|
+
}, [nav.view, nav.selectSidebarItem]);
|
|
3426
|
+
const singlePanel = width < 60 || height < 15;
|
|
1947
3427
|
const handleExit = () => {
|
|
1948
3428
|
process.stdout.write(
|
|
1949
3429
|
"\n" + colors.dim("Thank you for using ") + colors.primaryBold("Polter") + colors.dim("!") + "\n\n"
|
|
1950
3430
|
);
|
|
1951
3431
|
exit();
|
|
1952
3432
|
};
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
3433
|
+
useInput7((input2, key) => {
|
|
3434
|
+
if (modal.isOpen) {
|
|
3435
|
+
if (key.escape || input2 === "q") {
|
|
3436
|
+
modal.closeModal();
|
|
3437
|
+
}
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
if (input2 === "q") {
|
|
3441
|
+
handleExit();
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
if (key.tab) {
|
|
3445
|
+
focus.toggleFocus();
|
|
3446
|
+
return;
|
|
3447
|
+
}
|
|
3448
|
+
if (key.leftArrow && focus.isMainFocused && nav.innerScreen === "home") {
|
|
3449
|
+
focus.focusSidebar();
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
if (key.escape && focus.isMainFocused && nav.innerScreen === "home") {
|
|
3453
|
+
focus.focusSidebar();
|
|
3454
|
+
return;
|
|
3455
|
+
}
|
|
3456
|
+
if (input2 === "?") {
|
|
3457
|
+
modal.openModal(
|
|
3458
|
+
/* @__PURE__ */ jsxs31(Box29, { flexDirection: "column", children: [
|
|
3459
|
+
/* @__PURE__ */ jsxs31(Text30, { children: [
|
|
3460
|
+
/* @__PURE__ */ jsx31(Text30, { bold: true, children: "\u2190/\u2192" }),
|
|
3461
|
+
" Move between sidebar and main panel"
|
|
3462
|
+
] }),
|
|
3463
|
+
/* @__PURE__ */ jsxs31(Text30, { children: [
|
|
3464
|
+
/* @__PURE__ */ jsx31(Text30, { bold: true, children: "Tab" }),
|
|
3465
|
+
" Toggle sidebar / main panel"
|
|
3466
|
+
] }),
|
|
3467
|
+
/* @__PURE__ */ jsxs31(Text30, { children: [
|
|
3468
|
+
/* @__PURE__ */ jsx31(Text30, { bold: true, children: "j/k" }),
|
|
3469
|
+
" Navigate up/down"
|
|
3470
|
+
] }),
|
|
3471
|
+
/* @__PURE__ */ jsxs31(Text30, { children: [
|
|
3472
|
+
/* @__PURE__ */ jsx31(Text30, { bold: true, children: "Enter" }),
|
|
3473
|
+
" Select item"
|
|
3474
|
+
] }),
|
|
3475
|
+
/* @__PURE__ */ jsxs31(Text30, { children: [
|
|
3476
|
+
/* @__PURE__ */ jsx31(Text30, { bold: true, children: "Esc" }),
|
|
3477
|
+
" Go back (or return to sidebar)"
|
|
3478
|
+
] }),
|
|
3479
|
+
/* @__PURE__ */ jsxs31(Text30, { children: [
|
|
3480
|
+
/* @__PURE__ */ jsx31(Text30, { bold: true, children: "q" }),
|
|
3481
|
+
" Quit Polter"
|
|
3482
|
+
] }),
|
|
3483
|
+
/* @__PURE__ */ jsxs31(Text30, { children: [
|
|
3484
|
+
/* @__PURE__ */ jsx31(Text30, { bold: true, children: "?" }),
|
|
3485
|
+
" Show this help"
|
|
3486
|
+
] }),
|
|
3487
|
+
/* @__PURE__ */ jsxs31(Text30, { children: [
|
|
3488
|
+
/* @__PURE__ */ jsx31(Text30, { bold: true, children: "p" }),
|
|
3489
|
+
" Pin/unpin command"
|
|
3490
|
+
] })
|
|
3491
|
+
] }),
|
|
3492
|
+
"Keyboard Shortcuts"
|
|
1964
3493
|
);
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
3494
|
+
}
|
|
3495
|
+
});
|
|
3496
|
+
const activeSidebarId = (() => {
|
|
3497
|
+
switch (nav.view) {
|
|
3498
|
+
case "feature":
|
|
3499
|
+
return nav.featureId;
|
|
3500
|
+
case "pinned":
|
|
3501
|
+
return "pinned";
|
|
3502
|
+
case "custom-command":
|
|
3503
|
+
return "custom-command";
|
|
3504
|
+
case "pipelines":
|
|
3505
|
+
return "pipelines";
|
|
3506
|
+
case "tool-status":
|
|
3507
|
+
return "tool-status";
|
|
3508
|
+
case "config":
|
|
3509
|
+
return "config";
|
|
3510
|
+
case "self-update":
|
|
3511
|
+
return "self-update";
|
|
3512
|
+
default:
|
|
3513
|
+
return nav.featureId;
|
|
3514
|
+
}
|
|
3515
|
+
})();
|
|
3516
|
+
const handleSidebarHighlight = (itemId) => {
|
|
3517
|
+
if (itemId === "exit") return;
|
|
3518
|
+
nav.selectSidebarItem(itemId);
|
|
3519
|
+
};
|
|
3520
|
+
const handleSidebarSelect = (itemId) => {
|
|
3521
|
+
if (itemId === "exit") {
|
|
3522
|
+
handleExit();
|
|
3523
|
+
return;
|
|
3524
|
+
}
|
|
3525
|
+
nav.selectSidebarItem(itemId);
|
|
3526
|
+
focus.focusMain();
|
|
3527
|
+
};
|
|
3528
|
+
const bannerHeight = width < 60 ? 1 : 8;
|
|
3529
|
+
const footerHints = FOOTER_HINTS;
|
|
3530
|
+
const mainContentHeight = Math.max(5, height - bannerHeight - 1 - 2);
|
|
3531
|
+
const mainContentWidth = singlePanel ? width : width - Math.max(20, Math.min(35, Math.floor(width * 0.3)));
|
|
3532
|
+
const renderMainContent = () => {
|
|
3533
|
+
if (nav.innerScreen !== "home") {
|
|
3534
|
+
return renderInnerScreen();
|
|
3535
|
+
}
|
|
3536
|
+
switch (nav.view) {
|
|
3537
|
+
case "pinned":
|
|
3538
|
+
return /* @__PURE__ */ jsx31(
|
|
3539
|
+
PinnedCommands,
|
|
3540
|
+
{
|
|
3541
|
+
onNavigate: nav.navigateInner,
|
|
3542
|
+
onBack: focus.focusSidebar,
|
|
3543
|
+
onPinsChanged: refreshPins,
|
|
3544
|
+
width: mainContentWidth - 2,
|
|
3545
|
+
height: mainContentHeight,
|
|
3546
|
+
isInputActive: focus.isMainFocused
|
|
3547
|
+
}
|
|
3548
|
+
);
|
|
3549
|
+
case "feature": {
|
|
3550
|
+
const feature = getFeatureById(nav.featureId);
|
|
3551
|
+
if (!feature) {
|
|
3552
|
+
return /* @__PURE__ */ jsxs31(Text30, { color: "red", children: [
|
|
3553
|
+
"Feature not found: ",
|
|
3554
|
+
nav.featureId
|
|
3555
|
+
] });
|
|
1974
3556
|
}
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
3557
|
+
return /* @__PURE__ */ jsx31(
|
|
3558
|
+
FeatureCommands,
|
|
3559
|
+
{
|
|
3560
|
+
feature,
|
|
3561
|
+
onNavigate: nav.navigateInner,
|
|
3562
|
+
onExit: handleExit,
|
|
3563
|
+
onBack: focus.focusSidebar,
|
|
3564
|
+
onPinsChanged: refreshPins,
|
|
3565
|
+
width: mainContentWidth - 2,
|
|
3566
|
+
height: mainContentHeight,
|
|
3567
|
+
isInputActive: focus.isMainFocused
|
|
3568
|
+
}
|
|
3569
|
+
);
|
|
3570
|
+
}
|
|
3571
|
+
case "custom-command":
|
|
3572
|
+
return /* @__PURE__ */ jsx31(
|
|
3573
|
+
CustomCommand,
|
|
3574
|
+
{
|
|
3575
|
+
onNavigate: nav.navigateInner,
|
|
3576
|
+
onBack: focus.focusSidebar,
|
|
3577
|
+
width: mainContentWidth - 2,
|
|
3578
|
+
panelMode: true,
|
|
3579
|
+
isInputActive: focus.isMainFocused
|
|
3580
|
+
}
|
|
3581
|
+
);
|
|
3582
|
+
case "pipelines":
|
|
3583
|
+
return /* @__PURE__ */ jsx31(
|
|
3584
|
+
PipelineList,
|
|
3585
|
+
{
|
|
3586
|
+
onNavigate: nav.navigateInner,
|
|
3587
|
+
onBack: focus.focusSidebar,
|
|
3588
|
+
width: mainContentWidth - 2,
|
|
3589
|
+
panelMode: true,
|
|
3590
|
+
isInputActive: focus.isMainFocused
|
|
3591
|
+
}
|
|
3592
|
+
);
|
|
3593
|
+
case "tool-status":
|
|
3594
|
+
return /* @__PURE__ */ jsx31(
|
|
3595
|
+
ToolStatus,
|
|
3596
|
+
{
|
|
3597
|
+
onBack: focus.focusSidebar,
|
|
3598
|
+
width: mainContentWidth - 2,
|
|
3599
|
+
panelMode: true,
|
|
3600
|
+
isInputActive: focus.isMainFocused
|
|
3601
|
+
}
|
|
3602
|
+
);
|
|
3603
|
+
case "config":
|
|
3604
|
+
return /* @__PURE__ */ jsx31(
|
|
3605
|
+
ProjectConfig,
|
|
3606
|
+
{
|
|
3607
|
+
onBack: focus.focusSidebar,
|
|
3608
|
+
width: mainContentWidth - 2,
|
|
3609
|
+
panelMode: true,
|
|
3610
|
+
isInputActive: focus.isMainFocused
|
|
3611
|
+
}
|
|
3612
|
+
);
|
|
3613
|
+
case "self-update":
|
|
3614
|
+
return /* @__PURE__ */ jsx31(
|
|
3615
|
+
SelfUpdate,
|
|
3616
|
+
{
|
|
3617
|
+
onBack: focus.focusSidebar,
|
|
3618
|
+
onExit: handleExit,
|
|
3619
|
+
width: mainContentWidth - 2,
|
|
3620
|
+
panelMode: true,
|
|
3621
|
+
isInputActive: focus.isMainFocused
|
|
3622
|
+
}
|
|
3623
|
+
);
|
|
3624
|
+
default:
|
|
3625
|
+
return /* @__PURE__ */ jsx31(Text30, { children: "Select an item from the sidebar" });
|
|
3626
|
+
}
|
|
3627
|
+
};
|
|
3628
|
+
const renderInnerScreen = () => {
|
|
3629
|
+
const isActive = focus.isMainFocused;
|
|
3630
|
+
const w = mainContentWidth - 2;
|
|
3631
|
+
switch (nav.innerScreen) {
|
|
3632
|
+
case "command-args":
|
|
3633
|
+
return /* @__PURE__ */ jsx31(
|
|
3634
|
+
CommandArgs,
|
|
3635
|
+
{
|
|
3636
|
+
command: nav.innerParams.command ?? "",
|
|
3637
|
+
tool: nav.innerParams.tool,
|
|
3638
|
+
onNavigate: nav.navigateInner,
|
|
3639
|
+
onBack: nav.goBackInner,
|
|
3640
|
+
width: w,
|
|
3641
|
+
panelMode: true,
|
|
3642
|
+
isInputActive: isActive
|
|
3643
|
+
}
|
|
3644
|
+
);
|
|
3645
|
+
case "custom-command":
|
|
3646
|
+
return /* @__PURE__ */ jsx31(
|
|
3647
|
+
CustomCommand,
|
|
3648
|
+
{
|
|
3649
|
+
onNavigate: nav.navigateInner,
|
|
3650
|
+
onBack: nav.goBackInner,
|
|
3651
|
+
width: w,
|
|
3652
|
+
panelMode: true,
|
|
3653
|
+
isInputActive: isActive
|
|
3654
|
+
}
|
|
3655
|
+
);
|
|
3656
|
+
case "flag-selection":
|
|
3657
|
+
return /* @__PURE__ */ jsx31(
|
|
3658
|
+
FlagSelection,
|
|
3659
|
+
{
|
|
3660
|
+
args: nav.innerParams.args ?? [],
|
|
3661
|
+
tool: nav.innerParams.tool,
|
|
3662
|
+
onNavigate: nav.navigateInner,
|
|
3663
|
+
onBack: nav.goBackInner,
|
|
3664
|
+
width: w,
|
|
3665
|
+
panelMode: true,
|
|
3666
|
+
isInputActive: isActive
|
|
3667
|
+
}
|
|
3668
|
+
);
|
|
3669
|
+
case "confirm-execute":
|
|
3670
|
+
case "command-execution":
|
|
3671
|
+
return /* @__PURE__ */ jsx31(
|
|
3672
|
+
CommandExecution,
|
|
3673
|
+
{
|
|
3674
|
+
args: nav.innerParams.args ?? [],
|
|
3675
|
+
tool: nav.innerParams.tool,
|
|
3676
|
+
onBack: nav.goBackInner,
|
|
3677
|
+
onExit: handleExit,
|
|
3678
|
+
width: w,
|
|
3679
|
+
panelMode: true,
|
|
3680
|
+
isInputActive: isActive
|
|
3681
|
+
}
|
|
3682
|
+
);
|
|
3683
|
+
case "pipeline-list":
|
|
3684
|
+
return /* @__PURE__ */ jsx31(
|
|
3685
|
+
PipelineList,
|
|
3686
|
+
{
|
|
3687
|
+
onNavigate: nav.navigateInner,
|
|
3688
|
+
onBack: nav.goBackInner,
|
|
3689
|
+
width: w,
|
|
3690
|
+
panelMode: true,
|
|
3691
|
+
isInputActive: isActive
|
|
3692
|
+
}
|
|
3693
|
+
);
|
|
3694
|
+
case "pipeline-builder":
|
|
3695
|
+
return /* @__PURE__ */ jsx31(
|
|
3696
|
+
PipelineBuilder,
|
|
3697
|
+
{
|
|
3698
|
+
onBack: nav.goBackInner,
|
|
3699
|
+
width: w,
|
|
3700
|
+
height: mainContentHeight,
|
|
3701
|
+
panelMode: true,
|
|
3702
|
+
isInputActive: isActive
|
|
3703
|
+
}
|
|
3704
|
+
);
|
|
3705
|
+
case "pipeline-execution":
|
|
3706
|
+
return /* @__PURE__ */ jsx31(
|
|
3707
|
+
PipelineExecution,
|
|
3708
|
+
{
|
|
3709
|
+
pipelineId: nav.innerParams.pipelineId ?? "",
|
|
3710
|
+
onBack: nav.goBackInner,
|
|
3711
|
+
onExit: handleExit,
|
|
3712
|
+
width: w,
|
|
3713
|
+
panelMode: true,
|
|
3714
|
+
isInputActive: isActive
|
|
3715
|
+
}
|
|
3716
|
+
);
|
|
3717
|
+
case "self-update":
|
|
3718
|
+
return /* @__PURE__ */ jsx31(
|
|
3719
|
+
SelfUpdate,
|
|
3720
|
+
{
|
|
3721
|
+
onBack: nav.goBackInner,
|
|
3722
|
+
onExit: handleExit,
|
|
3723
|
+
width: w,
|
|
3724
|
+
panelMode: true,
|
|
3725
|
+
isInputActive: isActive
|
|
3726
|
+
}
|
|
3727
|
+
);
|
|
3728
|
+
case "tool-status":
|
|
3729
|
+
return /* @__PURE__ */ jsx31(
|
|
3730
|
+
ToolStatus,
|
|
3731
|
+
{
|
|
3732
|
+
onBack: nav.goBackInner,
|
|
3733
|
+
width: w,
|
|
3734
|
+
panelMode: true,
|
|
3735
|
+
isInputActive: isActive
|
|
3736
|
+
}
|
|
3737
|
+
);
|
|
3738
|
+
case "project-config":
|
|
3739
|
+
return /* @__PURE__ */ jsx31(
|
|
3740
|
+
ProjectConfig,
|
|
3741
|
+
{
|
|
3742
|
+
onBack: nav.goBackInner,
|
|
3743
|
+
width: w,
|
|
3744
|
+
panelMode: true,
|
|
3745
|
+
isInputActive: isActive
|
|
3746
|
+
}
|
|
3747
|
+
);
|
|
3748
|
+
default:
|
|
3749
|
+
return /* @__PURE__ */ jsxs31(Text30, { color: "red", children: [
|
|
3750
|
+
"Unknown screen: ",
|
|
3751
|
+
nav.innerScreen
|
|
3752
|
+
] });
|
|
3753
|
+
}
|
|
3754
|
+
};
|
|
3755
|
+
if (modal.isOpen) {
|
|
3756
|
+
return /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", width, height, children: /* @__PURE__ */ jsx31(Modal, { title: modal.modalTitle, width, height, children: modal.modalContent }) });
|
|
3757
|
+
}
|
|
3758
|
+
const header = /* @__PURE__ */ jsx31(GhostBanner, { width, compact: true });
|
|
3759
|
+
const sidebar = /* @__PURE__ */ jsx31(
|
|
3760
|
+
Panel,
|
|
3761
|
+
{
|
|
3762
|
+
id: "sidebar",
|
|
3763
|
+
title: "Menu",
|
|
3764
|
+
width: Math.max(20, Math.min(35, Math.floor(width * 0.3))),
|
|
3765
|
+
height: Math.max(5, height - bannerHeight - 1),
|
|
3766
|
+
focused: focus.isSidebarFocused,
|
|
3767
|
+
children: /* @__PURE__ */ jsx31(
|
|
3768
|
+
Sidebar,
|
|
1980
3769
|
{
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
3770
|
+
items: sidebarItems,
|
|
3771
|
+
selectedId: activeSidebarId,
|
|
3772
|
+
isFocused: focus.isSidebarFocused,
|
|
3773
|
+
height: Math.max(5, height - bannerHeight - 4),
|
|
3774
|
+
onSelect: handleSidebarSelect,
|
|
3775
|
+
onHighlight: handleSidebarHighlight
|
|
1984
3776
|
}
|
|
1985
|
-
)
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
3777
|
+
)
|
|
3778
|
+
}
|
|
3779
|
+
);
|
|
3780
|
+
const main2 = /* @__PURE__ */ jsx31(
|
|
3781
|
+
Panel,
|
|
3782
|
+
{
|
|
3783
|
+
id: "main",
|
|
3784
|
+
title: nav.view === "feature" ? getFeatureById(nav.featureId)?.label : nav.view === "pinned" ? "Pinned" : nav.view,
|
|
3785
|
+
width: mainContentWidth,
|
|
3786
|
+
height: Math.max(5, height - bannerHeight - 1),
|
|
3787
|
+
focused: focus.isMainFocused,
|
|
3788
|
+
children: renderMainContent()
|
|
3789
|
+
}
|
|
3790
|
+
);
|
|
3791
|
+
const footer = /* @__PURE__ */ jsx31(PanelFooter, { hints: footerHints, width });
|
|
3792
|
+
return /* @__PURE__ */ jsx31(
|
|
3793
|
+
PanelLayout,
|
|
3794
|
+
{
|
|
3795
|
+
header,
|
|
3796
|
+
footer,
|
|
3797
|
+
sidebar,
|
|
3798
|
+
main: main2,
|
|
3799
|
+
width,
|
|
3800
|
+
height,
|
|
3801
|
+
bannerHeight,
|
|
3802
|
+
singlePanel
|
|
3803
|
+
}
|
|
3804
|
+
);
|
|
1994
3805
|
}
|
|
1995
3806
|
|
|
1996
3807
|
// src/lib/cliArgs.ts
|
|
@@ -2009,20 +3820,39 @@ function takeValue(args, index) {
|
|
|
2009
3820
|
return { value: args[index + 1], nextIndex: index + 1 };
|
|
2010
3821
|
}
|
|
2011
3822
|
function parseCliArgs(argv) {
|
|
2012
|
-
|
|
2013
|
-
|
|
3823
|
+
const classic = argv.includes("--classic");
|
|
3824
|
+
const filteredArgv = argv.filter((a) => a !== "--classic");
|
|
3825
|
+
if (filteredArgv.length === 0) {
|
|
3826
|
+
return { mode: "interactive", options: {}, classic };
|
|
2014
3827
|
}
|
|
2015
|
-
if (
|
|
3828
|
+
if (filteredArgv[0] === "--help" || filteredArgv[0] === "help") {
|
|
2016
3829
|
return { mode: "help", options: {} };
|
|
2017
3830
|
}
|
|
2018
|
-
|
|
2019
|
-
|
|
3831
|
+
const argv_ = filteredArgv;
|
|
3832
|
+
if (argv_[0] === "pipeline" && argv_[1] === "run" && argv_[2]) {
|
|
3833
|
+
return { mode: "pipeline-run", options: {}, pipelineName: argv_[2] };
|
|
3834
|
+
}
|
|
3835
|
+
if (argv_[0] === "config") {
|
|
3836
|
+
const edit = argv_.includes("--edit");
|
|
3837
|
+
return { mode: "config", options: {}, configEdit: edit, classic };
|
|
3838
|
+
}
|
|
3839
|
+
if (argv_[0] === "plan") {
|
|
3840
|
+
return { mode: "plan", options: {} };
|
|
3841
|
+
}
|
|
3842
|
+
if (argv_[0] === "apply") {
|
|
3843
|
+
return { mode: "apply", options: {} };
|
|
3844
|
+
}
|
|
3845
|
+
if (argv_[0] === "status") {
|
|
3846
|
+
return { mode: "status", options: {} };
|
|
3847
|
+
}
|
|
3848
|
+
if (argv_[0] !== "app") {
|
|
3849
|
+
return { mode: "interactive", options: {}, classic };
|
|
2020
3850
|
}
|
|
2021
3851
|
const options = {};
|
|
2022
|
-
options.action =
|
|
2023
|
-
options.app =
|
|
2024
|
-
for (let index = 3; index <
|
|
2025
|
-
const token =
|
|
3852
|
+
options.action = argv_[1];
|
|
3853
|
+
options.app = argv_[2];
|
|
3854
|
+
for (let index = 3; index < argv_.length; index += 1) {
|
|
3855
|
+
const token = argv_[index];
|
|
2026
3856
|
if (token === "push" || token === "lint" || token === "reset" || token === "local-reset") {
|
|
2027
3857
|
options.migrationAction = token;
|
|
2028
3858
|
continue;
|
|
@@ -2044,25 +3874,25 @@ function parseCliArgs(argv) {
|
|
|
2044
3874
|
continue;
|
|
2045
3875
|
}
|
|
2046
3876
|
if (token.startsWith("--path")) {
|
|
2047
|
-
const parsed = takeValue(
|
|
3877
|
+
const parsed = takeValue(argv_, index);
|
|
2048
3878
|
options.path = parsed.value;
|
|
2049
3879
|
index = parsed.nextIndex;
|
|
2050
3880
|
continue;
|
|
2051
3881
|
}
|
|
2052
3882
|
if (token.startsWith("--version")) {
|
|
2053
|
-
const parsed = takeValue(
|
|
3883
|
+
const parsed = takeValue(argv_, index);
|
|
2054
3884
|
options.version = parsed.value;
|
|
2055
3885
|
index = parsed.nextIndex;
|
|
2056
3886
|
continue;
|
|
2057
3887
|
}
|
|
2058
3888
|
if (token.startsWith("--artifact-url")) {
|
|
2059
|
-
const parsed = takeValue(
|
|
3889
|
+
const parsed = takeValue(argv_, index);
|
|
2060
3890
|
options.artifactUrl = parsed.value;
|
|
2061
3891
|
index = parsed.nextIndex;
|
|
2062
3892
|
continue;
|
|
2063
3893
|
}
|
|
2064
3894
|
if (token.startsWith("--install-dir")) {
|
|
2065
|
-
const parsed = takeValue(
|
|
3895
|
+
const parsed = takeValue(argv_, index);
|
|
2066
3896
|
options.installDir = parsed.value;
|
|
2067
3897
|
index = parsed.nextIndex;
|
|
2068
3898
|
continue;
|
|
@@ -2076,10 +3906,17 @@ function parseCliArgs(argv) {
|
|
|
2076
3906
|
function printCliHelp() {
|
|
2077
3907
|
process.stdout.write(
|
|
2078
3908
|
[
|
|
2079
|
-
"Polter",
|
|
3909
|
+
"Polter \u2014 Project & Infrastructure Orchestrator",
|
|
2080
3910
|
"",
|
|
2081
3911
|
"Usage:",
|
|
2082
|
-
" polter",
|
|
3912
|
+
" polter Interactive mode",
|
|
3913
|
+
" polter pipeline run <name> Run a saved pipeline",
|
|
3914
|
+
" polter config Manage per-repo config",
|
|
3915
|
+
" polter config --edit Open config in $EDITOR",
|
|
3916
|
+
" polter plan Show declarative state diff",
|
|
3917
|
+
" polter apply Apply declarative state changes",
|
|
3918
|
+
" polter status Show current tool status",
|
|
3919
|
+
"",
|
|
2083
3920
|
" polter app setup ops [--path <dir>] [--create-project|--use-existing-project] [--yes]",
|
|
2084
3921
|
" polter app link ops [--path <dir>] [--relink]",
|
|
2085
3922
|
" polter app migrate ops [push|lint|reset|local-reset] [--path <dir>] [--relink]",
|
|
@@ -2087,12 +3924,8 @@ function printCliHelp() {
|
|
|
2087
3924
|
" polter app install ops [--version <version>] [--artifact-url <url>] [--install-dir <dir>] [--yes]",
|
|
2088
3925
|
" polter app update ops [--version <version>] [--artifact-url <url>] [--install-dir <dir>] [--yes]",
|
|
2089
3926
|
"",
|
|
2090
|
-
"
|
|
2091
|
-
"
|
|
2092
|
-
" - `install ops` is macOS-only and resolves the latest GitHub release from polterware/ops by default.",
|
|
2093
|
-
" - `update ops` replaces the installed app bundle without touching the persisted runtime/app state.",
|
|
2094
|
-
" - Use --artifact-url or POLTER_OPS_MACOS_ARTIFACT_URL to override the downloaded asset.",
|
|
2095
|
-
" - Use POLTER_OPS_GITHUB_REPO=owner/repo to resolve releases from a different repository.",
|
|
3927
|
+
"Options:",
|
|
3928
|
+
" --classic Use classic single-screen UI",
|
|
2096
3929
|
""
|
|
2097
3930
|
].join("\n")
|
|
2098
3931
|
);
|
|
@@ -2102,19 +3935,19 @@ function printCliHelp() {
|
|
|
2102
3935
|
import pc3 from "picocolors";
|
|
2103
3936
|
|
|
2104
3937
|
// src/apps/ops.ts
|
|
2105
|
-
import { existsSync
|
|
3938
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2106
3939
|
import { mkdtemp, readdir, stat } from "fs/promises";
|
|
2107
|
-
import { dirname
|
|
3940
|
+
import { dirname, join as join2, resolve } from "path";
|
|
2108
3941
|
import { tmpdir } from "os";
|
|
2109
3942
|
import pc2 from "picocolors";
|
|
2110
3943
|
|
|
2111
3944
|
// src/apps/bootstrapPaths.ts
|
|
2112
3945
|
import { homedir } from "os";
|
|
2113
|
-
import { join
|
|
3946
|
+
import { join } from "path";
|
|
2114
3947
|
function getOpsBootstrapPayloadPath() {
|
|
2115
3948
|
const home = homedir();
|
|
2116
3949
|
if (process.platform === "darwin") {
|
|
2117
|
-
return
|
|
3950
|
+
return join(
|
|
2118
3951
|
home,
|
|
2119
3952
|
"Library",
|
|
2120
3953
|
"Application Support",
|
|
@@ -2124,10 +3957,10 @@ function getOpsBootstrapPayloadPath() {
|
|
|
2124
3957
|
);
|
|
2125
3958
|
}
|
|
2126
3959
|
if (process.platform === "win32") {
|
|
2127
|
-
const appData = process.env.APPDATA ??
|
|
2128
|
-
return
|
|
3960
|
+
const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
|
|
3961
|
+
return join(appData, "ops", "bootstrap", "supabase.json");
|
|
2129
3962
|
}
|
|
2130
|
-
return
|
|
3963
|
+
return join(home, ".config", "ops", "bootstrap", "supabase.json");
|
|
2131
3964
|
}
|
|
2132
3965
|
|
|
2133
3966
|
// src/apps/opsRelease.ts
|
|
@@ -2398,33 +4231,22 @@ async function promptSelect(label, options, defaultValue) {
|
|
|
2398
4231
|
}
|
|
2399
4232
|
}
|
|
2400
4233
|
|
|
2401
|
-
// src/lib/system.ts
|
|
2402
|
-
import { execSync } from "child_process";
|
|
2403
|
-
function commandExists(command) {
|
|
2404
|
-
try {
|
|
2405
|
-
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
2406
|
-
return true;
|
|
2407
|
-
} catch {
|
|
2408
|
-
return false;
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
|
|
2412
4234
|
// src/apps/ops.ts
|
|
2413
|
-
var LINK_REF_FILE =
|
|
4235
|
+
var LINK_REF_FILE = join2("supabase", ".temp", "project-ref");
|
|
2414
4236
|
function isOpsProjectRoot(candidate) {
|
|
2415
|
-
return
|
|
4237
|
+
return existsSync(join2(candidate, "src-tauri", "tauri.conf.json")) && existsSync(join2(candidate, "supabase", "migrations")) && existsSync(join2(candidate, "package.json"));
|
|
2416
4238
|
}
|
|
2417
4239
|
function findNearestOpsRoot(startDir) {
|
|
2418
|
-
let currentDir =
|
|
4240
|
+
let currentDir = resolve(startDir);
|
|
2419
4241
|
while (true) {
|
|
2420
4242
|
if (isOpsProjectRoot(currentDir)) {
|
|
2421
4243
|
return currentDir;
|
|
2422
4244
|
}
|
|
2423
|
-
const siblingCandidate =
|
|
4245
|
+
const siblingCandidate = join2(currentDir, "ops");
|
|
2424
4246
|
if (isOpsProjectRoot(siblingCandidate)) {
|
|
2425
4247
|
return siblingCandidate;
|
|
2426
4248
|
}
|
|
2427
|
-
const parentDir =
|
|
4249
|
+
const parentDir = dirname(currentDir);
|
|
2428
4250
|
if (parentDir === currentDir) {
|
|
2429
4251
|
return void 0;
|
|
2430
4252
|
}
|
|
@@ -2432,7 +4254,7 @@ function findNearestOpsRoot(startDir) {
|
|
|
2432
4254
|
}
|
|
2433
4255
|
}
|
|
2434
4256
|
function readEnvFile(envPath) {
|
|
2435
|
-
if (!
|
|
4257
|
+
if (!existsSync(envPath)) {
|
|
2436
4258
|
return {};
|
|
2437
4259
|
}
|
|
2438
4260
|
const content = readFileSync(envPath, "utf-8");
|
|
@@ -2466,8 +4288,8 @@ function assertProjectRoot(projectRoot) {
|
|
|
2466
4288
|
return projectRoot;
|
|
2467
4289
|
}
|
|
2468
4290
|
function getLinkedProjectRef(projectRoot) {
|
|
2469
|
-
const refPath =
|
|
2470
|
-
if (!
|
|
4291
|
+
const refPath = join2(projectRoot, LINK_REF_FILE);
|
|
4292
|
+
if (!existsSync(refPath)) {
|
|
2471
4293
|
return null;
|
|
2472
4294
|
}
|
|
2473
4295
|
const value = readFileSync(refPath, "utf-8").trim();
|
|
@@ -2524,7 +4346,7 @@ async function ensureSupabaseLink(projectRoot, forceRelink = false) {
|
|
|
2524
4346
|
);
|
|
2525
4347
|
}
|
|
2526
4348
|
async function collectSupabaseConfig(projectRoot) {
|
|
2527
|
-
const envPath = projectRoot ?
|
|
4349
|
+
const envPath = projectRoot ? join2(projectRoot, ".env.local") : void 0;
|
|
2528
4350
|
const currentEnv = envPath ? readEnvFile(envPath) : {};
|
|
2529
4351
|
const currentRef = projectRoot ? getLinkedProjectRef(projectRoot) : null;
|
|
2530
4352
|
const url = await promptText("Supabase URL", {
|
|
@@ -2546,7 +4368,7 @@ async function collectSupabaseConfig(projectRoot) {
|
|
|
2546
4368
|
};
|
|
2547
4369
|
}
|
|
2548
4370
|
function writeOpsEnv(projectRoot, config2) {
|
|
2549
|
-
const envPath =
|
|
4371
|
+
const envPath = join2(projectRoot, ".env.local");
|
|
2550
4372
|
const currentEnv = readEnvFile(envPath);
|
|
2551
4373
|
const nextEnv = {
|
|
2552
4374
|
...currentEnv,
|
|
@@ -2557,7 +4379,7 @@ function writeOpsEnv(projectRoot, config2) {
|
|
|
2557
4379
|
}
|
|
2558
4380
|
function writeBootstrapPayload(config2) {
|
|
2559
4381
|
const payloadPath = getOpsBootstrapPayloadPath();
|
|
2560
|
-
mkdirSync(
|
|
4382
|
+
mkdirSync(dirname(payloadPath), { recursive: true });
|
|
2561
4383
|
writeFileSync(
|
|
2562
4384
|
payloadPath,
|
|
2563
4385
|
JSON.stringify(
|
|
@@ -2746,7 +4568,7 @@ async function extractArchive(archivePath, outputDir) {
|
|
|
2746
4568
|
async function findFirstAppBundle(dir) {
|
|
2747
4569
|
const entries = await readdir(dir);
|
|
2748
4570
|
for (const entry of entries) {
|
|
2749
|
-
const fullPath =
|
|
4571
|
+
const fullPath = join2(dir, entry);
|
|
2750
4572
|
const entryStat = await stat(fullPath);
|
|
2751
4573
|
if (entryStat.isDirectory() && entry.endsWith(".app")) {
|
|
2752
4574
|
return fullPath;
|
|
@@ -2762,9 +4584,9 @@ async function findFirstAppBundle(dir) {
|
|
|
2762
4584
|
}
|
|
2763
4585
|
async function deployMacosApp(context, options) {
|
|
2764
4586
|
const artifact = await resolveOpsMacosArtifact(context.options);
|
|
2765
|
-
const tempRoot = await mkdtemp(
|
|
2766
|
-
const archivePath =
|
|
2767
|
-
const extractDir =
|
|
4587
|
+
const tempRoot = await mkdtemp(join2(tmpdir(), "polter-ops-"));
|
|
4588
|
+
const archivePath = join2(tempRoot, artifact.fileName);
|
|
4589
|
+
const extractDir = join2(tempRoot, "extract");
|
|
2768
4590
|
mkdirSync(extractDir, { recursive: true });
|
|
2769
4591
|
if (artifact.source === "github-release") {
|
|
2770
4592
|
const releaseLabel = artifact.tagName ?? "latest";
|
|
@@ -2793,13 +4615,13 @@ async function deployMacosApp(context, options) {
|
|
|
2793
4615
|
}
|
|
2794
4616
|
const installDir = context.options.installDir ?? "/Applications";
|
|
2795
4617
|
mkdirSync(installDir, { recursive: true });
|
|
2796
|
-
const destination =
|
|
2797
|
-
if (options.requireExistingInstallation && !
|
|
4618
|
+
const destination = join2(installDir, "ops.app");
|
|
4619
|
+
if (options.requireExistingInstallation && !existsSync(destination)) {
|
|
2798
4620
|
throw new Error(
|
|
2799
4621
|
`No existing Ops installation was found at ${destination}. Run \`polter app install ops\` first.`
|
|
2800
4622
|
);
|
|
2801
4623
|
}
|
|
2802
|
-
if (
|
|
4624
|
+
if (existsSync(destination)) {
|
|
2803
4625
|
const confirmed = context.options.yes || await promptConfirm(`Replace existing installation at ${destination}?`, false);
|
|
2804
4626
|
if (!confirmed) {
|
|
2805
4627
|
process.stdout.write(`${pc2.yellow("Cancelled.")}
|
|
@@ -2854,7 +4676,7 @@ var opsProfile = {
|
|
|
2854
4676
|
},
|
|
2855
4677
|
resolveProjectRoot(startDir = process.cwd(), explicitPath) {
|
|
2856
4678
|
if (explicitPath) {
|
|
2857
|
-
const resolved =
|
|
4679
|
+
const resolved = resolve(explicitPath);
|
|
2858
4680
|
if (isOpsProjectRoot(resolved)) {
|
|
2859
4681
|
return resolved;
|
|
2860
4682
|
}
|
|
@@ -2915,6 +4737,7 @@ async function runAppCli(options) {
|
|
|
2915
4737
|
}
|
|
2916
4738
|
|
|
2917
4739
|
// src/index.tsx
|
|
4740
|
+
import pc4 from "picocolors";
|
|
2918
4741
|
async function main() {
|
|
2919
4742
|
const parsed = parseCliArgs(process.argv.slice(2));
|
|
2920
4743
|
if (parsed.mode === "help") {
|
|
@@ -2925,7 +4748,132 @@ async function main() {
|
|
|
2925
4748
|
const exitCode = await runAppCli(parsed.options);
|
|
2926
4749
|
process.exit(exitCode);
|
|
2927
4750
|
}
|
|
2928
|
-
|
|
4751
|
+
if (parsed.mode === "pipeline-run") {
|
|
4752
|
+
const pipeline = findPipelineByName(parsed.pipelineName);
|
|
4753
|
+
if (!pipeline) {
|
|
4754
|
+
process.stderr.write(`Pipeline not found: ${parsed.pipelineName}
|
|
4755
|
+
`);
|
|
4756
|
+
process.exit(1);
|
|
4757
|
+
}
|
|
4758
|
+
process.stdout.write(pc4.bold(`Running pipeline: ${pipeline.name}
|
|
4759
|
+
`));
|
|
4760
|
+
const results = await executePipeline(pipeline, (progress) => {
|
|
4761
|
+
if (progress.done) return;
|
|
4762
|
+
const step = pipeline.steps[progress.currentStepIndex];
|
|
4763
|
+
if (step) {
|
|
4764
|
+
const cmdDef = getCommandById(step.commandId);
|
|
4765
|
+
const label = cmdDef ? `${cmdDef.tool}: ${cmdDef.label}` : step.commandId;
|
|
4766
|
+
process.stdout.write(` ${progress.currentStepIndex + 1}. ${label}...
|
|
4767
|
+
`);
|
|
4768
|
+
}
|
|
4769
|
+
});
|
|
4770
|
+
const errors = results.filter((r) => r.status === "error");
|
|
4771
|
+
if (errors.length > 0) {
|
|
4772
|
+
process.stderr.write(pc4.red(`
|
|
4773
|
+
Pipeline completed with ${errors.length} error(s)
|
|
4774
|
+
`));
|
|
4775
|
+
process.exit(1);
|
|
4776
|
+
}
|
|
4777
|
+
process.stdout.write(pc4.green("\nPipeline completed successfully!\n"));
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
if (parsed.mode === "status") {
|
|
4781
|
+
const tools = ["supabase", "gh", "vercel"];
|
|
4782
|
+
process.stdout.write(pc4.bold("Tool Status\n\n"));
|
|
4783
|
+
for (const toolId of tools) {
|
|
4784
|
+
const info = getToolInfo(toolId);
|
|
4785
|
+
const status = info.installed ? pc4.green(`\u2713 ${info.label} ${info.version ?? ""}`) : pc4.red(`\u2717 ${info.label} not found`);
|
|
4786
|
+
process.stdout.write(` ${status}
|
|
4787
|
+
`);
|
|
4788
|
+
}
|
|
4789
|
+
process.stdout.write(pc4.bold("\nProject Status\n\n"));
|
|
4790
|
+
const projectStatus = getCurrentStatus();
|
|
4791
|
+
if (projectStatus.supabase) {
|
|
4792
|
+
process.stdout.write(` Supabase: ${projectStatus.supabase.linked ? "linked" : "not linked"}
|
|
4793
|
+
`);
|
|
4794
|
+
}
|
|
4795
|
+
if (projectStatus.vercel) {
|
|
4796
|
+
process.stdout.write(` Vercel: ${projectStatus.vercel.linked ? "linked" : "not linked"}
|
|
4797
|
+
`);
|
|
4798
|
+
}
|
|
4799
|
+
if (projectStatus.github) {
|
|
4800
|
+
process.stdout.write(` GitHub: ${projectStatus.github.repo ?? "no repo"} ${projectStatus.github.authenticated ? "(authenticated)" : "(not authenticated)"}
|
|
4801
|
+
`);
|
|
4802
|
+
}
|
|
4803
|
+
return;
|
|
4804
|
+
}
|
|
4805
|
+
if (parsed.mode === "plan") {
|
|
4806
|
+
const yaml = parsePolterYaml();
|
|
4807
|
+
if (!yaml) {
|
|
4808
|
+
process.stderr.write("No polter.yaml found in current directory.\n");
|
|
4809
|
+
process.exit(1);
|
|
4810
|
+
}
|
|
4811
|
+
const plan = planChanges(yaml);
|
|
4812
|
+
if (plan.noChanges) {
|
|
4813
|
+
process.stdout.write(pc4.green("No changes needed. State is up to date.\n"));
|
|
4814
|
+
return;
|
|
4815
|
+
}
|
|
4816
|
+
process.stdout.write(pc4.bold(`Plan: ${plan.actions.length} action(s)
|
|
4817
|
+
|
|
4818
|
+
`));
|
|
4819
|
+
for (const action of plan.actions) {
|
|
4820
|
+
const prefix = action.action === "create" ? "+" : action.action === "delete" ? "-" : "~";
|
|
4821
|
+
const color = action.action === "create" ? pc4.green : action.action === "delete" ? pc4.red : pc4.yellow;
|
|
4822
|
+
process.stdout.write(` ${color(`${prefix} [${action.tool}] ${action.description}`)}
|
|
4823
|
+
`);
|
|
4824
|
+
}
|
|
4825
|
+
return;
|
|
4826
|
+
}
|
|
4827
|
+
if (parsed.mode === "apply") {
|
|
4828
|
+
const yaml = parsePolterYaml();
|
|
4829
|
+
if (!yaml) {
|
|
4830
|
+
process.stderr.write("No polter.yaml found in current directory.\n");
|
|
4831
|
+
process.exit(1);
|
|
4832
|
+
}
|
|
4833
|
+
const plan = planChanges(yaml);
|
|
4834
|
+
if (plan.noChanges) {
|
|
4835
|
+
process.stdout.write(pc4.green("No changes needed. State is up to date.\n"));
|
|
4836
|
+
return;
|
|
4837
|
+
}
|
|
4838
|
+
process.stdout.write(pc4.bold(`Applying ${plan.actions.length} action(s)...
|
|
4839
|
+
|
|
4840
|
+
`));
|
|
4841
|
+
const results = await applyActions(plan.actions, process.cwd(), (i, total, action) => {
|
|
4842
|
+
process.stdout.write(` [${i + 1}/${total}] ${action.description}...
|
|
4843
|
+
`);
|
|
4844
|
+
});
|
|
4845
|
+
const errors = results.filter((r) => !r.success);
|
|
4846
|
+
if (errors.length > 0) {
|
|
4847
|
+
process.stderr.write(pc4.red(`
|
|
4848
|
+
Apply completed with ${errors.length} error(s).
|
|
4849
|
+
`));
|
|
4850
|
+
for (const err of errors) {
|
|
4851
|
+
process.stderr.write(pc4.red(` \u2717 ${err.action.description}
|
|
4852
|
+
`));
|
|
4853
|
+
}
|
|
4854
|
+
process.exit(1);
|
|
4855
|
+
}
|
|
4856
|
+
process.stdout.write(pc4.green("\nAll changes applied successfully!\n"));
|
|
4857
|
+
return;
|
|
4858
|
+
}
|
|
4859
|
+
if (parsed.mode === "config") {
|
|
4860
|
+
if (parsed.configEdit) {
|
|
4861
|
+
const configPath = getProjectConfigPath();
|
|
4862
|
+
if (!configPath) {
|
|
4863
|
+
process.stderr.write("No package.json found. Run from a project directory.\n");
|
|
4864
|
+
process.exit(1);
|
|
4865
|
+
}
|
|
4866
|
+
getOrCreateProjectConfig();
|
|
4867
|
+
writeProjectConfig(getOrCreateProjectConfig());
|
|
4868
|
+
const result = openInEditor(configPath.file);
|
|
4869
|
+
process.exit(result.exitCode ?? 0);
|
|
4870
|
+
}
|
|
4871
|
+
const AppComponent2 = parsed.classic ? AppClassic : AppPanel;
|
|
4872
|
+
render(React19.createElement(AppComponent2));
|
|
4873
|
+
return;
|
|
4874
|
+
}
|
|
4875
|
+
const AppComponent = parsed.classic ? AppClassic : AppPanel;
|
|
4876
|
+
render(React19.createElement(AppComponent));
|
|
2929
4877
|
}
|
|
2930
4878
|
main().catch((error) => {
|
|
2931
4879
|
const message = error instanceof Error ? error.message : String(error);
|