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 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.3.8",
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
- ${ui.primary.bold("f-export")} ${ui.muted(`v${package_default.version}`)} ${chip("beta ui")}`);
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
- " \u2584 \u2584 ",
180
- "\u2588\u2580\u2580 \u2588\u2580\u2588 \u2584\u2580\u2588 \u2588\u2580\u2584\u2580\u2588 \u2588\u2580\u2580 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2580\u2584\u2580 \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588\u2580\u2588 \u2580\u2588\u2580",
181
- "\u2588\u2580 \u2588\u2580\u2584 \u2588\u2580\u2588 \u2588 \u2580 \u2588 \u2588\u2588\u2584 \u2588\u2580\u2584 \u2588\u2588\u2584 \u2588 \u2588 \u2588\u2580\u2580 \u2588\u2584\u2588 \u2588\u2580\u2584 \u2588 ",
182
- "\u2580 \u2580 \u2580 \u2580 \u2580 \u2580 \u2580 \u2580\u2580\u2580 \u2580 \u2580 \u2580\u2580\u2580 \u2580 \u2580 \u2580 \u2580\u2580\u2580 \u2580 \u2580 \u2580 "
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 chalk2 from "chalk";
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) => chalk2.hex(THEME.primary)(s),
344
- (s) => chalk2.hex(THEME.primarySoft)(s),
345
- (s) => chalk2.hex(THEME.secondary)(s),
346
- (s) => chalk2.hex(THEME.accent)(s),
347
- (s) => chalk2.hex(THEME.info)(s),
348
- (s) => chalk2.hex(THEME.text)(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(`${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.primary)("[log]")} ${c(trunc(m, 120))}`);
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) => chalk2.hex(THEME.info)(s),
358
- (s) => chalk2.hex(THEME.secondary)(s),
359
- (s) => chalk2.hex(THEME.primarySoft)(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(`${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.info).bold("[info]")} ${c(trunc(m, 120))}`);
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) => chalk2.hex(THEME.warning)(s),
370
- (s) => chalk2.hex(THEME.primary)(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 = `${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.warning).bold("[warn]")} ${c(trunc(m, 120))}`;
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) => chalk2.hex(THEME.success)(s),
380
- (s) => chalk2.hex(THEME.info)(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(`${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.success)("[ok]")} ${c(trunc(m, 120))}`);
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
- console.log('\\x1b[32m%s\\x1b[0m', ' \u{1F680} Cooksite Local Server Running');
992
- console.log('\\x1b[36m%s\\x1b[0m', ' \u251C\u2500 URL: http://localhost:' + PORT);
993
- console.log('\\x1b[35m%s\\x1b[0m', ' \u2514\u2500 Directory: ' + ROOT);
994
- console.log('\\n Press Ctrl+C to stop.');
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("</head>", ` <meta name="description" content="Exported with Cooksite - Fast, SEO-optimized, and clean.">
1069
- </head>`);
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("</head>", ` <meta property="og:type" content="website">
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
- console.log("");
1463
- const def = String(enabledDefault + 1);
1464
- const ask = () => {
1465
- rl.question(
1466
- ` ${ui.primary(">")} ${ui.muted(`Choose [1-${options.length}] (${def})`)}: `,
1467
- (answer) => {
1468
- const trimmed = answer.trim();
1469
- if (!trimmed) {
1470
- rl.close();
1471
- const label = stripAnsi(options[enabledDefault].label);
1472
- console.log(` ${ui.success("\u2713")} ${ui.primary(label)}
1473
- `);
1474
- resolve(options[enabledDefault].value);
1475
- return;
1476
- }
1477
- const idx = parseInt(trimmed, 10);
1478
- if (idx >= 1 && idx <= options.length && !options[idx - 1].disabled) {
1479
- rl.close();
1480
- const label = stripAnsi(options[idx - 1].label);
1481
- console.log(` ${ui.success("\u2713")} ${ui.primary(label)}
1482
- `);
1483
- resolve(options[idx - 1].value);
1484
- } else if (idx >= 1 && idx <= options.length && options[idx - 1].disabled) {
1485
- console.log(` ${ui.error("\u2717")} ${ui.warning("Option unavailable for now")}
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 init_select = __esm({
1500
- "src/cli/select.ts"() {
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
- console.log("");
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 copyChoice = await select(
1543
- "Copy prompt to clipboard?",
2122
+ const promptAction = await select(
2123
+ "Prompt actions",
1544
2124
  [
1545
- { label: "Copy prompt", value: "yes" },
1546
- { label: "No, keep file only", value: "no" }
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 (copyChoice === "yes") {
2130
+ if (promptAction === "copy") {
1551
2131
  try {
1552
2132
  await copyToClipboard(prompt);
1553
- console.log(` ${chalk4.green("\u2713")} ${chalk4.white.bold("Prompt copied to clipboard")}
2133
+ console.log(` ${ui.success("\u2713")} ${ui.text.bold("Prompt copied to clipboard")}
1554
2134
  `);
1555
2135
  } catch (error) {
1556
2136
  console.log(
1557
- ` ${chalk4.yellow("!")} ${chalk4.yellow("Clipboard copy unavailable:")} ${chalk4.gray(error.message)}
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 Cooksite / Framer Export.`,
1619
- "This brief was generated by the Cooksite AI Prompt Assistant BETA.",
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(boxLine(w, chalk4.bold.white(" AI Prompt Ready - BETA")));
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(boxLine(w, ` Tool: ${aiTool.displayName}`));
1784
- console.log(boxLine(w, ` Stack: ${target.label}`));
1785
- console.log(boxLine(w, ` Situation: ${goal.label}`));
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(chalk4.bold.white(" AI Prompt Ready - BETA"));
1790
- console.log(` Tool: ${chalk4.hex("#D4A017")(aiTool.displayName)}`);
1791
- console.log(` Stack: ${chalk4.hex("#D4A017")(target.label)}`);
1792
- console.log(` Situation: ${chalk4.hex("#D4A017")(goal.label)}`);
1793
- console.log(` File: ${chalk4.hex("#D4A017")(relPath)}`);
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 chalk6 from "chalk";
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
- return `${platformName}-${siteName}`;
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 "framer-export-output";
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
- info("Source : " + chalk6.underline(this.siteUrl));
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(chalk6.red("Could not fetch SSR HTML: " + e.message));
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 chalk7 from "chalk";
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(` ${ui.muted("Export Framer, Webflow, and Wix sites into a clean local mirror.")}
2216
- `);
2217
- const rl = readline2.createInterface({ input: stdin3, output: stdout3 });
2218
- const ask = async (question, defaultVal) => {
2219
- const suffix = defaultVal ? chalk7.gray(` (${defaultVal})`) : "";
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("Enter the site URL");
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
- console.log(` ${ui.error("\u2717")} ${ui.error("Invalid URL. Enter a valid URL (https://...)")}
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
- console.log(` ${ui.success("\u2713")} ${ui.success("URL:")} ${chalk7.underline(siteUrl)}
2816
+ if (legacyMode) {
2817
+ console.log(` ${ui.success("\u2713")} ${ui.success("URL:")} ${chalk6.underline(siteUrl)}
2237
2818
  `);
2238
- drawHeader("Step 2 : Platform");
2239
- const detected = detectPlatform(siteUrl);
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
- rl.close();
2249
- const platforms = [
2250
- { label: `Framer${detected.name === "framer" ? chalk7.gray(" (detected)") : ""}`, value: "framer" },
2251
- { label: `Webflow${detected.name === "webflow" ? chalk7.gray(" (detected)") : ""}`, value: "webflow" },
2252
- { label: `Wix${detected.name === "wix" ? chalk7.gray(" (detected)") : ""}`, value: "wix" }
2253
- ];
2254
- const defaultIdx = ["framer", "webflow", "wix"].indexOf(detected.name);
2255
- platformName = await select("Select platform", platforms, Math.max(0, defaultIdx));
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
- const rl2 = legacyMode ? rl : readline2.createInterface({ input: stdin3, output: stdout3 });
2258
- const ask2 = async (question, defaultVal) => {
2259
- const suffix = defaultVal ? chalk7.gray(` (${defaultVal})`) : "";
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
- console.log(` ${ui.success("\u2713")} ${ui.success("Output:")} ${ui.primary(outDir)}
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
- drawHeader("Step 4 : Options");
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(` ${ui.success("\u2713")} Pretty-print: ${prettyPrint ? ui.success("yes") : ui.error("no")}
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(` ${ui.success("\u2713")} Sub-pages: ${includeSubpages ? ui.success("yes") : ui.error("no")}
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("Export sub-pages?", [
2294
- { label: "No", value: "no" },
2295
- { label: "Yes, crawl and export", value: "yes" }
2296
- ], 0);
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("Download concurrency", [
2299
- { label: "6 (slow connection)", value: "6" },
2300
- { label: "12 (default)", value: "12" },
2301
- { label: "20 (fast connection)", value: "20" }
2302
- ], 1);
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(` ${ui.primary("framer-export")} ${ui.warning("<url>")} ${ui.muted("[output-dir]")}`);
2379
- console.log(` ${ui.primary("fexport")} ${ui.warning("<url>")} ${ui.muted("[output-dir]")}`);
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(` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://mysite.framer.app")}`);
2405
- console.log(` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://mysite.webflow.io")}`);
2406
- console.log(` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://user.wixsite.com/my-site")}`);
2407
- console.log(` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--platform webflow")} ${ui.warning("https://custom.com")}`);
2408
- console.log(` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--subpages")} ${ui.warning("https://mysite.com")}`);
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(` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--setup --legacy-mode")}`);
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 !== currentVersion) return resolve(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 chalk8 = (await import("chalk")).default;
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(` ${ui.text.bold("Portfolio")} ${chalk8.underline.hex("#FAB283")("https://github.com/danbenba")}`);
2478
- console.log(` ${ui.text.bold("GitHub")} ${chalk8.underline.hex("#FAB283")(package_default.repository.url.replace("git+", "").replace(".git", ""))}`);
2479
- console.log(` ${ui.text.bold("npm")} ${chalk8.underline.hex("#FAB283")(`https://www.npmjs.com/package/${package_default.name}`)}`);
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(includeSubpages);
3247
+ await new FramerExporter2(url, path8.resolve(out), platformOverride || void 0).run(
3248
+ includeSubpages
3249
+ );
2527
3250
  } catch (e) {
2528
- const chalk8 = (await import("chalk")).default;
2529
- console.log(`
2530
- ${ui.error("\u2717")} ${ui.error.bold("FAILED:")} ${ui.text(e.message)}`);
2531
- console.log(chalk8.gray(e.stack || ""));
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
  }