@stackable-labs/cli-app-extension 1.2.0 → 1.3.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/index.js +385 -335
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -6,8 +6,8 @@ import { render } from "ink";
|
|
|
6
6
|
|
|
7
7
|
// src/App.tsx
|
|
8
8
|
import { join as join2 } from "path";
|
|
9
|
-
import { Box as
|
|
10
|
-
import { useCallback, useState as
|
|
9
|
+
import { Box as Box12, Text as Text12, useApp } from "ink";
|
|
10
|
+
import { useCallback, useState as useState8 } from "react";
|
|
11
11
|
|
|
12
12
|
// src/components/Confirm.tsx
|
|
13
13
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
@@ -43,8 +43,9 @@ var StepShell = ({ title, hint, children, footer, onBack, backFocused }) => {
|
|
|
43
43
|
|
|
44
44
|
// src/components/Confirm.tsx
|
|
45
45
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
46
|
-
var Confirm = ({ name, extensionPort, previewPort, targets, outputDir, onConfirm, onCancel, onBack }) => {
|
|
46
|
+
var Confirm = ({ name, extensionVersion, extensionPort, previewPort, targets, outputDir, onConfirm, onCancel, onBack }) => {
|
|
47
47
|
const [backFocused, setBackFocused] = useState(false);
|
|
48
|
+
const [selected, setSelected] = useState("y");
|
|
48
49
|
useInput((input, key) => {
|
|
49
50
|
if (key.upArrow && onBack) {
|
|
50
51
|
setBackFocused(true);
|
|
@@ -59,12 +60,21 @@ var Confirm = ({ name, extensionPort, previewPort, targets, outputDir, onConfirm
|
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
62
|
if (backFocused) return;
|
|
62
|
-
if (
|
|
63
|
-
|
|
63
|
+
if (key.leftArrow || key.rightArrow) {
|
|
64
|
+
setSelected((s) => s === "y" ? "n" : "y");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (input === "y") {
|
|
68
|
+
setSelected("y");
|
|
64
69
|
return;
|
|
65
70
|
}
|
|
66
71
|
if (input === "n") {
|
|
67
|
-
|
|
72
|
+
setSelected("n");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (key.return) {
|
|
76
|
+
if (selected === "y") onConfirm();
|
|
77
|
+
else onCancel();
|
|
68
78
|
}
|
|
69
79
|
});
|
|
70
80
|
return /* @__PURE__ */ jsx2(
|
|
@@ -75,15 +85,20 @@ var Confirm = ({ name, extensionPort, previewPort, targets, outputDir, onConfirm
|
|
|
75
85
|
onBack,
|
|
76
86
|
backFocused,
|
|
77
87
|
footer: /* @__PURE__ */ jsxs2(Text2, { children: [
|
|
78
|
-
"Proceed?
|
|
79
|
-
|
|
80
|
-
"
|
|
81
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
88
|
+
"Proceed?",
|
|
89
|
+
" ",
|
|
90
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "green", inverse: selected === "y", children: "Y" }),
|
|
91
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "/" }),
|
|
92
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "red", inverse: selected === "n", children: "n" })
|
|
82
93
|
] }),
|
|
83
94
|
children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", gap: 1, children: [
|
|
84
95
|
/* @__PURE__ */ jsxs2(Box2, { gap: 2, children: [
|
|
85
96
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Name " }),
|
|
86
|
-
/* @__PURE__ */ jsx2(Text2, { children: name })
|
|
97
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: !!extensionVersion, children: name })
|
|
98
|
+
] }),
|
|
99
|
+
extensionVersion && /* @__PURE__ */ jsxs2(Box2, { gap: 2, children: [
|
|
100
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Version " }),
|
|
101
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: extensionVersion })
|
|
87
102
|
] }),
|
|
88
103
|
/* @__PURE__ */ jsxs2(Box2, { gap: 2, children: [
|
|
89
104
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Directory " }),
|
|
@@ -109,15 +124,55 @@ var Confirm = ({ name, extensionPort, previewPort, targets, outputDir, onConfirm
|
|
|
109
124
|
);
|
|
110
125
|
};
|
|
111
126
|
|
|
112
|
-
// src/components/
|
|
113
|
-
import { Box as
|
|
127
|
+
// src/components/Done.tsx
|
|
128
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
129
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
130
|
+
var Done = ({ name, outputDir }) => /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", gap: 1, children: [
|
|
131
|
+
/* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
132
|
+
/* @__PURE__ */ jsx3(Text3, { color: "green", bold: true, children: "\u2714" }),
|
|
133
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "Extension scaffolded successfully!" })
|
|
134
|
+
] }),
|
|
135
|
+
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 2, children: [
|
|
136
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
137
|
+
"Created: ",
|
|
138
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: name })
|
|
139
|
+
] }),
|
|
140
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
141
|
+
"Location: ",
|
|
142
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: outputDir })
|
|
143
|
+
] })
|
|
144
|
+
] }),
|
|
145
|
+
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, children: [
|
|
146
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "Next steps:" }),
|
|
147
|
+
/* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, gap: 1, children: [
|
|
148
|
+
/* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
149
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "1." }),
|
|
150
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
151
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "cd " }),
|
|
152
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: outputDir })
|
|
153
|
+
] })
|
|
154
|
+
] }),
|
|
155
|
+
/* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
156
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "2." }),
|
|
157
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "pnpm install" })
|
|
158
|
+
] }),
|
|
159
|
+
/* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
|
|
160
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "3." }),
|
|
161
|
+
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "pnpm dev" })
|
|
162
|
+
] })
|
|
163
|
+
] })
|
|
164
|
+
] })
|
|
165
|
+
] });
|
|
166
|
+
|
|
167
|
+
// src/components/NamePrompt.tsx
|
|
168
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
114
169
|
import TextInput from "ink-text-input";
|
|
115
170
|
import { useState as useState3 } from "react";
|
|
116
171
|
|
|
117
172
|
// src/components/BackableInput.tsx
|
|
118
|
-
import { Box as
|
|
173
|
+
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
119
174
|
import { useState as useState2 } from "react";
|
|
120
|
-
import { jsx as
|
|
175
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
121
176
|
var BackableInput = ({ label, hint, onBack, children, error }) => {
|
|
122
177
|
const [focus, setFocus] = useState2("input");
|
|
123
178
|
useInput2((_input, key) => {
|
|
@@ -133,77 +188,17 @@ var BackableInput = ({ label, hint, onBack, children, error }) => {
|
|
|
133
188
|
onBack?.();
|
|
134
189
|
}
|
|
135
190
|
});
|
|
136
|
-
return /* @__PURE__ */
|
|
191
|
+
return /* @__PURE__ */ jsx4(StepShell, { title: label, hint, onBack, backFocused: focus === "back", children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
137
192
|
children(focus === "input"),
|
|
138
|
-
error && /* @__PURE__ */
|
|
139
|
-
] }) });
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
// src/components/DirPrompt.tsx
|
|
143
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
144
|
-
var DirPrompt = ({ defaultDir, onSubmit, onBack }) => {
|
|
145
|
-
const [value, setValue] = useState3(defaultDir);
|
|
146
|
-
const handleSubmit = (val) => {
|
|
147
|
-
const trimmed = val.trim();
|
|
148
|
-
if (trimmed.length === 0) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
onSubmit(trimmed);
|
|
152
|
-
};
|
|
153
|
-
return /* @__PURE__ */ jsx4(BackableInput, { label: "Output directory:", hint: "Press Enter to confirm", onBack, children: (isFocused) => /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
154
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u2192 " }),
|
|
155
|
-
/* @__PURE__ */ jsx4(TextInput, { value, onChange: setValue, onSubmit: handleSubmit, focus: isFocused })
|
|
193
|
+
error && /* @__PURE__ */ jsx4(Text4, { color: "red", children: error })
|
|
156
194
|
] }) });
|
|
157
195
|
};
|
|
158
196
|
|
|
159
|
-
// src/components/Done.tsx
|
|
160
|
-
import { Box as Box5, Text as Text5 } from "ink";
|
|
161
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
162
|
-
var Done = ({ name, outputDir }) => /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", gap: 1, children: [
|
|
163
|
-
/* @__PURE__ */ jsxs5(Box5, { gap: 1, children: [
|
|
164
|
-
/* @__PURE__ */ jsx5(Text5, { color: "green", bold: true, children: "\u2714" }),
|
|
165
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Extension scaffolded successfully!" })
|
|
166
|
-
] }),
|
|
167
|
-
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginLeft: 2, children: [
|
|
168
|
-
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
169
|
-
"Created: ",
|
|
170
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: name })
|
|
171
|
-
] }),
|
|
172
|
-
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
173
|
-
"Location: ",
|
|
174
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: outputDir })
|
|
175
|
-
] })
|
|
176
|
-
] }),
|
|
177
|
-
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, children: [
|
|
178
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Next steps:" }),
|
|
179
|
-
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, gap: 1, children: [
|
|
180
|
-
/* @__PURE__ */ jsxs5(Box5, { gap: 1, children: [
|
|
181
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "1." }),
|
|
182
|
-
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
183
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "cd " }),
|
|
184
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: outputDir })
|
|
185
|
-
] })
|
|
186
|
-
] }),
|
|
187
|
-
/* @__PURE__ */ jsxs5(Box5, { gap: 1, children: [
|
|
188
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "2." }),
|
|
189
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "pnpm install" })
|
|
190
|
-
] }),
|
|
191
|
-
/* @__PURE__ */ jsxs5(Box5, { gap: 1, children: [
|
|
192
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "3." }),
|
|
193
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "pnpm dev" })
|
|
194
|
-
] })
|
|
195
|
-
] })
|
|
196
|
-
] })
|
|
197
|
-
] });
|
|
198
|
-
|
|
199
197
|
// src/components/NamePrompt.tsx
|
|
200
|
-
import {
|
|
201
|
-
import TextInput2 from "ink-text-input";
|
|
202
|
-
import { useState as useState4 } from "react";
|
|
203
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
198
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
204
199
|
var NamePrompt = ({ initialValue = "", onSubmit, onBack }) => {
|
|
205
|
-
const [value, setValue] =
|
|
206
|
-
const [error, setError] =
|
|
200
|
+
const [value, setValue] = useState3(initialValue);
|
|
201
|
+
const [error, setError] = useState3();
|
|
207
202
|
const handleSubmit = (val) => {
|
|
208
203
|
const trimmed = val.trim();
|
|
209
204
|
if (trimmed.length === 0) {
|
|
@@ -213,88 +208,143 @@ var NamePrompt = ({ initialValue = "", onSubmit, onBack }) => {
|
|
|
213
208
|
setError(void 0);
|
|
214
209
|
onSubmit(trimmed);
|
|
215
210
|
};
|
|
216
|
-
return /* @__PURE__ */
|
|
217
|
-
/* @__PURE__ */
|
|
218
|
-
/* @__PURE__ */
|
|
211
|
+
return /* @__PURE__ */ jsx5(BackableInput, { label: "What is your Extension name?", hint: "Press Enter to confirm", onBack, error, children: (isFocused) => /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
212
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "> " }),
|
|
213
|
+
/* @__PURE__ */ jsx5(TextInput, { value, onChange: setValue, onSubmit: handleSubmit, focus: isFocused })
|
|
219
214
|
] }) });
|
|
220
215
|
};
|
|
221
216
|
|
|
222
|
-
// src/components/
|
|
223
|
-
import { Box as
|
|
224
|
-
import
|
|
225
|
-
import { useState as
|
|
226
|
-
import { jsx as
|
|
217
|
+
// src/components/SettingsPrompt.tsx
|
|
218
|
+
import { Box as Box6, Text as Text6, useFocus, useFocusManager, useInput as useInput3 } from "ink";
|
|
219
|
+
import TextInput2 from "ink-text-input";
|
|
220
|
+
import { useState as useState4 } from "react";
|
|
221
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
227
222
|
var DEFAULT_EXTENSION_PORT = 5173;
|
|
228
223
|
var DEFAULT_PREVIEW_PORT = DEFAULT_EXTENSION_PORT + 1;
|
|
229
|
-
var
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
224
|
+
var FieldRow = ({ label, value, onChange, onSubmit, onConfirm, placeholder, autoFocus, isFirst, isLast, onFocusBack }) => {
|
|
225
|
+
const { isFocused } = useFocus({ autoFocus });
|
|
226
|
+
const { focusNext, focusPrevious } = useFocusManager();
|
|
227
|
+
useInput3((_input, key) => {
|
|
228
|
+
if (!isFocused) return;
|
|
229
|
+
if (key.downArrow && !isLast) focusNext();
|
|
230
|
+
if (key.upArrow) {
|
|
231
|
+
if (isFirst && onFocusBack) {
|
|
232
|
+
onFocusBack();
|
|
233
|
+
} else if (!isFirst) {
|
|
234
|
+
focusPrevious();
|
|
235
|
+
}
|
|
238
236
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
});
|
|
238
|
+
return /* @__PURE__ */ jsxs6(Box6, { gap: 2, children: [
|
|
239
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: label }),
|
|
240
|
+
isFocused ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
241
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u2192 " }),
|
|
242
|
+
/* @__PURE__ */ jsx6(
|
|
243
|
+
TextInput2,
|
|
244
|
+
{
|
|
245
|
+
value,
|
|
246
|
+
onChange,
|
|
247
|
+
onSubmit: (val) => {
|
|
248
|
+
const result = onSubmit(val);
|
|
249
|
+
if (result) onConfirm(...result);
|
|
250
|
+
},
|
|
251
|
+
placeholder,
|
|
252
|
+
focus: isFocused
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
] }) : /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: value })
|
|
256
|
+
] });
|
|
257
|
+
};
|
|
258
|
+
var SettingsPrompt = ({ defaultDir, onSubmit, onBack }) => {
|
|
259
|
+
const [backFocused, setBackFocused] = useState4(false);
|
|
260
|
+
const [extensionPort, setExtensionPort] = useState4(String(DEFAULT_EXTENSION_PORT));
|
|
261
|
+
const [previewPort, setPreviewPort] = useState4(String(DEFAULT_PREVIEW_PORT));
|
|
262
|
+
const [outputDir, setOutputDir] = useState4(defaultDir);
|
|
263
|
+
const handleFocusBack = () => {
|
|
264
|
+
if (onBack) setBackFocused(true);
|
|
242
265
|
};
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const prevPort = trimmed === "" ? extPort + 1 : parseInt(trimmed, 10);
|
|
247
|
-
if (isNaN(prevPort) || prevPort < 1024 || prevPort > 65535) {
|
|
266
|
+
useInput3((_input, key) => {
|
|
267
|
+
if (key.downArrow && backFocused) {
|
|
268
|
+
setBackFocused(false);
|
|
248
269
|
return;
|
|
249
270
|
}
|
|
250
|
-
|
|
251
|
-
};
|
|
252
|
-
const handleBack = () => {
|
|
253
|
-
if (step === "preview") {
|
|
254
|
-
setStep("extension");
|
|
255
|
-
} else {
|
|
271
|
+
if (key.return && backFocused) {
|
|
256
272
|
onBack?.();
|
|
273
|
+
return;
|
|
257
274
|
}
|
|
275
|
+
});
|
|
276
|
+
const handleConfirm = (resolvedExtPort, resolvedPrevPort, resolvedDir) => {
|
|
277
|
+
const dir = resolvedDir.trim();
|
|
278
|
+
if (!dir) return;
|
|
279
|
+
onSubmit(parseInt(resolvedExtPort, 10), parseInt(resolvedPrevPort, 10), dir);
|
|
258
280
|
};
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
const handleExtensionPortSubmit = (value) => {
|
|
282
|
+
const trimmed = value.trim();
|
|
283
|
+
const port = trimmed === "" ? DEFAULT_EXTENSION_PORT : parseInt(trimmed, 10);
|
|
284
|
+
if (isNaN(port) || port < 1024 || port > 65535) return false;
|
|
285
|
+
const newExtPort = String(port);
|
|
286
|
+
const newPrevPort = String(port + 1);
|
|
287
|
+
setExtensionPort(newExtPort);
|
|
288
|
+
setPreviewPort(newPrevPort);
|
|
289
|
+
return [newExtPort, newPrevPort, outputDir];
|
|
290
|
+
};
|
|
291
|
+
const handlePreviewPortSubmit = (value) => {
|
|
292
|
+
const extPort = parseInt(extensionPort, 10);
|
|
293
|
+
const trimmed = value.trim();
|
|
294
|
+
const port = trimmed === "" ? extPort + 1 : parseInt(trimmed, 10);
|
|
295
|
+
if (isNaN(port) || port < 1024 || port > 65535) return false;
|
|
296
|
+
const newPrevPort = String(port);
|
|
297
|
+
setPreviewPort(newPrevPort);
|
|
298
|
+
return [extensionPort, newPrevPort, outputDir];
|
|
299
|
+
};
|
|
300
|
+
const handleOutputDirSubmit = (value) => {
|
|
301
|
+
const trimmed = value.trim();
|
|
302
|
+
if (trimmed.length === 0) return false;
|
|
303
|
+
setOutputDir(trimmed);
|
|
304
|
+
return [extensionPort, previewPort, trimmed];
|
|
305
|
+
};
|
|
306
|
+
return /* @__PURE__ */ jsx6(
|
|
307
|
+
StepShell,
|
|
284
308
|
{
|
|
285
|
-
|
|
286
|
-
hint: "
|
|
287
|
-
onBack
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
/* @__PURE__ */
|
|
291
|
-
|
|
309
|
+
title: "Project settings",
|
|
310
|
+
hint: "\u2191\u2193 or Tab to move between fields, Enter to confirm",
|
|
311
|
+
onBack,
|
|
312
|
+
backFocused,
|
|
313
|
+
children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", gap: 1, children: [
|
|
314
|
+
/* @__PURE__ */ jsx6(
|
|
315
|
+
FieldRow,
|
|
292
316
|
{
|
|
317
|
+
label: "Extension port ",
|
|
318
|
+
value: extensionPort,
|
|
319
|
+
onChange: setExtensionPort,
|
|
320
|
+
onSubmit: handleExtensionPortSubmit,
|
|
321
|
+
onConfirm: handleConfirm,
|
|
322
|
+
placeholder: String(DEFAULT_EXTENSION_PORT),
|
|
323
|
+
autoFocus: true,
|
|
324
|
+
isFirst: true,
|
|
325
|
+
onFocusBack: handleFocusBack
|
|
326
|
+
}
|
|
327
|
+
),
|
|
328
|
+
/* @__PURE__ */ jsx6(
|
|
329
|
+
FieldRow,
|
|
330
|
+
{
|
|
331
|
+
label: "Preview port ",
|
|
293
332
|
value: previewPort,
|
|
294
333
|
onChange: setPreviewPort,
|
|
295
|
-
onSubmit:
|
|
296
|
-
|
|
297
|
-
|
|
334
|
+
onSubmit: handlePreviewPortSubmit,
|
|
335
|
+
onConfirm: handleConfirm,
|
|
336
|
+
placeholder: String(DEFAULT_PREVIEW_PORT)
|
|
337
|
+
}
|
|
338
|
+
),
|
|
339
|
+
/* @__PURE__ */ jsx6(
|
|
340
|
+
FieldRow,
|
|
341
|
+
{
|
|
342
|
+
label: "Output directory",
|
|
343
|
+
value: outputDir,
|
|
344
|
+
onChange: setOutputDir,
|
|
345
|
+
onSubmit: handleOutputDirSubmit,
|
|
346
|
+
onConfirm: handleConfirm,
|
|
347
|
+
isLast: true
|
|
298
348
|
}
|
|
299
349
|
)
|
|
300
350
|
] })
|
|
@@ -303,33 +353,33 @@ var PortsPrompt = ({ onSubmit, onBack }) => {
|
|
|
303
353
|
};
|
|
304
354
|
|
|
305
355
|
// src/components/ScaffoldProgress.tsx
|
|
306
|
-
import { Box as
|
|
356
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
307
357
|
import Spinner from "ink-spinner";
|
|
308
|
-
import { jsx as
|
|
358
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
309
359
|
var stepIcon = (status) => {
|
|
310
360
|
switch (status) {
|
|
311
361
|
case "running":
|
|
312
|
-
return /* @__PURE__ */
|
|
362
|
+
return /* @__PURE__ */ jsx7(Spinner, { type: "dots" });
|
|
313
363
|
case "done":
|
|
314
|
-
return /* @__PURE__ */
|
|
364
|
+
return /* @__PURE__ */ jsx7(Text7, { color: "green", children: "\u2714" });
|
|
315
365
|
case "error":
|
|
316
|
-
return /* @__PURE__ */
|
|
366
|
+
return /* @__PURE__ */ jsx7(Text7, { color: "red", children: "\u2716" });
|
|
317
367
|
default:
|
|
318
|
-
return /* @__PURE__ */
|
|
368
|
+
return /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u25CB" });
|
|
319
369
|
}
|
|
320
370
|
};
|
|
321
|
-
var ScaffoldProgress = ({ steps }) => /* @__PURE__ */
|
|
322
|
-
/* @__PURE__ */
|
|
323
|
-
/* @__PURE__ */
|
|
371
|
+
var ScaffoldProgress = ({ steps }) => /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
|
|
372
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, children: "Scaffolding your Extension\u2026" }),
|
|
373
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: steps.map((step) => /* @__PURE__ */ jsxs7(Box7, { gap: 2, children: [
|
|
324
374
|
stepIcon(step.status),
|
|
325
|
-
/* @__PURE__ */
|
|
375
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: step.status === "pending", color: step.status === "running" ? "cyan" : void 0, children: step.label })
|
|
326
376
|
] }, step.label)) })
|
|
327
377
|
] });
|
|
328
378
|
|
|
329
379
|
// src/components/TargetSelect.tsx
|
|
330
|
-
import { Box as
|
|
331
|
-
import { useState as
|
|
332
|
-
import { jsx as
|
|
380
|
+
import { Box as Box8, Text as Text8, useInput as useInput4 } from "ink";
|
|
381
|
+
import { useState as useState5 } from "react";
|
|
382
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
333
383
|
var TARGET_DESCRIPTIONS = {
|
|
334
384
|
"slot.header": "Renders content in the panel header area",
|
|
335
385
|
"slot.content": "Renders the main panel body (includes store + navigation state)",
|
|
@@ -337,13 +387,13 @@ var TARGET_DESCRIPTIONS = {
|
|
|
337
387
|
"slot.footer-links": "Renders a link row in the global footer"
|
|
338
388
|
};
|
|
339
389
|
var TargetSelect = ({ availableTargets, preSelected, onSubmit, onBack }) => {
|
|
340
|
-
const [cursor, setCursor] =
|
|
341
|
-
const [backFocused, setBackFocused] =
|
|
342
|
-
const [selected, setSelected] =
|
|
390
|
+
const [cursor, setCursor] = useState5(0);
|
|
391
|
+
const [backFocused, setBackFocused] = useState5(false);
|
|
392
|
+
const [selected, setSelected] = useState5(
|
|
343
393
|
new Set(preSelected ?? (availableTargets.includes("slot.content") ? ["slot.content"] : []))
|
|
344
394
|
);
|
|
345
|
-
const [error, setError] =
|
|
346
|
-
|
|
395
|
+
const [error, setError] = useState5();
|
|
396
|
+
useInput4((input, key) => {
|
|
347
397
|
if (key.upArrow) {
|
|
348
398
|
if (cursor === 0 && onBack) {
|
|
349
399
|
setBackFocused(true);
|
|
@@ -387,7 +437,7 @@ var TargetSelect = ({ availableTargets, preSelected, onSubmit, onBack }) => {
|
|
|
387
437
|
onSubmit([...selected]);
|
|
388
438
|
}
|
|
389
439
|
});
|
|
390
|
-
return /* @__PURE__ */
|
|
440
|
+
return /* @__PURE__ */ jsxs8(
|
|
391
441
|
StepShell,
|
|
392
442
|
{
|
|
393
443
|
title: "Select Surface targets/slots",
|
|
@@ -395,28 +445,28 @@ var TargetSelect = ({ availableTargets, preSelected, onSubmit, onBack }) => {
|
|
|
395
445
|
onBack,
|
|
396
446
|
backFocused,
|
|
397
447
|
children: [
|
|
398
|
-
/* @__PURE__ */
|
|
448
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", gap: 1, children: availableTargets.map((target, i) => {
|
|
399
449
|
const isSelected = selected.has(target);
|
|
400
450
|
const isCursor = i === cursor && !backFocused;
|
|
401
|
-
return /* @__PURE__ */
|
|
402
|
-
/* @__PURE__ */
|
|
403
|
-
/* @__PURE__ */
|
|
404
|
-
/* @__PURE__ */
|
|
405
|
-
/* @__PURE__ */
|
|
406
|
-
/* @__PURE__ */
|
|
451
|
+
return /* @__PURE__ */ jsxs8(Box8, { gap: 1, children: [
|
|
452
|
+
/* @__PURE__ */ jsx8(Text8, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u276F" : " " }),
|
|
453
|
+
/* @__PURE__ */ jsx8(Text8, { color: isSelected ? "green" : void 0, children: isSelected ? "\u25C9" : "\u25CB" }),
|
|
454
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
455
|
+
/* @__PURE__ */ jsx8(Text8, { bold: isSelected, children: target }),
|
|
456
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: TARGET_DESCRIPTIONS[target] ?? target })
|
|
407
457
|
] })
|
|
408
458
|
] }, target);
|
|
409
459
|
}) }),
|
|
410
|
-
error && /* @__PURE__ */
|
|
460
|
+
error && /* @__PURE__ */ jsx8(Text8, { color: "red", children: error })
|
|
411
461
|
]
|
|
412
462
|
}
|
|
413
463
|
);
|
|
414
464
|
};
|
|
415
465
|
|
|
416
466
|
// src/components/AppSelect.tsx
|
|
417
|
-
import { Box as
|
|
467
|
+
import { Box as Box10, Text as Text10, useInput as useInput5 } from "ink";
|
|
418
468
|
import Spinner2 from "ink-spinner";
|
|
419
|
-
import { useEffect, useState as
|
|
469
|
+
import { useEffect, useState as useState6 } from "react";
|
|
420
470
|
|
|
421
471
|
// src/lib/api.ts
|
|
422
472
|
var DEFAULT_API_URL = "https://api.stackablelabs.io/app-extension/latest";
|
|
@@ -454,8 +504,8 @@ var fetchExtensions = async (appId) => {
|
|
|
454
504
|
};
|
|
455
505
|
|
|
456
506
|
// src/components/Banner.tsx
|
|
457
|
-
import { Box as
|
|
458
|
-
import { jsx as
|
|
507
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
508
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
459
509
|
var WORDMARK = [
|
|
460
510
|
" _ _ _ _ ",
|
|
461
511
|
" ___| |_ __ _ ___| | ____ _| |__ | | ___",
|
|
@@ -491,23 +541,23 @@ var gradientColor = (row, col, rows, cols) => {
|
|
|
491
541
|
var Banner = () => {
|
|
492
542
|
const termWidth = process.stdout.columns ?? 80;
|
|
493
543
|
const maxLen = Math.max(...WORDMARK.map((l) => l.length));
|
|
494
|
-
return /* @__PURE__ */
|
|
495
|
-
/* @__PURE__ */
|
|
496
|
-
/* @__PURE__ */
|
|
544
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
545
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2500".repeat(termWidth) }),
|
|
546
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", paddingX: 1, paddingY: 1, children: WORDMARK.map((line, row) => /* @__PURE__ */ jsx9(Box9, { children: line.split("").map((ch, col) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: ch === " " ? void 0 : gradientColor(row, col, WORDMARK.length, maxLen), children: ch }, col)) }, row)) })
|
|
497
547
|
] });
|
|
498
548
|
};
|
|
499
549
|
|
|
500
550
|
// src/components/AppSelect.tsx
|
|
501
|
-
import { jsx as
|
|
551
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
502
552
|
var AppSelect = ({ onSubmit }) => {
|
|
503
|
-
const [apps, setApps] =
|
|
504
|
-
const [loading, setLoading] =
|
|
505
|
-
const [error, setError] =
|
|
506
|
-
const [cursor, setCursor] =
|
|
553
|
+
const [apps, setApps] = useState6([]);
|
|
554
|
+
const [loading, setLoading] = useState6(true);
|
|
555
|
+
const [error, setError] = useState6();
|
|
556
|
+
const [cursor, setCursor] = useState6(0);
|
|
507
557
|
useEffect(() => {
|
|
508
558
|
fetchApps().then(setApps).catch((err) => setError(err instanceof Error ? err.message : String(err))).finally(() => setLoading(false));
|
|
509
559
|
}, []);
|
|
510
|
-
|
|
560
|
+
useInput5((_, key) => {
|
|
511
561
|
if (loading || error || apps.length === 0) return;
|
|
512
562
|
if (key.upArrow) {
|
|
513
563
|
setCursor((c) => Math.max(0, c - 1));
|
|
@@ -517,60 +567,59 @@ var AppSelect = ({ onSubmit }) => {
|
|
|
517
567
|
onSubmit(apps[cursor]);
|
|
518
568
|
}
|
|
519
569
|
});
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
/* @__PURE__ */
|
|
523
|
-
|
|
524
|
-
/* @__PURE__ */
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
return /* @__PURE__ */
|
|
537
|
-
}
|
|
538
|
-
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
539
|
-
/* @__PURE__ */ jsx11(Banner, {}),
|
|
540
|
-
/* @__PURE__ */ jsx11(StepShell, { title: "Select the App you are building an Extension for:", children: /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: apps.map((app, i) => {
|
|
570
|
+
const renderContent = () => {
|
|
571
|
+
if (loading) {
|
|
572
|
+
return /* @__PURE__ */ jsxs10(Box10, { gap: 2, children: [
|
|
573
|
+
/* @__PURE__ */ jsx10(Spinner2, { type: "dots" }),
|
|
574
|
+
/* @__PURE__ */ jsx10(Text10, { children: "Loading available Apps\u2026" })
|
|
575
|
+
] });
|
|
576
|
+
}
|
|
577
|
+
if (error) {
|
|
578
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", gap: 1, children: [
|
|
579
|
+
/* @__PURE__ */ jsx10(Text10, { color: "red", bold: true, children: "Failed to load Apps" }),
|
|
580
|
+
/* @__PURE__ */ jsx10(Text10, { color: "red", children: error })
|
|
581
|
+
] });
|
|
582
|
+
}
|
|
583
|
+
if (apps.length === 0) {
|
|
584
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: "No Apps available. Contact your administrator." });
|
|
585
|
+
}
|
|
586
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: apps.map((app, i) => {
|
|
541
587
|
const isCursor = i === cursor;
|
|
542
|
-
return /* @__PURE__ */
|
|
543
|
-
/* @__PURE__ */
|
|
544
|
-
/* @__PURE__ */
|
|
545
|
-
/* @__PURE__ */
|
|
588
|
+
return /* @__PURE__ */ jsxs10(Box10, { gap: 1, children: [
|
|
589
|
+
/* @__PURE__ */ jsx10(Text10, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u276F" : " " }),
|
|
590
|
+
/* @__PURE__ */ jsx10(Text10, { bold: isCursor, children: app.name }),
|
|
591
|
+
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
546
592
|
"(",
|
|
547
593
|
app.id,
|
|
548
594
|
")"
|
|
549
595
|
] })
|
|
550
596
|
] }, app.id);
|
|
551
|
-
}) })
|
|
597
|
+
}) });
|
|
598
|
+
};
|
|
599
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
600
|
+
/* @__PURE__ */ jsx10(Banner, {}),
|
|
601
|
+
/* @__PURE__ */ jsx10(StepShell, { title: "Select the App you are building an Extension for:", children: renderContent() })
|
|
552
602
|
] });
|
|
553
603
|
};
|
|
554
604
|
|
|
555
605
|
// src/components/ExtensionSelect.tsx
|
|
556
|
-
import { Box as
|
|
606
|
+
import { Box as Box11, Text as Text11, useInput as useInput6 } from "ink";
|
|
557
607
|
import Spinner3 from "ink-spinner";
|
|
558
|
-
import { useEffect as useEffect2, useState as
|
|
559
|
-
import { jsx as
|
|
608
|
+
import { useEffect as useEffect2, useState as useState7 } from "react";
|
|
609
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
560
610
|
var ExtensionSelect = ({ appId, onSubmit, onBack }) => {
|
|
561
|
-
const [extensions, setExtensions] =
|
|
562
|
-
const [loading, setLoading] =
|
|
563
|
-
const [error, setError] =
|
|
564
|
-
const [cursor, setCursor] =
|
|
565
|
-
const [backFocused, setBackFocused] =
|
|
611
|
+
const [extensions, setExtensions] = useState7([]);
|
|
612
|
+
const [loading, setLoading] = useState7(true);
|
|
613
|
+
const [error, setError] = useState7();
|
|
614
|
+
const [cursor, setCursor] = useState7(0);
|
|
615
|
+
const [backFocused, setBackFocused] = useState7(false);
|
|
566
616
|
useEffect2(() => {
|
|
567
617
|
fetchExtensions(appId).then((byId) => setExtensions(Object.values(byId))).catch((err) => setError(err instanceof Error ? err.message : String(err))).finally(() => setLoading(false));
|
|
568
618
|
}, [appId]);
|
|
569
|
-
|
|
570
|
-
if (loading || error || extensions.length === 0) return;
|
|
619
|
+
useInput6((_, key) => {
|
|
571
620
|
if (key.upArrow) {
|
|
572
|
-
if (
|
|
573
|
-
setBackFocused(true);
|
|
621
|
+
if (loading || error || extensions.length === 0 || cursor === 0) {
|
|
622
|
+
if (onBack) setBackFocused(true);
|
|
574
623
|
} else {
|
|
575
624
|
setBackFocused(false);
|
|
576
625
|
setCursor((c) => Math.max(0, c - 1));
|
|
@@ -580,7 +629,7 @@ var ExtensionSelect = ({ appId, onSubmit, onBack }) => {
|
|
|
580
629
|
if (key.downArrow) {
|
|
581
630
|
if (backFocused) {
|
|
582
631
|
setBackFocused(false);
|
|
583
|
-
} else {
|
|
632
|
+
} else if (!loading && !error && extensions.length > 0) {
|
|
584
633
|
setCursor((c) => Math.min(extensions.length - 1, c + 1));
|
|
585
634
|
}
|
|
586
635
|
return;
|
|
@@ -590,36 +639,45 @@ var ExtensionSelect = ({ appId, onSubmit, onBack }) => {
|
|
|
590
639
|
onBack?.();
|
|
591
640
|
return;
|
|
592
641
|
}
|
|
593
|
-
|
|
642
|
+
if (!loading && !error && extensions.length > 0) {
|
|
643
|
+
onSubmit(extensions[cursor]);
|
|
644
|
+
}
|
|
594
645
|
}
|
|
595
646
|
});
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
/* @__PURE__ */
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
/* @__PURE__ */
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
/* @__PURE__ */
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
647
|
+
const renderContent = () => {
|
|
648
|
+
if (loading) {
|
|
649
|
+
return /* @__PURE__ */ jsxs11(Box11, { gap: 2, children: [
|
|
650
|
+
/* @__PURE__ */ jsx11(Spinner3, { type: "dots" }),
|
|
651
|
+
/* @__PURE__ */ jsx11(Text11, { children: "Loading Extensions\u2026" })
|
|
652
|
+
] });
|
|
653
|
+
}
|
|
654
|
+
if (error) {
|
|
655
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", gap: 1, children: [
|
|
656
|
+
/* @__PURE__ */ jsx11(Text11, { color: "red", bold: true, children: "Failed to load Extensions" }),
|
|
657
|
+
/* @__PURE__ */ jsx11(Text11, { color: "red", children: error })
|
|
658
|
+
] });
|
|
659
|
+
}
|
|
660
|
+
if (extensions.length === 0) {
|
|
661
|
+
return /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "No Extensions found for this App." });
|
|
662
|
+
}
|
|
663
|
+
return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: extensions.map((ext, i) => {
|
|
664
|
+
const isCursor = i === cursor && !backFocused;
|
|
665
|
+
return /* @__PURE__ */ jsxs11(Box11, { gap: 1, children: [
|
|
666
|
+
/* @__PURE__ */ jsx11(Text11, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u276F" : " " }),
|
|
667
|
+
/* @__PURE__ */ jsx11(Text11, { bold: isCursor, children: ext.manifest.name }),
|
|
668
|
+
/* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
669
|
+
"v",
|
|
670
|
+
ext.manifest.version
|
|
671
|
+
] }),
|
|
672
|
+
/* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
673
|
+
"(",
|
|
674
|
+
ext.id,
|
|
675
|
+
")"
|
|
676
|
+
] })
|
|
677
|
+
] }, ext.id);
|
|
678
|
+
}) });
|
|
679
|
+
};
|
|
680
|
+
return /* @__PURE__ */ jsx11(StepShell, { title: "Select an existing Extension to scaffold:", onBack, backFocused, children: renderContent() });
|
|
623
681
|
};
|
|
624
682
|
|
|
625
683
|
// src/constants.ts
|
|
@@ -956,20 +1014,24 @@ var scaffold = async (options) => {
|
|
|
956
1014
|
};
|
|
957
1015
|
|
|
958
1016
|
// src/App.tsx
|
|
959
|
-
import { jsx as
|
|
960
|
-
var
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1017
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1018
|
+
var STEPS = {
|
|
1019
|
+
["create" /* CREATE */]: ["app", "name", "targets", "settings", "confirm"],
|
|
1020
|
+
["scaffold" /* SCAFFOLD */]: ["app", "extensionSelect", "confirmTargets", "settings", "confirm"]
|
|
1021
|
+
};
|
|
1022
|
+
var PROGRESS_STEPS = {
|
|
1023
|
+
["create" /* CREATE */]: [
|
|
1024
|
+
{ label: "Registering extension", status: "pending" },
|
|
1025
|
+
{ label: "Fetching template", status: "pending" },
|
|
1026
|
+
{ label: "Generating files", status: "pending" },
|
|
1027
|
+
{ label: "Installing dependencies", status: "pending" }
|
|
1028
|
+
],
|
|
1029
|
+
["scaffold" /* SCAFFOLD */]: [
|
|
1030
|
+
{ label: "Fetching template", status: "pending" },
|
|
1031
|
+
{ label: "Generating files", status: "pending" },
|
|
1032
|
+
{ label: "Installing dependencies", status: "pending" }
|
|
1033
|
+
]
|
|
1034
|
+
};
|
|
973
1035
|
var toKebabCase2 = (value) => value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
974
1036
|
var derivePermissions2 = (targets) => {
|
|
975
1037
|
const permissions = /* @__PURE__ */ new Set();
|
|
@@ -988,32 +1050,33 @@ var derivePermissions2 = (targets) => {
|
|
|
988
1050
|
}
|
|
989
1051
|
return [...permissions];
|
|
990
1052
|
};
|
|
991
|
-
var App = ({
|
|
1053
|
+
var App = ({ command, initialName, options }) => {
|
|
992
1054
|
const { exit } = useApp();
|
|
993
|
-
const [step, setStep] =
|
|
994
|
-
const [name, setName] =
|
|
995
|
-
const [extensionId, setExtensionId] =
|
|
996
|
-
const [
|
|
997
|
-
const [
|
|
1055
|
+
const [step, setStep] = useState8("app");
|
|
1056
|
+
const [name, setName] = useState8(initialName ?? "");
|
|
1057
|
+
const [extensionId, setExtensionId] = useState8("");
|
|
1058
|
+
const [extensionVersion, setExtensionVersion] = useState8("");
|
|
1059
|
+
const [selectedApp, setSelectedApp] = useState8(null);
|
|
1060
|
+
const [extensionPort, setExtensionPort] = useState8(
|
|
998
1061
|
options?.extensionPort ? parseInt(options.extensionPort, 10) : 5173
|
|
999
1062
|
);
|
|
1000
|
-
const [previewPort, setPreviewPort] =
|
|
1063
|
+
const [previewPort, setPreviewPort] = useState8(
|
|
1001
1064
|
options?.previewPort ? parseInt(options.previewPort, 10) : 5174
|
|
1002
1065
|
);
|
|
1003
|
-
const [targets, setTargets] =
|
|
1004
|
-
const [outputDir, setOutputDir] =
|
|
1005
|
-
const [progressSteps, setProgressSteps] =
|
|
1006
|
-
const [errorMessage, setErrorMessage] =
|
|
1066
|
+
const [targets, setTargets] = useState8([]);
|
|
1067
|
+
const [outputDir, setOutputDir] = useState8("");
|
|
1068
|
+
const [progressSteps, setProgressSteps] = useState8(PROGRESS_STEPS[command]);
|
|
1069
|
+
const [errorMessage, setErrorMessage] = useState8();
|
|
1007
1070
|
const updateStep = useCallback((index, status) => {
|
|
1008
1071
|
setProgressSteps((prev) => prev.map((s, i) => i === index ? { ...s, status } : s));
|
|
1009
1072
|
}, []);
|
|
1010
1073
|
const activeSteps = useCallback(() => {
|
|
1011
|
-
const base =
|
|
1074
|
+
const base = STEPS[command];
|
|
1012
1075
|
const skipped = /* @__PURE__ */ new Set();
|
|
1013
|
-
if (
|
|
1014
|
-
if (options?.extensionPort || options?.previewPort) skipped.add("
|
|
1076
|
+
if (command === "create" /* CREATE */ && initialName) skipped.add("name");
|
|
1077
|
+
if (options?.extensionPort || options?.previewPort) skipped.add("settings");
|
|
1015
1078
|
return base.filter((s) => !skipped.has(s));
|
|
1016
|
-
}, [
|
|
1079
|
+
}, [command, initialName, options?.extensionPort, options?.previewPort]);
|
|
1017
1080
|
const goBack = useCallback(() => {
|
|
1018
1081
|
setStep((prev) => {
|
|
1019
1082
|
const steps = activeSteps();
|
|
@@ -1023,7 +1086,7 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1023
1086
|
}, [activeSteps]);
|
|
1024
1087
|
const handleAppSelect = (app) => {
|
|
1025
1088
|
setSelectedApp(app);
|
|
1026
|
-
if (
|
|
1089
|
+
if (command === "scaffold" /* SCAFFOLD */) {
|
|
1027
1090
|
setStep("extensionSelect");
|
|
1028
1091
|
return;
|
|
1029
1092
|
}
|
|
@@ -1032,16 +1095,18 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1032
1095
|
const handleExtensionSelect = (ext) => {
|
|
1033
1096
|
setName(ext.manifest.name);
|
|
1034
1097
|
setExtensionId(ext.id);
|
|
1098
|
+
setExtensionVersion(ext.manifest.version);
|
|
1035
1099
|
setTargets(ext.manifest.targets);
|
|
1036
|
-
setStep("confirmName");
|
|
1037
|
-
};
|
|
1038
|
-
const handleConfirmName = (value) => {
|
|
1039
|
-
setName(value);
|
|
1040
1100
|
setStep("confirmTargets");
|
|
1041
1101
|
};
|
|
1042
1102
|
const handleConfirmTargets = (value) => {
|
|
1043
1103
|
setTargets(value);
|
|
1044
|
-
|
|
1104
|
+
if (options?.extensionPort || options?.previewPort) {
|
|
1105
|
+
setOutputDir(join2(process.cwd(), toKebabCase2(extensionId || name)));
|
|
1106
|
+
setStep("confirm");
|
|
1107
|
+
} else {
|
|
1108
|
+
setStep("settings");
|
|
1109
|
+
}
|
|
1045
1110
|
};
|
|
1046
1111
|
const handleName = (value) => {
|
|
1047
1112
|
setName(value);
|
|
@@ -1050,24 +1115,26 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1050
1115
|
};
|
|
1051
1116
|
const handleTargets = (value) => {
|
|
1052
1117
|
setTargets(value);
|
|
1053
|
-
|
|
1118
|
+
if (options?.extensionPort || options?.previewPort) {
|
|
1119
|
+
setOutputDir(join2(process.cwd(), toKebabCase2(extensionId || name)));
|
|
1120
|
+
setStep("confirm");
|
|
1121
|
+
} else {
|
|
1122
|
+
setStep("settings");
|
|
1123
|
+
}
|
|
1054
1124
|
};
|
|
1055
|
-
const
|
|
1125
|
+
const handleSettings = (extPort, prevPort, dir) => {
|
|
1056
1126
|
setExtensionPort(extPort);
|
|
1057
1127
|
setPreviewPort(prevPort);
|
|
1058
|
-
setStep("dir");
|
|
1059
|
-
};
|
|
1060
|
-
const handleDir = (dir) => {
|
|
1061
1128
|
setOutputDir(dir);
|
|
1062
1129
|
setStep("confirm");
|
|
1063
1130
|
};
|
|
1064
1131
|
const handleConfirm = async () => {
|
|
1065
1132
|
setStep("scaffolding");
|
|
1066
|
-
setProgressSteps(
|
|
1133
|
+
setProgressSteps(PROGRESS_STEPS[command]);
|
|
1067
1134
|
try {
|
|
1068
1135
|
let resolvedExtensionId = extensionId || toKebabCase2(name);
|
|
1069
1136
|
let scaffoldStepOffset = 0;
|
|
1070
|
-
if (
|
|
1137
|
+
if (command === "create" /* CREATE */) {
|
|
1071
1138
|
scaffoldStepOffset = 1;
|
|
1072
1139
|
updateStep(0, "running");
|
|
1073
1140
|
const created = await createExtensionRemote(selectedApp.id, {
|
|
@@ -1116,10 +1183,10 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1116
1183
|
};
|
|
1117
1184
|
switch (step) {
|
|
1118
1185
|
case "app": {
|
|
1119
|
-
return /* @__PURE__ */
|
|
1186
|
+
return /* @__PURE__ */ jsx12(AppSelect, { onSubmit: handleAppSelect });
|
|
1120
1187
|
}
|
|
1121
1188
|
case "extensionSelect": {
|
|
1122
|
-
return /* @__PURE__ */
|
|
1189
|
+
return /* @__PURE__ */ jsx12(
|
|
1123
1190
|
ExtensionSelect,
|
|
1124
1191
|
{
|
|
1125
1192
|
appId: selectedApp.id,
|
|
@@ -1128,18 +1195,8 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1128
1195
|
}
|
|
1129
1196
|
);
|
|
1130
1197
|
}
|
|
1131
|
-
case "confirmName": {
|
|
1132
|
-
return /* @__PURE__ */ jsx13(
|
|
1133
|
-
NamePrompt,
|
|
1134
|
-
{
|
|
1135
|
-
initialValue: name,
|
|
1136
|
-
onSubmit: handleConfirmName,
|
|
1137
|
-
onBack: goBack
|
|
1138
|
-
}
|
|
1139
|
-
);
|
|
1140
|
-
}
|
|
1141
1198
|
case "confirmTargets": {
|
|
1142
|
-
return /* @__PURE__ */
|
|
1199
|
+
return /* @__PURE__ */ jsx12(
|
|
1143
1200
|
TargetSelect,
|
|
1144
1201
|
{
|
|
1145
1202
|
availableTargets: selectedApp?.targets ?? [],
|
|
@@ -1150,7 +1207,7 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1150
1207
|
);
|
|
1151
1208
|
}
|
|
1152
1209
|
case "name": {
|
|
1153
|
-
return /* @__PURE__ */
|
|
1210
|
+
return /* @__PURE__ */ jsx12(
|
|
1154
1211
|
NamePrompt,
|
|
1155
1212
|
{
|
|
1156
1213
|
initialValue: name,
|
|
@@ -1160,7 +1217,7 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1160
1217
|
);
|
|
1161
1218
|
}
|
|
1162
1219
|
case "targets": {
|
|
1163
|
-
return /* @__PURE__ */
|
|
1220
|
+
return /* @__PURE__ */ jsx12(
|
|
1164
1221
|
TargetSelect,
|
|
1165
1222
|
{
|
|
1166
1223
|
availableTargets: selectedApp?.targets ?? [],
|
|
@@ -1169,27 +1226,18 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1169
1226
|
}
|
|
1170
1227
|
);
|
|
1171
1228
|
}
|
|
1172
|
-
case "
|
|
1173
|
-
return /* @__PURE__ */
|
|
1174
|
-
|
|
1175
|
-
{
|
|
1176
|
-
onSubmit: handlePorts,
|
|
1177
|
-
onBack: goBack
|
|
1178
|
-
}
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
case "dir": {
|
|
1182
|
-
return /* @__PURE__ */ jsx13(
|
|
1183
|
-
DirPrompt,
|
|
1229
|
+
case "settings": {
|
|
1230
|
+
return /* @__PURE__ */ jsx12(
|
|
1231
|
+
SettingsPrompt,
|
|
1184
1232
|
{
|
|
1185
1233
|
defaultDir: join2(process.cwd(), toKebabCase2(extensionId || name)),
|
|
1186
|
-
onSubmit:
|
|
1234
|
+
onSubmit: handleSettings,
|
|
1187
1235
|
onBack: goBack
|
|
1188
1236
|
}
|
|
1189
1237
|
);
|
|
1190
1238
|
}
|
|
1191
1239
|
case "confirm": {
|
|
1192
|
-
return /* @__PURE__ */
|
|
1240
|
+
return /* @__PURE__ */ jsx12(
|
|
1193
1241
|
Confirm,
|
|
1194
1242
|
{
|
|
1195
1243
|
name,
|
|
@@ -1197,6 +1245,7 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1197
1245
|
previewPort,
|
|
1198
1246
|
targets,
|
|
1199
1247
|
outputDir,
|
|
1248
|
+
extensionVersion: command === "scaffold" /* SCAFFOLD */ ? extensionVersion : void 0,
|
|
1200
1249
|
onConfirm: handleConfirm,
|
|
1201
1250
|
onCancel: handleCancel,
|
|
1202
1251
|
onBack: goBack
|
|
@@ -1204,30 +1253,31 @@ var App = ({ mode, initialName, options }) => {
|
|
|
1204
1253
|
);
|
|
1205
1254
|
}
|
|
1206
1255
|
case "scaffolding": {
|
|
1207
|
-
return /* @__PURE__ */
|
|
1256
|
+
return /* @__PURE__ */ jsx12(ScaffoldProgress, { steps: progressSteps });
|
|
1208
1257
|
}
|
|
1209
1258
|
case "done": {
|
|
1210
|
-
return /* @__PURE__ */
|
|
1259
|
+
return /* @__PURE__ */ jsx12(Done, { name, outputDir });
|
|
1211
1260
|
}
|
|
1212
1261
|
default: {
|
|
1213
|
-
return /* @__PURE__ */
|
|
1214
|
-
/* @__PURE__ */
|
|
1215
|
-
errorMessage && /* @__PURE__ */
|
|
1216
|
-
] });
|
|
1262
|
+
return /* @__PURE__ */ jsx12(StepShell, { title: "Scaffold failed", onBack: goBack, backFocused: false, children: /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", gap: 1, children: [
|
|
1263
|
+
/* @__PURE__ */ jsx12(Text12, { color: "red", bold: true, children: "An error occurred" }),
|
|
1264
|
+
errorMessage && /* @__PURE__ */ jsx12(Text12, { color: "red", children: errorMessage })
|
|
1265
|
+
] }) });
|
|
1217
1266
|
}
|
|
1218
1267
|
}
|
|
1219
1268
|
};
|
|
1220
1269
|
|
|
1221
1270
|
// src/index.tsx
|
|
1222
|
-
import { jsx as
|
|
1271
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1223
1272
|
program.name("stackable-extension").description("Stackable extension developer CLI");
|
|
1224
|
-
program.command("create").description("Create a new
|
|
1225
|
-
render(/* @__PURE__ */
|
|
1273
|
+
program.command("create" /* CREATE */).description("Create a new Extension project").argument("[name]", "Extension project name").option("--extension-port <port>", "Extension dev server port (default: 5173)").option("--preview-port <port>", "Preview host dev server port").option("--skip-install", "Skip package manager install").option("--skip-git", "Skip git initialization").action((name, options) => {
|
|
1274
|
+
render(/* @__PURE__ */ jsx13(App, { command: "create" /* CREATE */, initialName: name, options }));
|
|
1226
1275
|
});
|
|
1227
|
-
program.command("scaffold").description("Scaffold a local project from an existing
|
|
1228
|
-
render(/* @__PURE__ */
|
|
1276
|
+
program.command("scaffold" /* SCAFFOLD */).description("Scaffold a local project from an existing Extension").option("--extension-port <port>", "Extension dev server port (default: 5173)").option("--preview-port <port>", "Preview host dev server port").option("--skip-install", "Skip package manager install").option("--skip-git", "Skip git initialization").action((options) => {
|
|
1277
|
+
render(/* @__PURE__ */ jsx13(App, { command: "scaffold" /* SCAFFOLD */, options }));
|
|
1229
1278
|
});
|
|
1230
1279
|
if (process.argv[1]?.endsWith("create-extension")) {
|
|
1280
|
+
process.stderr.write("\u26A0\uFE0F DEPRECATED: `create-extension` is deprecated and will be removed in v2.0.0. Use `stackable-extension create` instead.\n");
|
|
1231
1281
|
program.parse(["node", "stackable-extension", "create", ...process.argv.slice(2).filter((arg) => arg !== "--")]);
|
|
1232
1282
|
} else {
|
|
1233
1283
|
program.parse(process.argv.filter((arg) => arg !== "--"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackable-labs/cli-app-extension",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"commander": "12.x",
|
|
17
17
|
"giget": "3.x",
|
|
18
18
|
"ink": "5.x",
|
|
19
|
-
"ink-select-input": "6.x",
|
|
20
19
|
"ink-spinner": "5.x",
|
|
21
20
|
"ink-text-input": "6.x",
|
|
22
21
|
"nypm": "0.4.x",
|