framer-export 4.3.8 → 4.4.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/dist/cli/index.js +1144 -419
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "framer-export",
|
|
18
|
-
version: "4.
|
|
18
|
+
version: "4.4.1",
|
|
19
19
|
description: "Export any Framer, Webflow, or Wix site into a fully working local mirror. Downloads all assets, strips badges, rewrites URLs, and pretty-prints JS.",
|
|
20
20
|
type: "module",
|
|
21
21
|
main: "dist/cli/index.js",
|
|
@@ -103,6 +103,16 @@ function chip(label) {
|
|
|
103
103
|
function bullet(label = "\u2022") {
|
|
104
104
|
return ui.primary(label);
|
|
105
105
|
}
|
|
106
|
+
function centerText(text, width) {
|
|
107
|
+
const visible = stripAnsi(text).length;
|
|
108
|
+
if (visible >= width) return text;
|
|
109
|
+
const left = Math.floor((width - visible) / 2);
|
|
110
|
+
return " ".repeat(left) + text + " ".repeat(width - visible - left);
|
|
111
|
+
}
|
|
112
|
+
function truncatePlain(text, max) {
|
|
113
|
+
if (text.length <= max) return text;
|
|
114
|
+
return text.slice(0, Math.max(0, max - 2)) + "..";
|
|
115
|
+
}
|
|
106
116
|
var THEME, ui;
|
|
107
117
|
var init_theme = __esm({
|
|
108
118
|
"src/cli/theme.ts"() {
|
|
@@ -150,8 +160,10 @@ function showBanner() {
|
|
|
150
160
|
const width = getWidth();
|
|
151
161
|
const isSmall = width < 65;
|
|
152
162
|
if (isSmall) {
|
|
153
|
-
console.log(
|
|
154
|
-
|
|
163
|
+
console.log(
|
|
164
|
+
`
|
|
165
|
+
${ui.primary.bold("f-export")} ${ui.muted(`v${package_default.version}`)} ${chip("beta ui")}`
|
|
166
|
+
);
|
|
155
167
|
console.log(` ${ui.text.bold("Framer Export")} ${ui.muted("for Framer, Webflow, and Wix")}
|
|
156
168
|
`);
|
|
157
169
|
return;
|
|
@@ -176,14 +188,679 @@ var init_banner = __esm({
|
|
|
176
188
|
init_package();
|
|
177
189
|
init_theme();
|
|
178
190
|
ASCII_ART = [
|
|
179
|
-
"
|
|
180
|
-
"\u2588\
|
|
181
|
-
"\u2588\
|
|
182
|
-
"\
|
|
191
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
|
|
192
|
+
"\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557",
|
|
193
|
+
"\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D",
|
|
194
|
+
"\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557",
|
|
195
|
+
"\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551",
|
|
196
|
+
"\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D",
|
|
197
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
198
|
+
"\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D",
|
|
199
|
+
"\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 ",
|
|
200
|
+
"\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 ",
|
|
201
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
|
|
202
|
+
"\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
|
|
183
203
|
];
|
|
184
204
|
}
|
|
185
205
|
});
|
|
186
206
|
|
|
207
|
+
// src/cli/cooking.ts
|
|
208
|
+
import chalk2 from "chalk";
|
|
209
|
+
async function showLoadingIntro(version) {
|
|
210
|
+
if (!process.stdout.isTTY || process.env.CI) return;
|
|
211
|
+
await new Promise((resolve) => {
|
|
212
|
+
let frame = 0;
|
|
213
|
+
const draw = () => {
|
|
214
|
+
const spinner = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
|
|
215
|
+
const title = renderShinyText(`Framer Export v${version}`, frame, {
|
|
216
|
+
baseColor: "#969696",
|
|
217
|
+
shineColor: "#ffffff",
|
|
218
|
+
shineWidth: 18
|
|
219
|
+
});
|
|
220
|
+
const dots = ".".repeat(frame % 4).padEnd(3, " ");
|
|
221
|
+
process.stdout.write(
|
|
222
|
+
`\r\x1B[2K ${ui.primary(spinner)} ${title} ${ui.muted(`Loading${dots}`)}`
|
|
223
|
+
);
|
|
224
|
+
frame++;
|
|
225
|
+
};
|
|
226
|
+
process.stdout.write("\x1B[?25l");
|
|
227
|
+
draw();
|
|
228
|
+
const interval = setInterval(draw, FRAME_INTERVAL);
|
|
229
|
+
setTimeout(() => {
|
|
230
|
+
clearInterval(interval);
|
|
231
|
+
process.stdout.write("\r\x1B[2K\x1B[?25h");
|
|
232
|
+
resolve();
|
|
233
|
+
}, INTRO_DURATION);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function renderShinyText(text, frame, options = {}) {
|
|
237
|
+
const baseColor = options.baseColor || "#808080";
|
|
238
|
+
const shineColor = options.shineColor || "#ffffff";
|
|
239
|
+
const shineWidth = options.shineWidth || SHINE_WIDTH;
|
|
240
|
+
const travel = text.length + shineWidth * 2;
|
|
241
|
+
const pos = frame * SHINE_SPEED % travel - shineWidth;
|
|
242
|
+
const base = hexToRgb(baseColor);
|
|
243
|
+
const shine = hexToRgb(shineColor);
|
|
244
|
+
let result = "";
|
|
245
|
+
for (let i = 0; i < text.length; i++) {
|
|
246
|
+
const dist = Math.abs(i - pos);
|
|
247
|
+
const intensity = smoothstep(0, 1, 1 - Math.min(1, dist / shineWidth));
|
|
248
|
+
const color = mixRgb(base, shine, intensity);
|
|
249
|
+
const paint = intensity > 0.82 ? chalk2.rgb(color.r, color.g, color.b).bold : chalk2.rgb(color.r, color.g, color.b);
|
|
250
|
+
result += paint(text[i]);
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
function smoothstep(edge0, edge1, value) {
|
|
255
|
+
const t = Math.max(0, Math.min(1, (value - edge0) / (edge1 - edge0)));
|
|
256
|
+
return t * t * (3 - 2 * t);
|
|
257
|
+
}
|
|
258
|
+
function hexToRgb(hex) {
|
|
259
|
+
const clean = hex.replace("#", "");
|
|
260
|
+
const normalized = clean.length === 3 ? clean.split("").map((char) => char + char).join("") : clean.padEnd(6, "0").slice(0, 6);
|
|
261
|
+
return {
|
|
262
|
+
r: Number.parseInt(normalized.slice(0, 2), 16),
|
|
263
|
+
g: Number.parseInt(normalized.slice(2, 4), 16),
|
|
264
|
+
b: Number.parseInt(normalized.slice(4, 6), 16)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function mixRgb(from, to, amount) {
|
|
268
|
+
return {
|
|
269
|
+
r: Math.round(from.r + (to.r - from.r) * amount),
|
|
270
|
+
g: Math.round(from.g + (to.g - from.g) * amount),
|
|
271
|
+
b: Math.round(from.b + (to.b - from.b) * amount)
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
var SHINE_WIDTH, FRAME_INTERVAL, INTRO_DURATION, SHINE_SPEED, SPINNER_FRAMES, CookingSpinner;
|
|
275
|
+
var init_cooking = __esm({
|
|
276
|
+
"src/cli/cooking.ts"() {
|
|
277
|
+
"use strict";
|
|
278
|
+
init_theme();
|
|
279
|
+
SHINE_WIDTH = 14;
|
|
280
|
+
FRAME_INTERVAL = 70;
|
|
281
|
+
INTRO_DURATION = 1400;
|
|
282
|
+
SHINE_SPEED = 0.55;
|
|
283
|
+
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
284
|
+
CookingSpinner = class {
|
|
285
|
+
interval = null;
|
|
286
|
+
frame = 0;
|
|
287
|
+
phase = "";
|
|
288
|
+
active = false;
|
|
289
|
+
start(phase = "") {
|
|
290
|
+
this.phase = phase;
|
|
291
|
+
this.frame = 0;
|
|
292
|
+
this.active = true;
|
|
293
|
+
this.draw();
|
|
294
|
+
this.interval = setInterval(() => {
|
|
295
|
+
this.frame++;
|
|
296
|
+
this.draw();
|
|
297
|
+
}, FRAME_INTERVAL);
|
|
298
|
+
}
|
|
299
|
+
update(phase) {
|
|
300
|
+
this.phase = phase;
|
|
301
|
+
}
|
|
302
|
+
log(message) {
|
|
303
|
+
if (this.active) {
|
|
304
|
+
process.stdout.write("\r\x1B[2K");
|
|
305
|
+
}
|
|
306
|
+
process.stdout.write(message + "\n");
|
|
307
|
+
if (this.active) {
|
|
308
|
+
this.draw();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
stop() {
|
|
312
|
+
this.active = false;
|
|
313
|
+
if (this.interval) {
|
|
314
|
+
clearInterval(this.interval);
|
|
315
|
+
this.interval = null;
|
|
316
|
+
}
|
|
317
|
+
process.stdout.write("\r\x1B[2K");
|
|
318
|
+
}
|
|
319
|
+
draw() {
|
|
320
|
+
if (!this.active) return;
|
|
321
|
+
const spinner = SPINNER_FRAMES[this.frame % SPINNER_FRAMES.length];
|
|
322
|
+
const shimmer = renderShinyText("Exporting", this.frame);
|
|
323
|
+
const frameStr = ui.primary(spinner);
|
|
324
|
+
const phaseStr = this.phase ? ` ${ui.muted(this.limitLen(this.phase, 52))}` : "";
|
|
325
|
+
process.stdout.write(`\r\x1B[2K ${frameStr} ${shimmer}${phaseStr}`);
|
|
326
|
+
}
|
|
327
|
+
limitLen(s, max) {
|
|
328
|
+
if (s.length <= max) return s;
|
|
329
|
+
return s.slice(0, max - 1) + "\u2026";
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// src/cli/box.ts
|
|
336
|
+
import chalk3 from "chalk";
|
|
337
|
+
function maxWidth() {
|
|
338
|
+
return Math.min(process.stdout.columns || 80, 76);
|
|
339
|
+
}
|
|
340
|
+
function padRight(text, w) {
|
|
341
|
+
const visible = stripAnsi(text).length;
|
|
342
|
+
if (visible >= w) return text;
|
|
343
|
+
return text + " ".repeat(w - visible);
|
|
344
|
+
}
|
|
345
|
+
function boxTop(w) {
|
|
346
|
+
const inner = w - 4;
|
|
347
|
+
return " " + ui.border("\u256D\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256E");
|
|
348
|
+
}
|
|
349
|
+
function panelTop(w) {
|
|
350
|
+
const inner = w - 4;
|
|
351
|
+
return ui.border("\u256D\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256E");
|
|
352
|
+
}
|
|
353
|
+
function boxBot(w) {
|
|
354
|
+
const inner = w - 4;
|
|
355
|
+
return " " + ui.border("\u2570\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256F");
|
|
356
|
+
}
|
|
357
|
+
function panelBot(w) {
|
|
358
|
+
const inner = w - 4;
|
|
359
|
+
return ui.border("\u2570\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256F");
|
|
360
|
+
}
|
|
361
|
+
function boxLine(w, text) {
|
|
362
|
+
const inner = w - 4;
|
|
363
|
+
const padded = padRight(text, inner);
|
|
364
|
+
return " " + ui.border("\u2502 ") + padded + ui.border(" \u2502");
|
|
365
|
+
}
|
|
366
|
+
function panelLine(w, text) {
|
|
367
|
+
const inner = w - 4;
|
|
368
|
+
const padded = padRight(text, inner);
|
|
369
|
+
return ui.border("\u2502 ") + padded + ui.border(" \u2502");
|
|
370
|
+
}
|
|
371
|
+
function boxSep(w) {
|
|
372
|
+
const inner = w - 4;
|
|
373
|
+
return " " + ui.border("\u251C\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u2524");
|
|
374
|
+
}
|
|
375
|
+
function panelSep(w) {
|
|
376
|
+
const inner = w - 4;
|
|
377
|
+
return ui.border("\u251C\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u2524");
|
|
378
|
+
}
|
|
379
|
+
function boxRow(w, label, value) {
|
|
380
|
+
const inner = w - 4;
|
|
381
|
+
const labelPlain = stripAnsi(chalk3.bold(label));
|
|
382
|
+
const visible = labelPlain.length + 1 + value.length;
|
|
383
|
+
if (visible > inner) {
|
|
384
|
+
const avail = inner - labelPlain.length - 2;
|
|
385
|
+
const truncated = value.length > avail ? value.slice(0, avail - 1) + ".." : value;
|
|
386
|
+
return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(truncated) + " ".repeat(Math.max(0, inner - labelPlain.length - 1 - truncated.length)) + ui.border(" \u2502");
|
|
387
|
+
}
|
|
388
|
+
const right = inner - labelPlain.length - 1 - value.length;
|
|
389
|
+
return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(value) + " ".repeat(right) + ui.border(" \u2502");
|
|
390
|
+
}
|
|
391
|
+
var init_box = __esm({
|
|
392
|
+
"src/cli/box.ts"() {
|
|
393
|
+
"use strict";
|
|
394
|
+
init_theme();
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// src/cli/select.ts
|
|
399
|
+
import readline from "readline";
|
|
400
|
+
import { stdin, stdout } from "process";
|
|
401
|
+
async function select(question, options, defaultIndex = 0, config = {}) {
|
|
402
|
+
const isTTY = stdin.isTTY && stdout.isTTY;
|
|
403
|
+
if (!isTTY) {
|
|
404
|
+
return fallbackPrompt(question, options, defaultIndex, config);
|
|
405
|
+
}
|
|
406
|
+
return arrowSelect(question, options, defaultIndex, config);
|
|
407
|
+
}
|
|
408
|
+
async function promptInput(question, defaultValue = "", config = {}) {
|
|
409
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
410
|
+
return fallbackInput(question, defaultValue);
|
|
411
|
+
}
|
|
412
|
+
return fullscreenInput(question, defaultValue, config);
|
|
413
|
+
}
|
|
414
|
+
async function arrowSelect(question, options, defaultIndex, config) {
|
|
415
|
+
const actions = config.actions ?? [];
|
|
416
|
+
const headerLines = config.headerLines ?? [];
|
|
417
|
+
const width = Math.max(44, Math.min(maxWidth(), 72));
|
|
418
|
+
const inner = width - 4;
|
|
419
|
+
const actionLineOffset = 2 + headerLines.length;
|
|
420
|
+
const hasActions = actions.length > 0;
|
|
421
|
+
const optionStartOffset = 3 + headerLines.length + (hasActions ? 1 : 0);
|
|
422
|
+
const lineCount = options.length + 4 + headerLines.length + (hasActions ? 1 : 0);
|
|
423
|
+
const rows = process.stdout.rows || 24;
|
|
424
|
+
const columns = process.stdout.columns || 80;
|
|
425
|
+
const panelTopRow = Math.max(2, Math.floor((rows - lineCount) / 2) + 1);
|
|
426
|
+
const panelLeftCol = Math.max(1, Math.floor((columns - width) / 2) + 1);
|
|
427
|
+
const footerRow = Math.min(rows, panelTopRow + lineCount + 1);
|
|
428
|
+
return new Promise((resolve) => {
|
|
429
|
+
const firstEnabled = options.findIndex((option) => !option.disabled);
|
|
430
|
+
let selected = options[defaultIndex]?.disabled ? firstEnabled : defaultIndex;
|
|
431
|
+
let selectedAction = null;
|
|
432
|
+
if (selected < 0) selected = 0;
|
|
433
|
+
const move = (direction) => {
|
|
434
|
+
let next = selected + direction;
|
|
435
|
+
while (next >= 0 && next < options.length) {
|
|
436
|
+
if (!options[next].disabled) {
|
|
437
|
+
selected = next;
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
next += direction;
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
const render = (initial = false) => {
|
|
444
|
+
if (!initial) {
|
|
445
|
+
stdout.write("\x1B[2J");
|
|
446
|
+
}
|
|
447
|
+
const lines = [];
|
|
448
|
+
lines.push(panelTop(width));
|
|
449
|
+
lines.push(
|
|
450
|
+
panelLine(width, centerText(`${ui.primary("\u25CF")} ${ui.text.bold(question)}`, inner))
|
|
451
|
+
);
|
|
452
|
+
for (const header of headerLines) {
|
|
453
|
+
lines.push(panelLine(width, centerText(ui.muted(header), inner)));
|
|
454
|
+
}
|
|
455
|
+
if (hasActions) {
|
|
456
|
+
lines.push(
|
|
457
|
+
panelLine(width, centerText(renderActions(actions, selectedAction, inner), inner))
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
lines.push(panelSep(width));
|
|
461
|
+
for (let i = 0; i < options.length; i++) {
|
|
462
|
+
lines.push(
|
|
463
|
+
panelLine(
|
|
464
|
+
width,
|
|
465
|
+
centerText(
|
|
466
|
+
renderOption(options[i], selectedAction === null && i === selected, inner),
|
|
467
|
+
inner
|
|
468
|
+
)
|
|
469
|
+
)
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
lines.push(panelBot(width));
|
|
473
|
+
lines.forEach((line, index) => writeAt(panelTopRow + index, panelLeftCol, line));
|
|
474
|
+
writeAt(
|
|
475
|
+
footerRow,
|
|
476
|
+
panelLeftCol,
|
|
477
|
+
centerText(
|
|
478
|
+
ui.muted(config.footer || "\u2191\u2193 move \xB7 enter select \xB7 mouse hover/click \xB7 esc close"),
|
|
479
|
+
width
|
|
480
|
+
)
|
|
481
|
+
);
|
|
482
|
+
};
|
|
483
|
+
const choose = (value = options[selected].value) => {
|
|
484
|
+
cleanup();
|
|
485
|
+
console.log(
|
|
486
|
+
` ${ui.success("\u2713")} ${ui.text.bold(question)} ${ui.primary(labelForValue(value, options, actions))}
|
|
487
|
+
`
|
|
488
|
+
);
|
|
489
|
+
resolve(value);
|
|
490
|
+
};
|
|
491
|
+
const onMouseData = (chunk) => {
|
|
492
|
+
const mouse = parseMouseEvent(chunk);
|
|
493
|
+
if (!mouse) return;
|
|
494
|
+
if (mouse.kind === "wheel-up") {
|
|
495
|
+
move(-1);
|
|
496
|
+
render();
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (mouse.kind === "wheel-down") {
|
|
500
|
+
move(1);
|
|
501
|
+
render();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (hasActions && mouse.y === panelTopRow + actionLineOffset) {
|
|
505
|
+
const actionIdx = actionIndexAtX(actions, mouse.x, panelLeftCol, inner);
|
|
506
|
+
if (actionIdx === null || actions[actionIdx].disabled) return;
|
|
507
|
+
if (selectedAction !== actionIdx) {
|
|
508
|
+
selectedAction = actionIdx;
|
|
509
|
+
render();
|
|
510
|
+
}
|
|
511
|
+
if (mouse.kind === "click") choose(actions[actionIdx].value);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const idx = mouse.y - panelTopRow - optionStartOffset;
|
|
515
|
+
if (idx < 0 || idx >= options.length || options[idx].disabled) return;
|
|
516
|
+
if (selected !== idx) {
|
|
517
|
+
selected = idx;
|
|
518
|
+
selectedAction = null;
|
|
519
|
+
render();
|
|
520
|
+
}
|
|
521
|
+
if (mouse.kind === "click") {
|
|
522
|
+
choose();
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
const cleanup = () => {
|
|
526
|
+
leaveInteractiveScreen();
|
|
527
|
+
stdin.setRawMode(false);
|
|
528
|
+
stdin.removeListener("keypress", onKeypress);
|
|
529
|
+
stdin.removeListener("data", onMouseData);
|
|
530
|
+
stdin.pause();
|
|
531
|
+
};
|
|
532
|
+
readline.emitKeypressEvents(stdin);
|
|
533
|
+
stdin.setRawMode(true);
|
|
534
|
+
enterInteractiveScreen(true);
|
|
535
|
+
render(true);
|
|
536
|
+
const onKeypress = (_str, key) => {
|
|
537
|
+
if (!key) return;
|
|
538
|
+
if (key.name === "up" && selected > 0) {
|
|
539
|
+
move(-1);
|
|
540
|
+
selectedAction = null;
|
|
541
|
+
render();
|
|
542
|
+
} else if (key.name === "down" && selected < options.length - 1) {
|
|
543
|
+
move(1);
|
|
544
|
+
selectedAction = null;
|
|
545
|
+
render();
|
|
546
|
+
} else if (key.name === "tab" && hasActions) {
|
|
547
|
+
selectedAction = selectedAction === null ? 0 : null;
|
|
548
|
+
render();
|
|
549
|
+
} else if (key.name === "return") {
|
|
550
|
+
choose(selectedAction === null ? options[selected].value : actions[selectedAction].value);
|
|
551
|
+
} else if (key.ctrl && key.name === "c" || key.name === "escape") {
|
|
552
|
+
cleanup();
|
|
553
|
+
process.exit(0);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
stdin.resume();
|
|
557
|
+
stdin.on("data", onMouseData);
|
|
558
|
+
stdin.on("keypress", onKeypress);
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
async function fallbackPrompt(question, options, defaultIndex, config) {
|
|
562
|
+
if (!stdin.isTTY) {
|
|
563
|
+
const firstEnabled = options.findIndex((option) => !option.disabled);
|
|
564
|
+
const enabledDefault = options[defaultIndex]?.disabled ? firstEnabled : defaultIndex;
|
|
565
|
+
const def = String(enabledDefault + 1);
|
|
566
|
+
printFallbackOptions(question, options, enabledDefault, config);
|
|
567
|
+
while (true) {
|
|
568
|
+
const trimmed = (await readPipedLine()).trim();
|
|
569
|
+
if (trimmed.toLowerCase() === "a") {
|
|
570
|
+
const action = config.actions?.find((item) => !item.disabled);
|
|
571
|
+
if (action) return action.value;
|
|
572
|
+
}
|
|
573
|
+
if (!trimmed) {
|
|
574
|
+
const label = stripAnsi(options[enabledDefault].label);
|
|
575
|
+
console.log(` ${ui.success("\u2713")} ${ui.primary(label)}
|
|
576
|
+
`);
|
|
577
|
+
return options[enabledDefault].value;
|
|
578
|
+
}
|
|
579
|
+
const idx = parseInt(trimmed, 10);
|
|
580
|
+
if (idx >= 1 && idx <= options.length && !options[idx - 1].disabled) {
|
|
581
|
+
const label = stripAnsi(options[idx - 1].label);
|
|
582
|
+
console.log(` ${ui.success("\u2713")} ${ui.primary(label)}
|
|
583
|
+
`);
|
|
584
|
+
return options[idx - 1].value;
|
|
585
|
+
}
|
|
586
|
+
console.log(` ${ui.error("\u2717")} ${ui.warning(`Enter 1-${options.length} or ${def}`)}
|
|
587
|
+
`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return new Promise((resolve) => {
|
|
591
|
+
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
592
|
+
const firstEnabled = options.findIndex((option) => !option.disabled);
|
|
593
|
+
const enabledDefault = options[defaultIndex]?.disabled ? firstEnabled : defaultIndex;
|
|
594
|
+
printFallbackOptions(question, options, enabledDefault, config);
|
|
595
|
+
const def = String(enabledDefault + 1);
|
|
596
|
+
const ask = () => {
|
|
597
|
+
rl.question(
|
|
598
|
+
` ${ui.primary(">")} ${ui.muted(`Choose [1-${options.length}] (${def})`)}: `,
|
|
599
|
+
(answer) => {
|
|
600
|
+
const trimmed = answer.trim();
|
|
601
|
+
if (trimmed.toLowerCase() === "a") {
|
|
602
|
+
const action = config.actions?.find((item) => !item.disabled);
|
|
603
|
+
if (action) {
|
|
604
|
+
rl.close();
|
|
605
|
+
resolve(action.value);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (!trimmed) {
|
|
610
|
+
rl.close();
|
|
611
|
+
const label = stripAnsi(options[enabledDefault].label);
|
|
612
|
+
console.log(` ${ui.success("\u2713")} ${ui.primary(label)}
|
|
613
|
+
`);
|
|
614
|
+
resolve(options[enabledDefault].value);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
const idx = parseInt(trimmed, 10);
|
|
618
|
+
if (idx >= 1 && idx <= options.length && !options[idx - 1].disabled) {
|
|
619
|
+
rl.close();
|
|
620
|
+
const label = stripAnsi(options[idx - 1].label);
|
|
621
|
+
console.log(` ${ui.success("\u2713")} ${ui.primary(label)}
|
|
622
|
+
`);
|
|
623
|
+
resolve(options[idx - 1].value);
|
|
624
|
+
} else if (idx >= 1 && idx <= options.length && options[idx - 1].disabled) {
|
|
625
|
+
console.log(` ${ui.error("\u2717")} ${ui.warning("Option unavailable for now")}
|
|
626
|
+
`);
|
|
627
|
+
ask();
|
|
628
|
+
} else {
|
|
629
|
+
console.log(` ${ui.error("\u2717")} ${ui.warning(`Enter 1-${options.length}`)}
|
|
630
|
+
`);
|
|
631
|
+
ask();
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
);
|
|
635
|
+
};
|
|
636
|
+
ask();
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
function fullscreenInput(question, defaultValue, config) {
|
|
640
|
+
const headerLines = config.headerLines ?? [];
|
|
641
|
+
const width = Math.max(44, Math.min(maxWidth(), 72));
|
|
642
|
+
const inner = width - 4;
|
|
643
|
+
const lineCount = 6 + headerLines.length;
|
|
644
|
+
const rows = process.stdout.rows || 24;
|
|
645
|
+
const columns = process.stdout.columns || 80;
|
|
646
|
+
const panelTopRow = Math.max(2, Math.floor((rows - lineCount) / 2) + 1);
|
|
647
|
+
const panelLeftCol = Math.max(1, Math.floor((columns - width) / 2) + 1);
|
|
648
|
+
const footerRow = Math.min(rows, panelTopRow + lineCount + 1);
|
|
649
|
+
return new Promise((resolve) => {
|
|
650
|
+
let value = defaultValue;
|
|
651
|
+
const render = () => {
|
|
652
|
+
stdout.write("\x1B[2J");
|
|
653
|
+
const shown = value || "";
|
|
654
|
+
const clipped = truncatePlain(shown, Math.max(12, inner - 10));
|
|
655
|
+
const input = `${ui.primary(">")} ${ui.text(clipped)}${ui.primary("\u258C")}`;
|
|
656
|
+
const lines = [];
|
|
657
|
+
lines.push(panelTop(width));
|
|
658
|
+
lines.push(
|
|
659
|
+
panelLine(width, centerText(`${ui.primary("\u25CF")} ${ui.text.bold(question)}`, inner))
|
|
660
|
+
);
|
|
661
|
+
for (const header of headerLines) {
|
|
662
|
+
lines.push(panelLine(width, centerText(ui.muted(header), inner)));
|
|
663
|
+
}
|
|
664
|
+
lines.push(panelSep(width));
|
|
665
|
+
lines.push(panelLine(width, centerText(input, inner)));
|
|
666
|
+
lines.push(panelBot(width));
|
|
667
|
+
lines.forEach((line, index) => writeAt(panelTopRow + index, panelLeftCol, line));
|
|
668
|
+
writeAt(
|
|
669
|
+
footerRow,
|
|
670
|
+
panelLeftCol,
|
|
671
|
+
centerText(ui.muted(config.footer || "type value \xB7 enter confirm \xB7 esc close"), width)
|
|
672
|
+
);
|
|
673
|
+
};
|
|
674
|
+
const cleanup = () => {
|
|
675
|
+
leaveInteractiveScreen();
|
|
676
|
+
stdin.setRawMode(false);
|
|
677
|
+
stdin.removeListener("keypress", onKeypress);
|
|
678
|
+
stdin.pause();
|
|
679
|
+
};
|
|
680
|
+
const submit = () => {
|
|
681
|
+
const output2 = cleanInputValue(value.trim() || defaultValue);
|
|
682
|
+
cleanup();
|
|
683
|
+
resolve(output2);
|
|
684
|
+
};
|
|
685
|
+
const onKeypress = (str, key) => {
|
|
686
|
+
if (key.ctrl && key.name === "c" || key.name === "escape") {
|
|
687
|
+
cleanup();
|
|
688
|
+
process.exit(0);
|
|
689
|
+
}
|
|
690
|
+
if (key.name === "return") {
|
|
691
|
+
submit();
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (key.name === "backspace") {
|
|
695
|
+
value = value.slice(0, -1);
|
|
696
|
+
render();
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (key.name === "delete") {
|
|
700
|
+
value = "";
|
|
701
|
+
render();
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (str && !key.ctrl && !key.meta && str >= " " && !isTerminalSequence(str, key)) {
|
|
705
|
+
value += cleanInputValue(str.replace(/[\r\n]/g, ""));
|
|
706
|
+
render();
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
readline.emitKeypressEvents(stdin);
|
|
710
|
+
stdin.setRawMode(true);
|
|
711
|
+
enterInteractiveScreen(false);
|
|
712
|
+
render();
|
|
713
|
+
stdin.resume();
|
|
714
|
+
stdin.on("keypress", onKeypress);
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
async function fallbackInput(question, defaultValue) {
|
|
718
|
+
if (!stdin.isTTY) {
|
|
719
|
+
const suffix = defaultValue ? ui.muted(` (${defaultValue})`) : "";
|
|
720
|
+
console.log(` ${ui.primary(">")} ${ui.text.bold(question)}${suffix}: `);
|
|
721
|
+
const answer = await readPipedLine();
|
|
722
|
+
return cleanInputValue(answer.trim() || defaultValue);
|
|
723
|
+
}
|
|
724
|
+
return new Promise((resolve) => {
|
|
725
|
+
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
726
|
+
const suffix = defaultValue ? ui.muted(` (${defaultValue})`) : "";
|
|
727
|
+
rl.question(` ${ui.primary(">")} ${ui.text.bold(question)}${suffix}: `, (answer) => {
|
|
728
|
+
rl.close();
|
|
729
|
+
resolve(cleanInputValue(answer.trim() || defaultValue));
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
function printFallbackOptions(question, options, enabledDefault, config) {
|
|
734
|
+
console.log(` ${ui.primary("\u25CF")} ${ui.text.bold(question)}
|
|
735
|
+
`);
|
|
736
|
+
for (const header of config.headerLines ?? []) {
|
|
737
|
+
console.log(` ${ui.muted(header)}`);
|
|
738
|
+
}
|
|
739
|
+
for (const action of config.actions ?? []) {
|
|
740
|
+
console.log(
|
|
741
|
+
` ${ui.muted("[A]")} ${action.disabled ? ui.muted(action.label) : ui.text(action.label)}`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
if ((config.headerLines?.length || 0) > 0 || (config.actions?.length || 0) > 0) {
|
|
745
|
+
console.log("");
|
|
746
|
+
}
|
|
747
|
+
for (let i = 0; i < options.length; i++) {
|
|
748
|
+
const marker = i === enabledDefault ? ui.success(" \u25C6") : " ";
|
|
749
|
+
const label = options[i].disabled ? ui.muted(stripAnsi(options[i].label)) : ui.text(options[i].label);
|
|
750
|
+
console.log(` ${ui.muted(`[${i + 1}]`)}${marker} ${label}`);
|
|
751
|
+
}
|
|
752
|
+
console.log("");
|
|
753
|
+
}
|
|
754
|
+
function readPipedLine() {
|
|
755
|
+
if (!pipedLinesPromise) {
|
|
756
|
+
pipedLinesPromise = new Promise((resolve) => {
|
|
757
|
+
let data = "";
|
|
758
|
+
stdin.setEncoding("utf8");
|
|
759
|
+
stdin.on("data", (chunk) => {
|
|
760
|
+
data += chunk;
|
|
761
|
+
});
|
|
762
|
+
stdin.on("end", () => {
|
|
763
|
+
resolve(data.split(/\r?\n/));
|
|
764
|
+
});
|
|
765
|
+
stdin.on("error", () => {
|
|
766
|
+
resolve([]);
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
return pipedLinesPromise.then((lines) => lines[pipedLineIndex++] ?? "");
|
|
771
|
+
}
|
|
772
|
+
function renderActions(actions, active, width) {
|
|
773
|
+
return actions.map((action, index) => {
|
|
774
|
+
const label = truncatePlain(
|
|
775
|
+
stripAnsi(action.label),
|
|
776
|
+
Math.max(8, Math.floor(width / actions.length) - 8)
|
|
777
|
+
);
|
|
778
|
+
if (action.disabled) return `${ui.border("[")} ${ui.muted(label)} ${ui.border("]")}`;
|
|
779
|
+
if (active === index)
|
|
780
|
+
return `${ui.primary("\u203A")} ${ui.primary("[")} ${ui.text.bold(label)} ${ui.primary("]")} ${ui.primary("\u2039")}`;
|
|
781
|
+
return `${ui.border("[")} ${ui.primary(label)} ${ui.border("]")}`;
|
|
782
|
+
}).join(` ${ui.muted("\xB7")} `);
|
|
783
|
+
}
|
|
784
|
+
function actionIndexAtX(actions, mouseX, panelLeftCol, inner) {
|
|
785
|
+
if (actions.length === 0) return null;
|
|
786
|
+
const contentStart = panelLeftCol + 2;
|
|
787
|
+
const relative = mouseX - contentStart;
|
|
788
|
+
if (relative < 0 || relative > inner) return null;
|
|
789
|
+
return Math.min(actions.length - 1, Math.floor(relative / Math.max(1, inner) * actions.length));
|
|
790
|
+
}
|
|
791
|
+
function labelForValue(value, options, actions) {
|
|
792
|
+
return stripAnsi(
|
|
793
|
+
options.find((option) => option.value === value)?.label || actions.find((action) => action.value === value)?.label || value
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
function renderOption(option, selected, width) {
|
|
797
|
+
const plain = truncatePlain(stripAnsi(option.label), Math.max(10, width - 12));
|
|
798
|
+
if (option.disabled) {
|
|
799
|
+
return `${ui.border("[")} ${ui.muted(plain)} ${ui.border("]")}`;
|
|
800
|
+
}
|
|
801
|
+
if (selected) {
|
|
802
|
+
return `${ui.primary("\u203A")} ${ui.primary("[")} ${ui.text.bold(plain)} ${ui.primary("]")} ${ui.primary("\u2039")}`;
|
|
803
|
+
}
|
|
804
|
+
return `${ui.muted(" ")} ${ui.border("[")} ${ui.muted(plain)} ${ui.border("]")} ${ui.muted(" ")}`;
|
|
805
|
+
}
|
|
806
|
+
function enterInteractiveScreen(enableMouse) {
|
|
807
|
+
stdout.write("\x1B[?1049h\x1B[2J\x1B[H\x1B[?25l");
|
|
808
|
+
if (enableMouse) {
|
|
809
|
+
stdout.write("\x1B[?1006h\x1B[?1000h\x1B[?1002h\x1B[?1003h");
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function leaveInteractiveScreen() {
|
|
813
|
+
stdout.write(
|
|
814
|
+
"\x1B[?1003l\x1B[?1002l\x1B[?1000l\x1B[?1006l\x1B[?25h\x1B[?1049l"
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
function parseMouseEvent(chunk) {
|
|
818
|
+
const text = chunk.toString("utf-8");
|
|
819
|
+
const match = text.match(/\x1B\[<(\d+);(\d+);(\d+)([mM])/);
|
|
820
|
+
if (!match) return parseLegacyMouseEvent(text);
|
|
821
|
+
const code = Number(match[1]);
|
|
822
|
+
const x = Number(match[2]);
|
|
823
|
+
const y = Number(match[3]);
|
|
824
|
+
const state = match[4];
|
|
825
|
+
if (code === 64) return { kind: "wheel-up", x, y };
|
|
826
|
+
if (code === 65) return { kind: "wheel-down", x, y };
|
|
827
|
+
if (state === "m") return { kind: "click", x, y };
|
|
828
|
+
if ((code & 32) === 32 || code === 35) return { kind: "hover", x, y };
|
|
829
|
+
if ((code & 3) === 0) return { kind: "hover", x, y };
|
|
830
|
+
return null;
|
|
831
|
+
}
|
|
832
|
+
function parseLegacyMouseEvent(text) {
|
|
833
|
+
const match = text.match(/\x1B\[M([\s\S])([\s\S])([\s\S])/);
|
|
834
|
+
if (!match) return null;
|
|
835
|
+
const code = match[1].charCodeAt(0) - 32;
|
|
836
|
+
const x = match[2].charCodeAt(0) - 32;
|
|
837
|
+
const y = match[3].charCodeAt(0) - 32;
|
|
838
|
+
if (code === 64) return { kind: "wheel-up", x, y };
|
|
839
|
+
if (code === 65) return { kind: "wheel-down", x, y };
|
|
840
|
+
if ((code & 3) === 3) return { kind: "click", x, y };
|
|
841
|
+
if ((code & 32) === 32) return { kind: "hover", x, y };
|
|
842
|
+
return { kind: "hover", x, y };
|
|
843
|
+
}
|
|
844
|
+
function writeAt(row, col, text) {
|
|
845
|
+
stdout.write(`\x1B[${row};${col}H${text}`);
|
|
846
|
+
}
|
|
847
|
+
function isTerminalSequence(str, key) {
|
|
848
|
+
return str.includes("\x1B") || !!key.sequence?.includes("\x1B") || /^(?:\d+;){2}\d+[mM]$/.test(str);
|
|
849
|
+
}
|
|
850
|
+
function cleanInputValue(value) {
|
|
851
|
+
return value.replace(/\x1B\[<\d+;\d+;\d+[mM]/g, "").replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "").replace(/(?:\d+;){2}\d+[mM]/g, "").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
|
852
|
+
}
|
|
853
|
+
var pipedLinesPromise, pipedLineIndex;
|
|
854
|
+
var init_select = __esm({
|
|
855
|
+
"src/cli/select.ts"() {
|
|
856
|
+
"use strict";
|
|
857
|
+
init_box();
|
|
858
|
+
init_theme();
|
|
859
|
+
pipedLinesPromise = null;
|
|
860
|
+
pipedLineIndex = 0;
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
|
|
187
864
|
// src/assets/asset-map.ts
|
|
188
865
|
import path from "path";
|
|
189
866
|
import crypto from "crypto";
|
|
@@ -320,7 +997,7 @@ var init_download = __esm({
|
|
|
320
997
|
});
|
|
321
998
|
|
|
322
999
|
// src/logger/index.ts
|
|
323
|
-
import
|
|
1000
|
+
import chalk4 from "chalk";
|
|
324
1001
|
function setCooking(spinner) {
|
|
325
1002
|
_cooking = spinner;
|
|
326
1003
|
}
|
|
@@ -340,47 +1017,53 @@ var init_logger = __esm({
|
|
|
340
1017
|
_cooking = null;
|
|
341
1018
|
T = () => (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
|
|
342
1019
|
LOG_PALETTE = [
|
|
343
|
-
(s) =>
|
|
344
|
-
(s) =>
|
|
345
|
-
(s) =>
|
|
346
|
-
(s) =>
|
|
347
|
-
(s) =>
|
|
348
|
-
(s) =>
|
|
1020
|
+
(s) => chalk4.hex(THEME.primary)(s),
|
|
1021
|
+
(s) => chalk4.hex(THEME.primarySoft)(s),
|
|
1022
|
+
(s) => chalk4.hex(THEME.secondary)(s),
|
|
1023
|
+
(s) => chalk4.hex(THEME.accent)(s),
|
|
1024
|
+
(s) => chalk4.hex(THEME.info)(s),
|
|
1025
|
+
(s) => chalk4.hex(THEME.text)(s)
|
|
349
1026
|
];
|
|
350
1027
|
_li = 0;
|
|
351
1028
|
log = (m) => {
|
|
352
1029
|
_li++;
|
|
353
1030
|
const c = LOG_PALETTE[_li % LOG_PALETTE.length];
|
|
354
|
-
output(
|
|
1031
|
+
output(
|
|
1032
|
+
`${chalk4.hex(THEME.muted)(`[${T()}]`)} ${chalk4.hex(THEME.primary)("[log]")} ${c(trunc(m, 120))}`
|
|
1033
|
+
);
|
|
355
1034
|
};
|
|
356
1035
|
INFO_PALETTE = [
|
|
357
|
-
(s) =>
|
|
358
|
-
(s) =>
|
|
359
|
-
(s) =>
|
|
1036
|
+
(s) => chalk4.hex(THEME.info)(s),
|
|
1037
|
+
(s) => chalk4.hex(THEME.secondary)(s),
|
|
1038
|
+
(s) => chalk4.hex(THEME.primarySoft)(s)
|
|
360
1039
|
];
|
|
361
1040
|
_ii = 0;
|
|
362
1041
|
info = (m) => {
|
|
363
1042
|
_ii++;
|
|
364
1043
|
const c = INFO_PALETTE[_ii % INFO_PALETTE.length];
|
|
365
|
-
output(
|
|
1044
|
+
output(
|
|
1045
|
+
`${chalk4.hex(THEME.muted)(`[${T()}]`)} ${chalk4.hex(THEME.info).bold("[info]")} ${c(trunc(m, 120))}`
|
|
1046
|
+
);
|
|
366
1047
|
};
|
|
367
1048
|
warn = (m) => {
|
|
368
1049
|
const colors = [
|
|
369
|
-
(s) =>
|
|
370
|
-
(s) =>
|
|
1050
|
+
(s) => chalk4.hex(THEME.warning)(s),
|
|
1051
|
+
(s) => chalk4.hex(THEME.primary)(s)
|
|
371
1052
|
];
|
|
372
1053
|
const c = colors[Math.floor(Math.random() * colors.length)];
|
|
373
|
-
const line = `${
|
|
1054
|
+
const line = `${chalk4.hex(THEME.muted)(`[${T()}]`)} ${chalk4.hex(THEME.warning).bold("[warn]")} ${c(trunc(m, 120))}`;
|
|
374
1055
|
if (_cooking) _cooking.log(line);
|
|
375
1056
|
else console.warn(line);
|
|
376
1057
|
};
|
|
377
1058
|
success = (m) => {
|
|
378
1059
|
const colors = [
|
|
379
|
-
(s) =>
|
|
380
|
-
(s) =>
|
|
1060
|
+
(s) => chalk4.hex(THEME.success)(s),
|
|
1061
|
+
(s) => chalk4.hex(THEME.info)(s)
|
|
381
1062
|
];
|
|
382
1063
|
const c = colors[Math.floor(Math.random() * colors.length)];
|
|
383
|
-
output(
|
|
1064
|
+
output(
|
|
1065
|
+
`${chalk4.hex(THEME.muted)(`[${T()}]`)} ${chalk4.hex(THEME.success)("[ok]")} ${c(trunc(m, 120))}`
|
|
1066
|
+
);
|
|
384
1067
|
};
|
|
385
1068
|
}
|
|
386
1069
|
});
|
|
@@ -987,11 +1670,64 @@ const server = http.createServer((req, res) => {
|
|
|
987
1670
|
serveFile(filePath);
|
|
988
1671
|
});
|
|
989
1672
|
|
|
1673
|
+
const ANSI = {
|
|
1674
|
+
reset: '\\x1b[0m',
|
|
1675
|
+
primary: '\\x1b[38;2;250;178;131m',
|
|
1676
|
+
soft: '\\x1b[38;2;255;192;159m',
|
|
1677
|
+
text: '\\x1b[38;2;238;238;238m',
|
|
1678
|
+
muted: '\\x1b[38;2;128;128;128m',
|
|
1679
|
+
border: '\\x1b[38;2;72;72;72m',
|
|
1680
|
+
success: '\\x1b[38;2;127;216;143m',
|
|
1681
|
+
info: '\\x1b[38;2;86;182;194m',
|
|
1682
|
+
};
|
|
1683
|
+
|
|
1684
|
+
function color(name, value) {
|
|
1685
|
+
return ANSI[name] + value + ANSI.reset;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
function frameLine(label, value) {
|
|
1689
|
+
const text = ' ' + label.padEnd(10) + value;
|
|
1690
|
+
const pad = Math.max(0, 56 - text.length);
|
|
1691
|
+
console.log(' ' + color('border', '\u2502 ') + color('muted', label.padEnd(10)) + color('text', value) + ' '.repeat(pad) + color('border', ' \u2502'));
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
function drawServerUi() {
|
|
1695
|
+
const logo = [
|
|
1696
|
+
'\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ',
|
|
1697
|
+
'\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557',
|
|
1698
|
+
'\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D',
|
|
1699
|
+
'\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557',
|
|
1700
|
+
'\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D',
|
|
1701
|
+
'\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 ',
|
|
1702
|
+
];
|
|
1703
|
+
|
|
1704
|
+
console.log('');
|
|
1705
|
+
for (const line of logo) console.log(' ' + color('primary', line));
|
|
1706
|
+
console.log('');
|
|
1707
|
+
console.log(' ' + color('border', '\u256D\u2500' + '\u2500'.repeat(56) + '\u2500\u256E'));
|
|
1708
|
+
console.log(' ' + color('border', '\u2502 ') + color('text', ' Framer Export local server') + ' '.repeat(28) + color('border', ' \u2502'));
|
|
1709
|
+
console.log(' ' + color('border', '\u251C\u2500' + '\u2500'.repeat(56) + '\u2500\u2524'));
|
|
1710
|
+
frameLine('URL', 'http://localhost:' + PORT);
|
|
1711
|
+
frameLine('Root', ROOT);
|
|
1712
|
+
frameLine('Mode', 'static mirror + SPA fallback');
|
|
1713
|
+
console.log(' ' + color('border', '\u2570\u2500' + '\u2500'.repeat(56) + '\u2500\u256F'));
|
|
1714
|
+
console.log('');
|
|
1715
|
+
console.log(' ' + color('success', 'ready') + color('muted', ' press Ctrl+C to stop') + '\\n');
|
|
1716
|
+
}
|
|
1717
|
+
|
|
990
1718
|
server.listen(PORT, () => {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1719
|
+
const frames = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];
|
|
1720
|
+
let i = 0;
|
|
1721
|
+
const timer = setInterval(() => {
|
|
1722
|
+
process.stdout.write('\\r\\x1B[2K ' + color('primary', frames[i % frames.length]) + ' ' + color('muted', 'starting Framer Export server'));
|
|
1723
|
+
i++;
|
|
1724
|
+
}, 80);
|
|
1725
|
+
|
|
1726
|
+
setTimeout(() => {
|
|
1727
|
+
clearInterval(timer);
|
|
1728
|
+
process.stdout.write('\\r\\x1B[2K');
|
|
1729
|
+
drawServerUi();
|
|
1730
|
+
}, 720);
|
|
995
1731
|
});
|
|
996
1732
|
`;
|
|
997
1733
|
}
|
|
@@ -1065,15 +1801,21 @@ function processSEO(html, url) {
|
|
|
1065
1801
|
</head>`);
|
|
1066
1802
|
}
|
|
1067
1803
|
if (!html.includes('name="description"')) {
|
|
1068
|
-
html = html.replace(
|
|
1069
|
-
|
|
1804
|
+
html = html.replace(
|
|
1805
|
+
"</head>",
|
|
1806
|
+
` <meta name="description" content="Exported with Framer Export - Fast, SEO-optimized, and clean.">
|
|
1807
|
+
</head>`
|
|
1808
|
+
);
|
|
1070
1809
|
}
|
|
1071
1810
|
if (!html.includes('property="og:')) {
|
|
1072
|
-
html = html.replace(
|
|
1811
|
+
html = html.replace(
|
|
1812
|
+
"</head>",
|
|
1813
|
+
` <meta property="og:type" content="website">
|
|
1073
1814
|
<meta property="og:url" content="${canonical}">
|
|
1074
1815
|
<meta property="og:title" content="Exported Site">
|
|
1075
1816
|
<meta property="og:description" content="A fast, clean version of this site, exported for performance.">
|
|
1076
|
-
</head>`
|
|
1817
|
+
</head>`
|
|
1818
|
+
);
|
|
1077
1819
|
}
|
|
1078
1820
|
if (!html.includes('name="robots"')) {
|
|
1079
1821
|
html = html.replace("</head>", ` <meta name="robots" content="index, follow">
|
|
@@ -1233,52 +1975,6 @@ var init_output = __esm({
|
|
|
1233
1975
|
}
|
|
1234
1976
|
});
|
|
1235
1977
|
|
|
1236
|
-
// src/cli/box.ts
|
|
1237
|
-
import chalk3 from "chalk";
|
|
1238
|
-
function maxWidth() {
|
|
1239
|
-
return Math.min(process.stdout.columns || 80, 62);
|
|
1240
|
-
}
|
|
1241
|
-
function padRight(text, w) {
|
|
1242
|
-
const visible = stripAnsi(text).length;
|
|
1243
|
-
if (visible >= w) return text;
|
|
1244
|
-
return text + " ".repeat(w - visible);
|
|
1245
|
-
}
|
|
1246
|
-
function boxTop(w) {
|
|
1247
|
-
const inner = w - 4;
|
|
1248
|
-
return " " + ui.border("\u256D\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256E");
|
|
1249
|
-
}
|
|
1250
|
-
function boxBot(w) {
|
|
1251
|
-
const inner = w - 4;
|
|
1252
|
-
return " " + ui.border("\u2570\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256F");
|
|
1253
|
-
}
|
|
1254
|
-
function boxLine(w, text) {
|
|
1255
|
-
const inner = w - 4;
|
|
1256
|
-
const padded = padRight(text, inner);
|
|
1257
|
-
return " " + ui.border("\u2502 ") + padded + ui.border(" \u2502");
|
|
1258
|
-
}
|
|
1259
|
-
function boxSep(w) {
|
|
1260
|
-
const inner = w - 4;
|
|
1261
|
-
return " " + ui.border("\u251C\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u2524");
|
|
1262
|
-
}
|
|
1263
|
-
function boxRow(w, label, value) {
|
|
1264
|
-
const inner = w - 4;
|
|
1265
|
-
const labelPlain = stripAnsi(chalk3.bold(label));
|
|
1266
|
-
const visible = labelPlain.length + 1 + value.length;
|
|
1267
|
-
if (visible > inner) {
|
|
1268
|
-
const avail = inner - labelPlain.length - 2;
|
|
1269
|
-
const truncated = value.length > avail ? value.slice(0, avail - 1) + ".." : value;
|
|
1270
|
-
return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(truncated) + " ".repeat(Math.max(0, inner - labelPlain.length - 1 - truncated.length)) + ui.border(" \u2502");
|
|
1271
|
-
}
|
|
1272
|
-
const right = inner - labelPlain.length - 1 - value.length;
|
|
1273
|
-
return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(value) + " ".repeat(right) + ui.border(" \u2502");
|
|
1274
|
-
}
|
|
1275
|
-
var init_box = __esm({
|
|
1276
|
-
"src/cli/box.ts"() {
|
|
1277
|
-
"use strict";
|
|
1278
|
-
init_theme();
|
|
1279
|
-
}
|
|
1280
|
-
});
|
|
1281
|
-
|
|
1282
1978
|
// src/exporter/summary.ts
|
|
1283
1979
|
import fs3 from "fs/promises";
|
|
1284
1980
|
import path4 from "path";
|
|
@@ -1334,171 +2030,44 @@ async function printSummary(exporter) {
|
|
|
1334
2030
|
if (cnt === 0) continue;
|
|
1335
2031
|
const inner = w - 4;
|
|
1336
2032
|
const l = label.padEnd(16);
|
|
1337
|
-
const c = String(cnt).padStart(3);
|
|
1338
|
-
const rowText = `${color(l)}${ui.text(c)} ${ui.muted(type)}`;
|
|
1339
|
-
const visible = 16 + 3 + 2 + type.length;
|
|
1340
|
-
const pad = Math.max(0, inner - visible);
|
|
1341
|
-
if (isSmall) {
|
|
1342
|
-
console.log(` ${color(label)} ${ui.text(String(cnt))} ${ui.muted(type)}`);
|
|
1343
|
-
} else {
|
|
1344
|
-
console.log(" " + ui.border("\u2502 ") + rowText + " ".repeat(pad) + ui.border(" \u2502"));
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
if (!isSmall) {
|
|
1348
|
-
console.log(boxBot(w));
|
|
1349
|
-
}
|
|
1350
|
-
console.log("");
|
|
1351
|
-
const cdCmd = "cd " + path4.basename(exporter.outDir) + " && node serve.cjs";
|
|
1352
|
-
if (!isSmall) {
|
|
1353
|
-
console.log(boxTop(w));
|
|
1354
|
-
console.log(boxLine(w, ui.text.bold(" To serve locally")));
|
|
1355
|
-
console.log(boxSep(w));
|
|
1356
|
-
const inner = w - 6;
|
|
1357
|
-
const cmdLen = cdCmd.length;
|
|
1358
|
-
const pad = Math.max(0, inner - cmdLen);
|
|
1359
|
-
console.log(" " + ui.border("\u2502 ") + G(cdCmd) + " ".repeat(pad) + ui.muted(" copy") + ui.border(" \u2502"));
|
|
1360
|
-
console.log(boxBot(w));
|
|
1361
|
-
} else {
|
|
1362
|
-
console.log(ui.text.bold(" To serve locally:"));
|
|
1363
|
-
console.log(` ${G(cdCmd)}`);
|
|
1364
|
-
}
|
|
1365
|
-
console.log("");
|
|
1366
|
-
console.log(ui.muted(" note: must be served via HTTP for JS modules to work."));
|
|
1367
|
-
console.log("");
|
|
1368
|
-
}
|
|
1369
|
-
var init_summary = __esm({
|
|
1370
|
-
"src/exporter/summary.ts"() {
|
|
1371
|
-
"use strict";
|
|
1372
|
-
init_box();
|
|
1373
|
-
init_theme();
|
|
1374
|
-
}
|
|
1375
|
-
});
|
|
1376
|
-
|
|
1377
|
-
// src/cli/select.ts
|
|
1378
|
-
import readline from "readline";
|
|
1379
|
-
import { stdin, stdout } from "process";
|
|
1380
|
-
async function select(question, options, defaultIndex = 0) {
|
|
1381
|
-
const isTTY = stdin.isTTY && stdout.isTTY;
|
|
1382
|
-
if (!isTTY) {
|
|
1383
|
-
return fallbackPrompt(question, options, defaultIndex);
|
|
1384
|
-
}
|
|
1385
|
-
return arrowSelect(question, options, defaultIndex);
|
|
1386
|
-
}
|
|
1387
|
-
async function arrowSelect(question, options, defaultIndex) {
|
|
1388
|
-
return new Promise((resolve) => {
|
|
1389
|
-
const firstEnabled = options.findIndex((option) => !option.disabled);
|
|
1390
|
-
let selected = options[defaultIndex]?.disabled ? firstEnabled : defaultIndex;
|
|
1391
|
-
if (selected < 0) selected = 0;
|
|
1392
|
-
const move = (direction) => {
|
|
1393
|
-
let next = selected + direction;
|
|
1394
|
-
while (next >= 0 && next < options.length) {
|
|
1395
|
-
if (!options[next].disabled) {
|
|
1396
|
-
selected = next;
|
|
1397
|
-
return;
|
|
1398
|
-
}
|
|
1399
|
-
next += direction;
|
|
1400
|
-
}
|
|
1401
|
-
};
|
|
1402
|
-
const render = (initial = false) => {
|
|
1403
|
-
if (!initial) {
|
|
1404
|
-
stdout.write(`\x1B[${options.length + 1}A`);
|
|
1405
|
-
stdout.write("\x1B[J");
|
|
1406
|
-
}
|
|
1407
|
-
console.log(` ${ui.primary("\u25CF")} ${ui.text.bold(question)}`);
|
|
1408
|
-
for (let i = 0; i < options.length; i++) {
|
|
1409
|
-
if (options[i].disabled) {
|
|
1410
|
-
console.log(` ${ui.muted(stripAnsi(options[i].label))}`);
|
|
1411
|
-
} else if (i === selected) {
|
|
1412
|
-
console.log(` ${ui.primary(">")} ${ui.text.bold(options[i].label)}`);
|
|
1413
|
-
} else {
|
|
1414
|
-
console.log(` ${ui.muted(options[i].label)}`);
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
};
|
|
1418
|
-
render(true);
|
|
1419
|
-
readline.emitKeypressEvents(stdin);
|
|
1420
|
-
stdin.setRawMode(true);
|
|
1421
|
-
const onKeypress = (_str, key) => {
|
|
1422
|
-
if (!key) return;
|
|
1423
|
-
if (key.name === "up" && selected > 0) {
|
|
1424
|
-
move(-1);
|
|
1425
|
-
render();
|
|
1426
|
-
} else if (key.name === "down" && selected < options.length - 1) {
|
|
1427
|
-
move(1);
|
|
1428
|
-
render();
|
|
1429
|
-
} else if (key.name === "return") {
|
|
1430
|
-
stdin.setRawMode(false);
|
|
1431
|
-
stdin.removeListener("keypress", onKeypress);
|
|
1432
|
-
stdin.pause();
|
|
1433
|
-
stdout.write(`\x1B[${options.length + 1}A`);
|
|
1434
|
-
stdout.write("\x1B[J");
|
|
1435
|
-
console.log(
|
|
1436
|
-
` ${ui.success("\u2713")} ${ui.text.bold(question)} ${ui.primary(stripAnsi(options[selected].label))}
|
|
1437
|
-
`
|
|
1438
|
-
);
|
|
1439
|
-
resolve(options[selected].value);
|
|
1440
|
-
} else if (key.ctrl && key.name === "c" || key.name === "escape") {
|
|
1441
|
-
stdin.setRawMode(false);
|
|
1442
|
-
stdin.removeListener("keypress", onKeypress);
|
|
1443
|
-
process.exit(0);
|
|
1444
|
-
}
|
|
1445
|
-
};
|
|
1446
|
-
stdin.resume();
|
|
1447
|
-
stdin.on("keypress", onKeypress);
|
|
1448
|
-
});
|
|
1449
|
-
}
|
|
1450
|
-
async function fallbackPrompt(question, options, defaultIndex) {
|
|
1451
|
-
return new Promise((resolve) => {
|
|
1452
|
-
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
1453
|
-
const firstEnabled = options.findIndex((option) => !option.disabled);
|
|
1454
|
-
const enabledDefault = options[defaultIndex]?.disabled ? firstEnabled : defaultIndex;
|
|
1455
|
-
console.log(` ${ui.primary("\u25CF")} ${ui.text.bold(question)}
|
|
1456
|
-
`);
|
|
1457
|
-
for (let i = 0; i < options.length; i++) {
|
|
1458
|
-
const marker = i === enabledDefault ? ui.success(" \u25C6") : " ";
|
|
1459
|
-
const label = options[i].disabled ? ui.muted(stripAnsi(options[i].label)) : ui.text(options[i].label);
|
|
1460
|
-
console.log(` ${ui.muted(`[${i + 1}]`)}${marker} ${label}`);
|
|
2033
|
+
const c = String(cnt).padStart(3);
|
|
2034
|
+
const rowText = `${color(l)}${ui.text(c)} ${ui.muted(type)}`;
|
|
2035
|
+
const visible = 16 + 3 + 2 + type.length;
|
|
2036
|
+
const pad = Math.max(0, inner - visible);
|
|
2037
|
+
if (isSmall) {
|
|
2038
|
+
console.log(` ${color(label)} ${ui.text(String(cnt))} ${ui.muted(type)}`);
|
|
2039
|
+
} else {
|
|
2040
|
+
console.log(" " + ui.border("\u2502 ") + rowText + " ".repeat(pad) + ui.border(" \u2502"));
|
|
1461
2041
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
`);
|
|
1487
|
-
ask();
|
|
1488
|
-
} else {
|
|
1489
|
-
console.log(` ${ui.error("\u2717")} ${ui.warning(`Enter 1-${options.length}`)}
|
|
1490
|
-
`);
|
|
1491
|
-
ask();
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
);
|
|
1495
|
-
};
|
|
1496
|
-
ask();
|
|
1497
|
-
});
|
|
2042
|
+
}
|
|
2043
|
+
if (!isSmall) {
|
|
2044
|
+
console.log(boxBot(w));
|
|
2045
|
+
}
|
|
2046
|
+
console.log("");
|
|
2047
|
+
const cdCmd = "cd " + path4.basename(exporter.outDir) + " && node serve.cjs";
|
|
2048
|
+
if (!isSmall) {
|
|
2049
|
+
console.log(boxTop(w));
|
|
2050
|
+
console.log(boxLine(w, ui.text.bold(" To serve locally")));
|
|
2051
|
+
console.log(boxSep(w));
|
|
2052
|
+
const inner = w - 6;
|
|
2053
|
+
const cmdLen = cdCmd.length;
|
|
2054
|
+
const pad = Math.max(0, inner - cmdLen);
|
|
2055
|
+
console.log(
|
|
2056
|
+
" " + ui.border("\u2502 ") + G(cdCmd) + " ".repeat(pad) + ui.muted(" copy") + ui.border(" \u2502")
|
|
2057
|
+
);
|
|
2058
|
+
console.log(boxBot(w));
|
|
2059
|
+
} else {
|
|
2060
|
+
console.log(ui.text.bold(" To serve locally:"));
|
|
2061
|
+
console.log(` ${G(cdCmd)}`);
|
|
2062
|
+
}
|
|
2063
|
+
console.log("");
|
|
2064
|
+
console.log(ui.muted(" note: must be served via HTTP for JS modules to work."));
|
|
2065
|
+
console.log("");
|
|
1498
2066
|
}
|
|
1499
|
-
var
|
|
1500
|
-
"src/
|
|
2067
|
+
var init_summary = __esm({
|
|
2068
|
+
"src/exporter/summary.ts"() {
|
|
1501
2069
|
"use strict";
|
|
2070
|
+
init_box();
|
|
1502
2071
|
init_theme();
|
|
1503
2072
|
}
|
|
1504
2073
|
});
|
|
@@ -1508,17 +2077,28 @@ import fs4 from "fs/promises";
|
|
|
1508
2077
|
import path5 from "path";
|
|
1509
2078
|
import { stdin as stdin2, stdout as stdout2 } from "process";
|
|
1510
2079
|
import { spawn } from "child_process";
|
|
1511
|
-
import chalk4 from "chalk";
|
|
1512
2080
|
async function runAiPromptAssistant(exporter) {
|
|
1513
2081
|
if (!stdin2.isTTY || !stdout2.isTTY) return;
|
|
2082
|
+
printConvertPanel(exporter);
|
|
2083
|
+
const action = await select(
|
|
2084
|
+
"Framer Export AI Convert",
|
|
2085
|
+
[
|
|
2086
|
+
{ label: `${buttonLabel("Convert")} open AI prompt modal`, value: "convert" },
|
|
2087
|
+
{ label: `${buttonLabel("Skip")} finish export`, value: "skip" }
|
|
2088
|
+
],
|
|
2089
|
+
0
|
|
2090
|
+
);
|
|
2091
|
+
if (action === "skip") return;
|
|
2092
|
+
printAssistantModal("AI conversion prompt", [
|
|
2093
|
+
"Choose a target stack, AI tool, and conversion situation.",
|
|
2094
|
+
"Mouse clicks are supported in the terminal when available.",
|
|
2095
|
+
"A detailed prompt file will be generated inside the export folder."
|
|
2096
|
+
]);
|
|
1514
2097
|
const targetOptions = [
|
|
1515
2098
|
...TARGETS.map((target2) => ({ label: target2.label, value: target2.id })),
|
|
1516
|
-
{ label: "Customize with AI - BETA in development", value: "custom-ai", disabled: true }
|
|
1517
|
-
{ label: "Skip AI prompt", value: "skip" }
|
|
2099
|
+
{ label: "Customize with AI - BETA in development", value: "custom-ai", disabled: true }
|
|
1518
2100
|
];
|
|
1519
|
-
|
|
1520
|
-
const targetId = await select("AI Prompt Assistant BETA: choose target stack", targetOptions, 0);
|
|
1521
|
-
if (targetId === "skip") return;
|
|
2101
|
+
const targetId = await select("Choose target stack", targetOptions, 0);
|
|
1522
2102
|
const aiToolId = await select(
|
|
1523
2103
|
"Choose the AI coding tool",
|
|
1524
2104
|
AI_TOOLS.map((tool) => ({ label: tool.label, value: tool.id })),
|
|
@@ -1539,22 +2119,22 @@ async function runAiPromptAssistant(exporter) {
|
|
|
1539
2119
|
await fs4.mkdir(aiDir, { recursive: true });
|
|
1540
2120
|
await fs4.writeFile(promptPath, prompt, "utf-8");
|
|
1541
2121
|
printPromptResult(promptPath, target, aiTool, goal);
|
|
1542
|
-
const
|
|
1543
|
-
"
|
|
2122
|
+
const promptAction = await select(
|
|
2123
|
+
"Prompt actions",
|
|
1544
2124
|
[
|
|
1545
|
-
{ label: "Copy prompt"
|
|
1546
|
-
{ label: "
|
|
2125
|
+
{ label: `${buttonLabel("Copy prompt")} clipboard`, value: "copy" },
|
|
2126
|
+
{ label: `${buttonLabel("Done")} keep file only`, value: "done" }
|
|
1547
2127
|
],
|
|
1548
2128
|
0
|
|
1549
2129
|
);
|
|
1550
|
-
if (
|
|
2130
|
+
if (promptAction === "copy") {
|
|
1551
2131
|
try {
|
|
1552
2132
|
await copyToClipboard(prompt);
|
|
1553
|
-
console.log(` ${
|
|
2133
|
+
console.log(` ${ui.success("\u2713")} ${ui.text.bold("Prompt copied to clipboard")}
|
|
1554
2134
|
`);
|
|
1555
2135
|
} catch (error) {
|
|
1556
2136
|
console.log(
|
|
1557
|
-
` ${
|
|
2137
|
+
` ${ui.warning("!")} ${ui.warning("Clipboard copy unavailable:")} ${ui.muted(error.message)}
|
|
1558
2138
|
`
|
|
1559
2139
|
);
|
|
1560
2140
|
}
|
|
@@ -1596,6 +2176,43 @@ async function countEntries(dir) {
|
|
|
1596
2176
|
return 0;
|
|
1597
2177
|
}
|
|
1598
2178
|
}
|
|
2179
|
+
function buttonLabel(label) {
|
|
2180
|
+
return `${ui.border("[")} ${ui.text.bold(label)} ${ui.border("]")}`;
|
|
2181
|
+
}
|
|
2182
|
+
function centerText2(text, width) {
|
|
2183
|
+
const visible = stripAnsi(text).length;
|
|
2184
|
+
if (visible >= width) return text;
|
|
2185
|
+
const left = Math.floor((width - visible) / 2);
|
|
2186
|
+
return " ".repeat(left) + text + " ".repeat(width - visible - left);
|
|
2187
|
+
}
|
|
2188
|
+
function printConvertPanel(exporter) {
|
|
2189
|
+
const w = maxWidth();
|
|
2190
|
+
const inner = w - 4;
|
|
2191
|
+
const rows = [
|
|
2192
|
+
centerText2(`${ui.text.bold("AI Convert")} ${ui.muted("BETA")}`, inner),
|
|
2193
|
+
centerText2(ui.muted("Generate a conversion prompt after the export."), inner),
|
|
2194
|
+
centerText2(`${buttonLabel("Convert")} ${ui.muted("or")} ${buttonLabel("Skip")}`, inner),
|
|
2195
|
+
centerText2(ui.muted(path5.basename(exporter.outDir)), inner)
|
|
2196
|
+
];
|
|
2197
|
+
console.log("");
|
|
2198
|
+
console.log(boxTop(w));
|
|
2199
|
+
for (const row of rows) console.log(boxLine(w, row));
|
|
2200
|
+
console.log(boxBot(w));
|
|
2201
|
+
console.log("");
|
|
2202
|
+
}
|
|
2203
|
+
function printAssistantModal(title, lines) {
|
|
2204
|
+
const w = maxWidth();
|
|
2205
|
+
const inner = w - 4;
|
|
2206
|
+
console.log("");
|
|
2207
|
+
console.log(boxTop(w));
|
|
2208
|
+
console.log(boxLine(w, centerText2(`${ui.primary("\u25C6")} ${ui.text.bold(title)}`, inner)));
|
|
2209
|
+
console.log(boxSep(w));
|
|
2210
|
+
for (const line of lines) {
|
|
2211
|
+
console.log(boxLine(w, centerText2(ui.muted(line), inner)));
|
|
2212
|
+
}
|
|
2213
|
+
console.log(boxBot(w));
|
|
2214
|
+
console.log("");
|
|
2215
|
+
}
|
|
1599
2216
|
function buildConversionPrompt(target, aiTool, goal, facts) {
|
|
1600
2217
|
const rootEntries = facts.rootEntries.length ? facts.rootEntries.join(", ") : "No root entries detected";
|
|
1601
2218
|
const exportDir = quoteForPrompt(facts.outputDir);
|
|
@@ -1615,8 +2232,8 @@ function buildConversionPrompt(target, aiTool, goal, facts) {
|
|
|
1615
2232
|
(dir) => `Inspect ${dir}: ${facts.counts[dir]} item(s) detected in the export.`
|
|
1616
2233
|
);
|
|
1617
2234
|
const promptLines = [
|
|
1618
|
-
`You are ${aiTool.agentName} working inside a local export created by
|
|
1619
|
-
"This brief was generated by the
|
|
2235
|
+
`You are ${aiTool.agentName} working inside a local export created by Framer Export.`,
|
|
2236
|
+
"This brief was generated by the Framer Export AI Prompt Assistant BETA.",
|
|
1620
2237
|
"Treat this beta prompt as a strict conversion checklist, not as permission to skip inspection.",
|
|
1621
2238
|
"Your mission is to convert this exported static mirror into a clean production project.",
|
|
1622
2239
|
`Target stack: ${target.stack}.`,
|
|
@@ -1774,23 +2391,38 @@ function pipeToCommand(command, args, input) {
|
|
|
1774
2391
|
function printPromptResult(promptPath, target, aiTool, goal) {
|
|
1775
2392
|
const w = maxWidth();
|
|
1776
2393
|
const isSmall = w < 50;
|
|
2394
|
+
const inner = w - 4;
|
|
1777
2395
|
const relPath = path5.relative(process.cwd(), promptPath) || promptPath;
|
|
1778
2396
|
console.log("");
|
|
1779
2397
|
if (!isSmall) {
|
|
1780
2398
|
console.log(boxTop(w));
|
|
1781
|
-
console.log(
|
|
2399
|
+
console.log(
|
|
2400
|
+
boxLine(
|
|
2401
|
+
w,
|
|
2402
|
+
centerText2(
|
|
2403
|
+
`${ui.success("\u2713")} ${ui.text.bold("AI Prompt Ready")} ${ui.muted("BETA")}`,
|
|
2404
|
+
inner
|
|
2405
|
+
)
|
|
2406
|
+
)
|
|
2407
|
+
);
|
|
2408
|
+
console.log(boxSep(w));
|
|
2409
|
+
console.log(
|
|
2410
|
+
boxLine(w, centerText2(`${ui.muted("Tool")} ${ui.primary(aiTool.displayName)}`, inner))
|
|
2411
|
+
);
|
|
2412
|
+
console.log(boxLine(w, centerText2(`${ui.muted("Stack")} ${ui.primary(target.label)}`, inner)));
|
|
2413
|
+
console.log(boxLine(w, centerText2(`${ui.muted("Mode")} ${ui.primary(goal.label)}`, inner)));
|
|
2414
|
+
console.log(boxLine(w, centerText2(`${ui.muted("File")} ${ui.primary(relPath)}`, inner)));
|
|
1782
2415
|
console.log(boxSep(w));
|
|
1783
|
-
console.log(
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
console.log(boxLine(w, ` File: ${relPath}`));
|
|
2416
|
+
console.log(
|
|
2417
|
+
boxLine(w, centerText2(`${buttonLabel("Copy prompt")} ${buttonLabel("Done")}`, inner))
|
|
2418
|
+
);
|
|
1787
2419
|
console.log(boxBot(w));
|
|
1788
2420
|
} else {
|
|
1789
|
-
console.log(
|
|
1790
|
-
console.log(` Tool: ${
|
|
1791
|
-
console.log(` Stack: ${
|
|
1792
|
-
console.log(` Situation: ${
|
|
1793
|
-
console.log(` File: ${
|
|
2421
|
+
console.log(ui.text.bold(" AI Prompt Ready - BETA"));
|
|
2422
|
+
console.log(` Tool: ${ui.primary(aiTool.displayName)}`);
|
|
2423
|
+
console.log(` Stack: ${ui.primary(target.label)}`);
|
|
2424
|
+
console.log(` Situation: ${ui.primary(goal.label)}`);
|
|
2425
|
+
console.log(` File: ${ui.primary(relPath)}`);
|
|
1794
2426
|
}
|
|
1795
2427
|
console.log("");
|
|
1796
2428
|
}
|
|
@@ -1800,6 +2432,7 @@ var init_prompt_assistant = __esm({
|
|
|
1800
2432
|
"use strict";
|
|
1801
2433
|
init_select();
|
|
1802
2434
|
init_box();
|
|
2435
|
+
init_theme();
|
|
1803
2436
|
IMPORTANT_DIRS = [
|
|
1804
2437
|
"index.html",
|
|
1805
2438
|
"styles",
|
|
@@ -1919,82 +2552,6 @@ var init_prompt_assistant = __esm({
|
|
|
1919
2552
|
}
|
|
1920
2553
|
});
|
|
1921
2554
|
|
|
1922
|
-
// src/cli/cooking.ts
|
|
1923
|
-
import chalk5 from "chalk";
|
|
1924
|
-
var SHINE_WIDTH, FRAME_INTERVAL, SPINNER_FRAMES, CookingSpinner;
|
|
1925
|
-
var init_cooking = __esm({
|
|
1926
|
-
"src/cli/cooking.ts"() {
|
|
1927
|
-
"use strict";
|
|
1928
|
-
init_theme();
|
|
1929
|
-
SHINE_WIDTH = 10;
|
|
1930
|
-
FRAME_INTERVAL = 80;
|
|
1931
|
-
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1932
|
-
CookingSpinner = class {
|
|
1933
|
-
interval = null;
|
|
1934
|
-
frame = 0;
|
|
1935
|
-
phase = "";
|
|
1936
|
-
active = false;
|
|
1937
|
-
start(phase = "") {
|
|
1938
|
-
this.phase = phase;
|
|
1939
|
-
this.frame = 0;
|
|
1940
|
-
this.active = true;
|
|
1941
|
-
this.draw();
|
|
1942
|
-
this.interval = setInterval(() => {
|
|
1943
|
-
this.frame++;
|
|
1944
|
-
this.draw();
|
|
1945
|
-
}, FRAME_INTERVAL);
|
|
1946
|
-
}
|
|
1947
|
-
update(phase) {
|
|
1948
|
-
this.phase = phase;
|
|
1949
|
-
}
|
|
1950
|
-
log(message) {
|
|
1951
|
-
if (this.active) {
|
|
1952
|
-
process.stdout.write("\r\x1B[2K");
|
|
1953
|
-
}
|
|
1954
|
-
process.stdout.write(message + "\n");
|
|
1955
|
-
if (this.active) {
|
|
1956
|
-
this.draw();
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
stop() {
|
|
1960
|
-
this.active = false;
|
|
1961
|
-
if (this.interval) {
|
|
1962
|
-
clearInterval(this.interval);
|
|
1963
|
-
this.interval = null;
|
|
1964
|
-
}
|
|
1965
|
-
process.stdout.write("\r\x1B[2K");
|
|
1966
|
-
}
|
|
1967
|
-
draw() {
|
|
1968
|
-
if (!this.active) return;
|
|
1969
|
-
const spinner = SPINNER_FRAMES[this.frame % SPINNER_FRAMES.length];
|
|
1970
|
-
const shimmer = this.renderShimmer("f-export");
|
|
1971
|
-
const frameStr = ui.primary(spinner);
|
|
1972
|
-
const phaseStr = this.phase ? ` ${ui.muted(this.limitLen(this.phase, 52))}` : "";
|
|
1973
|
-
process.stdout.write(`\r\x1B[2K ${frameStr} ${shimmer}${phaseStr}`);
|
|
1974
|
-
}
|
|
1975
|
-
renderShimmer(text) {
|
|
1976
|
-
let result = "";
|
|
1977
|
-
const pos = this.frame % (text.length + SHINE_WIDTH * 2) - SHINE_WIDTH;
|
|
1978
|
-
for (let i = 0; i < text.length; i++) {
|
|
1979
|
-
const dist = Math.abs(i - pos);
|
|
1980
|
-
if (dist < SHINE_WIDTH) {
|
|
1981
|
-
const t = 1 - dist / SHINE_WIDTH;
|
|
1982
|
-
const g = Math.floor(160 + t * 95);
|
|
1983
|
-
result += chalk5.rgb(250, Math.min(255, g), Math.min(255, Math.floor(g * 0.75)))(text[i]);
|
|
1984
|
-
} else {
|
|
1985
|
-
result += ui.muted(text[i]);
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
return result;
|
|
1989
|
-
}
|
|
1990
|
-
limitLen(s, max) {
|
|
1991
|
-
if (s.length <= max) return s;
|
|
1992
|
-
return s.slice(0, max - 1) + "\u2026";
|
|
1993
|
-
}
|
|
1994
|
-
};
|
|
1995
|
-
}
|
|
1996
|
-
});
|
|
1997
|
-
|
|
1998
2555
|
// src/exporter/index.ts
|
|
1999
2556
|
var exporter_exports = {};
|
|
2000
2557
|
__export(exporter_exports, {
|
|
@@ -2003,7 +2560,7 @@ __export(exporter_exports, {
|
|
|
2003
2560
|
});
|
|
2004
2561
|
import fs5 from "fs/promises";
|
|
2005
2562
|
import path6 from "path";
|
|
2006
|
-
import
|
|
2563
|
+
import chalk5 from "chalk";
|
|
2007
2564
|
function deriveOutputName(url, platformName) {
|
|
2008
2565
|
try {
|
|
2009
2566
|
const parsed = new URL(url);
|
|
@@ -2019,11 +2576,20 @@ function deriveOutputName(url, platformName) {
|
|
|
2019
2576
|
} else {
|
|
2020
2577
|
siteName = hostname.replace(/\./g, "-");
|
|
2021
2578
|
}
|
|
2022
|
-
|
|
2579
|
+
const cleanName = siteName.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "site";
|
|
2580
|
+
return `${platformName}-${cleanName}-${randomOutputSuffix()}`;
|
|
2023
2581
|
} catch {
|
|
2024
|
-
return
|
|
2582
|
+
return `framer-export-output-${randomOutputSuffix()}`;
|
|
2025
2583
|
}
|
|
2026
2584
|
}
|
|
2585
|
+
function randomOutputSuffix() {
|
|
2586
|
+
const adjectives = ["clean", "bright", "swift", "sharp", "fresh", "solid", "tidy", "prime"];
|
|
2587
|
+
const nouns = ["mirror", "export", "site", "build", "copy", "page", "stack", "bundle"];
|
|
2588
|
+
const adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
2589
|
+
const noun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
2590
|
+
const id = Math.random().toString(36).slice(2, 6);
|
|
2591
|
+
return `${adjective}-${noun}-${id}`;
|
|
2592
|
+
}
|
|
2027
2593
|
var FramerExporter;
|
|
2028
2594
|
var init_exporter = __esm({
|
|
2029
2595
|
"src/exporter/index.ts"() {
|
|
@@ -2064,10 +2630,12 @@ var init_exporter = __esm({
|
|
|
2064
2630
|
}
|
|
2065
2631
|
}
|
|
2066
2632
|
async run(includeSubpages = false) {
|
|
2067
|
-
console.log(
|
|
2633
|
+
console.log(
|
|
2634
|
+
`
|
|
2068
2635
|
${ui.text.bold("Framer Export")} ${chip("mirror")} ${ui.muted("v4 pipeline")}
|
|
2069
|
-
`
|
|
2070
|
-
|
|
2636
|
+
`
|
|
2637
|
+
);
|
|
2638
|
+
info("Source : " + chalk5.underline(this.siteUrl));
|
|
2071
2639
|
info("Output : " + ui.primary(this.outDir));
|
|
2072
2640
|
info("Platform : " + ui.primary(this.platform.displayName));
|
|
2073
2641
|
if (includeSubpages) {
|
|
@@ -2099,14 +2667,12 @@ var init_exporter = __esm({
|
|
|
2099
2667
|
this.ssrHTML = buf.toString("utf-8");
|
|
2100
2668
|
success("SSR HTML fetched (" + (this.ssrHTML.length / 1024).toFixed(1) + " KB)");
|
|
2101
2669
|
} catch (e) {
|
|
2102
|
-
log(
|
|
2670
|
+
log(chalk5.red("Could not fetch SSR HTML: " + e.message));
|
|
2103
2671
|
}
|
|
2104
2672
|
const htmlDetected = detectPlatform(this.siteUrl, this.ssrHTML);
|
|
2105
2673
|
if (htmlDetected.name !== this.platform.name) {
|
|
2106
2674
|
this.platform = htmlDetected;
|
|
2107
|
-
log(
|
|
2108
|
-
"Platform refined: " + ui.primary(this.platform.displayName) + " (from HTML analysis)"
|
|
2109
|
-
);
|
|
2675
|
+
log("Platform refined: " + ui.primary(this.platform.displayName) + " (from HTML analysis)");
|
|
2110
2676
|
}
|
|
2111
2677
|
await launchAndCapture(this);
|
|
2112
2678
|
if (includeSubpages && this.page) {
|
|
@@ -2196,7 +2762,7 @@ import readline2 from "readline/promises";
|
|
|
2196
2762
|
import { stdin as stdin3, stdout as stdout3 } from "process";
|
|
2197
2763
|
import path7 from "path";
|
|
2198
2764
|
import { URL as URL4 } from "url";
|
|
2199
|
-
import
|
|
2765
|
+
import chalk6 from "chalk";
|
|
2200
2766
|
function drawHeader(title) {
|
|
2201
2767
|
const w = maxWidth();
|
|
2202
2768
|
if (w < 50) {
|
|
@@ -2212,94 +2778,165 @@ function drawHeader(title) {
|
|
|
2212
2778
|
async function runSetup(legacyMode = false) {
|
|
2213
2779
|
showBanner();
|
|
2214
2780
|
console.log(` ${ui.text.bold("Framer Export setup")} ${chip("interactive")}`);
|
|
2215
|
-
console.log(
|
|
2216
|
-
`)
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2781
|
+
console.log(
|
|
2782
|
+
` ${ui.muted("Export Framer, Webflow, and Wix sites into a clean local mirror.")}
|
|
2783
|
+
`
|
|
2784
|
+
);
|
|
2785
|
+
const rl = legacyMode ? readline2.createInterface({ input: stdin3, output: stdout3 }) : null;
|
|
2786
|
+
const ask = async (question, defaultVal, headerLines = []) => {
|
|
2787
|
+
if (!legacyMode) {
|
|
2788
|
+
return promptInput(question, defaultVal || "", { headerLines });
|
|
2789
|
+
}
|
|
2790
|
+
const suffix = defaultVal ? chalk6.gray(` (${defaultVal})`) : "";
|
|
2220
2791
|
const prompt = ` ${ui.primary("\u25CF")} ${ui.text.bold(question)}${suffix} ${ui.muted(">")} `;
|
|
2221
2792
|
const answer = await rl.question(prompt);
|
|
2222
2793
|
return answer.trim() || defaultVal || "";
|
|
2223
2794
|
};
|
|
2224
|
-
drawHeader("Step 1 : Site URL");
|
|
2795
|
+
if (legacyMode) drawHeader("Step 1 : Site URL");
|
|
2225
2796
|
let siteUrl = "";
|
|
2797
|
+
let urlError = "";
|
|
2226
2798
|
while (!siteUrl) {
|
|
2227
|
-
const input = await ask(
|
|
2799
|
+
const input = await ask(
|
|
2800
|
+
"Enter the site URL",
|
|
2801
|
+
"",
|
|
2802
|
+
["Step 1 : Site URL", urlError].filter(Boolean)
|
|
2803
|
+
);
|
|
2228
2804
|
try {
|
|
2229
2805
|
new URL4(input);
|
|
2230
2806
|
siteUrl = input;
|
|
2807
|
+
urlError = "";
|
|
2231
2808
|
} catch {
|
|
2232
|
-
|
|
2809
|
+
urlError = "Invalid URL. Enter a valid URL (https://...)";
|
|
2810
|
+
if (legacyMode) {
|
|
2811
|
+
console.log(` ${ui.error("\u2717")} ${ui.error(urlError)}
|
|
2233
2812
|
`);
|
|
2813
|
+
}
|
|
2234
2814
|
}
|
|
2235
2815
|
}
|
|
2236
|
-
|
|
2816
|
+
if (legacyMode) {
|
|
2817
|
+
console.log(` ${ui.success("\u2713")} ${ui.success("URL:")} ${chalk6.underline(siteUrl)}
|
|
2237
2818
|
`);
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
let platformName;
|
|
2819
|
+
}
|
|
2820
|
+
let platformName = null;
|
|
2241
2821
|
if (legacyMode) {
|
|
2822
|
+
drawHeader("Step 2 : Platform");
|
|
2823
|
+
const detected = detectPlatform(siteUrl);
|
|
2242
2824
|
console.log(` ${ui.info("i")} Auto-detected: ${ui.primary(detected.displayName)}`);
|
|
2243
2825
|
const platformInput = await ask("Platform (framer/webflow/wix)", detected.name);
|
|
2244
2826
|
platformName = ["framer", "webflow", "wix"].includes(platformInput) ? platformInput : detected.name;
|
|
2245
2827
|
console.log(` ${ui.success("\u2713")} ${ui.success("Platform:")} ${ui.primary(platformName)}
|
|
2246
2828
|
`);
|
|
2247
2829
|
} else {
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2830
|
+
while (!platformName) {
|
|
2831
|
+
const detected = detectPlatform(siteUrl);
|
|
2832
|
+
const platforms = [
|
|
2833
|
+
{
|
|
2834
|
+
label: `Framer${detected.name === "framer" ? chalk6.gray(" (detected)") : ""}`,
|
|
2835
|
+
value: "framer"
|
|
2836
|
+
},
|
|
2837
|
+
{
|
|
2838
|
+
label: `Webflow${detected.name === "webflow" ? chalk6.gray(" (detected)") : ""}`,
|
|
2839
|
+
value: "webflow"
|
|
2840
|
+
},
|
|
2841
|
+
{ label: `Wix${detected.name === "wix" ? chalk6.gray(" (detected)") : ""}`, value: "wix" }
|
|
2842
|
+
];
|
|
2843
|
+
const defaultIdx = ["framer", "webflow", "wix"].indexOf(detected.name);
|
|
2844
|
+
const platformChoice = await select("Select platform", platforms, Math.max(0, defaultIdx), {
|
|
2845
|
+
headerLines: [`URL: ${siteUrl}`],
|
|
2846
|
+
actions: [{ label: "Modify URL", value: "modify-url" }],
|
|
2847
|
+
footer: "tab focus button \xB7 mouse hover/click \xB7 enter select"
|
|
2848
|
+
});
|
|
2849
|
+
if (platformChoice === "modify-url") {
|
|
2850
|
+
siteUrl = "";
|
|
2851
|
+
urlError = "";
|
|
2852
|
+
while (!siteUrl) {
|
|
2853
|
+
const input = await ask(
|
|
2854
|
+
"Modify site URL",
|
|
2855
|
+
"",
|
|
2856
|
+
["Step 1 : Site URL", urlError].filter(Boolean)
|
|
2857
|
+
);
|
|
2858
|
+
try {
|
|
2859
|
+
new URL4(input);
|
|
2860
|
+
siteUrl = input;
|
|
2861
|
+
urlError = "";
|
|
2862
|
+
} catch {
|
|
2863
|
+
urlError = "Invalid URL. Enter a valid URL (https://...)";
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
continue;
|
|
2867
|
+
}
|
|
2868
|
+
platformName = platformChoice;
|
|
2869
|
+
}
|
|
2256
2870
|
}
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2871
|
+
if (!platformName) {
|
|
2872
|
+
throw new Error("Platform selection failed");
|
|
2873
|
+
}
|
|
2874
|
+
const rl2 = legacyMode ? rl : null;
|
|
2875
|
+
const ask2 = async (question, defaultVal, headerLines = []) => {
|
|
2876
|
+
if (!legacyMode) {
|
|
2877
|
+
return promptInput(question, defaultVal || "", { headerLines });
|
|
2878
|
+
}
|
|
2879
|
+
const suffix = defaultVal ? chalk6.gray(` (${defaultVal})`) : "";
|
|
2260
2880
|
const prompt = ` ${ui.primary("\u25CF")} ${ui.text.bold(question)}${suffix} ${ui.muted(">")} `;
|
|
2261
2881
|
const answer = await rl2.question(prompt);
|
|
2262
2882
|
return answer.trim() || defaultVal || "";
|
|
2263
2883
|
};
|
|
2264
2884
|
const derivedName = deriveOutputName(siteUrl, platformName);
|
|
2265
|
-
drawHeader("Step 3 : Output Directory");
|
|
2266
|
-
const outDir = await ask2("Output directory", "./" + derivedName
|
|
2267
|
-
|
|
2885
|
+
if (legacyMode) drawHeader("Step 3 : Output Directory");
|
|
2886
|
+
const outDir = await ask2("Output directory", "./" + derivedName, [
|
|
2887
|
+
"Step 3 : Output Directory",
|
|
2888
|
+
`URL: ${siteUrl}`,
|
|
2889
|
+
`Platform: ${platformName}`
|
|
2890
|
+
]);
|
|
2891
|
+
if (legacyMode) {
|
|
2892
|
+
console.log(` ${ui.success("\u2713")} ${ui.success("Output:")} ${ui.primary(outDir)}
|
|
2268
2893
|
`);
|
|
2269
|
-
|
|
2894
|
+
}
|
|
2895
|
+
if (legacyMode) drawHeader("Step 4 : Options");
|
|
2270
2896
|
let prettyPrint;
|
|
2271
2897
|
let concurrency;
|
|
2272
2898
|
let includeSubpages;
|
|
2273
2899
|
if (legacyMode) {
|
|
2274
2900
|
const prettyAnswer = await ask2("Pretty-print JS files? (y/n)", "y");
|
|
2275
2901
|
prettyPrint = prettyAnswer.toLowerCase().startsWith("y");
|
|
2276
|
-
console.log(
|
|
2277
|
-
`)
|
|
2902
|
+
console.log(
|
|
2903
|
+
` ${ui.success("\u2713")} Pretty-print: ${prettyPrint ? ui.success("yes") : ui.error("no")}
|
|
2904
|
+
`
|
|
2905
|
+
);
|
|
2278
2906
|
const subpagesAnswer = await ask2("Export sub-pages? (y/n)", "n");
|
|
2279
2907
|
includeSubpages = subpagesAnswer.toLowerCase().startsWith("y");
|
|
2280
|
-
console.log(
|
|
2281
|
-
`)
|
|
2908
|
+
console.log(
|
|
2909
|
+
` ${ui.success("\u2713")} Sub-pages: ${includeSubpages ? ui.success("yes") : ui.error("no")}
|
|
2910
|
+
`
|
|
2911
|
+
);
|
|
2282
2912
|
const concurrencyAnswer = await ask2("Download concurrency", "12");
|
|
2283
2913
|
concurrency = parseInt(concurrencyAnswer, 10) || 12;
|
|
2284
2914
|
console.log(` ${ui.success("\u2713")} Concurrency: ${ui.primary(String(concurrency))}
|
|
2285
2915
|
`);
|
|
2286
2916
|
} else {
|
|
2287
|
-
rl2.close();
|
|
2288
2917
|
const prettyVal = await select("Pretty-print JS files?", [
|
|
2289
2918
|
{ label: "Yes", value: "yes" },
|
|
2290
2919
|
{ label: "No", value: "no" }
|
|
2291
2920
|
]);
|
|
2292
2921
|
prettyPrint = prettyVal === "yes";
|
|
2293
|
-
const subpagesVal = await select(
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2922
|
+
const subpagesVal = await select(
|
|
2923
|
+
"Export sub-pages?",
|
|
2924
|
+
[
|
|
2925
|
+
{ label: "No", value: "no" },
|
|
2926
|
+
{ label: "Yes, crawl and export", value: "yes" }
|
|
2927
|
+
],
|
|
2928
|
+
0
|
|
2929
|
+
);
|
|
2297
2930
|
includeSubpages = subpagesVal === "yes";
|
|
2298
|
-
const concurrencyVal = await select(
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2931
|
+
const concurrencyVal = await select(
|
|
2932
|
+
"Download concurrency",
|
|
2933
|
+
[
|
|
2934
|
+
{ label: "6 (slow connection)", value: "6" },
|
|
2935
|
+
{ label: "12 (default)", value: "12" },
|
|
2936
|
+
{ label: "20 (fast connection)", value: "20" }
|
|
2937
|
+
],
|
|
2938
|
+
1
|
|
2939
|
+
);
|
|
2303
2940
|
concurrency = parseInt(concurrencyVal, 10);
|
|
2304
2941
|
}
|
|
2305
2942
|
const w = maxWidth();
|
|
@@ -2367,6 +3004,7 @@ var init_setup = __esm({
|
|
|
2367
3004
|
init_package();
|
|
2368
3005
|
import path8 from "path";
|
|
2369
3006
|
import { URL as URL5 } from "url";
|
|
3007
|
+
import { spawnSync } from "child_process";
|
|
2370
3008
|
|
|
2371
3009
|
// src/cli/help.ts
|
|
2372
3010
|
init_banner();
|
|
@@ -2375,8 +3013,12 @@ function showHelp() {
|
|
|
2375
3013
|
showBanner();
|
|
2376
3014
|
console.log(`${ui.text.bold(" USAGE")} ${chip("cli")}
|
|
2377
3015
|
`);
|
|
2378
|
-
console.log(
|
|
2379
|
-
|
|
3016
|
+
console.log(
|
|
3017
|
+
` ${ui.primary("framer-export")} ${ui.warning("<url>")} ${ui.muted("[output-dir]")}`
|
|
3018
|
+
);
|
|
3019
|
+
console.log(
|
|
3020
|
+
` ${ui.primary("fexport")} ${ui.warning("<url>")} ${ui.muted("[output-dir]")}`
|
|
3021
|
+
);
|
|
2380
3022
|
console.log("");
|
|
2381
3023
|
console.log(ui.text.bold(" OPTIONS\n"));
|
|
2382
3024
|
const opts = [
|
|
@@ -2401,18 +3043,31 @@ function showHelp() {
|
|
|
2401
3043
|
}
|
|
2402
3044
|
console.log("");
|
|
2403
3045
|
console.log(ui.text.bold(" EXAMPLES\n"));
|
|
2404
|
-
console.log(
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
console.log(
|
|
2408
|
-
|
|
3046
|
+
console.log(
|
|
3047
|
+
` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://mysite.framer.app")}`
|
|
3048
|
+
);
|
|
3049
|
+
console.log(
|
|
3050
|
+
` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://mysite.webflow.io")}`
|
|
3051
|
+
);
|
|
3052
|
+
console.log(
|
|
3053
|
+
` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://user.wixsite.com/my-site")}`
|
|
3054
|
+
);
|
|
3055
|
+
console.log(
|
|
3056
|
+
` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--platform webflow")} ${ui.warning("https://custom.com")}`
|
|
3057
|
+
);
|
|
3058
|
+
console.log(
|
|
3059
|
+
` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--subpages")} ${ui.warning("https://mysite.com")}`
|
|
3060
|
+
);
|
|
2409
3061
|
console.log(` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--setup")}`);
|
|
2410
|
-
console.log(
|
|
3062
|
+
console.log(
|
|
3063
|
+
` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--setup --legacy-mode")}`
|
|
3064
|
+
);
|
|
2411
3065
|
console.log("");
|
|
2412
3066
|
}
|
|
2413
3067
|
|
|
2414
3068
|
// src/cli/index.ts
|
|
2415
3069
|
init_banner();
|
|
3070
|
+
init_cooking();
|
|
2416
3071
|
|
|
2417
3072
|
// src/cli/update-check.ts
|
|
2418
3073
|
import https from "https";
|
|
@@ -2429,7 +3084,7 @@ async function checkForUpdates(currentVersion) {
|
|
|
2429
3084
|
const json = JSON.parse(data);
|
|
2430
3085
|
const latest = json.version;
|
|
2431
3086
|
if (!latest) return resolve(null);
|
|
2432
|
-
if (latest
|
|
3087
|
+
if (isNewerVersion(latest, currentVersion)) return resolve(latest);
|
|
2433
3088
|
resolve(null);
|
|
2434
3089
|
} catch {
|
|
2435
3090
|
resolve(null);
|
|
@@ -2444,8 +3099,23 @@ async function checkForUpdates(currentVersion) {
|
|
|
2444
3099
|
});
|
|
2445
3100
|
});
|
|
2446
3101
|
}
|
|
3102
|
+
function isNewerVersion(latest, current) {
|
|
3103
|
+
const latestParts = parseVersion(latest);
|
|
3104
|
+
const currentParts = parseVersion(current);
|
|
3105
|
+
for (let i = 0; i < Math.max(latestParts.length, currentParts.length); i++) {
|
|
3106
|
+
const latestPart = latestParts[i] ?? 0;
|
|
3107
|
+
const currentPart = currentParts[i] ?? 0;
|
|
3108
|
+
if (latestPart > currentPart) return true;
|
|
3109
|
+
if (latestPart < currentPart) return false;
|
|
3110
|
+
}
|
|
3111
|
+
return false;
|
|
3112
|
+
}
|
|
3113
|
+
function parseVersion(version) {
|
|
3114
|
+
return version.replace(/^v/, "").split(/[.-]/).map((part) => Number.parseInt(part, 10)).filter((part) => Number.isFinite(part));
|
|
3115
|
+
}
|
|
2447
3116
|
|
|
2448
3117
|
// src/cli/index.ts
|
|
3118
|
+
init_select();
|
|
2449
3119
|
init_theme();
|
|
2450
3120
|
var VERSION = package_default.version;
|
|
2451
3121
|
function extractFlag(args, flag) {
|
|
@@ -2461,6 +3131,56 @@ function hasFlag(args, flag) {
|
|
|
2461
3131
|
args.splice(idx, 1);
|
|
2462
3132
|
return true;
|
|
2463
3133
|
}
|
|
3134
|
+
async function showUpdateNotice() {
|
|
3135
|
+
const latest = await checkForUpdates(VERSION);
|
|
3136
|
+
if (!latest) return;
|
|
3137
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
3138
|
+
console.log("");
|
|
3139
|
+
console.log(
|
|
3140
|
+
` ${ui.warning("\u21B3")} Update available: ${ui.muted(VERSION)} -> ${ui.success(latest)}`
|
|
3141
|
+
);
|
|
3142
|
+
console.log(` ${ui.primary(" Run:")} ${ui.primarySoft("npm i -g framer-export@latest")}`);
|
|
3143
|
+
console.log("");
|
|
3144
|
+
return;
|
|
3145
|
+
}
|
|
3146
|
+
const action = await select(
|
|
3147
|
+
"Update available",
|
|
3148
|
+
[
|
|
3149
|
+
{ label: "Continue without updating", value: "continue" },
|
|
3150
|
+
{ label: "Update now", value: "update" }
|
|
3151
|
+
],
|
|
3152
|
+
0,
|
|
3153
|
+
{
|
|
3154
|
+
headerLines: [
|
|
3155
|
+
`Current version: ${VERSION}`,
|
|
3156
|
+
`Latest version: ${latest}`,
|
|
3157
|
+
"You can continue now and update later."
|
|
3158
|
+
],
|
|
3159
|
+
footer: "enter continue \xB7 mouse hover/click"
|
|
3160
|
+
}
|
|
3161
|
+
);
|
|
3162
|
+
if (action === "update") {
|
|
3163
|
+
console.log(
|
|
3164
|
+
` ${ui.primary("Updating:")} ${ui.primarySoft("npm i -g framer-export@latest")}
|
|
3165
|
+
`
|
|
3166
|
+
);
|
|
3167
|
+
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
3168
|
+
const result = spawnSync(npmCommand, ["i", "-g", "framer-export@latest"], {
|
|
3169
|
+
stdio: "inherit"
|
|
3170
|
+
});
|
|
3171
|
+
if (result.status === 0) {
|
|
3172
|
+
console.log(`
|
|
3173
|
+
${ui.success("\u2713")} Updated. Re-run your command to use the new version.
|
|
3174
|
+
`);
|
|
3175
|
+
process.exit(0);
|
|
3176
|
+
}
|
|
3177
|
+
console.log(
|
|
3178
|
+
`
|
|
3179
|
+
${ui.error("\u2717")} Update failed. Run manually: ${ui.primarySoft("npm i -g framer-export@latest")}
|
|
3180
|
+
`
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
2464
3184
|
async function main() {
|
|
2465
3185
|
const args = process.argv.slice(2);
|
|
2466
3186
|
if (args.includes("--version") || args.includes("-v")) {
|
|
@@ -2468,31 +3188,32 @@ async function main() {
|
|
|
2468
3188
|
process.exit(0);
|
|
2469
3189
|
}
|
|
2470
3190
|
if (args.includes("--about")) {
|
|
2471
|
-
const
|
|
3191
|
+
const chalk7 = (await import("chalk")).default;
|
|
2472
3192
|
showBanner();
|
|
2473
3193
|
console.log(` ${ui.text.bold("Framer Export")} ${ui.muted(`v${package_default.version}`)}`);
|
|
2474
3194
|
console.log(` ${ui.text(package_default.description)}
|
|
2475
3195
|
`);
|
|
2476
3196
|
console.log(` ${ui.text.bold("Author")} ${ui.primary("Dany (danbenba)")}`);
|
|
2477
|
-
console.log(
|
|
2478
|
-
|
|
2479
|
-
|
|
3197
|
+
console.log(
|
|
3198
|
+
` ${ui.text.bold("Portfolio")} ${chalk7.underline.hex("#FAB283")("https://github.com/danbenba")}`
|
|
3199
|
+
);
|
|
3200
|
+
console.log(
|
|
3201
|
+
` ${ui.text.bold("GitHub")} ${chalk7.underline.hex("#FAB283")(package_default.repository.url.replace("git+", "").replace(".git", ""))}`
|
|
3202
|
+
);
|
|
3203
|
+
console.log(
|
|
3204
|
+
` ${ui.text.bold("npm")} ${chalk7.underline.hex("#FAB283")(`https://www.npmjs.com/package/${package_default.name}`)}`
|
|
3205
|
+
);
|
|
2480
3206
|
console.log(` ${ui.text.bold("License")} ${ui.success(package_default.license)}`);
|
|
2481
3207
|
console.log(` ${ui.text.bold("Node")} ${ui.muted(`>=${package_default.engines.node}`)}`);
|
|
2482
3208
|
console.log("");
|
|
2483
3209
|
process.exit(0);
|
|
2484
3210
|
}
|
|
2485
|
-
checkForUpdates(VERSION).then((latest) => {
|
|
2486
|
-
if (!latest) return;
|
|
2487
|
-
console.log("");
|
|
2488
|
-
console.log(` ${ui.warning("\u21B3")} Update available: ${ui.muted(VERSION)} -> ${ui.success(latest)}`);
|
|
2489
|
-
console.log(` ${ui.primary(" Run:")} ${ui.primarySoft("npm i -g framer-export@latest")}`);
|
|
2490
|
-
console.log("");
|
|
2491
|
-
});
|
|
2492
3211
|
if (args.includes("--help") || args.includes("-h")) {
|
|
2493
3212
|
showHelp();
|
|
2494
3213
|
process.exit(0);
|
|
2495
3214
|
}
|
|
3215
|
+
await showLoadingIntro(VERSION);
|
|
3216
|
+
await showUpdateNotice();
|
|
2496
3217
|
if (args.includes("--setup")) {
|
|
2497
3218
|
hasFlag(args, "--setup");
|
|
2498
3219
|
const legacyMode = hasFlag(args, "--legacy-mode");
|
|
@@ -2523,12 +3244,16 @@ async function main() {
|
|
|
2523
3244
|
const defaultDir = deriveOutputName2(url, detected);
|
|
2524
3245
|
const out = args[1] || `./${defaultDir}`;
|
|
2525
3246
|
try {
|
|
2526
|
-
await new FramerExporter2(url, path8.resolve(out), platformOverride || void 0).run(
|
|
3247
|
+
await new FramerExporter2(url, path8.resolve(out), platformOverride || void 0).run(
|
|
3248
|
+
includeSubpages
|
|
3249
|
+
);
|
|
2527
3250
|
} catch (e) {
|
|
2528
|
-
const
|
|
2529
|
-
console.log(
|
|
2530
|
-
|
|
2531
|
-
|
|
3251
|
+
const chalk7 = (await import("chalk")).default;
|
|
3252
|
+
console.log(
|
|
3253
|
+
`
|
|
3254
|
+
${ui.error("\u2717")} ${ui.error.bold("FAILED:")} ${ui.text(e.message)}`
|
|
3255
|
+
);
|
|
3256
|
+
console.log(chalk7.gray(e.stack || ""));
|
|
2532
3257
|
process.exit(1);
|
|
2533
3258
|
}
|
|
2534
3259
|
}
|