framer-export 4.3.10 → 4.4.2
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 +991 -552
- 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.2",
|
|
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",
|
|
@@ -204,6 +204,663 @@ var init_banner = __esm({
|
|
|
204
204
|
}
|
|
205
205
|
});
|
|
206
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
|
+
|
|
207
864
|
// src/assets/asset-map.ts
|
|
208
865
|
import path from "path";
|
|
209
866
|
import crypto from "crypto";
|
|
@@ -340,7 +997,7 @@ var init_download = __esm({
|
|
|
340
997
|
});
|
|
341
998
|
|
|
342
999
|
// src/logger/index.ts
|
|
343
|
-
import
|
|
1000
|
+
import chalk4 from "chalk";
|
|
344
1001
|
function setCooking(spinner) {
|
|
345
1002
|
_cooking = spinner;
|
|
346
1003
|
}
|
|
@@ -360,52 +1017,52 @@ var init_logger = __esm({
|
|
|
360
1017
|
_cooking = null;
|
|
361
1018
|
T = () => (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
|
|
362
1019
|
LOG_PALETTE = [
|
|
363
|
-
(s) =>
|
|
364
|
-
(s) =>
|
|
365
|
-
(s) =>
|
|
366
|
-
(s) =>
|
|
367
|
-
(s) =>
|
|
368
|
-
(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)
|
|
369
1026
|
];
|
|
370
1027
|
_li = 0;
|
|
371
1028
|
log = (m) => {
|
|
372
1029
|
_li++;
|
|
373
1030
|
const c = LOG_PALETTE[_li % LOG_PALETTE.length];
|
|
374
1031
|
output(
|
|
375
|
-
`${
|
|
1032
|
+
`${chalk4.hex(THEME.muted)(`[${T()}]`)} ${chalk4.hex(THEME.primary)("[log]")} ${c(trunc(m, 120))}`
|
|
376
1033
|
);
|
|
377
1034
|
};
|
|
378
1035
|
INFO_PALETTE = [
|
|
379
|
-
(s) =>
|
|
380
|
-
(s) =>
|
|
381
|
-
(s) =>
|
|
1036
|
+
(s) => chalk4.hex(THEME.info)(s),
|
|
1037
|
+
(s) => chalk4.hex(THEME.secondary)(s),
|
|
1038
|
+
(s) => chalk4.hex(THEME.primarySoft)(s)
|
|
382
1039
|
];
|
|
383
1040
|
_ii = 0;
|
|
384
1041
|
info = (m) => {
|
|
385
1042
|
_ii++;
|
|
386
1043
|
const c = INFO_PALETTE[_ii % INFO_PALETTE.length];
|
|
387
1044
|
output(
|
|
388
|
-
`${
|
|
1045
|
+
`${chalk4.hex(THEME.muted)(`[${T()}]`)} ${chalk4.hex(THEME.info).bold("[info]")} ${c(trunc(m, 120))}`
|
|
389
1046
|
);
|
|
390
1047
|
};
|
|
391
1048
|
warn = (m) => {
|
|
392
1049
|
const colors = [
|
|
393
|
-
(s) =>
|
|
394
|
-
(s) =>
|
|
1050
|
+
(s) => chalk4.hex(THEME.warning)(s),
|
|
1051
|
+
(s) => chalk4.hex(THEME.primary)(s)
|
|
395
1052
|
];
|
|
396
1053
|
const c = colors[Math.floor(Math.random() * colors.length)];
|
|
397
|
-
const line = `${
|
|
1054
|
+
const line = `${chalk4.hex(THEME.muted)(`[${T()}]`)} ${chalk4.hex(THEME.warning).bold("[warn]")} ${c(trunc(m, 120))}`;
|
|
398
1055
|
if (_cooking) _cooking.log(line);
|
|
399
1056
|
else console.warn(line);
|
|
400
1057
|
};
|
|
401
1058
|
success = (m) => {
|
|
402
1059
|
const colors = [
|
|
403
|
-
(s) =>
|
|
404
|
-
(s) =>
|
|
1060
|
+
(s) => chalk4.hex(THEME.success)(s),
|
|
1061
|
+
(s) => chalk4.hex(THEME.info)(s)
|
|
405
1062
|
];
|
|
406
1063
|
const c = colors[Math.floor(Math.random() * colors.length)];
|
|
407
1064
|
output(
|
|
408
|
-
`${
|
|
1065
|
+
`${chalk4.hex(THEME.muted)(`[${T()}]`)} ${chalk4.hex(THEME.success)("[ok]")} ${c(trunc(m, 120))}`
|
|
409
1066
|
);
|
|
410
1067
|
};
|
|
411
1068
|
}
|
|
@@ -946,12 +1603,13 @@ var init_template = __esm({
|
|
|
946
1603
|
"src/server/template.ts"() {
|
|
947
1604
|
"use strict";
|
|
948
1605
|
SERVE_SCRIPT = `#!/usr/bin/env node
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1606
|
+
import http from 'node:http';
|
|
1607
|
+
import fs from 'node:fs';
|
|
1608
|
+
import path from 'node:path';
|
|
1609
|
+
import { fileURLToPath } from 'node:url';
|
|
952
1610
|
|
|
953
1611
|
const PORT = process.env.PORT || 3000;
|
|
954
|
-
const ROOT =
|
|
1612
|
+
const ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
955
1613
|
|
|
956
1614
|
const MIME = {
|
|
957
1615
|
'.html': 'text/html; charset=utf-8',
|
|
@@ -1022,6 +1680,7 @@ const ANSI = {
|
|
|
1022
1680
|
border: '\\x1b[38;2;72;72;72m',
|
|
1023
1681
|
success: '\\x1b[38;2;127;216;143m',
|
|
1024
1682
|
info: '\\x1b[38;2;86;182;194m',
|
|
1683
|
+
error: '\\x1b[38;2;224;108;117m',
|
|
1025
1684
|
};
|
|
1026
1685
|
|
|
1027
1686
|
function color(name, value) {
|
|
@@ -1035,23 +1694,13 @@ function frameLine(label, value) {
|
|
|
1035
1694
|
}
|
|
1036
1695
|
|
|
1037
1696
|
function drawServerUi() {
|
|
1038
|
-
const logo = [
|
|
1039
|
-
'\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 ',
|
|
1040
|
-
'\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',
|
|
1041
|
-
'\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',
|
|
1042
|
-
'\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',
|
|
1043
|
-
'\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',
|
|
1044
|
-
'\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 ',
|
|
1045
|
-
];
|
|
1046
|
-
|
|
1047
|
-
console.log('');
|
|
1048
|
-
for (const line of logo) console.log(' ' + color('primary', line));
|
|
1049
1697
|
console.log('');
|
|
1050
1698
|
console.log(' ' + color('border', '\u256D\u2500' + '\u2500'.repeat(56) + '\u2500\u256E'));
|
|
1051
|
-
console.log(' ' + color('border', '\u2502 ') + color('text', ' Framer Export
|
|
1699
|
+
console.log(' ' + color('border', '\u2502 ') + color('text', ' Framer Export preview server') + ' '.repeat(26) + color('border', ' \u2502'));
|
|
1052
1700
|
console.log(' ' + color('border', '\u251C\u2500' + '\u2500'.repeat(56) + '\u2500\u2524'));
|
|
1053
1701
|
frameLine('URL', 'http://localhost:' + PORT);
|
|
1054
1702
|
frameLine('Root', ROOT);
|
|
1703
|
+
frameLine('Run', 'node serve.js');
|
|
1055
1704
|
frameLine('Mode', 'static mirror + SPA fallback');
|
|
1056
1705
|
console.log(' ' + color('border', '\u2570\u2500' + '\u2500'.repeat(56) + '\u2500\u256F'));
|
|
1057
1706
|
console.log('');
|
|
@@ -1060,9 +1709,12 @@ function drawServerUi() {
|
|
|
1060
1709
|
|
|
1061
1710
|
server.listen(PORT, () => {
|
|
1062
1711
|
const frames = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];
|
|
1712
|
+
const steps = ['Preparing server', 'Checking exported files', 'Binding local port', 'Serving static mirror'];
|
|
1063
1713
|
let i = 0;
|
|
1064
1714
|
const timer = setInterval(() => {
|
|
1065
|
-
|
|
1715
|
+
const step = steps[Math.min(steps.length - 1, Math.floor(i / 6))];
|
|
1716
|
+
const dots = color('error', '.'.repeat(i % 4).padEnd(3, ' '));
|
|
1717
|
+
process.stdout.write('\\r\\x1B[2K ' + color('primary', frames[i % frames.length]) + ' ' + color('text', step) + dots);
|
|
1066
1718
|
i++;
|
|
1067
1719
|
}, 80);
|
|
1068
1720
|
|
|
@@ -1070,7 +1722,7 @@ server.listen(PORT, () => {
|
|
|
1070
1722
|
clearInterval(timer);
|
|
1071
1723
|
process.stdout.write('\\r\\x1B[2K');
|
|
1072
1724
|
drawServerUi();
|
|
1073
|
-
},
|
|
1725
|
+
}, 1280);
|
|
1074
1726
|
});
|
|
1075
1727
|
`;
|
|
1076
1728
|
}
|
|
@@ -1236,8 +1888,13 @@ async function buildOutput(exporter) {
|
|
|
1236
1888
|
log("Writing index.html (" + (html.length / 1024).toFixed(1) + " KB)...");
|
|
1237
1889
|
await fs2.writeFile(path3.join(exporter.outDir, "index.html"), html);
|
|
1238
1890
|
success("index.html written");
|
|
1239
|
-
await fs2.writeFile(path3.join(exporter.outDir, "serve.
|
|
1240
|
-
log("serve.
|
|
1891
|
+
await fs2.writeFile(path3.join(exporter.outDir, "serve.js"), SERVE_SCRIPT);
|
|
1892
|
+
log("serve.js written");
|
|
1893
|
+
await fs2.writeFile(
|
|
1894
|
+
path3.join(exporter.outDir, "package.json"),
|
|
1895
|
+
JSON.stringify({ type: "module", scripts: { serve: "node serve.js" } }, null, 2) + "\n"
|
|
1896
|
+
);
|
|
1897
|
+
log("package.json written for serve.js");
|
|
1241
1898
|
success("Output build complete");
|
|
1242
1899
|
}
|
|
1243
1900
|
async function rewriteDownloadedFiles(exporter) {
|
|
@@ -1318,52 +1975,6 @@ var init_output = __esm({
|
|
|
1318
1975
|
}
|
|
1319
1976
|
});
|
|
1320
1977
|
|
|
1321
|
-
// src/cli/box.ts
|
|
1322
|
-
import chalk3 from "chalk";
|
|
1323
|
-
function maxWidth() {
|
|
1324
|
-
return Math.min(process.stdout.columns || 80, 76);
|
|
1325
|
-
}
|
|
1326
|
-
function padRight(text, w) {
|
|
1327
|
-
const visible = stripAnsi(text).length;
|
|
1328
|
-
if (visible >= w) return text;
|
|
1329
|
-
return text + " ".repeat(w - visible);
|
|
1330
|
-
}
|
|
1331
|
-
function boxTop(w) {
|
|
1332
|
-
const inner = w - 4;
|
|
1333
|
-
return " " + ui.border("\u256D\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256E");
|
|
1334
|
-
}
|
|
1335
|
-
function boxBot(w) {
|
|
1336
|
-
const inner = w - 4;
|
|
1337
|
-
return " " + ui.border("\u2570\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256F");
|
|
1338
|
-
}
|
|
1339
|
-
function boxLine(w, text) {
|
|
1340
|
-
const inner = w - 4;
|
|
1341
|
-
const padded = padRight(text, inner);
|
|
1342
|
-
return " " + ui.border("\u2502 ") + padded + ui.border(" \u2502");
|
|
1343
|
-
}
|
|
1344
|
-
function boxSep(w) {
|
|
1345
|
-
const inner = w - 4;
|
|
1346
|
-
return " " + ui.border("\u251C\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u2524");
|
|
1347
|
-
}
|
|
1348
|
-
function boxRow(w, label, value) {
|
|
1349
|
-
const inner = w - 4;
|
|
1350
|
-
const labelPlain = stripAnsi(chalk3.bold(label));
|
|
1351
|
-
const visible = labelPlain.length + 1 + value.length;
|
|
1352
|
-
if (visible > inner) {
|
|
1353
|
-
const avail = inner - labelPlain.length - 2;
|
|
1354
|
-
const truncated = value.length > avail ? value.slice(0, avail - 1) + ".." : value;
|
|
1355
|
-
return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(truncated) + " ".repeat(Math.max(0, inner - labelPlain.length - 1 - truncated.length)) + ui.border(" \u2502");
|
|
1356
|
-
}
|
|
1357
|
-
const right = inner - labelPlain.length - 1 - value.length;
|
|
1358
|
-
return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(value) + " ".repeat(right) + ui.border(" \u2502");
|
|
1359
|
-
}
|
|
1360
|
-
var init_box = __esm({
|
|
1361
|
-
"src/cli/box.ts"() {
|
|
1362
|
-
"use strict";
|
|
1363
|
-
init_theme();
|
|
1364
|
-
}
|
|
1365
|
-
});
|
|
1366
|
-
|
|
1367
1978
|
// src/exporter/summary.ts
|
|
1368
1979
|
import fs3 from "fs/promises";
|
|
1369
1980
|
import path4 from "path";
|
|
@@ -1409,277 +2020,50 @@ async function printSummary(exporter) {
|
|
|
1409
2020
|
];
|
|
1410
2021
|
console.log("");
|
|
1411
2022
|
if (!isSmall) {
|
|
1412
|
-
console.log(boxTop(w));
|
|
1413
|
-
console.log(boxLine(w, `${ui.text.bold(" Export Summary")} ${chip("done")}`));
|
|
1414
|
-
console.log(boxSep(w));
|
|
1415
|
-
} else {
|
|
1416
|
-
console.log(ui.text.bold(" Export Summary:"));
|
|
1417
|
-
}
|
|
1418
|
-
for (const [label, cnt, type, color] of entries) {
|
|
1419
|
-
if (cnt === 0) continue;
|
|
1420
|
-
const inner = w - 4;
|
|
1421
|
-
const l = label.padEnd(16);
|
|
1422
|
-
const c = String(cnt).padStart(3);
|
|
1423
|
-
const rowText = `${color(l)}${ui.text(c)} ${ui.muted(type)}`;
|
|
1424
|
-
const visible = 16 + 3 + 2 + type.length;
|
|
1425
|
-
const pad = Math.max(0, inner - visible);
|
|
1426
|
-
if (isSmall) {
|
|
1427
|
-
console.log(` ${color(label)} ${ui.text(String(cnt))} ${ui.muted(type)}`);
|
|
1428
|
-
} else {
|
|
1429
|
-
console.log(" " + ui.border("\u2502 ") + rowText + " ".repeat(pad) + ui.border(" \u2502"));
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
if (!isSmall) {
|
|
1433
|
-
console.log(boxBot(w));
|
|
1434
|
-
}
|
|
1435
|
-
console.log("");
|
|
1436
|
-
const cdCmd = "cd " + path4.basename(exporter.outDir) + " && node serve.cjs";
|
|
1437
|
-
if (!isSmall) {
|
|
1438
|
-
console.log(boxTop(w));
|
|
1439
|
-
console.log(boxLine(w, ui.text.bold(" To serve locally")));
|
|
1440
|
-
console.log(boxSep(w));
|
|
1441
|
-
const inner = w - 6;
|
|
1442
|
-
const cmdLen = cdCmd.length;
|
|
1443
|
-
const pad = Math.max(0, inner - cmdLen);
|
|
1444
|
-
console.log(
|
|
1445
|
-
" " + ui.border("\u2502 ") + G(cdCmd) + " ".repeat(pad) + ui.muted(" copy") + ui.border(" \u2502")
|
|
1446
|
-
);
|
|
1447
|
-
console.log(boxBot(w));
|
|
1448
|
-
} else {
|
|
1449
|
-
console.log(ui.text.bold(" To serve locally:"));
|
|
1450
|
-
console.log(` ${G(cdCmd)}`);
|
|
1451
|
-
}
|
|
1452
|
-
console.log("");
|
|
1453
|
-
console.log(ui.muted(" note: must be served via HTTP for JS modules to work."));
|
|
1454
|
-
console.log("");
|
|
1455
|
-
}
|
|
1456
|
-
var init_summary = __esm({
|
|
1457
|
-
"src/exporter/summary.ts"() {
|
|
1458
|
-
"use strict";
|
|
1459
|
-
init_box();
|
|
1460
|
-
init_theme();
|
|
1461
|
-
}
|
|
1462
|
-
});
|
|
1463
|
-
|
|
1464
|
-
// src/cli/select.ts
|
|
1465
|
-
import readline from "readline";
|
|
1466
|
-
import { stdin, stdout } from "process";
|
|
1467
|
-
async function select(question, options, defaultIndex = 0) {
|
|
1468
|
-
const isTTY = stdin.isTTY && stdout.isTTY;
|
|
1469
|
-
if (!isTTY) {
|
|
1470
|
-
return fallbackPrompt(question, options, defaultIndex);
|
|
1471
|
-
}
|
|
1472
|
-
return arrowSelect(question, options, defaultIndex);
|
|
1473
|
-
}
|
|
1474
|
-
async function arrowSelect(question, options, defaultIndex) {
|
|
1475
|
-
const panelTopRow = await queryCursorRow();
|
|
1476
|
-
const width = Math.min(maxWidth(), 72);
|
|
1477
|
-
const inner = width - 4;
|
|
1478
|
-
const optionStartOffset = 3;
|
|
1479
|
-
const lineCount = options.length + 4;
|
|
1480
|
-
return new Promise((resolve) => {
|
|
1481
|
-
const firstEnabled = options.findIndex((option) => !option.disabled);
|
|
1482
|
-
let selected = options[defaultIndex]?.disabled ? firstEnabled : defaultIndex;
|
|
1483
|
-
if (selected < 0) selected = 0;
|
|
1484
|
-
const move = (direction) => {
|
|
1485
|
-
let next = selected + direction;
|
|
1486
|
-
while (next >= 0 && next < options.length) {
|
|
1487
|
-
if (!options[next].disabled) {
|
|
1488
|
-
selected = next;
|
|
1489
|
-
return;
|
|
1490
|
-
}
|
|
1491
|
-
next += direction;
|
|
1492
|
-
}
|
|
1493
|
-
};
|
|
1494
|
-
const render = (initial = false) => {
|
|
1495
|
-
if (!initial) {
|
|
1496
|
-
stdout.write(`\x1B[${lineCount}A`);
|
|
1497
|
-
stdout.write("\x1B[J");
|
|
1498
|
-
}
|
|
1499
|
-
console.log(boxTop(width));
|
|
1500
|
-
console.log(
|
|
1501
|
-
boxLine(width, centerText(`${ui.primary("\u25CF")} ${ui.text.bold(question)}`, inner))
|
|
1502
|
-
);
|
|
1503
|
-
console.log(boxSep(width));
|
|
1504
|
-
for (let i = 0; i < options.length; i++) {
|
|
1505
|
-
console.log(
|
|
1506
|
-
boxLine(width, centerText(renderOption(options[i], i === selected, inner), inner))
|
|
1507
|
-
);
|
|
1508
|
-
}
|
|
1509
|
-
console.log(boxBot(width));
|
|
1510
|
-
};
|
|
1511
|
-
const choose = () => {
|
|
1512
|
-
cleanup();
|
|
1513
|
-
stdout.write(`\x1B[${lineCount}A`);
|
|
1514
|
-
stdout.write("\x1B[J");
|
|
1515
|
-
console.log(
|
|
1516
|
-
` ${ui.success("\u2713")} ${ui.text.bold(question)} ${ui.primary(stripAnsi(options[selected].label))}
|
|
1517
|
-
`
|
|
1518
|
-
);
|
|
1519
|
-
resolve(options[selected].value);
|
|
1520
|
-
};
|
|
1521
|
-
const onMouseData = (chunk) => {
|
|
1522
|
-
const mouse = parseMouseEvent(chunk);
|
|
1523
|
-
if (!mouse) return;
|
|
1524
|
-
if (mouse.kind === "wheel-up") {
|
|
1525
|
-
move(-1);
|
|
1526
|
-
render();
|
|
1527
|
-
return;
|
|
1528
|
-
}
|
|
1529
|
-
if (mouse.kind === "wheel-down") {
|
|
1530
|
-
move(1);
|
|
1531
|
-
render();
|
|
1532
|
-
return;
|
|
1533
|
-
}
|
|
1534
|
-
if (panelTopRow === null) return;
|
|
1535
|
-
const idx = mouse.y - panelTopRow - optionStartOffset;
|
|
1536
|
-
if (idx < 0 || idx >= options.length || options[idx].disabled) return;
|
|
1537
|
-
if (selected !== idx) {
|
|
1538
|
-
selected = idx;
|
|
1539
|
-
render();
|
|
1540
|
-
}
|
|
1541
|
-
if (mouse.kind === "click") {
|
|
1542
|
-
choose();
|
|
1543
|
-
}
|
|
1544
|
-
};
|
|
1545
|
-
const cleanup = () => {
|
|
1546
|
-
disableMouse();
|
|
1547
|
-
stdin.setRawMode(false);
|
|
1548
|
-
stdin.removeListener("keypress", onKeypress);
|
|
1549
|
-
stdin.removeListener("data", onMouseData);
|
|
1550
|
-
stdin.pause();
|
|
1551
|
-
};
|
|
1552
|
-
render(true);
|
|
1553
|
-
readline.emitKeypressEvents(stdin);
|
|
1554
|
-
stdin.setRawMode(true);
|
|
1555
|
-
enableInteractiveInput();
|
|
1556
|
-
const onKeypress = (_str, key) => {
|
|
1557
|
-
if (!key) return;
|
|
1558
|
-
if (key.name === "up" && selected > 0) {
|
|
1559
|
-
move(-1);
|
|
1560
|
-
render();
|
|
1561
|
-
} else if (key.name === "down" && selected < options.length - 1) {
|
|
1562
|
-
move(1);
|
|
1563
|
-
render();
|
|
1564
|
-
} else if (key.name === "return") {
|
|
1565
|
-
choose();
|
|
1566
|
-
} else if (key.ctrl && key.name === "c" || key.name === "escape") {
|
|
1567
|
-
cleanup();
|
|
1568
|
-
process.exit(0);
|
|
1569
|
-
}
|
|
1570
|
-
};
|
|
1571
|
-
stdin.resume();
|
|
1572
|
-
stdin.on("data", onMouseData);
|
|
1573
|
-
stdin.on("keypress", onKeypress);
|
|
1574
|
-
});
|
|
1575
|
-
}
|
|
1576
|
-
async function fallbackPrompt(question, options, defaultIndex) {
|
|
1577
|
-
return new Promise((resolve) => {
|
|
1578
|
-
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
1579
|
-
const firstEnabled = options.findIndex((option) => !option.disabled);
|
|
1580
|
-
const enabledDefault = options[defaultIndex]?.disabled ? firstEnabled : defaultIndex;
|
|
1581
|
-
console.log(` ${ui.primary("\u25CF")} ${ui.text.bold(question)}
|
|
1582
|
-
`);
|
|
1583
|
-
for (let i = 0; i < options.length; i++) {
|
|
1584
|
-
const marker = i === enabledDefault ? ui.success(" \u25C6") : " ";
|
|
1585
|
-
const label = options[i].disabled ? ui.muted(stripAnsi(options[i].label)) : ui.text(options[i].label);
|
|
1586
|
-
console.log(` ${ui.muted(`[${i + 1}]`)}${marker} ${label}`);
|
|
1587
|
-
}
|
|
1588
|
-
console.log("");
|
|
1589
|
-
const def = String(enabledDefault + 1);
|
|
1590
|
-
const ask = () => {
|
|
1591
|
-
rl.question(
|
|
1592
|
-
` ${ui.primary(">")} ${ui.muted(`Choose [1-${options.length}] (${def})`)}: `,
|
|
1593
|
-
(answer) => {
|
|
1594
|
-
const trimmed = answer.trim();
|
|
1595
|
-
if (!trimmed) {
|
|
1596
|
-
rl.close();
|
|
1597
|
-
const label = stripAnsi(options[enabledDefault].label);
|
|
1598
|
-
console.log(` ${ui.success("\u2713")} ${ui.primary(label)}
|
|
1599
|
-
`);
|
|
1600
|
-
resolve(options[enabledDefault].value);
|
|
1601
|
-
return;
|
|
1602
|
-
}
|
|
1603
|
-
const idx = parseInt(trimmed, 10);
|
|
1604
|
-
if (idx >= 1 && idx <= options.length && !options[idx - 1].disabled) {
|
|
1605
|
-
rl.close();
|
|
1606
|
-
const label = stripAnsi(options[idx - 1].label);
|
|
1607
|
-
console.log(` ${ui.success("\u2713")} ${ui.primary(label)}
|
|
1608
|
-
`);
|
|
1609
|
-
resolve(options[idx - 1].value);
|
|
1610
|
-
} else if (idx >= 1 && idx <= options.length && options[idx - 1].disabled) {
|
|
1611
|
-
console.log(` ${ui.error("\u2717")} ${ui.warning("Option unavailable for now")}
|
|
1612
|
-
`);
|
|
1613
|
-
ask();
|
|
1614
|
-
} else {
|
|
1615
|
-
console.log(` ${ui.error("\u2717")} ${ui.warning(`Enter 1-${options.length}`)}
|
|
1616
|
-
`);
|
|
1617
|
-
ask();
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
);
|
|
1621
|
-
};
|
|
1622
|
-
ask();
|
|
1623
|
-
});
|
|
1624
|
-
}
|
|
1625
|
-
function renderOption(option, selected, width) {
|
|
1626
|
-
const plain = truncatePlain(stripAnsi(option.label), Math.max(10, width - 12));
|
|
1627
|
-
if (option.disabled) {
|
|
1628
|
-
return `${ui.border("[")} ${ui.muted(plain)} ${ui.border("]")}`;
|
|
2023
|
+
console.log(boxTop(w));
|
|
2024
|
+
console.log(boxLine(w, `${ui.text.bold(" Export Summary")} ${chip("done")}`));
|
|
2025
|
+
console.log(boxSep(w));
|
|
2026
|
+
} else {
|
|
2027
|
+
console.log(ui.text.bold(" Export Summary:"));
|
|
1629
2028
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
2029
|
+
for (const [label, cnt, type, color] of entries) {
|
|
2030
|
+
if (cnt === 0) continue;
|
|
2031
|
+
const inner = w - 4;
|
|
2032
|
+
const l = label.padEnd(16);
|
|
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"));
|
|
2041
|
+
}
|
|
1632
2042
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
return null;
|
|
1655
|
-
}
|
|
1656
|
-
function queryCursorRow() {
|
|
1657
|
-
if (!stdin.isTTY || !stdout.isTTY) return Promise.resolve(null);
|
|
1658
|
-
return new Promise((resolve) => {
|
|
1659
|
-
const wasRaw = stdin.isRaw;
|
|
1660
|
-
let done = false;
|
|
1661
|
-
const finish = (row) => {
|
|
1662
|
-
if (done) return;
|
|
1663
|
-
done = true;
|
|
1664
|
-
clearTimeout(timer);
|
|
1665
|
-
stdin.removeListener("data", onData);
|
|
1666
|
-
if (!wasRaw) stdin.setRawMode(false);
|
|
1667
|
-
resolve(row);
|
|
1668
|
-
};
|
|
1669
|
-
const onData = (chunk) => {
|
|
1670
|
-
const match = chunk.toString("utf-8").match(/\x1B\[(\d+);\d+R/);
|
|
1671
|
-
if (!match) return;
|
|
1672
|
-
finish(Number(match[1]));
|
|
1673
|
-
};
|
|
1674
|
-
const timer = setTimeout(() => finish(null), 80);
|
|
1675
|
-
stdin.setRawMode(true);
|
|
1676
|
-
stdin.resume();
|
|
1677
|
-
stdin.on("data", onData);
|
|
1678
|
-
stdout.write("\x1B[6n");
|
|
1679
|
-
});
|
|
2043
|
+
if (!isSmall) {
|
|
2044
|
+
console.log(boxBot(w));
|
|
2045
|
+
}
|
|
2046
|
+
console.log("");
|
|
2047
|
+
const cdCmd = "cd " + path4.basename(exporter.outDir);
|
|
2048
|
+
const runCmd = "node serve.js";
|
|
2049
|
+
if (!isSmall) {
|
|
2050
|
+
console.log(boxTop(w));
|
|
2051
|
+
console.log(boxLine(w, ui.text.bold(" To serve locally")));
|
|
2052
|
+
console.log(boxSep(w));
|
|
2053
|
+
console.log(boxRow(w, "Open", cdCmd));
|
|
2054
|
+
console.log(boxRow(w, "Run", runCmd));
|
|
2055
|
+
console.log(boxBot(w));
|
|
2056
|
+
} else {
|
|
2057
|
+
console.log(ui.text.bold(" To serve locally:"));
|
|
2058
|
+
console.log(` ${G(cdCmd)}`);
|
|
2059
|
+
console.log(` ${G(runCmd)}`);
|
|
2060
|
+
}
|
|
2061
|
+
console.log("");
|
|
2062
|
+
console.log(ui.muted(" note: must be served via HTTP for JS modules to work."));
|
|
2063
|
+
console.log("");
|
|
1680
2064
|
}
|
|
1681
|
-
var
|
|
1682
|
-
"src/
|
|
2065
|
+
var init_summary = __esm({
|
|
2066
|
+
"src/exporter/summary.ts"() {
|
|
1683
2067
|
"use strict";
|
|
1684
2068
|
init_box();
|
|
1685
2069
|
init_theme();
|
|
@@ -1693,17 +2077,10 @@ import { stdin as stdin2, stdout as stdout2 } from "process";
|
|
|
1693
2077
|
import { spawn } from "child_process";
|
|
1694
2078
|
async function runAiPromptAssistant(exporter) {
|
|
1695
2079
|
if (!stdin2.isTTY || !stdout2.isTTY) return;
|
|
1696
|
-
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
{ label: `${buttonLabel("Convert")} open AI prompt modal`, value: "convert" },
|
|
1701
|
-
{ label: `${buttonLabel("Skip")} finish export`, value: "skip" }
|
|
1702
|
-
],
|
|
1703
|
-
0
|
|
1704
|
-
);
|
|
1705
|
-
if (action === "skip") return;
|
|
1706
|
-
printAssistantModal("AI conversion prompt", [
|
|
2080
|
+
const serveCommand = buildServeCommand(exporter);
|
|
2081
|
+
const shouldConvert = await runExportCompletePrompt(exporter, serveCommand);
|
|
2082
|
+
if (!shouldConvert) return;
|
|
2083
|
+
printAssistantModal(`${ui.text.bold("AI conversion prompt")} ${ui.error("BETA")}`, [
|
|
1707
2084
|
"Choose a target stack, AI tool, and conversion situation.",
|
|
1708
2085
|
"Mouse clicks are supported in the terminal when available.",
|
|
1709
2086
|
"A detailed prompt file will be generated inside the export folder."
|
|
@@ -1732,16 +2109,8 @@ async function runAiPromptAssistant(exporter) {
|
|
|
1732
2109
|
const promptPath = path5.join(aiDir, `${aiTool.id}-${target.id}-${goal.id}-prompt.md`);
|
|
1733
2110
|
await fs4.mkdir(aiDir, { recursive: true });
|
|
1734
2111
|
await fs4.writeFile(promptPath, prompt, "utf-8");
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
"Prompt actions",
|
|
1738
|
-
[
|
|
1739
|
-
{ label: `${buttonLabel("Copy prompt")} clipboard`, value: "copy" },
|
|
1740
|
-
{ label: `${buttonLabel("Done")} keep file only`, value: "done" }
|
|
1741
|
-
],
|
|
1742
|
-
0
|
|
1743
|
-
);
|
|
1744
|
-
if (promptAction === "copy") {
|
|
2112
|
+
const copyPrompt = await runPromptReadyPrompt(promptPath, target, aiTool, goal);
|
|
2113
|
+
if (copyPrompt) {
|
|
1745
2114
|
try {
|
|
1746
2115
|
await copyToClipboard(prompt);
|
|
1747
2116
|
console.log(` ${ui.success("\u2713")} ${ui.text.bold("Prompt copied to clipboard")}
|
|
@@ -1799,27 +2168,97 @@ function centerText2(text, width) {
|
|
|
1799
2168
|
const left = Math.floor((width - visible) / 2);
|
|
1800
2169
|
return " ".repeat(left) + text + " ".repeat(width - visible - left);
|
|
1801
2170
|
}
|
|
1802
|
-
function
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
2171
|
+
function buildServeCommand(exporter) {
|
|
2172
|
+
return `cd ${path5.basename(exporter.outDir)} && node serve.js`;
|
|
2173
|
+
}
|
|
2174
|
+
async function runExportCompletePrompt(exporter, serveCommand) {
|
|
2175
|
+
let copyServeCommand = false;
|
|
2176
|
+
while (true) {
|
|
2177
|
+
const action = await select(
|
|
2178
|
+
"Export complete",
|
|
2179
|
+
[
|
|
2180
|
+
{
|
|
2181
|
+
label: `${checkboxLabel(copyServeCommand)} Copy serve command when finishing`,
|
|
2182
|
+
value: "toggle-copy"
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
label: `${buttonLabel("Convert to AI code")} ${ui.error("BETA")}`,
|
|
2186
|
+
value: "convert"
|
|
2187
|
+
},
|
|
2188
|
+
{ label: buttonLabel("Finish"), value: "finish" }
|
|
2189
|
+
],
|
|
2190
|
+
2,
|
|
2191
|
+
{
|
|
2192
|
+
headerLines: [
|
|
2193
|
+
`Output: ${path5.basename(exporter.outDir)}`,
|
|
2194
|
+
`Run: ${serveCommand}`,
|
|
2195
|
+
"Use Convert to open the AI conversion assistant."
|
|
2196
|
+
],
|
|
2197
|
+
footer: "enter select \xB7 checkbox toggles copy \xB7 mouse hover/click"
|
|
2198
|
+
}
|
|
2199
|
+
);
|
|
2200
|
+
if (action === "toggle-copy") {
|
|
2201
|
+
copyServeCommand = !copyServeCommand;
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
if (action === "finish") {
|
|
2205
|
+
if (copyServeCommand) {
|
|
2206
|
+
try {
|
|
2207
|
+
await copyToClipboard(serveCommand);
|
|
2208
|
+
console.log(` ${ui.success("\u2713")} ${ui.text.bold("Serve command copied")}
|
|
2209
|
+
`);
|
|
2210
|
+
} catch (error) {
|
|
2211
|
+
console.log(
|
|
2212
|
+
` ${ui.warning("!")} ${ui.warning("Clipboard copy unavailable:")} ${ui.muted(error.message)}
|
|
2213
|
+
`
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
return false;
|
|
2218
|
+
}
|
|
2219
|
+
return true;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
async function runPromptReadyPrompt(promptPath, target, aiTool, goal) {
|
|
2223
|
+
const relPath = path5.relative(process.cwd(), promptPath) || promptPath;
|
|
2224
|
+
let copyPrompt = false;
|
|
2225
|
+
while (true) {
|
|
2226
|
+
const action = await select(
|
|
2227
|
+
`AI Prompt Ready ${ui.error("BETA")}`,
|
|
2228
|
+
[
|
|
2229
|
+
{
|
|
2230
|
+
label: `${checkboxLabel(copyPrompt)} Copy prompt when finishing`,
|
|
2231
|
+
value: "toggle-copy"
|
|
2232
|
+
},
|
|
2233
|
+
{ label: buttonLabel("Finish"), value: "finish" }
|
|
2234
|
+
],
|
|
2235
|
+
1,
|
|
2236
|
+
{
|
|
2237
|
+
headerLines: [
|
|
2238
|
+
`Tool: ${aiTool.displayName}`,
|
|
2239
|
+
`Stack: ${target.label}`,
|
|
2240
|
+
`Mode: ${goal.label}`,
|
|
2241
|
+
`File: ${relPath}`
|
|
2242
|
+
],
|
|
2243
|
+
footer: "enter finish \xB7 checkbox toggles copy \xB7 mouse hover/click"
|
|
2244
|
+
}
|
|
2245
|
+
);
|
|
2246
|
+
if (action === "toggle-copy") {
|
|
2247
|
+
copyPrompt = !copyPrompt;
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
return copyPrompt;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
function checkboxLabel(checked) {
|
|
2254
|
+
return checked ? ui.success("\u2611") : ui.muted("\u2610");
|
|
1816
2255
|
}
|
|
1817
2256
|
function printAssistantModal(title, lines) {
|
|
1818
2257
|
const w = maxWidth();
|
|
1819
2258
|
const inner = w - 4;
|
|
1820
2259
|
console.log("");
|
|
1821
2260
|
console.log(boxTop(w));
|
|
1822
|
-
console.log(boxLine(w, centerText2(`${ui.primary("\u25C6")} ${
|
|
2261
|
+
console.log(boxLine(w, centerText2(`${ui.primary("\u25C6")} ${title}`, inner)));
|
|
1823
2262
|
console.log(boxSep(w));
|
|
1824
2263
|
for (const line of lines) {
|
|
1825
2264
|
console.log(boxLine(w, centerText2(ui.muted(line), inner)));
|
|
@@ -2002,44 +2441,6 @@ function pipeToCommand(command, args, input) {
|
|
|
2002
2441
|
child.stdin?.end(input);
|
|
2003
2442
|
});
|
|
2004
2443
|
}
|
|
2005
|
-
function printPromptResult(promptPath, target, aiTool, goal) {
|
|
2006
|
-
const w = maxWidth();
|
|
2007
|
-
const isSmall = w < 50;
|
|
2008
|
-
const inner = w - 4;
|
|
2009
|
-
const relPath = path5.relative(process.cwd(), promptPath) || promptPath;
|
|
2010
|
-
console.log("");
|
|
2011
|
-
if (!isSmall) {
|
|
2012
|
-
console.log(boxTop(w));
|
|
2013
|
-
console.log(
|
|
2014
|
-
boxLine(
|
|
2015
|
-
w,
|
|
2016
|
-
centerText2(
|
|
2017
|
-
`${ui.success("\u2713")} ${ui.text.bold("AI Prompt Ready")} ${ui.muted("BETA")}`,
|
|
2018
|
-
inner
|
|
2019
|
-
)
|
|
2020
|
-
)
|
|
2021
|
-
);
|
|
2022
|
-
console.log(boxSep(w));
|
|
2023
|
-
console.log(
|
|
2024
|
-
boxLine(w, centerText2(`${ui.muted("Tool")} ${ui.primary(aiTool.displayName)}`, inner))
|
|
2025
|
-
);
|
|
2026
|
-
console.log(boxLine(w, centerText2(`${ui.muted("Stack")} ${ui.primary(target.label)}`, inner)));
|
|
2027
|
-
console.log(boxLine(w, centerText2(`${ui.muted("Mode")} ${ui.primary(goal.label)}`, inner)));
|
|
2028
|
-
console.log(boxLine(w, centerText2(`${ui.muted("File")} ${ui.primary(relPath)}`, inner)));
|
|
2029
|
-
console.log(boxSep(w));
|
|
2030
|
-
console.log(
|
|
2031
|
-
boxLine(w, centerText2(`${buttonLabel("Copy prompt")} ${buttonLabel("Done")}`, inner))
|
|
2032
|
-
);
|
|
2033
|
-
console.log(boxBot(w));
|
|
2034
|
-
} else {
|
|
2035
|
-
console.log(ui.text.bold(" AI Prompt Ready - BETA"));
|
|
2036
|
-
console.log(` Tool: ${ui.primary(aiTool.displayName)}`);
|
|
2037
|
-
console.log(` Stack: ${ui.primary(target.label)}`);
|
|
2038
|
-
console.log(` Situation: ${ui.primary(goal.label)}`);
|
|
2039
|
-
console.log(` File: ${ui.primary(relPath)}`);
|
|
2040
|
-
}
|
|
2041
|
-
console.log("");
|
|
2042
|
-
}
|
|
2043
2444
|
var IMPORTANT_DIRS, AI_TOOLS, TARGETS, GOALS;
|
|
2044
2445
|
var init_prompt_assistant = __esm({
|
|
2045
2446
|
"src/ai/prompt-assistant.ts"() {
|
|
@@ -2166,82 +2567,6 @@ var init_prompt_assistant = __esm({
|
|
|
2166
2567
|
}
|
|
2167
2568
|
});
|
|
2168
2569
|
|
|
2169
|
-
// src/cli/cooking.ts
|
|
2170
|
-
import chalk4 from "chalk";
|
|
2171
|
-
var SHINE_WIDTH, FRAME_INTERVAL, SPINNER_FRAMES, CookingSpinner;
|
|
2172
|
-
var init_cooking = __esm({
|
|
2173
|
-
"src/cli/cooking.ts"() {
|
|
2174
|
-
"use strict";
|
|
2175
|
-
init_theme();
|
|
2176
|
-
SHINE_WIDTH = 10;
|
|
2177
|
-
FRAME_INTERVAL = 80;
|
|
2178
|
-
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2179
|
-
CookingSpinner = class {
|
|
2180
|
-
interval = null;
|
|
2181
|
-
frame = 0;
|
|
2182
|
-
phase = "";
|
|
2183
|
-
active = false;
|
|
2184
|
-
start(phase = "") {
|
|
2185
|
-
this.phase = phase;
|
|
2186
|
-
this.frame = 0;
|
|
2187
|
-
this.active = true;
|
|
2188
|
-
this.draw();
|
|
2189
|
-
this.interval = setInterval(() => {
|
|
2190
|
-
this.frame++;
|
|
2191
|
-
this.draw();
|
|
2192
|
-
}, FRAME_INTERVAL);
|
|
2193
|
-
}
|
|
2194
|
-
update(phase) {
|
|
2195
|
-
this.phase = phase;
|
|
2196
|
-
}
|
|
2197
|
-
log(message) {
|
|
2198
|
-
if (this.active) {
|
|
2199
|
-
process.stdout.write("\r\x1B[2K");
|
|
2200
|
-
}
|
|
2201
|
-
process.stdout.write(message + "\n");
|
|
2202
|
-
if (this.active) {
|
|
2203
|
-
this.draw();
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
stop() {
|
|
2207
|
-
this.active = false;
|
|
2208
|
-
if (this.interval) {
|
|
2209
|
-
clearInterval(this.interval);
|
|
2210
|
-
this.interval = null;
|
|
2211
|
-
}
|
|
2212
|
-
process.stdout.write("\r\x1B[2K");
|
|
2213
|
-
}
|
|
2214
|
-
draw() {
|
|
2215
|
-
if (!this.active) return;
|
|
2216
|
-
const spinner = SPINNER_FRAMES[this.frame % SPINNER_FRAMES.length];
|
|
2217
|
-
const shimmer = this.renderShimmer("Exporting");
|
|
2218
|
-
const frameStr = ui.primary(spinner);
|
|
2219
|
-
const phaseStr = this.phase ? ` ${ui.muted(this.limitLen(this.phase, 52))}` : "";
|
|
2220
|
-
process.stdout.write(`\r\x1B[2K ${frameStr} ${shimmer}${phaseStr}`);
|
|
2221
|
-
}
|
|
2222
|
-
renderShimmer(text) {
|
|
2223
|
-
let result = "";
|
|
2224
|
-
const pos = this.frame % (text.length + SHINE_WIDTH * 2) - SHINE_WIDTH;
|
|
2225
|
-
for (let i = 0; i < text.length; i++) {
|
|
2226
|
-
const dist = Math.abs(i - pos);
|
|
2227
|
-
if (dist < SHINE_WIDTH) {
|
|
2228
|
-
const t = 1 - dist / SHINE_WIDTH;
|
|
2229
|
-
const g = Math.floor(160 + t * 95);
|
|
2230
|
-
result += chalk4.rgb(250, Math.min(255, g), Math.min(255, Math.floor(g * 0.75)))(text[i]);
|
|
2231
|
-
} else {
|
|
2232
|
-
result += ui.muted(text[i]);
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
return result;
|
|
2236
|
-
}
|
|
2237
|
-
limitLen(s, max) {
|
|
2238
|
-
if (s.length <= max) return s;
|
|
2239
|
-
return s.slice(0, max - 1) + "\u2026";
|
|
2240
|
-
}
|
|
2241
|
-
};
|
|
2242
|
-
}
|
|
2243
|
-
});
|
|
2244
|
-
|
|
2245
2570
|
// src/exporter/index.ts
|
|
2246
2571
|
var exporter_exports = {};
|
|
2247
2572
|
__export(exporter_exports, {
|
|
@@ -2266,11 +2591,20 @@ function deriveOutputName(url, platformName) {
|
|
|
2266
2591
|
} else {
|
|
2267
2592
|
siteName = hostname.replace(/\./g, "-");
|
|
2268
2593
|
}
|
|
2269
|
-
|
|
2594
|
+
const cleanName = siteName.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "site";
|
|
2595
|
+
return `${platformName}-${cleanName}-${randomOutputSuffix()}`;
|
|
2270
2596
|
} catch {
|
|
2271
|
-
return
|
|
2597
|
+
return `framer-export-output-${randomOutputSuffix()}`;
|
|
2272
2598
|
}
|
|
2273
2599
|
}
|
|
2600
|
+
function randomOutputSuffix() {
|
|
2601
|
+
const adjectives = ["clean", "bright", "swift", "sharp", "fresh", "solid", "tidy", "prime"];
|
|
2602
|
+
const nouns = ["mirror", "export", "site", "build", "copy", "page", "stack", "bundle"];
|
|
2603
|
+
const adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
2604
|
+
const noun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
2605
|
+
const id = Math.random().toString(36).slice(2, 6);
|
|
2606
|
+
return `${adjective}-${noun}-${id}`;
|
|
2607
|
+
}
|
|
2274
2608
|
var FramerExporter;
|
|
2275
2609
|
var init_exporter = __esm({
|
|
2276
2610
|
"src/exporter/index.ts"() {
|
|
@@ -2463,71 +2797,117 @@ async function runSetup(legacyMode = false) {
|
|
|
2463
2797
|
` ${ui.muted("Export Framer, Webflow, and Wix sites into a clean local mirror.")}
|
|
2464
2798
|
`
|
|
2465
2799
|
);
|
|
2466
|
-
const rl = readline2.createInterface({ input: stdin3, output: stdout3 });
|
|
2467
|
-
const ask = async (question, defaultVal) => {
|
|
2800
|
+
const rl = legacyMode ? readline2.createInterface({ input: stdin3, output: stdout3 }) : null;
|
|
2801
|
+
const ask = async (question, defaultVal, headerLines = []) => {
|
|
2802
|
+
if (!legacyMode) {
|
|
2803
|
+
return promptInput(question, defaultVal || "", { headerLines });
|
|
2804
|
+
}
|
|
2468
2805
|
const suffix = defaultVal ? chalk6.gray(` (${defaultVal})`) : "";
|
|
2469
2806
|
const prompt = ` ${ui.primary("\u25CF")} ${ui.text.bold(question)}${suffix} ${ui.muted(">")} `;
|
|
2470
2807
|
const answer = await rl.question(prompt);
|
|
2471
2808
|
return answer.trim() || defaultVal || "";
|
|
2472
2809
|
};
|
|
2473
|
-
drawHeader("Step 1 : Site URL");
|
|
2810
|
+
if (legacyMode) drawHeader("Step 1 : Site URL");
|
|
2474
2811
|
let siteUrl = "";
|
|
2812
|
+
let urlError = "";
|
|
2475
2813
|
while (!siteUrl) {
|
|
2476
|
-
const input = await ask(
|
|
2814
|
+
const input = await ask(
|
|
2815
|
+
"Enter the site URL",
|
|
2816
|
+
"",
|
|
2817
|
+
["Step 1 : Site URL", urlError].filter(Boolean)
|
|
2818
|
+
);
|
|
2477
2819
|
try {
|
|
2478
2820
|
new URL4(input);
|
|
2479
2821
|
siteUrl = input;
|
|
2822
|
+
urlError = "";
|
|
2480
2823
|
} catch {
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
`
|
|
2484
|
-
|
|
2824
|
+
urlError = "Invalid URL. Enter a valid URL (https://...)";
|
|
2825
|
+
if (legacyMode) {
|
|
2826
|
+
console.log(` ${ui.error("\u2717")} ${ui.error(urlError)}
|
|
2827
|
+
`);
|
|
2828
|
+
}
|
|
2485
2829
|
}
|
|
2486
2830
|
}
|
|
2487
|
-
|
|
2831
|
+
if (legacyMode) {
|
|
2832
|
+
console.log(` ${ui.success("\u2713")} ${ui.success("URL:")} ${chalk6.underline(siteUrl)}
|
|
2488
2833
|
`);
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
let platformName;
|
|
2834
|
+
}
|
|
2835
|
+
let platformName = null;
|
|
2492
2836
|
if (legacyMode) {
|
|
2837
|
+
drawHeader("Step 2 : Platform");
|
|
2838
|
+
const detected = detectPlatform(siteUrl);
|
|
2493
2839
|
console.log(` ${ui.info("i")} Auto-detected: ${ui.primary(detected.displayName)}`);
|
|
2494
2840
|
const platformInput = await ask("Platform (framer/webflow/wix)", detected.name);
|
|
2495
2841
|
platformName = ["framer", "webflow", "wix"].includes(platformInput) ? platformInput : detected.name;
|
|
2496
2842
|
console.log(` ${ui.success("\u2713")} ${ui.success("Platform:")} ${ui.primary(platformName)}
|
|
2497
2843
|
`);
|
|
2498
2844
|
} else {
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
"Select platform",
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2845
|
+
while (!platformName) {
|
|
2846
|
+
const detected = detectPlatform(siteUrl);
|
|
2847
|
+
const platforms = [
|
|
2848
|
+
{
|
|
2849
|
+
label: `Framer${detected.name === "framer" ? chalk6.gray(" (detected)") : ""}`,
|
|
2850
|
+
value: "framer"
|
|
2851
|
+
},
|
|
2852
|
+
{
|
|
2853
|
+
label: `Webflow${detected.name === "webflow" ? chalk6.gray(" (detected)") : ""}`,
|
|
2854
|
+
value: "webflow"
|
|
2855
|
+
},
|
|
2856
|
+
{ label: `Wix${detected.name === "wix" ? chalk6.gray(" (detected)") : ""}`, value: "wix" }
|
|
2857
|
+
];
|
|
2858
|
+
const defaultIdx = ["framer", "webflow", "wix"].indexOf(detected.name);
|
|
2859
|
+
const platformChoice = await select("Select platform", platforms, Math.max(0, defaultIdx), {
|
|
2860
|
+
headerLines: [`URL: ${siteUrl}`],
|
|
2861
|
+
actions: [{ label: "Modify URL", value: "modify-url" }],
|
|
2862
|
+
footer: "tab focus button \xB7 mouse hover/click \xB7 enter select"
|
|
2863
|
+
});
|
|
2864
|
+
if (platformChoice === "modify-url") {
|
|
2865
|
+
siteUrl = "";
|
|
2866
|
+
urlError = "";
|
|
2867
|
+
while (!siteUrl) {
|
|
2868
|
+
const input = await ask(
|
|
2869
|
+
"Modify site URL",
|
|
2870
|
+
"",
|
|
2871
|
+
["Step 1 : Site URL", urlError].filter(Boolean)
|
|
2872
|
+
);
|
|
2873
|
+
try {
|
|
2874
|
+
new URL4(input);
|
|
2875
|
+
siteUrl = input;
|
|
2876
|
+
urlError = "";
|
|
2877
|
+
} catch {
|
|
2878
|
+
urlError = "Invalid URL. Enter a valid URL (https://...)";
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
continue;
|
|
2882
|
+
}
|
|
2883
|
+
platformName = platformChoice;
|
|
2884
|
+
}
|
|
2517
2885
|
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2886
|
+
if (!platformName) {
|
|
2887
|
+
throw new Error("Platform selection failed");
|
|
2888
|
+
}
|
|
2889
|
+
const rl2 = legacyMode ? rl : null;
|
|
2890
|
+
const ask2 = async (question, defaultVal, headerLines = []) => {
|
|
2891
|
+
if (!legacyMode) {
|
|
2892
|
+
return promptInput(question, defaultVal || "", { headerLines });
|
|
2893
|
+
}
|
|
2520
2894
|
const suffix = defaultVal ? chalk6.gray(` (${defaultVal})`) : "";
|
|
2521
2895
|
const prompt = ` ${ui.primary("\u25CF")} ${ui.text.bold(question)}${suffix} ${ui.muted(">")} `;
|
|
2522
2896
|
const answer = await rl2.question(prompt);
|
|
2523
2897
|
return answer.trim() || defaultVal || "";
|
|
2524
2898
|
};
|
|
2525
2899
|
const derivedName = deriveOutputName(siteUrl, platformName);
|
|
2526
|
-
drawHeader("Step 3 : Output Directory");
|
|
2527
|
-
const outDir = await ask2("Output directory", "./" + derivedName
|
|
2528
|
-
|
|
2900
|
+
if (legacyMode) drawHeader("Step 3 : Output Directory");
|
|
2901
|
+
const outDir = await ask2("Output directory", "./" + derivedName, [
|
|
2902
|
+
"Step 3 : Output Directory",
|
|
2903
|
+
`URL: ${siteUrl}`,
|
|
2904
|
+
`Platform: ${platformName}`
|
|
2905
|
+
]);
|
|
2906
|
+
if (legacyMode) {
|
|
2907
|
+
console.log(` ${ui.success("\u2713")} ${ui.success("Output:")} ${ui.primary(outDir)}
|
|
2529
2908
|
`);
|
|
2530
|
-
|
|
2909
|
+
}
|
|
2910
|
+
if (legacyMode) drawHeader("Step 4 : Options");
|
|
2531
2911
|
let prettyPrint;
|
|
2532
2912
|
let concurrency;
|
|
2533
2913
|
let includeSubpages;
|
|
@@ -2549,7 +2929,6 @@ async function runSetup(legacyMode = false) {
|
|
|
2549
2929
|
console.log(` ${ui.success("\u2713")} Concurrency: ${ui.primary(String(concurrency))}
|
|
2550
2930
|
`);
|
|
2551
2931
|
} else {
|
|
2552
|
-
rl2.close();
|
|
2553
2932
|
const prettyVal = await select("Pretty-print JS files?", [
|
|
2554
2933
|
{ label: "Yes", value: "yes" },
|
|
2555
2934
|
{ label: "No", value: "no" }
|
|
@@ -2640,6 +3019,7 @@ var init_setup = __esm({
|
|
|
2640
3019
|
init_package();
|
|
2641
3020
|
import path8 from "path";
|
|
2642
3021
|
import { URL as URL5 } from "url";
|
|
3022
|
+
import { spawnSync } from "child_process";
|
|
2643
3023
|
|
|
2644
3024
|
// src/cli/help.ts
|
|
2645
3025
|
init_banner();
|
|
@@ -2702,6 +3082,7 @@ function showHelp() {
|
|
|
2702
3082
|
|
|
2703
3083
|
// src/cli/index.ts
|
|
2704
3084
|
init_banner();
|
|
3085
|
+
init_cooking();
|
|
2705
3086
|
|
|
2706
3087
|
// src/cli/update-check.ts
|
|
2707
3088
|
import https from "https";
|
|
@@ -2718,7 +3099,7 @@ async function checkForUpdates(currentVersion) {
|
|
|
2718
3099
|
const json = JSON.parse(data);
|
|
2719
3100
|
const latest = json.version;
|
|
2720
3101
|
if (!latest) return resolve(null);
|
|
2721
|
-
if (latest
|
|
3102
|
+
if (isNewerVersion(latest, currentVersion)) return resolve(latest);
|
|
2722
3103
|
resolve(null);
|
|
2723
3104
|
} catch {
|
|
2724
3105
|
resolve(null);
|
|
@@ -2733,8 +3114,23 @@ async function checkForUpdates(currentVersion) {
|
|
|
2733
3114
|
});
|
|
2734
3115
|
});
|
|
2735
3116
|
}
|
|
3117
|
+
function isNewerVersion(latest, current) {
|
|
3118
|
+
const latestParts = parseVersion(latest);
|
|
3119
|
+
const currentParts = parseVersion(current);
|
|
3120
|
+
for (let i = 0; i < Math.max(latestParts.length, currentParts.length); i++) {
|
|
3121
|
+
const latestPart = latestParts[i] ?? 0;
|
|
3122
|
+
const currentPart = currentParts[i] ?? 0;
|
|
3123
|
+
if (latestPart > currentPart) return true;
|
|
3124
|
+
if (latestPart < currentPart) return false;
|
|
3125
|
+
}
|
|
3126
|
+
return false;
|
|
3127
|
+
}
|
|
3128
|
+
function parseVersion(version) {
|
|
3129
|
+
return version.replace(/^v/, "").split(/[.-]/).map((part) => Number.parseInt(part, 10)).filter((part) => Number.isFinite(part));
|
|
3130
|
+
}
|
|
2736
3131
|
|
|
2737
3132
|
// src/cli/index.ts
|
|
3133
|
+
init_select();
|
|
2738
3134
|
init_theme();
|
|
2739
3135
|
var VERSION = package_default.version;
|
|
2740
3136
|
function extractFlag(args, flag) {
|
|
@@ -2750,6 +3146,56 @@ function hasFlag(args, flag) {
|
|
|
2750
3146
|
args.splice(idx, 1);
|
|
2751
3147
|
return true;
|
|
2752
3148
|
}
|
|
3149
|
+
async function showUpdateNotice() {
|
|
3150
|
+
const latest = await checkForUpdates(VERSION);
|
|
3151
|
+
if (!latest) return;
|
|
3152
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
3153
|
+
console.log("");
|
|
3154
|
+
console.log(
|
|
3155
|
+
` ${ui.warning("\u21B3")} Update available: ${ui.muted(VERSION)} -> ${ui.success(latest)}`
|
|
3156
|
+
);
|
|
3157
|
+
console.log(` ${ui.primary(" Run:")} ${ui.primarySoft("npm i -g framer-export@latest")}`);
|
|
3158
|
+
console.log("");
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
const action = await select(
|
|
3162
|
+
"Update available",
|
|
3163
|
+
[
|
|
3164
|
+
{ label: "Continue without updating", value: "continue" },
|
|
3165
|
+
{ label: "Update now", value: "update" }
|
|
3166
|
+
],
|
|
3167
|
+
0,
|
|
3168
|
+
{
|
|
3169
|
+
headerLines: [
|
|
3170
|
+
`Current version: ${VERSION}`,
|
|
3171
|
+
`Latest version: ${latest}`,
|
|
3172
|
+
"You can continue now and update later."
|
|
3173
|
+
],
|
|
3174
|
+
footer: "enter continue \xB7 mouse hover/click"
|
|
3175
|
+
}
|
|
3176
|
+
);
|
|
3177
|
+
if (action === "update") {
|
|
3178
|
+
console.log(
|
|
3179
|
+
` ${ui.primary("Updating:")} ${ui.primarySoft("npm i -g framer-export@latest")}
|
|
3180
|
+
`
|
|
3181
|
+
);
|
|
3182
|
+
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
3183
|
+
const result = spawnSync(npmCommand, ["i", "-g", "framer-export@latest"], {
|
|
3184
|
+
stdio: "inherit"
|
|
3185
|
+
});
|
|
3186
|
+
if (result.status === 0) {
|
|
3187
|
+
console.log(`
|
|
3188
|
+
${ui.success("\u2713")} Updated. Re-run your command to use the new version.
|
|
3189
|
+
`);
|
|
3190
|
+
process.exit(0);
|
|
3191
|
+
}
|
|
3192
|
+
console.log(
|
|
3193
|
+
`
|
|
3194
|
+
${ui.error("\u2717")} Update failed. Run manually: ${ui.primarySoft("npm i -g framer-export@latest")}
|
|
3195
|
+
`
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
2753
3199
|
async function main() {
|
|
2754
3200
|
const args = process.argv.slice(2);
|
|
2755
3201
|
if (args.includes("--version") || args.includes("-v")) {
|
|
@@ -2777,19 +3223,12 @@ async function main() {
|
|
|
2777
3223
|
console.log("");
|
|
2778
3224
|
process.exit(0);
|
|
2779
3225
|
}
|
|
2780
|
-
checkForUpdates(VERSION).then((latest) => {
|
|
2781
|
-
if (!latest) return;
|
|
2782
|
-
console.log("");
|
|
2783
|
-
console.log(
|
|
2784
|
-
` ${ui.warning("\u21B3")} Update available: ${ui.muted(VERSION)} -> ${ui.success(latest)}`
|
|
2785
|
-
);
|
|
2786
|
-
console.log(` ${ui.primary(" Run:")} ${ui.primarySoft("npm i -g framer-export@latest")}`);
|
|
2787
|
-
console.log("");
|
|
2788
|
-
});
|
|
2789
3226
|
if (args.includes("--help") || args.includes("-h")) {
|
|
2790
3227
|
showHelp();
|
|
2791
3228
|
process.exit(0);
|
|
2792
3229
|
}
|
|
3230
|
+
await showLoadingIntro(VERSION);
|
|
3231
|
+
await showUpdateNotice();
|
|
2793
3232
|
if (args.includes("--setup")) {
|
|
2794
3233
|
hasFlag(args, "--setup");
|
|
2795
3234
|
const legacyMode = hasFlag(args, "--legacy-mode");
|