@polterware/polterbase 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -11
- package/dist/index.js +1638 -196
- package/package.json +9 -5
package/dist/index.js
CHANGED
|
@@ -1,225 +1,1667 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/index.
|
|
4
|
-
import
|
|
3
|
+
// src/index.tsx
|
|
4
|
+
import React7 from "react";
|
|
5
|
+
import { render } from "ink";
|
|
6
|
+
|
|
7
|
+
// src/app.tsx
|
|
8
|
+
import { Box as Box13, Text as Text12, useApp } from "ink";
|
|
9
|
+
import pc2 from "picocolors";
|
|
10
|
+
|
|
11
|
+
// src/hooks/useNavigation.ts
|
|
12
|
+
import { useState, useCallback } from "react";
|
|
13
|
+
function useNavigation() {
|
|
14
|
+
const [state, setState] = useState({
|
|
15
|
+
screen: "main-menu",
|
|
16
|
+
params: {}
|
|
17
|
+
});
|
|
18
|
+
const navigate = useCallback((screen, params) => {
|
|
19
|
+
setState({
|
|
20
|
+
screen,
|
|
21
|
+
params: params ?? {}
|
|
22
|
+
});
|
|
23
|
+
}, []);
|
|
24
|
+
const goBack = useCallback(() => {
|
|
25
|
+
setState({ screen: "main-menu", params: {} });
|
|
26
|
+
}, []);
|
|
27
|
+
return { screen: state.screen, params: state.params, navigate, goBack };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/screens/MainMenu.tsx
|
|
31
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState3 } from "react";
|
|
32
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
33
|
+
|
|
34
|
+
// src/components/GhostBanner.tsx
|
|
35
|
+
import { Box, Text } from "ink";
|
|
36
|
+
|
|
37
|
+
// src/theme.ts
|
|
5
38
|
import pc from "picocolors";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{ value: "config", label: "config - Manage Supabase project configurations" },
|
|
34
|
-
{ value: "domains", label: "domains - Manage custom domain names for Supabase projects" },
|
|
35
|
-
{ value: "encryption", label: "encryption - Manage encryption keys of Supabase projects" },
|
|
36
|
-
{ value: "functions", label: "functions - Manage Supabase Edge functions" },
|
|
37
|
-
{ value: "network-bans", label: "network-bans - Manage network bans" },
|
|
38
|
-
{ value: "network-restrictions", label: "network-restrictions - Manage network restrictions" },
|
|
39
|
-
{ value: "orgs", label: "orgs - Manage Supabase organizations" },
|
|
40
|
-
{ value: "postgres-config", label: "postgres-config - Manage Postgres database config" },
|
|
41
|
-
{ value: "projects", label: "projects - Manage Supabase projects" },
|
|
42
|
-
{ value: "secrets", label: "secrets - Manage Supabase secrets" },
|
|
43
|
-
{ value: "snippets", label: "snippets - Manage Supabase SQL snippets" },
|
|
44
|
-
{ value: "ssl-enforcement", label: "ssl-enforcement - Manage SSL enforcement configuration" },
|
|
45
|
-
{ value: "sso", label: "sso - Manage Single Sign-On (SSO) authentication for projects" },
|
|
46
|
-
{ value: "storage", label: "storage - Manage Supabase Storage objects" },
|
|
47
|
-
{ value: "vanity-subdomains", label: "vanity-subdomains - Manage vanity subdomains for Supabase projects" }
|
|
48
|
-
],
|
|
49
|
-
"Additional Commands": [
|
|
50
|
-
{ value: "completion", label: "completion - Generate the autocompletion script" },
|
|
51
|
-
{ value: "help", label: "help - Help about any command" }
|
|
39
|
+
var VERSION = "0.1.2";
|
|
40
|
+
var colors = {
|
|
41
|
+
primary: pc.cyan,
|
|
42
|
+
primaryBold: (s) => pc.bold(pc.cyan(s)),
|
|
43
|
+
accent: pc.magenta,
|
|
44
|
+
accentBold: (s) => pc.bold(pc.magenta(s)),
|
|
45
|
+
success: pc.green,
|
|
46
|
+
successBold: (s) => pc.bold(pc.green(s)),
|
|
47
|
+
error: pc.red,
|
|
48
|
+
errorBold: (s) => pc.bold(pc.red(s)),
|
|
49
|
+
warning: pc.yellow,
|
|
50
|
+
warningBold: (s) => pc.bold(pc.yellow(s)),
|
|
51
|
+
dim: pc.dim,
|
|
52
|
+
bold: pc.bold,
|
|
53
|
+
white: pc.white,
|
|
54
|
+
highlight: (s) => pc.bgCyan(pc.black(pc.bold(s)))
|
|
55
|
+
};
|
|
56
|
+
var ghost = {
|
|
57
|
+
art: [
|
|
58
|
+
" \u2584\u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584\u2584",
|
|
59
|
+
" \u2588 \u2588",
|
|
60
|
+
" \u2588 \u2584\u2588\u2588\u2584 \u2582\u2588\u2582 \u2588",
|
|
61
|
+
" \u2588 \u2580\u2588\u2588\u2580 \u2594\u2588\u2594 \u2588",
|
|
62
|
+
" \u2588 \u2588",
|
|
63
|
+
" \u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588",
|
|
64
|
+
" \u2588 \u2588",
|
|
65
|
+
" \u2588\u2584\u2588\u2588\u2580\u2580\u2588\u2588\u2584\u2584\u2584\u2584\u2588\u2588\u2580\u2580\u2588\u2588\u2584\u2588"
|
|
52
66
|
]
|
|
53
67
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
|
|
69
|
+
// src/components/GhostBanner.tsx
|
|
70
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
71
|
+
function GhostBanner() {
|
|
72
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", alignItems: "flex-start", gap: 2, marginBottom: 1, children: [
|
|
73
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "cyan", children: line }, i)) }),
|
|
74
|
+
/* @__PURE__ */ jsxs(
|
|
75
|
+
Box,
|
|
76
|
+
{
|
|
77
|
+
flexDirection: "column",
|
|
78
|
+
borderStyle: "single",
|
|
79
|
+
borderColor: "cyan",
|
|
80
|
+
paddingX: 1,
|
|
81
|
+
children: [
|
|
82
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: "cyan", color: "black", bold: true, children: " POLTERBASE " }),
|
|
83
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
84
|
+
"Version: ",
|
|
85
|
+
VERSION
|
|
86
|
+
] }),
|
|
87
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "The modern interactive CLI for Supabase" })
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
] });
|
|
62
92
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
93
|
+
|
|
94
|
+
// src/components/SelectList.tsx
|
|
95
|
+
import { useEffect, useMemo, useState as useState2 } from "react";
|
|
96
|
+
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
97
|
+
|
|
98
|
+
// src/components/selectListSections.ts
|
|
99
|
+
function isHeader(item) {
|
|
100
|
+
return item?.kind === "header";
|
|
101
|
+
}
|
|
102
|
+
function buildEntryKey(item, globalIndex) {
|
|
103
|
+
return item.id ?? `${item.value}-${globalIndex}`;
|
|
104
|
+
}
|
|
105
|
+
function findNearestHeaderLabel(items, startIndex) {
|
|
106
|
+
for (let index = startIndex; index >= 0; index -= 1) {
|
|
107
|
+
const item = items[index];
|
|
108
|
+
if (isHeader(item)) {
|
|
109
|
+
return item?.label;
|
|
110
|
+
}
|
|
67
111
|
}
|
|
112
|
+
return void 0;
|
|
68
113
|
}
|
|
69
|
-
function
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
{ value: "Management APIs", label: "Management APIs (projects, secrets, domains, etc.)" },
|
|
83
|
-
{ value: "Additional Commands", label: "Additional Commands (help, completion)" },
|
|
84
|
-
{ value: "Custom", label: "Custom Command / Check Version" }
|
|
85
|
-
];
|
|
86
|
-
if (pinned.length > 0) {
|
|
87
|
-
mainOptions.unshift(
|
|
88
|
-
{ value: "MANAGE_PINS", label: pc.magenta("\u2699\uFE0F Manage Pinned Commands") },
|
|
89
|
-
...pinned.map((cmd) => ({ value: `PIN:${cmd}`, label: `${pc.cyan("\u{1F4CC}")} ${cmd}` })),
|
|
90
|
-
// @ts-ignore
|
|
91
|
-
{ value: "SEPARATOR", label: pc.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), disabled: true }
|
|
92
|
-
);
|
|
114
|
+
function buildBoxedSectionLayout(items, start, end) {
|
|
115
|
+
const visibleEntries = items.slice(start, end).map((item, offset) => ({
|
|
116
|
+
item,
|
|
117
|
+
globalIndex: start + offset
|
|
118
|
+
}));
|
|
119
|
+
const inheritedHeader = visibleEntries.length > 0 && !isHeader(visibleEntries[0]?.item) ? findNearestHeaderLabel(items, start - 1) : void 0;
|
|
120
|
+
const sections = [];
|
|
121
|
+
let currentTitle = inheritedHeader;
|
|
122
|
+
let currentKey = currentTitle ? `derived-${start}` : void 0;
|
|
123
|
+
let currentRows = [];
|
|
124
|
+
const flush = () => {
|
|
125
|
+
if (!currentTitle && currentRows.length === 0) {
|
|
126
|
+
return;
|
|
93
127
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
maxItems: 12
|
|
100
|
-
});
|
|
101
|
-
if (p.isCancel(category) || category === "EXIT") {
|
|
102
|
-
p.cancel("See you later!");
|
|
103
|
-
process.exit(0);
|
|
104
|
-
}
|
|
105
|
-
if (category === "MANAGE_PINS") {
|
|
106
|
-
const toRemove = await p.multiselect({
|
|
107
|
-
message: "Select commands to remove from pins (Space to select):",
|
|
108
|
-
options: pinned.map((cmd) => ({ value: cmd, label: cmd })),
|
|
109
|
-
required: false
|
|
128
|
+
if (currentRows.length === 0) {
|
|
129
|
+
sections.push({
|
|
130
|
+
type: "heading",
|
|
131
|
+
key: currentKey ?? `heading-${sections.length}`,
|
|
132
|
+
label: currentTitle ?? ""
|
|
110
133
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
134
|
+
} else {
|
|
135
|
+
sections.push({
|
|
136
|
+
type: "box",
|
|
137
|
+
key: currentKey ?? `box-${sections.length}`,
|
|
138
|
+
title: currentTitle ?? "",
|
|
139
|
+
rows: [...currentRows]
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
currentTitle = void 0;
|
|
143
|
+
currentKey = void 0;
|
|
144
|
+
currentRows = [];
|
|
145
|
+
};
|
|
146
|
+
for (const entry of visibleEntries) {
|
|
147
|
+
if (isHeader(entry.item)) {
|
|
148
|
+
flush();
|
|
149
|
+
currentTitle = entry.item.label;
|
|
150
|
+
currentKey = buildEntryKey(entry.item, entry.globalIndex);
|
|
115
151
|
continue;
|
|
116
152
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
if (!currentTitle) {
|
|
154
|
+
currentTitle = "";
|
|
155
|
+
currentKey = buildEntryKey(entry.item, entry.globalIndex);
|
|
156
|
+
}
|
|
157
|
+
currentRows.push(entry);
|
|
158
|
+
}
|
|
159
|
+
flush();
|
|
160
|
+
return sections.filter(
|
|
161
|
+
(section) => section.type === "box" ? section.rows.length > 0 : Boolean(section.label)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function countBoxedSectionLines(sections) {
|
|
165
|
+
return sections.reduce((lineCount, section) => {
|
|
166
|
+
if (section.type === "heading") {
|
|
167
|
+
return lineCount + 1;
|
|
168
|
+
}
|
|
169
|
+
return lineCount + section.rows.length + (section.title ? 3 : 2);
|
|
170
|
+
}, 0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/components/SelectList.tsx
|
|
174
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
175
|
+
function SelectList({
|
|
176
|
+
items,
|
|
177
|
+
onSelect,
|
|
178
|
+
onRightAction,
|
|
179
|
+
onCancel,
|
|
180
|
+
maxVisible = 16,
|
|
181
|
+
labelWidth = 34,
|
|
182
|
+
boxedSections = false
|
|
183
|
+
}) {
|
|
184
|
+
const isHeader2 = (item) => item.kind === "header";
|
|
185
|
+
const isSelectable = (item) => item.selectable ?? !isHeader2(item);
|
|
186
|
+
const isPinnedRow = (item) => item.section === "pinned-runs" || item.section === "pinned-commands";
|
|
187
|
+
const selectableIndexes = useMemo(
|
|
188
|
+
() => items.reduce((acc, item, index) => {
|
|
189
|
+
if (isSelectable(item)) {
|
|
190
|
+
acc.push(index);
|
|
191
|
+
}
|
|
192
|
+
return acc;
|
|
193
|
+
}, []),
|
|
194
|
+
[items]
|
|
195
|
+
);
|
|
196
|
+
const [selectedSelectableIndex, setSelectedSelectableIndex] = useState2(0);
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
if (selectableIndexes.length === 0) {
|
|
199
|
+
setSelectedSelectableIndex(0);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
setSelectedSelectableIndex((prev) => {
|
|
203
|
+
if (prev < 0) return 0;
|
|
204
|
+
if (prev >= selectableIndexes.length) return selectableIndexes.length - 1;
|
|
205
|
+
return prev;
|
|
206
|
+
});
|
|
207
|
+
}, [selectableIndexes]);
|
|
208
|
+
const selectedItemIndex = selectableIndexes[selectedSelectableIndex] ?? -1;
|
|
209
|
+
const selectedItem = selectedItemIndex >= 0 ? items[selectedItemIndex] : void 0;
|
|
210
|
+
useInput((input, key) => {
|
|
211
|
+
if (selectableIndexes.length === 0) {
|
|
212
|
+
if (key.escape && onCancel) {
|
|
213
|
+
onCancel();
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (key.upArrow || input === "k") {
|
|
218
|
+
setSelectedSelectableIndex((prev) => {
|
|
219
|
+
let next = prev - 1;
|
|
220
|
+
if (next < 0) next = selectableIndexes.length - 1;
|
|
221
|
+
return next;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (key.downArrow || input === "j") {
|
|
225
|
+
setSelectedSelectableIndex((prev) => {
|
|
226
|
+
let next = prev + 1;
|
|
227
|
+
if (next >= selectableIndexes.length) next = 0;
|
|
228
|
+
return next;
|
|
156
229
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
230
|
+
}
|
|
231
|
+
if (key.return) {
|
|
232
|
+
if (selectedItem) {
|
|
233
|
+
onSelect(selectedItem.value, selectedItem);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (key.rightArrow && onRightAction && selectedItem?.rightActionable) {
|
|
237
|
+
onRightAction(selectedItem);
|
|
238
|
+
}
|
|
239
|
+
if (key.escape && onCancel) {
|
|
240
|
+
onCancel();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
const getWindowStart = () => {
|
|
244
|
+
if (items.length <= maxVisible) {
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
const safeSelectedIndex = selectedItemIndex >= 0 ? selectedItemIndex : 0;
|
|
248
|
+
let start = Math.max(
|
|
249
|
+
0,
|
|
250
|
+
Math.min(
|
|
251
|
+
safeSelectedIndex - Math.floor(maxVisible / 2),
|
|
252
|
+
items.length - maxVisible
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
const firstVisible = items[start];
|
|
256
|
+
if (start > 0 && firstVisible && !isHeader2(firstVisible)) {
|
|
257
|
+
let previousHeaderIndex = -1;
|
|
258
|
+
for (let i = start - 1; i >= 0; i--) {
|
|
259
|
+
if (isHeader2(items[i])) {
|
|
260
|
+
previousHeaderIndex = i;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (previousHeaderIndex >= 0 && safeSelectedIndex - previousHeaderIndex < maxVisible) {
|
|
265
|
+
start = previousHeaderIndex;
|
|
161
266
|
}
|
|
162
267
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
268
|
+
return start;
|
|
269
|
+
};
|
|
270
|
+
const getWindowRange = () => {
|
|
271
|
+
const initialStart = getWindowStart();
|
|
272
|
+
const initialEnd = Math.min(initialStart + maxVisible, items.length);
|
|
273
|
+
if (!boxedSections) {
|
|
274
|
+
return [initialStart, initialEnd];
|
|
275
|
+
}
|
|
276
|
+
let start = initialStart;
|
|
277
|
+
let end = initialEnd;
|
|
278
|
+
while (start < end) {
|
|
279
|
+
const sections = buildBoxedSectionLayout(items, start, end);
|
|
280
|
+
if (countBoxedSectionLines(sections) <= maxVisible) {
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
if (selectedItemIndex >= 0 && start === selectedItemIndex && end === selectedItemIndex + 1) {
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
const canTrimStart = selectedItemIndex > start;
|
|
287
|
+
const canTrimEnd = selectedItemIndex < end - 1;
|
|
288
|
+
if (canTrimEnd && (!canTrimStart || end - selectedItemIndex >= selectedItemIndex - start)) {
|
|
289
|
+
end -= 1;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (canTrimStart) {
|
|
293
|
+
start += 1;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
end -= 1;
|
|
297
|
+
}
|
|
298
|
+
return [start, end];
|
|
299
|
+
};
|
|
300
|
+
const [windowStart, windowEnd] = getWindowRange();
|
|
301
|
+
const visibleItems = items.slice(windowStart, windowEnd);
|
|
302
|
+
const boxedLayout = boxedSections ? buildBoxedSectionLayout(items, windowStart, windowEnd) : [];
|
|
303
|
+
const showScrollUp = windowStart > 0;
|
|
304
|
+
const showScrollDown = windowEnd < items.length;
|
|
305
|
+
const renderSelectableRow = (item, globalIdx) => {
|
|
306
|
+
const isSelected = globalIdx === selectedItemIndex;
|
|
307
|
+
return /* @__PURE__ */ jsxs2(Box2, { gap: 1, children: [
|
|
308
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected ? "cyan" : void 0, children: isSelected ? "\u276F" : " " }),
|
|
309
|
+
/* @__PURE__ */ jsx2(Box2, { width: labelWidth, children: /* @__PURE__ */ jsxs2(
|
|
310
|
+
Text2,
|
|
311
|
+
{
|
|
312
|
+
color: isSelected ? "cyan" : isPinnedRow(item) ? "white" : void 0,
|
|
313
|
+
bold: isSelected || isPinnedRow(item),
|
|
314
|
+
children: [
|
|
315
|
+
item.icon ? `${item.icon} ` : "",
|
|
316
|
+
item.label
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
) }),
|
|
320
|
+
item.hint && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: item.hint }),
|
|
321
|
+
isSelected && item.rightActionable && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2192 pin/unpin" })
|
|
322
|
+
] }, item.id ?? `${item.value}-${globalIdx}`);
|
|
323
|
+
};
|
|
324
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
325
|
+
showScrollUp && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u2191 more" }),
|
|
326
|
+
boxedSections ? boxedLayout.map((section) => {
|
|
327
|
+
if (section.type === "heading") {
|
|
328
|
+
return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsx2(Text2, { color: "yellow", bold: true, children: section.label }) }, section.key);
|
|
329
|
+
}
|
|
330
|
+
const hasSelectedRow = section.rows.some(
|
|
331
|
+
(row) => row.globalIndex === selectedItemIndex
|
|
332
|
+
);
|
|
333
|
+
const isPinnedSection = section.rows.some(
|
|
334
|
+
(row) => isPinnedRow(row.item)
|
|
335
|
+
);
|
|
336
|
+
return /* @__PURE__ */ jsxs2(
|
|
337
|
+
Box2,
|
|
338
|
+
{
|
|
339
|
+
flexDirection: "column",
|
|
340
|
+
borderStyle: "round",
|
|
341
|
+
borderColor: hasSelectedRow ? "cyan" : isPinnedSection ? "green" : "yellow",
|
|
342
|
+
paddingX: 1,
|
|
343
|
+
children: [
|
|
344
|
+
section.title && /* @__PURE__ */ jsx2(Text2, { color: hasSelectedRow ? "cyan" : "yellow", bold: true, children: section.title }),
|
|
345
|
+
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: section.rows.map(
|
|
346
|
+
(row) => renderSelectableRow(row.item, row.globalIndex)
|
|
347
|
+
) })
|
|
348
|
+
]
|
|
349
|
+
},
|
|
350
|
+
section.key
|
|
351
|
+
);
|
|
352
|
+
}) : visibleItems.map((item, i) => {
|
|
353
|
+
const globalIdx = windowStart + i;
|
|
354
|
+
const selectable = isSelectable(item);
|
|
355
|
+
if (!selectable) {
|
|
356
|
+
return /* @__PURE__ */ jsx2(
|
|
357
|
+
Box2,
|
|
358
|
+
{
|
|
359
|
+
marginTop: i === 0 ? 0 : 1,
|
|
360
|
+
children: /* @__PURE__ */ jsx2(Text2, { color: "yellow", bold: true, children: item.label })
|
|
361
|
+
},
|
|
362
|
+
item.id ?? `${item.value}-${globalIdx}`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
return renderSelectableRow(item, globalIdx);
|
|
366
|
+
}),
|
|
367
|
+
showScrollDown && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u2193 more" })
|
|
368
|
+
] });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/components/StatusBar.tsx
|
|
372
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
373
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
374
|
+
function StatusBar({ hint }) {
|
|
375
|
+
return /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, justifyContent: "space-between", children: [
|
|
376
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: hint || "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back" }),
|
|
377
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
378
|
+
"polterbase v",
|
|
379
|
+
VERSION
|
|
380
|
+
] })
|
|
381
|
+
] });
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/data/pins.ts
|
|
385
|
+
import Conf from "conf";
|
|
386
|
+
var config = new Conf({
|
|
387
|
+
projectName: process.env.NODE_ENV === "test" ? "polterbase-test" : "polterbase"
|
|
388
|
+
});
|
|
389
|
+
var COMMAND_PINS_KEY = "pinnedCommandBasesV2";
|
|
390
|
+
var RUN_PINS_KEY = "pinnedCommandRunsV1";
|
|
391
|
+
var LEGACY_PINS_KEY = "pinnedCommands";
|
|
392
|
+
function ensurePinsInitialized() {
|
|
393
|
+
if (!config.has(COMMAND_PINS_KEY)) {
|
|
394
|
+
config.set(COMMAND_PINS_KEY, []);
|
|
395
|
+
}
|
|
396
|
+
if (!config.has(RUN_PINS_KEY)) {
|
|
397
|
+
config.set(RUN_PINS_KEY, []);
|
|
398
|
+
}
|
|
399
|
+
if (config.has(LEGACY_PINS_KEY)) {
|
|
400
|
+
config.delete(LEGACY_PINS_KEY);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function getPinnedCommands() {
|
|
404
|
+
ensurePinsInitialized();
|
|
405
|
+
return config.get(COMMAND_PINS_KEY) || [];
|
|
406
|
+
}
|
|
407
|
+
function setPinnedCommands(commands) {
|
|
408
|
+
config.set(COMMAND_PINS_KEY, commands);
|
|
409
|
+
}
|
|
410
|
+
function togglePinnedCommand(cmd) {
|
|
411
|
+
ensurePinsInitialized();
|
|
412
|
+
const current = getPinnedCommands();
|
|
413
|
+
if (current.includes(cmd)) {
|
|
414
|
+
setPinnedCommands(current.filter((c) => c !== cmd));
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
setPinnedCommands([cmd, ...current.filter((c) => c !== cmd)]);
|
|
418
|
+
}
|
|
419
|
+
function getPinnedRuns() {
|
|
420
|
+
ensurePinsInitialized();
|
|
421
|
+
return config.get(RUN_PINS_KEY) || [];
|
|
422
|
+
}
|
|
423
|
+
function setPinnedRuns(runs) {
|
|
424
|
+
config.set(RUN_PINS_KEY, runs);
|
|
425
|
+
}
|
|
426
|
+
function togglePinnedRun(runCommand) {
|
|
427
|
+
ensurePinsInitialized();
|
|
428
|
+
const current = getPinnedRuns();
|
|
429
|
+
if (current.includes(runCommand)) {
|
|
430
|
+
setPinnedRuns(current.filter((run) => run !== runCommand));
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
setPinnedRuns([runCommand, ...current.filter((run) => run !== runCommand)]);
|
|
434
|
+
}
|
|
435
|
+
function isPinnedRun(runCommand) {
|
|
436
|
+
return getPinnedRuns().includes(runCommand);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/data/commands.ts
|
|
440
|
+
var categories = {
|
|
441
|
+
"quick-start": {
|
|
442
|
+
icon: "\u{1F680}",
|
|
443
|
+
label: "Quick Start",
|
|
444
|
+
description: "Get started with Supabase",
|
|
445
|
+
commands: [
|
|
446
|
+
{
|
|
447
|
+
value: "bootstrap",
|
|
448
|
+
label: "bootstrap",
|
|
449
|
+
hint: "Bootstrap from a starter template"
|
|
450
|
+
},
|
|
451
|
+
{ value: "init", label: "init", hint: "Initialize a local project" },
|
|
452
|
+
{
|
|
453
|
+
value: "login",
|
|
454
|
+
label: "login",
|
|
455
|
+
hint: "Authenticate with access token"
|
|
456
|
+
},
|
|
457
|
+
{ value: "logout", label: "logout", hint: "Remove local auth token" }
|
|
458
|
+
]
|
|
459
|
+
},
|
|
460
|
+
"local-dev": {
|
|
461
|
+
icon: "\u{1F6E0}",
|
|
462
|
+
label: "Local Dev",
|
|
463
|
+
description: "Day-to-day local workflow",
|
|
464
|
+
commands: [
|
|
465
|
+
{
|
|
466
|
+
value: "start",
|
|
467
|
+
label: "start",
|
|
468
|
+
hint: "Start local Supabase containers"
|
|
469
|
+
},
|
|
470
|
+
{ value: "stop", label: "stop", hint: "Stop local Supabase containers" },
|
|
471
|
+
{ value: "status", label: "status", hint: "Show container status" },
|
|
472
|
+
{ value: "db", label: "db", hint: "Manage Postgres databases" },
|
|
473
|
+
{
|
|
474
|
+
value: "migration",
|
|
475
|
+
label: "migration",
|
|
476
|
+
hint: "Manage migration scripts"
|
|
477
|
+
},
|
|
478
|
+
{ value: "seed", label: "seed", hint: "Seed from config.toml" },
|
|
479
|
+
{ value: "test", label: "test", hint: "Run tests on local stack" }
|
|
480
|
+
]
|
|
481
|
+
},
|
|
482
|
+
"inspect-generate": {
|
|
483
|
+
icon: "\u{1F50D}",
|
|
484
|
+
label: "Inspect & Generate",
|
|
485
|
+
description: "Tooling and introspection",
|
|
486
|
+
commands: [
|
|
487
|
+
{ value: "inspect", label: "inspect", hint: "Inspect project resources" },
|
|
488
|
+
{ value: "gen", label: "gen", hint: "Run code generation tools" },
|
|
489
|
+
{ value: "services", label: "services", hint: "Show service versions" },
|
|
490
|
+
{ value: "link", label: "link", hint: "Link to remote project" },
|
|
491
|
+
{ value: "unlink", label: "unlink", hint: "Unlink remote project" }
|
|
492
|
+
]
|
|
493
|
+
},
|
|
494
|
+
cloud: {
|
|
495
|
+
icon: "\u2601\uFE0F",
|
|
496
|
+
label: "Cloud Management",
|
|
497
|
+
description: "Core cloud resources",
|
|
498
|
+
commands: [
|
|
499
|
+
{
|
|
500
|
+
value: "projects",
|
|
501
|
+
label: "projects",
|
|
502
|
+
hint: "Manage Supabase projects"
|
|
503
|
+
},
|
|
504
|
+
{ value: "functions", label: "functions", hint: "Manage Edge Functions" },
|
|
505
|
+
{ value: "secrets", label: "secrets", hint: "Manage project secrets" },
|
|
506
|
+
{
|
|
507
|
+
value: "config",
|
|
508
|
+
label: "config",
|
|
509
|
+
hint: "Manage project configuration"
|
|
510
|
+
},
|
|
511
|
+
{ value: "storage", label: "storage", hint: "Manage Storage objects" }
|
|
512
|
+
]
|
|
513
|
+
},
|
|
514
|
+
networking: {
|
|
515
|
+
icon: "\u{1F310}",
|
|
516
|
+
label: "Networking & Security",
|
|
517
|
+
description: "Network and security config",
|
|
518
|
+
commands: [
|
|
519
|
+
{ value: "domains", label: "domains", hint: "Manage custom domains" },
|
|
520
|
+
{
|
|
521
|
+
value: "ssl-enforcement",
|
|
522
|
+
label: "ssl-enforcement",
|
|
523
|
+
hint: "Manage SSL config"
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
value: "network-bans",
|
|
527
|
+
label: "network-bans",
|
|
528
|
+
hint: "Manage network bans"
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
value: "network-restrictions",
|
|
532
|
+
label: "network-restrictions",
|
|
533
|
+
hint: "Manage network restrictions"
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
value: "vanity-subdomains",
|
|
537
|
+
label: "vanity-subdomains",
|
|
538
|
+
hint: "Manage vanity subdomains"
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
value: "encryption",
|
|
542
|
+
label: "encryption",
|
|
543
|
+
hint: "Manage encryption keys"
|
|
544
|
+
}
|
|
545
|
+
]
|
|
546
|
+
},
|
|
547
|
+
"org-auth": {
|
|
548
|
+
icon: "\u{1F465}",
|
|
549
|
+
label: "Organizations & Admin",
|
|
550
|
+
description: "Team management and admin tools",
|
|
551
|
+
commands: [
|
|
552
|
+
{ value: "orgs", label: "orgs", hint: "Manage organizations" },
|
|
553
|
+
{ value: "sso", label: "sso", hint: "Manage Single Sign-On" },
|
|
554
|
+
{ value: "branches", label: "branches", hint: "Manage preview branches" },
|
|
555
|
+
{ value: "backups", label: "backups", hint: "Manage physical backups" },
|
|
556
|
+
{ value: "snippets", label: "snippets", hint: "Manage SQL snippets" },
|
|
557
|
+
{
|
|
558
|
+
value: "postgres-config",
|
|
559
|
+
label: "postgres-config",
|
|
560
|
+
hint: "Manage Postgres config"
|
|
561
|
+
}
|
|
562
|
+
]
|
|
563
|
+
},
|
|
564
|
+
utilities: {
|
|
565
|
+
icon: "\u2699\uFE0F",
|
|
566
|
+
label: "Utilities",
|
|
567
|
+
description: "Help and shell completions",
|
|
568
|
+
commands: [
|
|
569
|
+
{
|
|
570
|
+
value: "completion",
|
|
571
|
+
label: "completion",
|
|
572
|
+
hint: "Generate shell completion script"
|
|
573
|
+
},
|
|
574
|
+
{ value: "help", label: "help", hint: "Help about any command" }
|
|
575
|
+
]
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// src/screens/mainMenuModel.ts
|
|
580
|
+
var commandCatalog = Object.entries(categories).flatMap(
|
|
581
|
+
([, category]) => category.commands.map((command) => ({
|
|
582
|
+
categoryIcon: category.icon,
|
|
583
|
+
categoryLabel: category.label,
|
|
584
|
+
value: command.value,
|
|
585
|
+
hint: command.hint
|
|
586
|
+
}))
|
|
587
|
+
);
|
|
588
|
+
var commandInfoByValue = new Map(
|
|
589
|
+
commandCatalog.map((entry) => [entry.value, entry])
|
|
590
|
+
);
|
|
591
|
+
function getCommandInfo(commandValue) {
|
|
592
|
+
return commandInfoByValue.get(commandValue);
|
|
593
|
+
}
|
|
594
|
+
function buildMainMenuItems({
|
|
595
|
+
pinnedCommands,
|
|
596
|
+
pinnedRuns
|
|
597
|
+
}) {
|
|
598
|
+
const pinnedCommandSet = new Set(pinnedCommands);
|
|
599
|
+
const nextItems = [];
|
|
600
|
+
if (pinnedRuns.length > 0) {
|
|
601
|
+
nextItems.push({
|
|
602
|
+
id: "section-pinned-runs",
|
|
603
|
+
value: "__section_pinned_runs__",
|
|
604
|
+
label: "\u25B6 Pinned Runs",
|
|
605
|
+
kind: "header",
|
|
606
|
+
selectable: false
|
|
167
607
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
608
|
+
for (const runCommand of pinnedRuns) {
|
|
609
|
+
const baseCommand = runCommand.split(" ").filter(Boolean)[0] ?? "";
|
|
610
|
+
const info = getCommandInfo(baseCommand);
|
|
611
|
+
const infoHint = info ? `${info.categoryIcon} ${info.categoryLabel} \xB7 exact pinned run` : "Exact pinned run";
|
|
612
|
+
nextItems.push({
|
|
613
|
+
id: `run:${runCommand}`,
|
|
614
|
+
value: runCommand,
|
|
615
|
+
label: runCommand,
|
|
616
|
+
hint: infoHint,
|
|
617
|
+
icon: "\u25B6",
|
|
618
|
+
kind: "run",
|
|
619
|
+
rightActionable: true,
|
|
620
|
+
section: "pinned-runs"
|
|
176
621
|
});
|
|
177
|
-
if (action === "RETRY") continue;
|
|
178
|
-
break;
|
|
179
622
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
623
|
+
}
|
|
624
|
+
if (pinnedCommands.length > 0) {
|
|
625
|
+
nextItems.push({
|
|
626
|
+
id: "section-pinned-commands",
|
|
627
|
+
value: "__section_pinned_commands__",
|
|
628
|
+
label: "\u{1F4CC} Pinned Commands",
|
|
629
|
+
kind: "header",
|
|
630
|
+
selectable: false
|
|
184
631
|
});
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
632
|
+
for (const command of pinnedCommands) {
|
|
633
|
+
const info = getCommandInfo(command);
|
|
634
|
+
const infoHint = info ? `${info.categoryIcon} ${info.categoryLabel}${info.hint ? ` \xB7 ${info.hint}` : ""}` : "Pinned command";
|
|
635
|
+
nextItems.push({
|
|
636
|
+
id: `command:${command}`,
|
|
637
|
+
value: command,
|
|
638
|
+
label: command,
|
|
639
|
+
hint: infoHint,
|
|
640
|
+
icon: "\u{1F4CC}",
|
|
641
|
+
kind: "command",
|
|
642
|
+
rightActionable: true,
|
|
643
|
+
section: "pinned-commands"
|
|
189
644
|
});
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
nextItems.push({
|
|
648
|
+
id: "section-all-commands",
|
|
649
|
+
value: "__section_all_commands__",
|
|
650
|
+
label: "\u{1F4C2} All Commands",
|
|
651
|
+
kind: "header",
|
|
652
|
+
selectable: false
|
|
653
|
+
});
|
|
654
|
+
for (const [categoryKey, category] of Object.entries(categories)) {
|
|
655
|
+
nextItems.push({
|
|
656
|
+
id: `category-${categoryKey}`,
|
|
657
|
+
value: `__category_${categoryKey}__`,
|
|
658
|
+
label: `${category.icon} ${category.label}`,
|
|
659
|
+
kind: "header",
|
|
660
|
+
selectable: false
|
|
661
|
+
});
|
|
662
|
+
for (const command of category.commands) {
|
|
663
|
+
const pinHint = pinnedCommandSet.has(command.value) ? "pinned" : void 0;
|
|
664
|
+
nextItems.push({
|
|
665
|
+
id: `cmd-${categoryKey}-${command.value}`,
|
|
666
|
+
value: command.value,
|
|
667
|
+
label: command.label,
|
|
668
|
+
hint: [command.hint, pinHint].filter(Boolean).join(" \xB7 "),
|
|
669
|
+
kind: "command",
|
|
670
|
+
rightActionable: true,
|
|
671
|
+
section: categoryKey,
|
|
672
|
+
groupLabel: category.label
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
nextItems.push({
|
|
677
|
+
id: "section-actions",
|
|
678
|
+
value: "__section_actions__",
|
|
679
|
+
label: "\u26A1 Actions",
|
|
680
|
+
kind: "header",
|
|
681
|
+
selectable: false
|
|
682
|
+
});
|
|
683
|
+
nextItems.push({
|
|
684
|
+
id: "action-custom",
|
|
685
|
+
value: "__action_custom__",
|
|
686
|
+
label: "Custom Command",
|
|
687
|
+
hint: "Free-form args or check version",
|
|
688
|
+
icon: "\u270F\uFE0F",
|
|
689
|
+
kind: "action"
|
|
690
|
+
});
|
|
691
|
+
nextItems.push({
|
|
692
|
+
id: "action-exit",
|
|
693
|
+
value: "__action_exit__",
|
|
694
|
+
label: "Exit",
|
|
695
|
+
icon: "\u{1F6AA}",
|
|
696
|
+
kind: "action"
|
|
697
|
+
});
|
|
698
|
+
return nextItems;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// src/screens/MainMenu.tsx
|
|
702
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
703
|
+
function MainMenu({
|
|
704
|
+
onNavigate,
|
|
705
|
+
onExit
|
|
706
|
+
}) {
|
|
707
|
+
const [pinnedCommands, setPinnedCommands2] = useState3(
|
|
708
|
+
() => getPinnedCommands()
|
|
709
|
+
);
|
|
710
|
+
const [pinnedRuns, setPinnedRuns2] = useState3(
|
|
711
|
+
() => getPinnedRuns()
|
|
712
|
+
);
|
|
713
|
+
const [pinFeedback, setPinFeedback] = useState3();
|
|
714
|
+
useEffect2(() => {
|
|
715
|
+
if (!pinFeedback) return;
|
|
716
|
+
const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
|
|
717
|
+
return () => clearTimeout(timeout);
|
|
718
|
+
}, [pinFeedback]);
|
|
719
|
+
const items = useMemo2(
|
|
720
|
+
() => buildMainMenuItems({
|
|
721
|
+
pinnedCommands,
|
|
722
|
+
pinnedRuns
|
|
723
|
+
}),
|
|
724
|
+
[pinnedCommands, pinnedRuns]
|
|
725
|
+
);
|
|
726
|
+
const pinnedCommandSet = useMemo2(
|
|
727
|
+
() => new Set(pinnedCommands),
|
|
728
|
+
[pinnedCommands]
|
|
729
|
+
);
|
|
730
|
+
const pinnedRunSet = useMemo2(() => new Set(pinnedRuns), [pinnedRuns]);
|
|
731
|
+
const refreshPins = () => {
|
|
732
|
+
setPinnedCommands2(getPinnedCommands());
|
|
733
|
+
setPinnedRuns2(getPinnedRuns());
|
|
734
|
+
};
|
|
735
|
+
const handleSelect = (value, item) => {
|
|
736
|
+
if (!item) return;
|
|
737
|
+
if (item.kind === "command") {
|
|
738
|
+
onNavigate("command-args", { command: value });
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (item.kind === "run") {
|
|
742
|
+
const args = value.split(" ").filter(Boolean);
|
|
743
|
+
if (args.length > 0) {
|
|
744
|
+
onNavigate("confirm-execute", { args });
|
|
745
|
+
}
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
if (value === "__action_custom__") {
|
|
749
|
+
onNavigate("custom-command");
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (value === "__action_exit__") {
|
|
753
|
+
onExit();
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
const handleRightAction = (item) => {
|
|
757
|
+
if (item.kind === "command") {
|
|
758
|
+
const command = item.value;
|
|
759
|
+
const wasPinned = pinnedCommandSet.has(command);
|
|
760
|
+
togglePinnedCommand(command);
|
|
761
|
+
refreshPins();
|
|
762
|
+
setPinFeedback(
|
|
763
|
+
wasPinned ? `Unpinned "${command}"` : `Pinned "${command}"`
|
|
764
|
+
);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (item.kind === "run") {
|
|
768
|
+
const runCommand = item.value;
|
|
769
|
+
const wasPinned = pinnedRunSet.has(runCommand);
|
|
770
|
+
togglePinnedRun(runCommand);
|
|
771
|
+
refreshPins();
|
|
772
|
+
setPinFeedback(
|
|
773
|
+
wasPinned ? `Unpinned exact run "${runCommand}"` : `Pinned exact run "${runCommand}"`
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
778
|
+
/* @__PURE__ */ jsx4(GhostBanner, {}),
|
|
779
|
+
/* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Choose a command, then press Enter to continue." }) }),
|
|
780
|
+
pinFeedback && /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
|
|
781
|
+
"\u2713 ",
|
|
782
|
+
pinFeedback
|
|
783
|
+
] }) }),
|
|
784
|
+
/* @__PURE__ */ jsx4(
|
|
785
|
+
SelectList,
|
|
786
|
+
{
|
|
787
|
+
items,
|
|
788
|
+
onSelect: handleSelect,
|
|
789
|
+
onRightAction: handleRightAction,
|
|
790
|
+
boxedSections: true
|
|
791
|
+
}
|
|
792
|
+
),
|
|
793
|
+
/* @__PURE__ */ jsx4(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 \u2192 pin/unpin \xB7 Esc back" })
|
|
794
|
+
] });
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// src/screens/CommandArgs.tsx
|
|
798
|
+
import { useEffect as useEffect3, useMemo as useMemo3, useState as useState5 } from "react";
|
|
799
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
800
|
+
|
|
801
|
+
// src/components/TextPrompt.tsx
|
|
802
|
+
import { useState as useState4 } from "react";
|
|
803
|
+
import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
|
|
804
|
+
import TextInputComponent from "ink-text-input";
|
|
805
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
806
|
+
function TextPrompt({
|
|
807
|
+
label,
|
|
808
|
+
placeholder,
|
|
809
|
+
onSubmit,
|
|
810
|
+
onCancel,
|
|
811
|
+
validate
|
|
812
|
+
}) {
|
|
813
|
+
const [value, setValue] = useState4("");
|
|
814
|
+
const [error, setError] = useState4();
|
|
815
|
+
useInput2((_input, key) => {
|
|
816
|
+
if (key.escape && onCancel) {
|
|
817
|
+
onCancel();
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
const handleSubmit = (val) => {
|
|
821
|
+
if (validate) {
|
|
822
|
+
const err = validate(val);
|
|
823
|
+
if (err) {
|
|
824
|
+
setError(err);
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
setError(void 0);
|
|
829
|
+
onSubmit(val);
|
|
830
|
+
};
|
|
831
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
832
|
+
/* @__PURE__ */ jsxs5(Box5, { gap: 1, children: [
|
|
833
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "?" }),
|
|
834
|
+
/* @__PURE__ */ jsx5(Text5, { children: label })
|
|
835
|
+
] }),
|
|
836
|
+
/* @__PURE__ */ jsxs5(Box5, { gap: 1, marginLeft: 2, children: [
|
|
837
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u276F" }),
|
|
838
|
+
/* @__PURE__ */ jsx5(
|
|
839
|
+
TextInputComponent,
|
|
840
|
+
{
|
|
841
|
+
value,
|
|
842
|
+
onChange: (val) => {
|
|
843
|
+
setValue(val);
|
|
844
|
+
setError(void 0);
|
|
845
|
+
},
|
|
846
|
+
onSubmit: handleSubmit,
|
|
847
|
+
placeholder
|
|
848
|
+
}
|
|
849
|
+
)
|
|
850
|
+
] }),
|
|
851
|
+
error && /* @__PURE__ */ jsx5(Box5, { marginLeft: 2, children: /* @__PURE__ */ jsxs5(Text5, { color: "red", children: [
|
|
852
|
+
"\u2717 ",
|
|
853
|
+
error
|
|
854
|
+
] }) })
|
|
855
|
+
] });
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/data/suggestedArgs.ts
|
|
859
|
+
var suggestedArgsByCommand = {
|
|
860
|
+
db: [
|
|
861
|
+
{ value: "pull", label: "pull", hint: "Pull schema from remote", args: ["pull"] },
|
|
862
|
+
{ value: "push", label: "push", hint: "Push local migrations", args: ["push"] },
|
|
863
|
+
{ value: "reset", label: "reset", hint: "Reset local database", args: ["reset"] },
|
|
864
|
+
{ value: "dump", label: "dump", hint: "Dump schema/data", args: ["dump"] },
|
|
865
|
+
{ value: "diff", label: "diff", hint: "Show migration diff", args: ["diff"] },
|
|
866
|
+
{ value: "lint", label: "lint", hint: "Lint SQL migrations", args: ["lint"] }
|
|
867
|
+
],
|
|
868
|
+
migration: [
|
|
869
|
+
{ value: "new", label: "new <name>", hint: "Create migration file", args: ["new"] },
|
|
870
|
+
{ value: "up", label: "up", hint: "Apply pending migrations", args: ["up"] },
|
|
871
|
+
{ value: "down", label: "down", hint: "Rollback last migration", args: ["down"] },
|
|
872
|
+
{ value: "list", label: "list", hint: "List local migrations", args: ["list"] },
|
|
873
|
+
{ value: "repair", label: "repair", hint: "Repair migration history", args: ["repair"] },
|
|
874
|
+
{ value: "fetch", label: "fetch", hint: "Fetch migration history", args: ["fetch"] }
|
|
875
|
+
],
|
|
876
|
+
projects: [
|
|
877
|
+
{ value: "list", label: "list", hint: "List projects", args: ["list"] },
|
|
878
|
+
{ value: "create", label: "create", hint: "Create project", args: ["create"] },
|
|
879
|
+
{ value: "delete", label: "delete", hint: "Delete project", args: ["delete"] }
|
|
880
|
+
],
|
|
881
|
+
functions: [
|
|
882
|
+
{ value: "list", label: "list", hint: "List functions", args: ["list"] },
|
|
883
|
+
{ value: "new", label: "new <name>", hint: "Create a function", args: ["new"] },
|
|
884
|
+
{ value: "serve", label: "serve", hint: "Serve functions locally", args: ["serve"] },
|
|
885
|
+
{ value: "deploy", label: "deploy <name>", hint: "Deploy function", args: ["deploy"] },
|
|
886
|
+
{ value: "delete", label: "delete <name>", hint: "Delete function", args: ["delete"] }
|
|
887
|
+
],
|
|
888
|
+
storage: [
|
|
889
|
+
{ value: "ls", label: "ls", hint: "List storage objects", args: ["ls"] },
|
|
890
|
+
{ value: "cp", label: "cp", hint: "Copy storage object", args: ["cp"] },
|
|
891
|
+
{ value: "mv", label: "mv", hint: "Move storage object", args: ["mv"] },
|
|
892
|
+
{ value: "rm", label: "rm", hint: "Remove storage object", args: ["rm"] }
|
|
893
|
+
],
|
|
894
|
+
seed: [
|
|
895
|
+
{ value: "run", label: "run", hint: "Run configured seed file", args: ["run"] }
|
|
896
|
+
],
|
|
897
|
+
orgs: [
|
|
898
|
+
{ value: "list", label: "list", hint: "List organizations", args: ["list"] },
|
|
899
|
+
{ value: "create", label: "create", hint: "Create organization", args: ["create"] }
|
|
900
|
+
],
|
|
901
|
+
snippets: [
|
|
902
|
+
{ value: "list", label: "list", hint: "List SQL snippets", args: ["list"] },
|
|
903
|
+
{ value: "create", label: "create", hint: "Create SQL snippet", args: ["create"] },
|
|
904
|
+
{ value: "delete", label: "delete", hint: "Delete SQL snippet", args: ["delete"] }
|
|
905
|
+
],
|
|
906
|
+
backups: [
|
|
907
|
+
{ value: "list", label: "list", hint: "List backups", args: ["list"] },
|
|
908
|
+
{ value: "download", label: "download", hint: "Download backup", args: ["download"] }
|
|
909
|
+
]
|
|
910
|
+
};
|
|
911
|
+
function getSuggestedArgOptions(command) {
|
|
912
|
+
return suggestedArgsByCommand[command] ?? [];
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// src/screens/commandArgsModel.ts
|
|
916
|
+
function buildRunCommand(command, extraArgs) {
|
|
917
|
+
return [command, ...extraArgs].join(" ");
|
|
918
|
+
}
|
|
919
|
+
function getRunCommandFromArgsSelection(command, value, suggestions) {
|
|
920
|
+
if (value === "__run_base__") {
|
|
921
|
+
return command;
|
|
922
|
+
}
|
|
923
|
+
if (!value.startsWith("suggest:")) {
|
|
924
|
+
return void 0;
|
|
925
|
+
}
|
|
926
|
+
const optionKey = value.replace("suggest:", "");
|
|
927
|
+
const option = suggestions.find((entry) => entry.value === optionKey);
|
|
928
|
+
if (!option) {
|
|
929
|
+
return void 0;
|
|
930
|
+
}
|
|
931
|
+
return buildRunCommand(command, option.args);
|
|
932
|
+
}
|
|
933
|
+
function buildCommandArgItems({
|
|
934
|
+
command,
|
|
935
|
+
suggestions,
|
|
936
|
+
pinnedRuns
|
|
937
|
+
}) {
|
|
938
|
+
const pinnedRunSet = new Set(pinnedRuns);
|
|
939
|
+
const baseRunCommand = buildRunCommand(command, []);
|
|
940
|
+
return [
|
|
941
|
+
{
|
|
942
|
+
value: "__suggested_header__",
|
|
943
|
+
label: `Suggested args for ${command}`,
|
|
944
|
+
kind: "header",
|
|
945
|
+
selectable: false
|
|
946
|
+
},
|
|
947
|
+
...suggestions.map((option) => {
|
|
948
|
+
const runCommand = buildRunCommand(command, option.args);
|
|
949
|
+
const pinHint = pinnedRunSet.has(runCommand) ? "pinned run" : void 0;
|
|
950
|
+
return {
|
|
951
|
+
value: `suggest:${option.value}`,
|
|
952
|
+
label: option.label,
|
|
953
|
+
hint: [option.hint, pinHint].filter(Boolean).join(" \xB7 "),
|
|
954
|
+
kind: "command",
|
|
955
|
+
rightActionable: true
|
|
956
|
+
};
|
|
957
|
+
}),
|
|
958
|
+
{
|
|
959
|
+
value: "__actions_header__",
|
|
960
|
+
label: "\u26A1 Actions",
|
|
961
|
+
kind: "header",
|
|
962
|
+
selectable: false
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
value: "__run_base__",
|
|
966
|
+
label: "Run without additional args",
|
|
967
|
+
hint: [
|
|
968
|
+
`supabase ${command}`,
|
|
969
|
+
pinnedRunSet.has(baseRunCommand) ? "pinned run" : void 0
|
|
970
|
+
].filter(Boolean).join(" \xB7 "),
|
|
971
|
+
kind: "action",
|
|
972
|
+
rightActionable: true
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
value: "__custom__",
|
|
976
|
+
label: "Custom args...",
|
|
977
|
+
hint: "Type any arguments manually",
|
|
978
|
+
kind: "action"
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
value: "__back__",
|
|
982
|
+
label: "\u2190 Back to menu",
|
|
983
|
+
kind: "action"
|
|
984
|
+
}
|
|
985
|
+
];
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// src/screens/CommandArgs.tsx
|
|
989
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
990
|
+
function CommandArgs({
|
|
991
|
+
command,
|
|
992
|
+
onNavigate,
|
|
993
|
+
onBack
|
|
994
|
+
}) {
|
|
995
|
+
const suggestions = useMemo3(() => getSuggestedArgOptions(command), [command]);
|
|
996
|
+
const [pinnedRuns, setPinnedRuns2] = useState5(() => getPinnedRuns());
|
|
997
|
+
const [pinFeedback, setPinFeedback] = useState5();
|
|
998
|
+
const [phase, setPhase] = useState5(
|
|
999
|
+
suggestions.length > 0 ? "select" : "custom"
|
|
1000
|
+
);
|
|
1001
|
+
useEffect3(() => {
|
|
1002
|
+
setPhase(suggestions.length > 0 ? "select" : "custom");
|
|
1003
|
+
}, [command, suggestions.length]);
|
|
1004
|
+
useEffect3(() => {
|
|
1005
|
+
if (!pinFeedback) return;
|
|
1006
|
+
const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
|
|
1007
|
+
return () => clearTimeout(timeout);
|
|
1008
|
+
}, [pinFeedback]);
|
|
1009
|
+
const navigateWithExtraArgs = (extraArgs) => {
|
|
1010
|
+
onNavigate("flag-selection", {
|
|
1011
|
+
args: [command, ...extraArgs],
|
|
1012
|
+
command
|
|
1013
|
+
});
|
|
1014
|
+
};
|
|
1015
|
+
if (!command) {
|
|
1016
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1017
|
+
/* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "red", children: "Command not provided." }) }),
|
|
1018
|
+
/* @__PURE__ */ jsx6(
|
|
1019
|
+
SelectList,
|
|
1020
|
+
{
|
|
1021
|
+
items: [{ value: "__back__", label: "\u2190 Back to menu" }],
|
|
1022
|
+
onSelect: onBack,
|
|
1023
|
+
onCancel: onBack
|
|
1024
|
+
}
|
|
1025
|
+
)
|
|
1026
|
+
] });
|
|
1027
|
+
}
|
|
1028
|
+
if (phase === "custom") {
|
|
1029
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1030
|
+
/* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, gap: 1, children: [
|
|
1031
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", bold: true, children: "Command" }),
|
|
1032
|
+
/* @__PURE__ */ jsx6(Text6, { children: command })
|
|
1033
|
+
] }),
|
|
1034
|
+
/* @__PURE__ */ jsx6(
|
|
1035
|
+
TextPrompt,
|
|
1036
|
+
{
|
|
1037
|
+
label: `Additional args for supabase ${command}?`,
|
|
1038
|
+
placeholder: "e.g. list, pull, push (Enter to skip)",
|
|
1039
|
+
onSubmit: (extra) => {
|
|
1040
|
+
const extraArgs = extra.trim().split(" ").filter(Boolean);
|
|
1041
|
+
navigateWithExtraArgs(extraArgs);
|
|
1042
|
+
},
|
|
1043
|
+
onCancel: () => {
|
|
1044
|
+
if (suggestions.length > 0) {
|
|
1045
|
+
setPhase("select");
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
onBack();
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
),
|
|
1052
|
+
/* @__PURE__ */ jsx6(StatusBar, { hint: "Type args \xB7 Enter to continue \xB7 Esc to go back" })
|
|
1053
|
+
] });
|
|
1054
|
+
}
|
|
1055
|
+
const items = useMemo3(
|
|
1056
|
+
() => buildCommandArgItems({
|
|
1057
|
+
command,
|
|
1058
|
+
suggestions,
|
|
1059
|
+
pinnedRuns
|
|
1060
|
+
}),
|
|
1061
|
+
[command, pinnedRuns, suggestions]
|
|
1062
|
+
);
|
|
1063
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1064
|
+
/* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, gap: 1, children: [
|
|
1065
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", bold: true, children: "Command" }),
|
|
1066
|
+
/* @__PURE__ */ jsx6(Text6, { children: command })
|
|
1067
|
+
] }),
|
|
1068
|
+
pinFeedback && /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: "green", children: [
|
|
1069
|
+
"\u2713 ",
|
|
1070
|
+
pinFeedback
|
|
1071
|
+
] }) }),
|
|
1072
|
+
/* @__PURE__ */ jsx6(
|
|
1073
|
+
SelectList,
|
|
1074
|
+
{
|
|
1075
|
+
items,
|
|
1076
|
+
onSelect: (value) => {
|
|
1077
|
+
if (value === "__back__") {
|
|
1078
|
+
onBack();
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
if (value === "__custom__") {
|
|
1082
|
+
setPhase("custom");
|
|
1083
|
+
return;
|
|
207
1084
|
}
|
|
1085
|
+
if (value === "__run_base__") {
|
|
1086
|
+
navigateWithExtraArgs([]);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
if (value.startsWith("suggest:")) {
|
|
1090
|
+
const runCommand = getRunCommandFromArgsSelection(
|
|
1091
|
+
command,
|
|
1092
|
+
value,
|
|
1093
|
+
suggestions
|
|
1094
|
+
);
|
|
1095
|
+
if (runCommand) {
|
|
1096
|
+
navigateWithExtraArgs(
|
|
1097
|
+
runCommand.split(" ").slice(1).filter(Boolean)
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
},
|
|
1103
|
+
onRightAction: (item) => {
|
|
1104
|
+
const runCommand = getRunCommandFromArgsSelection(
|
|
1105
|
+
command,
|
|
1106
|
+
item.value,
|
|
1107
|
+
suggestions
|
|
1108
|
+
);
|
|
1109
|
+
if (!runCommand) {
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
const wasPinned = pinnedRuns.includes(runCommand);
|
|
1113
|
+
togglePinnedRun(runCommand);
|
|
1114
|
+
setPinnedRuns2(getPinnedRuns());
|
|
1115
|
+
setPinFeedback(
|
|
1116
|
+
wasPinned ? `Unpinned exact run "${runCommand}"` : `Pinned exact run "${runCommand}"`
|
|
1117
|
+
);
|
|
1118
|
+
},
|
|
1119
|
+
onCancel: onBack,
|
|
1120
|
+
boxedSections: true
|
|
1121
|
+
}
|
|
1122
|
+
),
|
|
1123
|
+
/* @__PURE__ */ jsx6(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 \u2192 pin run \xB7 Esc back" })
|
|
1124
|
+
] });
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// src/screens/CustomCommand.tsx
|
|
1128
|
+
import { Box as Box7 } from "ink";
|
|
1129
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1130
|
+
function CustomCommand({
|
|
1131
|
+
onNavigate,
|
|
1132
|
+
onBack
|
|
1133
|
+
}) {
|
|
1134
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1135
|
+
/* @__PURE__ */ jsx7(
|
|
1136
|
+
TextPrompt,
|
|
1137
|
+
{
|
|
1138
|
+
label: "Enter your Supabase command/flags:",
|
|
1139
|
+
placeholder: "e.g. -v, status -o json, db pull",
|
|
1140
|
+
validate: (val) => {
|
|
1141
|
+
if (!val || !val.trim()) return "Please enter a command";
|
|
1142
|
+
return void 0;
|
|
1143
|
+
},
|
|
1144
|
+
onSubmit: (value) => {
|
|
1145
|
+
const args = value.split(" ").filter(Boolean);
|
|
1146
|
+
onNavigate("flag-selection", { args });
|
|
1147
|
+
},
|
|
1148
|
+
onCancel: onBack
|
|
1149
|
+
}
|
|
1150
|
+
),
|
|
1151
|
+
/* @__PURE__ */ jsx7(StatusBar, { hint: "Type a command \xB7 Enter to continue \xB7 Esc to go back" })
|
|
1152
|
+
] });
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// src/screens/FlagSelection.tsx
|
|
1156
|
+
import { Box as Box9 } from "ink";
|
|
1157
|
+
|
|
1158
|
+
// src/components/FlagToggle.tsx
|
|
1159
|
+
import { useState as useState6 } from "react";
|
|
1160
|
+
import { Box as Box8, Text as Text7, useInput as useInput3 } from "ink";
|
|
1161
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1162
|
+
function FlagToggle({
|
|
1163
|
+
flags,
|
|
1164
|
+
onSubmit,
|
|
1165
|
+
onCancel
|
|
1166
|
+
}) {
|
|
1167
|
+
const [cursor, setCursor] = useState6(0);
|
|
1168
|
+
const [selected, setSelected] = useState6(/* @__PURE__ */ new Set());
|
|
1169
|
+
useInput3((input, key) => {
|
|
1170
|
+
if (key.upArrow || input === "k") {
|
|
1171
|
+
setCursor((prev) => prev > 0 ? prev - 1 : flags.length - 1);
|
|
1172
|
+
}
|
|
1173
|
+
if (key.downArrow || input === "j") {
|
|
1174
|
+
setCursor((prev) => prev < flags.length - 1 ? prev + 1 : 0);
|
|
1175
|
+
}
|
|
1176
|
+
if (input === " ") {
|
|
1177
|
+
setSelected((prev) => {
|
|
1178
|
+
const next = new Set(prev);
|
|
1179
|
+
const flag = flags[cursor];
|
|
1180
|
+
if (next.has(flag.value)) {
|
|
1181
|
+
next.delete(flag.value);
|
|
1182
|
+
} else {
|
|
1183
|
+
next.add(flag.value);
|
|
208
1184
|
}
|
|
209
|
-
|
|
1185
|
+
return next;
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
if (key.return) {
|
|
1189
|
+
onSubmit(Array.from(selected));
|
|
1190
|
+
}
|
|
1191
|
+
if (key.escape && onCancel) {
|
|
1192
|
+
onCancel();
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
1196
|
+
/* @__PURE__ */ jsxs8(Box8, { marginBottom: 1, children: [
|
|
1197
|
+
/* @__PURE__ */ jsx8(Text7, { bold: true, color: "cyan", children: "\u2691 Global Flags" }),
|
|
1198
|
+
/* @__PURE__ */ jsx8(Text7, { dimColor: true, children: " (Space to toggle, Enter to confirm)" })
|
|
1199
|
+
] }),
|
|
1200
|
+
flags.map((flag, i) => {
|
|
1201
|
+
const isActive = cursor === i;
|
|
1202
|
+
const isChecked = selected.has(flag.value);
|
|
1203
|
+
return /* @__PURE__ */ jsxs8(Box8, { gap: 1, children: [
|
|
1204
|
+
/* @__PURE__ */ jsx8(Text7, { color: isActive ? "cyan" : void 0, children: isActive ? "\u276F" : " " }),
|
|
1205
|
+
/* @__PURE__ */ jsx8(
|
|
1206
|
+
Text7,
|
|
1207
|
+
{
|
|
1208
|
+
color: isChecked ? "green" : isActive ? "cyan" : void 0,
|
|
1209
|
+
bold: isChecked,
|
|
1210
|
+
children: isChecked ? "\u25C9" : "\u25CB"
|
|
1211
|
+
}
|
|
1212
|
+
),
|
|
1213
|
+
/* @__PURE__ */ jsx8(
|
|
1214
|
+
Text7,
|
|
1215
|
+
{
|
|
1216
|
+
color: isActive ? "cyan" : void 0,
|
|
1217
|
+
bold: isActive,
|
|
1218
|
+
children: flag.label
|
|
1219
|
+
}
|
|
1220
|
+
),
|
|
1221
|
+
flag.hint && /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: flag.hint })
|
|
1222
|
+
] }, flag.value);
|
|
1223
|
+
}),
|
|
1224
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: selected.size > 0 ? `${selected.size} flag${selected.size > 1 ? "s" : ""} selected` : "No flags selected (Enter to skip)" }) })
|
|
1225
|
+
] });
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// src/data/flags.ts
|
|
1229
|
+
var globalFlags = [
|
|
1230
|
+
{ value: "--debug", label: "--debug", hint: "Output debug logs to stderr" },
|
|
1231
|
+
{ value: "--yes", label: "--yes", hint: "Answer yes to all prompts" },
|
|
1232
|
+
{
|
|
1233
|
+
value: "--create-ticket",
|
|
1234
|
+
label: "--create-ticket",
|
|
1235
|
+
hint: "Create support ticket on error"
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
value: "--experimental",
|
|
1239
|
+
label: "--experimental",
|
|
1240
|
+
hint: "Enable experimental features"
|
|
1241
|
+
}
|
|
1242
|
+
];
|
|
1243
|
+
|
|
1244
|
+
// src/screens/FlagSelection.tsx
|
|
1245
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1246
|
+
function FlagSelection({
|
|
1247
|
+
args,
|
|
1248
|
+
onNavigate,
|
|
1249
|
+
onBack
|
|
1250
|
+
}) {
|
|
1251
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
1252
|
+
/* @__PURE__ */ jsx9(
|
|
1253
|
+
FlagToggle,
|
|
1254
|
+
{
|
|
1255
|
+
flags: globalFlags,
|
|
1256
|
+
onSubmit: (selectedFlags) => {
|
|
1257
|
+
const finalArgs = selectedFlags.length > 0 ? [...args, ...selectedFlags] : args;
|
|
1258
|
+
onNavigate("confirm-execute", { args: finalArgs });
|
|
1259
|
+
},
|
|
1260
|
+
onCancel: onBack
|
|
1261
|
+
}
|
|
1262
|
+
),
|
|
1263
|
+
/* @__PURE__ */ jsx9(StatusBar, { hint: "Space toggle \xB7 Enter confirm \xB7 Esc back" })
|
|
1264
|
+
] });
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// src/screens/CommandExecution.tsx
|
|
1268
|
+
import { useState as useState8, useEffect as useEffect4 } from "react";
|
|
1269
|
+
import { Box as Box12, Text as Text11 } from "ink";
|
|
1270
|
+
|
|
1271
|
+
// src/components/Spinner.tsx
|
|
1272
|
+
import { Text as Text8 } from "ink";
|
|
1273
|
+
import InkSpinner from "ink-spinner";
|
|
1274
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1275
|
+
function Spinner({
|
|
1276
|
+
label = "Running...",
|
|
1277
|
+
color = "cyan"
|
|
1278
|
+
}) {
|
|
1279
|
+
return /* @__PURE__ */ jsxs10(Text8, { children: [
|
|
1280
|
+
/* @__PURE__ */ jsx10(Text8, { color, children: /* @__PURE__ */ jsx10(InkSpinner, { type: "dots" }) }),
|
|
1281
|
+
" ",
|
|
1282
|
+
/* @__PURE__ */ jsx10(Text8, { children: label })
|
|
1283
|
+
] });
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
// src/components/ConfirmPrompt.tsx
|
|
1287
|
+
import { Box as Box10, Text as Text9, useInput as useInput4 } from "ink";
|
|
1288
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1289
|
+
function ConfirmPrompt({
|
|
1290
|
+
message,
|
|
1291
|
+
defaultValue = true,
|
|
1292
|
+
onConfirm
|
|
1293
|
+
}) {
|
|
1294
|
+
useInput4((input) => {
|
|
1295
|
+
if (input === "y" || input === "Y") {
|
|
1296
|
+
onConfirm(true);
|
|
1297
|
+
} else if (input === "n" || input === "N") {
|
|
1298
|
+
onConfirm(false);
|
|
1299
|
+
} else if (input === "\r") {
|
|
1300
|
+
onConfirm(defaultValue);
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
return /* @__PURE__ */ jsxs11(Box10, { gap: 1, children: [
|
|
1304
|
+
/* @__PURE__ */ jsx11(Text9, { color: "cyan", bold: true, children: "?" }),
|
|
1305
|
+
/* @__PURE__ */ jsx11(Text9, { children: message }),
|
|
1306
|
+
/* @__PURE__ */ jsx11(Text9, { dimColor: true, children: defaultValue ? "(Y/n)" : "(y/N)" })
|
|
1307
|
+
] });
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// src/components/Divider.tsx
|
|
1311
|
+
import { Text as Text10 } from "ink";
|
|
1312
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1313
|
+
function Divider({
|
|
1314
|
+
label,
|
|
1315
|
+
width = 50
|
|
1316
|
+
}) {
|
|
1317
|
+
if (label) {
|
|
1318
|
+
const labelLen = label.length + 2;
|
|
1319
|
+
const sideLen = Math.max(2, Math.floor((width - labelLen) / 2));
|
|
1320
|
+
const left = "\u2500".repeat(sideLen);
|
|
1321
|
+
const right = "\u2500".repeat(sideLen);
|
|
1322
|
+
return /* @__PURE__ */ jsxs12(Text10, { dimColor: true, children: [
|
|
1323
|
+
left,
|
|
1324
|
+
" ",
|
|
1325
|
+
label,
|
|
1326
|
+
" ",
|
|
1327
|
+
right
|
|
1328
|
+
] });
|
|
1329
|
+
}
|
|
1330
|
+
return /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "\u2500".repeat(width) });
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// src/hooks/useCommand.ts
|
|
1334
|
+
import { useState as useState7, useCallback as useCallback2 } from "react";
|
|
1335
|
+
|
|
1336
|
+
// src/lib/runner.ts
|
|
1337
|
+
import { spawn } from "child_process";
|
|
1338
|
+
async function runSupabaseCommand(args) {
|
|
1339
|
+
return new Promise((resolve) => {
|
|
1340
|
+
let stdout = "";
|
|
1341
|
+
let stderr = "";
|
|
1342
|
+
const child = spawn("supabase", args, {
|
|
1343
|
+
shell: true,
|
|
1344
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
1345
|
+
});
|
|
1346
|
+
child.stdout?.on("data", (data) => {
|
|
1347
|
+
const text = data.toString();
|
|
1348
|
+
stdout += text;
|
|
1349
|
+
process.stdout.write(text);
|
|
1350
|
+
});
|
|
1351
|
+
child.stderr?.on("data", (data) => {
|
|
1352
|
+
const text = data.toString();
|
|
1353
|
+
stderr += text;
|
|
1354
|
+
process.stderr.write(text);
|
|
1355
|
+
});
|
|
1356
|
+
child.on("error", (err) => {
|
|
1357
|
+
resolve({
|
|
1358
|
+
exitCode: null,
|
|
1359
|
+
signal: null,
|
|
1360
|
+
stdout,
|
|
1361
|
+
stderr,
|
|
1362
|
+
spawnError: err.message
|
|
210
1363
|
});
|
|
211
1364
|
});
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
initialValue: true
|
|
1365
|
+
child.on("exit", (code, signal) => {
|
|
1366
|
+
resolve({ exitCode: code, signal, stdout, stderr });
|
|
215
1367
|
});
|
|
216
|
-
|
|
217
|
-
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// src/hooks/useCommand.ts
|
|
1372
|
+
function useCommand() {
|
|
1373
|
+
const [status, setStatus] = useState7("idle");
|
|
1374
|
+
const [result, setResult] = useState7(null);
|
|
1375
|
+
const run = useCallback2(async (args) => {
|
|
1376
|
+
setStatus("running");
|
|
1377
|
+
setResult(null);
|
|
1378
|
+
const res = await runSupabaseCommand(args);
|
|
1379
|
+
setResult(res);
|
|
1380
|
+
if (res.spawnError || res.exitCode !== null && res.exitCode !== 0) {
|
|
1381
|
+
setStatus("error");
|
|
218
1382
|
} else {
|
|
219
|
-
|
|
220
|
-
|
|
1383
|
+
setStatus("success");
|
|
1384
|
+
}
|
|
1385
|
+
return res;
|
|
1386
|
+
}, []);
|
|
1387
|
+
const reset = useCallback2(() => {
|
|
1388
|
+
setStatus("idle");
|
|
1389
|
+
setResult(null);
|
|
1390
|
+
}, []);
|
|
1391
|
+
return { status, result, run, reset };
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// src/lib/clipboard.ts
|
|
1395
|
+
import { spawn as spawn2, exec } from "child_process";
|
|
1396
|
+
async function openInBrowser(url) {
|
|
1397
|
+
return new Promise((resolve) => {
|
|
1398
|
+
const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "${url}"` : `xdg-open "${url}"`;
|
|
1399
|
+
exec(cmd, () => resolve());
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
async function copyToClipboard(text) {
|
|
1403
|
+
return new Promise((resolve) => {
|
|
1404
|
+
const cmd = process.platform === "darwin" ? "pbcopy" : process.platform === "win32" ? "clip" : "xclip -selection clipboard";
|
|
1405
|
+
const child = spawn2(cmd, [], { shell: true });
|
|
1406
|
+
child.stdin?.write(text);
|
|
1407
|
+
child.stdin?.end();
|
|
1408
|
+
child.on("exit", () => resolve());
|
|
1409
|
+
child.on("error", () => resolve());
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// src/screens/CommandExecution.tsx
|
|
1414
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1415
|
+
function CommandExecution({
|
|
1416
|
+
args: initialArgs,
|
|
1417
|
+
onBack,
|
|
1418
|
+
onExit
|
|
1419
|
+
}) {
|
|
1420
|
+
const [phase, setPhase] = useState8("confirm");
|
|
1421
|
+
const [currentArgs, setCurrentArgs] = useState8(initialArgs);
|
|
1422
|
+
const [pinMessage, setPinMessage] = useState8();
|
|
1423
|
+
const { status, result, run, reset } = useCommand();
|
|
1424
|
+
const cmdDisplay = `supabase ${currentArgs.join(" ")}`;
|
|
1425
|
+
const runCommand = currentArgs.join(" ");
|
|
1426
|
+
useEffect4(() => {
|
|
1427
|
+
if (phase === "running" && status === "idle") {
|
|
1428
|
+
run(currentArgs);
|
|
221
1429
|
}
|
|
1430
|
+
}, [phase, status, run, currentArgs]);
|
|
1431
|
+
useEffect4(() => {
|
|
1432
|
+
if (phase === "running" && status === "success") {
|
|
1433
|
+
if (isPinnedRun(runCommand)) {
|
|
1434
|
+
setPhase("success");
|
|
1435
|
+
} else {
|
|
1436
|
+
setPhase("success-pin-offer");
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
if (phase === "running" && status === "error") {
|
|
1440
|
+
setPhase("error-menu");
|
|
1441
|
+
}
|
|
1442
|
+
}, [phase, runCommand, status]);
|
|
1443
|
+
if (phase === "confirm") {
|
|
1444
|
+
return /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx13(
|
|
1445
|
+
ConfirmPrompt,
|
|
1446
|
+
{
|
|
1447
|
+
message: `Execute ${cmdDisplay}?`,
|
|
1448
|
+
defaultValue: true,
|
|
1449
|
+
onConfirm: (confirmed) => {
|
|
1450
|
+
if (confirmed) {
|
|
1451
|
+
setPhase("running");
|
|
1452
|
+
} else {
|
|
1453
|
+
onBack();
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
) });
|
|
1458
|
+
}
|
|
1459
|
+
if (phase === "running") {
|
|
1460
|
+
return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
|
|
1461
|
+
/* @__PURE__ */ jsx13(Divider, {}),
|
|
1462
|
+
/* @__PURE__ */ jsxs13(Box12, { marginY: 1, gap: 1, children: [
|
|
1463
|
+
/* @__PURE__ */ jsx13(Text11, { color: "cyan", bold: true, children: "\u25B6" }),
|
|
1464
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Running:" }),
|
|
1465
|
+
/* @__PURE__ */ jsx13(Text11, { children: cmdDisplay })
|
|
1466
|
+
] }),
|
|
1467
|
+
/* @__PURE__ */ jsx13(Divider, {}),
|
|
1468
|
+
/* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(Spinner, { label: `Executing ${cmdDisplay}...` }) })
|
|
1469
|
+
] });
|
|
1470
|
+
}
|
|
1471
|
+
if (phase === "success-pin-offer") {
|
|
1472
|
+
return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
|
|
1473
|
+
/* @__PURE__ */ jsx13(Divider, {}),
|
|
1474
|
+
/* @__PURE__ */ jsxs13(Box12, { marginY: 1, gap: 1, children: [
|
|
1475
|
+
/* @__PURE__ */ jsx13(Text11, { color: "green", bold: true, children: "\u2713" }),
|
|
1476
|
+
/* @__PURE__ */ jsx13(Text11, { color: "green", bold: true, children: "Command completed successfully!" })
|
|
1477
|
+
] }),
|
|
1478
|
+
/* @__PURE__ */ jsx13(
|
|
1479
|
+
ConfirmPrompt,
|
|
1480
|
+
{
|
|
1481
|
+
message: "Pin this exact command?",
|
|
1482
|
+
defaultValue: false,
|
|
1483
|
+
onConfirm: (shouldPin) => {
|
|
1484
|
+
if (shouldPin && !isPinnedRun(runCommand)) {
|
|
1485
|
+
togglePinnedRun(runCommand);
|
|
1486
|
+
setPinMessage("Exact command pinned to Pinned Runs.");
|
|
1487
|
+
}
|
|
1488
|
+
setPhase("success");
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
)
|
|
1492
|
+
] });
|
|
1493
|
+
}
|
|
1494
|
+
if (phase === "success") {
|
|
1495
|
+
return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
|
|
1496
|
+
/* @__PURE__ */ jsx13(Divider, {}),
|
|
1497
|
+
/* @__PURE__ */ jsxs13(Box12, { marginY: 1, gap: 1, children: [
|
|
1498
|
+
/* @__PURE__ */ jsx13(Text11, { color: "green", bold: true, children: "\u2713" }),
|
|
1499
|
+
/* @__PURE__ */ jsx13(Text11, { color: "green", bold: true, children: "Command completed successfully!" })
|
|
1500
|
+
] }),
|
|
1501
|
+
pinMessage && /* @__PURE__ */ jsx13(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { color: "green", children: pinMessage }) }),
|
|
1502
|
+
/* @__PURE__ */ jsx13(
|
|
1503
|
+
SelectList,
|
|
1504
|
+
{
|
|
1505
|
+
items: [{ value: "__back__", label: "\u2190 Back to menu" }],
|
|
1506
|
+
onSelect: onBack,
|
|
1507
|
+
onCancel: onBack
|
|
1508
|
+
}
|
|
1509
|
+
)
|
|
1510
|
+
] });
|
|
1511
|
+
}
|
|
1512
|
+
const hasDebug = currentArgs.includes("--debug");
|
|
1513
|
+
const errorItems = [];
|
|
1514
|
+
if (!result?.spawnError) {
|
|
1515
|
+
errorItems.push({ value: "retry", label: "\u{1F504} Retry the same command" });
|
|
1516
|
+
if (!hasDebug) {
|
|
1517
|
+
errorItems.push({
|
|
1518
|
+
value: "retry-debug",
|
|
1519
|
+
label: "\u{1F41B} Retry with --debug",
|
|
1520
|
+
hint: "Append --debug for verbose logs"
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
errorItems.push(
|
|
1525
|
+
{
|
|
1526
|
+
value: "docs",
|
|
1527
|
+
label: "\u{1F4D6} Open Supabase CLI docs",
|
|
1528
|
+
hint: "Opens in browser"
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
value: "copy",
|
|
1532
|
+
label: "\u{1F4CB} Copy command to clipboard"
|
|
1533
|
+
},
|
|
1534
|
+
{ value: "menu", label: "\u2190 Return to main menu" },
|
|
1535
|
+
{ value: "exit", label: "\u{1F6AA} Exit Polterbase" }
|
|
1536
|
+
);
|
|
1537
|
+
return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
|
|
1538
|
+
/* @__PURE__ */ jsx13(Divider, {}),
|
|
1539
|
+
result?.spawnError ? /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginY: 1, children: [
|
|
1540
|
+
/* @__PURE__ */ jsxs13(Box12, { gap: 1, children: [
|
|
1541
|
+
/* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: "\u2717" }),
|
|
1542
|
+
/* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: "Failed to start command" })
|
|
1543
|
+
] }),
|
|
1544
|
+
/* @__PURE__ */ jsxs13(Box12, { marginLeft: 2, marginTop: 1, children: [
|
|
1545
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Error: " }),
|
|
1546
|
+
/* @__PURE__ */ jsx13(Text11, { color: "red", children: result.spawnError })
|
|
1547
|
+
] }),
|
|
1548
|
+
(result.spawnError.includes("ENOENT") || result.spawnError.includes("not found")) && /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
|
|
1549
|
+
/* @__PURE__ */ jsx13(Text11, { color: "yellow", bold: true, children: "\u{1F4A1} Supabase CLI not found in PATH" }),
|
|
1550
|
+
/* @__PURE__ */ jsxs13(Box12, { gap: 1, children: [
|
|
1551
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Install it:" }),
|
|
1552
|
+
/* @__PURE__ */ jsx13(Text11, { color: "cyan", children: "https://supabase.com/docs/guides/cli" })
|
|
1553
|
+
] })
|
|
1554
|
+
] })
|
|
1555
|
+
] }) : /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginY: 1, children: [
|
|
1556
|
+
/* @__PURE__ */ jsxs13(Box12, { gap: 1, children: [
|
|
1557
|
+
/* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: "\u2717" }),
|
|
1558
|
+
/* @__PURE__ */ jsx13(Text11, { color: "red", children: "Command failed " }),
|
|
1559
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "(exit code " }),
|
|
1560
|
+
/* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: String(result?.exitCode) }),
|
|
1561
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ")" })
|
|
1562
|
+
] }),
|
|
1563
|
+
/* @__PURE__ */ jsxs13(Box12, { marginLeft: 2, marginTop: 1, children: [
|
|
1564
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Command: " }),
|
|
1565
|
+
/* @__PURE__ */ jsx13(Text11, { children: cmdDisplay })
|
|
1566
|
+
] }),
|
|
1567
|
+
!hasDebug && /* @__PURE__ */ jsxs13(Box12, { marginLeft: 2, marginTop: 1, gap: 1, children: [
|
|
1568
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "\u{1F4A1} Tip: retry with" }),
|
|
1569
|
+
/* @__PURE__ */ jsx13(Text11, { color: "cyan", children: "--debug" }),
|
|
1570
|
+
/* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "to see detailed logs" })
|
|
1571
|
+
] })
|
|
1572
|
+
] }),
|
|
1573
|
+
/* @__PURE__ */ jsx13(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: "What would you like to do?" }) }),
|
|
1574
|
+
/* @__PURE__ */ jsx13(
|
|
1575
|
+
SelectList,
|
|
1576
|
+
{
|
|
1577
|
+
items: errorItems,
|
|
1578
|
+
onSelect: async (action) => {
|
|
1579
|
+
switch (action) {
|
|
1580
|
+
case "retry":
|
|
1581
|
+
setPinMessage(void 0);
|
|
1582
|
+
reset();
|
|
1583
|
+
setPhase("running");
|
|
1584
|
+
break;
|
|
1585
|
+
case "retry-debug": {
|
|
1586
|
+
const newArgs = [...currentArgs, "--debug"];
|
|
1587
|
+
setCurrentArgs(newArgs);
|
|
1588
|
+
setPinMessage(void 0);
|
|
1589
|
+
reset();
|
|
1590
|
+
setPhase("running");
|
|
1591
|
+
break;
|
|
1592
|
+
}
|
|
1593
|
+
case "docs":
|
|
1594
|
+
await openInBrowser("https://supabase.com/docs/guides/cli");
|
|
1595
|
+
break;
|
|
1596
|
+
case "copy":
|
|
1597
|
+
await copyToClipboard(cmdDisplay);
|
|
1598
|
+
break;
|
|
1599
|
+
case "menu":
|
|
1600
|
+
onBack();
|
|
1601
|
+
break;
|
|
1602
|
+
case "exit":
|
|
1603
|
+
onExit();
|
|
1604
|
+
break;
|
|
1605
|
+
}
|
|
1606
|
+
},
|
|
1607
|
+
onCancel: onBack
|
|
1608
|
+
}
|
|
1609
|
+
),
|
|
1610
|
+
/* @__PURE__ */ jsx13(StatusBar, {})
|
|
1611
|
+
] });
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// src/app.tsx
|
|
1615
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1616
|
+
function App() {
|
|
1617
|
+
const { screen, params, navigate, goBack } = useNavigation();
|
|
1618
|
+
const { exit } = useApp();
|
|
1619
|
+
const handleExit = () => {
|
|
1620
|
+
process.stdout.write(
|
|
1621
|
+
"\n" + pc2.dim("Thank you for using ") + pc2.cyan(pc2.bold("Polterbase")) + pc2.dim("!") + "\n\n"
|
|
1622
|
+
);
|
|
1623
|
+
exit();
|
|
1624
|
+
};
|
|
1625
|
+
switch (screen) {
|
|
1626
|
+
case "main-menu":
|
|
1627
|
+
return /* @__PURE__ */ jsx14(MainMenu, { onNavigate: navigate, onExit: handleExit });
|
|
1628
|
+
case "command-args":
|
|
1629
|
+
return /* @__PURE__ */ jsx14(
|
|
1630
|
+
CommandArgs,
|
|
1631
|
+
{
|
|
1632
|
+
command: params.command ?? "",
|
|
1633
|
+
onNavigate: navigate,
|
|
1634
|
+
onBack: goBack
|
|
1635
|
+
}
|
|
1636
|
+
);
|
|
1637
|
+
case "custom-command":
|
|
1638
|
+
return /* @__PURE__ */ jsx14(CustomCommand, { onNavigate: navigate, onBack: goBack });
|
|
1639
|
+
case "flag-selection":
|
|
1640
|
+
return /* @__PURE__ */ jsx14(
|
|
1641
|
+
FlagSelection,
|
|
1642
|
+
{
|
|
1643
|
+
args: params.args ?? [],
|
|
1644
|
+
onNavigate: navigate,
|
|
1645
|
+
onBack: goBack
|
|
1646
|
+
}
|
|
1647
|
+
);
|
|
1648
|
+
case "confirm-execute":
|
|
1649
|
+
case "command-execution":
|
|
1650
|
+
return /* @__PURE__ */ jsx14(
|
|
1651
|
+
CommandExecution,
|
|
1652
|
+
{
|
|
1653
|
+
args: params.args ?? [],
|
|
1654
|
+
onBack: goBack,
|
|
1655
|
+
onExit: handleExit
|
|
1656
|
+
}
|
|
1657
|
+
);
|
|
1658
|
+
default:
|
|
1659
|
+
return /* @__PURE__ */ jsx14(Box13, { children: /* @__PURE__ */ jsxs14(Text12, { color: "red", children: [
|
|
1660
|
+
"Unknown screen: ",
|
|
1661
|
+
screen
|
|
1662
|
+
] }) });
|
|
222
1663
|
}
|
|
223
|
-
p.outro("Thank you for using Polterbase!");
|
|
224
1664
|
}
|
|
225
|
-
|
|
1665
|
+
|
|
1666
|
+
// src/index.tsx
|
|
1667
|
+
render(React7.createElement(App));
|