framer-export 4.3.7 → 4.3.10

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.7",
18
+ version: "4.3.10",
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",
@@ -82,8 +82,77 @@ var init_package = __esm({
82
82
  }
83
83
  });
84
84
 
85
- // src/cli/banner.ts
85
+ // src/cli/theme.ts
86
86
  import chalk from "chalk";
87
+ function stripAnsi(s) {
88
+ return s.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
89
+ }
90
+ function softGradient(text) {
91
+ const colors = [THEME.primary, THEME.primarySoft, THEME.text, THEME.primarySoft, THEME.primary];
92
+ let cursor = 0;
93
+ return text.split("").map((char) => {
94
+ if (char === " ") return char;
95
+ const color = colors[cursor % colors.length];
96
+ cursor++;
97
+ return chalk.hex(color).bold(char);
98
+ }).join("");
99
+ }
100
+ function chip(label) {
101
+ return `${ui.border("[")}${ui.primary(label)}${ui.border("]")}`;
102
+ }
103
+ function bullet(label = "\u2022") {
104
+ return ui.primary(label);
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
+ }
116
+ var THEME, ui;
117
+ var init_theme = __esm({
118
+ "src/cli/theme.ts"() {
119
+ "use strict";
120
+ THEME = {
121
+ background: "#0A0A0A",
122
+ panel: "#141414",
123
+ element: "#1E1E1E",
124
+ border: "#484848",
125
+ borderActive: "#606060",
126
+ text: "#EEEEEE",
127
+ muted: "#808080",
128
+ primary: "#FAB283",
129
+ primarySoft: "#FFC09F",
130
+ secondary: "#5C9CF5",
131
+ accent: "#9D7CD8",
132
+ success: "#7FD88F",
133
+ warning: "#F5A742",
134
+ error: "#E06C75",
135
+ info: "#56B6C2"
136
+ };
137
+ ui = {
138
+ text: chalk.hex(THEME.text),
139
+ muted: chalk.hex(THEME.muted),
140
+ primary: chalk.hex(THEME.primary),
141
+ primarySoft: chalk.hex(THEME.primarySoft),
142
+ secondary: chalk.hex(THEME.secondary),
143
+ accent: chalk.hex(THEME.accent),
144
+ success: chalk.hex(THEME.success),
145
+ warning: chalk.hex(THEME.warning),
146
+ error: chalk.hex(THEME.error),
147
+ info: chalk.hex(THEME.info),
148
+ border: chalk.hex(THEME.border),
149
+ borderActive: chalk.hex(THEME.borderActive),
150
+ panel: chalk.hex(THEME.panel)
151
+ };
152
+ }
153
+ });
154
+
155
+ // src/cli/banner.ts
87
156
  function getWidth() {
88
157
  return process.stdout.columns || 80;
89
158
  }
@@ -91,34 +160,46 @@ function showBanner() {
91
160
  const width = getWidth();
92
161
  const isSmall = width < 65;
93
162
  if (isSmall) {
94
- console.log(`
95
- ${chalk.hex("#D4A017").bold("F-EXPORT")} ${chalk.gray(`v${package_default.version}`)}`);
96
- console.log(` ${chalk.white.bold("Framer \xB7 Webflow \xB7 Wix Exporter")}
163
+ console.log(
164
+ `
165
+ ${ui.primary.bold("f-export")} ${ui.muted(`v${package_default.version}`)} ${chip("beta ui")}`
166
+ );
167
+ console.log(` ${ui.text.bold("Framer Export")} ${ui.muted("for Framer, Webflow, and Wix")}
97
168
  `);
98
169
  return;
99
170
  }
100
- const gold = chalk.hex("#D4A017").bold;
101
- const gray = chalk.gray;
102
171
  console.log("");
103
172
  ASCII_ART.forEach((line) => {
104
- console.log(" " + gold(line));
173
+ console.log(" " + softGradient(line));
105
174
  });
106
175
  console.log("");
107
- console.log(` ${gray(`v${package_default.version}`)} ${chalk.white.bold("Framer \xB7 Webflow \xB7 Wix Exporter")}
108
- `);
176
+ console.log(
177
+ ` ${ui.muted(`v${package_default.version}`)} ${ui.text.bold("Framer Export")} ${chip("fexport")} ${ui.muted("local mirror exporter")}`
178
+ );
179
+ console.log(
180
+ ` ${ui.muted("Framer")} ${ui.border("/")} ${ui.muted("Webflow")} ${ui.border("/")} ${ui.muted("Wix")} ${ui.border("\xB7")} ${ui.primary("clean assets")} ${ui.border("\xB7")} ${ui.secondary("local serve")}
181
+ `
182
+ );
109
183
  }
110
184
  var ASCII_ART;
111
185
  var init_banner = __esm({
112
186
  "src/cli/banner.ts"() {
113
187
  "use strict";
114
188
  init_package();
189
+ init_theme();
115
190
  ASCII_ART = [
116
- "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\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",
117
- "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\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",
118
- "\u2588\u2588\u2588\u2588\u2588\u2557 \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 ",
119
- "\u2588\u2588\u2554\u2550\u2550\u255D \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 ",
120
- "\u2588\u2588\u2551 \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 ",
121
- "\u255A\u2550\u255D \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"
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 "
122
203
  ];
123
204
  }
124
205
  });
@@ -259,7 +340,7 @@ var init_download = __esm({
259
340
  });
260
341
 
261
342
  // src/logger/index.ts
262
- import chalk3 from "chalk";
343
+ import chalk2 from "chalk";
263
344
  function setCooking(spinner) {
264
345
  _cooking = spinner;
265
346
  }
@@ -275,64 +356,57 @@ var _cooking, T, LOG_PALETTE, _li, log, INFO_PALETTE, _ii, info, warn, success;
275
356
  var init_logger = __esm({
276
357
  "src/logger/index.ts"() {
277
358
  "use strict";
359
+ init_theme();
278
360
  _cooking = null;
279
361
  T = () => (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
280
362
  LOG_PALETTE = [
281
- (s) => chalk3.hex("#D4A017")(s),
282
- (s) => chalk3.hex("#DAA520")(s),
283
- (s) => chalk3.hex("#C8A951")(s),
284
- (s) => chalk3.hex("#E0B040")(s),
285
- (s) => chalk3.hex("#daa520")(s),
286
- (s) => chalk3.hex("#c4953a")(s),
287
- (s) => chalk3.hex("#e8c33a")(s),
288
- (s) => chalk3.hex("#dab660")(s),
289
- (s) => chalk3.hex("#b8960a")(s),
290
- (s) => chalk3.hex("#d4a76a")(s),
291
- (s) => chalk3.hex("#e0a030")(s),
292
- (s) => chalk3.hex("#c0a060")(s)
363
+ (s) => chalk2.hex(THEME.primary)(s),
364
+ (s) => chalk2.hex(THEME.primarySoft)(s),
365
+ (s) => chalk2.hex(THEME.secondary)(s),
366
+ (s) => chalk2.hex(THEME.accent)(s),
367
+ (s) => chalk2.hex(THEME.info)(s),
368
+ (s) => chalk2.hex(THEME.text)(s)
293
369
  ];
294
370
  _li = 0;
295
371
  log = (m) => {
296
372
  _li++;
297
373
  const c = LOG_PALETTE[_li % LOG_PALETTE.length];
298
- output(`${chalk3.hex("#6d6d6d")(`[${T()}]`)} ${chalk3.hex("#B8960A")("[LOG]")} ${c(trunc(m, 120))}`);
374
+ output(
375
+ `${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.primary)("[log]")} ${c(trunc(m, 120))}`
376
+ );
299
377
  };
300
378
  INFO_PALETTE = [
301
- (s) => chalk3.hex("#4DB8B0")(s),
302
- (s) => chalk3.hex("#5CC0BA")(s),
303
- (s) => chalk3.hex("#3AA89E")(s),
304
- (s) => chalk3.hex("#6BC4BE")(s),
305
- (s) => chalk3.hex("#48B5AD")(s),
306
- (s) => chalk3.hex("#52BAB4")(s)
379
+ (s) => chalk2.hex(THEME.info)(s),
380
+ (s) => chalk2.hex(THEME.secondary)(s),
381
+ (s) => chalk2.hex(THEME.primarySoft)(s)
307
382
  ];
308
383
  _ii = 0;
309
384
  info = (m) => {
310
385
  _ii++;
311
386
  const c = INFO_PALETTE[_ii % INFO_PALETTE.length];
312
- output(`${chalk3.hex("#6d6d6d")(`[${T()}]`)} ${chalk3.hex("#3AA89E").bold("[INFO]")} ${c(trunc(m, 120))}`);
387
+ output(
388
+ `${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.info).bold("[info]")} ${c(trunc(m, 120))}`
389
+ );
313
390
  };
314
391
  warn = (m) => {
315
392
  const colors = [
316
- (s) => chalk3.hex("#E8833A")(s),
317
- (s) => chalk3.hex("#F09040")(s),
318
- (s) => chalk3.hex("#CC7722")(s),
319
- (s) => chalk3.hex("#E87D2A")(s)
393
+ (s) => chalk2.hex(THEME.warning)(s),
394
+ (s) => chalk2.hex(THEME.primary)(s)
320
395
  ];
321
396
  const c = colors[Math.floor(Math.random() * colors.length)];
322
- const line = `${chalk3.hex("#7a7a7a")(`[${T()}]`)} ${chalk3.hex("#E8833A").bold("[WARN]")} ${c(trunc(m, 120))}`;
397
+ const line = `${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.warning).bold("[warn]")} ${c(trunc(m, 120))}`;
323
398
  if (_cooking) _cooking.log(line);
324
399
  else console.warn(line);
325
400
  };
326
401
  success = (m) => {
327
402
  const colors = [
328
- (s) => chalk3.hex("#4CAF50")(s),
329
- (s) => chalk3.hex("#66BB6A")(s),
330
- (s) => chalk3.hex("#43A047")(s),
331
- (s) => chalk3.hex("#81C784")(s),
332
- (s) => chalk3.hex("#2E7D32")(s)
403
+ (s) => chalk2.hex(THEME.success)(s),
404
+ (s) => chalk2.hex(THEME.info)(s)
333
405
  ];
334
406
  const c = colors[Math.floor(Math.random() * colors.length)];
335
- output(`${chalk3.hex("#6d6d6d")(`[${T()}]`)} ${chalk3.greenBright("[OK]")} ${c(trunc(m, 120))}`);
407
+ output(
408
+ `${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.success)("[ok]")} ${c(trunc(m, 120))}`
409
+ );
336
410
  };
337
411
  }
338
412
  });
@@ -939,11 +1013,64 @@ const server = http.createServer((req, res) => {
939
1013
  serveFile(filePath);
940
1014
  });
941
1015
 
1016
+ const ANSI = {
1017
+ reset: '\\x1b[0m',
1018
+ primary: '\\x1b[38;2;250;178;131m',
1019
+ soft: '\\x1b[38;2;255;192;159m',
1020
+ text: '\\x1b[38;2;238;238;238m',
1021
+ muted: '\\x1b[38;2;128;128;128m',
1022
+ border: '\\x1b[38;2;72;72;72m',
1023
+ success: '\\x1b[38;2;127;216;143m',
1024
+ info: '\\x1b[38;2;86;182;194m',
1025
+ };
1026
+
1027
+ function color(name, value) {
1028
+ return ANSI[name] + value + ANSI.reset;
1029
+ }
1030
+
1031
+ function frameLine(label, value) {
1032
+ const text = ' ' + label.padEnd(10) + value;
1033
+ const pad = Math.max(0, 56 - text.length);
1034
+ console.log(' ' + color('border', '\u2502 ') + color('muted', label.padEnd(10)) + color('text', value) + ' '.repeat(pad) + color('border', ' \u2502'));
1035
+ }
1036
+
1037
+ 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
+ console.log('');
1050
+ console.log(' ' + color('border', '\u256D\u2500' + '\u2500'.repeat(56) + '\u2500\u256E'));
1051
+ console.log(' ' + color('border', '\u2502 ') + color('text', ' Framer Export local server') + ' '.repeat(28) + color('border', ' \u2502'));
1052
+ console.log(' ' + color('border', '\u251C\u2500' + '\u2500'.repeat(56) + '\u2500\u2524'));
1053
+ frameLine('URL', 'http://localhost:' + PORT);
1054
+ frameLine('Root', ROOT);
1055
+ frameLine('Mode', 'static mirror + SPA fallback');
1056
+ console.log(' ' + color('border', '\u2570\u2500' + '\u2500'.repeat(56) + '\u2500\u256F'));
1057
+ console.log('');
1058
+ console.log(' ' + color('success', 'ready') + color('muted', ' press Ctrl+C to stop') + '\\n');
1059
+ }
1060
+
942
1061
  server.listen(PORT, () => {
943
- console.log('\\x1b[32m%s\\x1b[0m', ' \u{1F680} Cooksite Local Server Running');
944
- console.log('\\x1b[36m%s\\x1b[0m', ' \u251C\u2500 URL: http://localhost:' + PORT);
945
- console.log('\\x1b[35m%s\\x1b[0m', ' \u2514\u2500 Directory: ' + ROOT);
946
- console.log('\\n Press Ctrl+C to stop.');
1062
+ const frames = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];
1063
+ let i = 0;
1064
+ const timer = setInterval(() => {
1065
+ process.stdout.write('\\r\\x1B[2K ' + color('primary', frames[i % frames.length]) + ' ' + color('muted', 'starting Framer Export server'));
1066
+ i++;
1067
+ }, 80);
1068
+
1069
+ setTimeout(() => {
1070
+ clearInterval(timer);
1071
+ process.stdout.write('\\r\\x1B[2K');
1072
+ drawServerUi();
1073
+ }, 720);
947
1074
  });
948
1075
  `;
949
1076
  }
@@ -1017,15 +1144,21 @@ function processSEO(html, url) {
1017
1144
  </head>`);
1018
1145
  }
1019
1146
  if (!html.includes('name="description"')) {
1020
- html = html.replace("</head>", ` <meta name="description" content="Exported with Cooksite - Fast, SEO-optimized, and clean.">
1021
- </head>`);
1147
+ html = html.replace(
1148
+ "</head>",
1149
+ ` <meta name="description" content="Exported with Framer Export - Fast, SEO-optimized, and clean.">
1150
+ </head>`
1151
+ );
1022
1152
  }
1023
1153
  if (!html.includes('property="og:')) {
1024
- html = html.replace("</head>", ` <meta property="og:type" content="website">
1154
+ html = html.replace(
1155
+ "</head>",
1156
+ ` <meta property="og:type" content="website">
1025
1157
  <meta property="og:url" content="${canonical}">
1026
1158
  <meta property="og:title" content="Exported Site">
1027
1159
  <meta property="og:description" content="A fast, clean version of this site, exported for performance.">
1028
- </head>`);
1160
+ </head>`
1161
+ );
1029
1162
  }
1030
1163
  if (!html.includes('name="robots"')) {
1031
1164
  html = html.replace("</head>", ` <meta name="robots" content="index, follow">
@@ -1186,60 +1319,54 @@ var init_output = __esm({
1186
1319
  });
1187
1320
 
1188
1321
  // src/cli/box.ts
1189
- import chalk4 from "chalk";
1322
+ import chalk3 from "chalk";
1190
1323
  function maxWidth() {
1191
- return Math.min(process.stdout.columns || 80, 62);
1324
+ return Math.min(process.stdout.columns || 80, 76);
1192
1325
  }
1193
1326
  function padRight(text, w) {
1194
1327
  const visible = stripAnsi(text).length;
1195
1328
  if (visible >= w) return text;
1196
1329
  return text + " ".repeat(w - visible);
1197
1330
  }
1198
- function stripAnsi(s) {
1199
- return s.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
1200
- }
1201
1331
  function boxTop(w) {
1202
1332
  const inner = w - 4;
1203
- return " " + B("\u2554\u2550") + B("\u2550".repeat(inner)) + B("\u2550\u2557");
1333
+ return " " + ui.border("\u256D\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256E");
1204
1334
  }
1205
1335
  function boxBot(w) {
1206
1336
  const inner = w - 4;
1207
- return " " + B("\u255A\u2550") + B("\u2550".repeat(inner)) + B("\u2550\u255D");
1337
+ return " " + ui.border("\u2570\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256F");
1208
1338
  }
1209
1339
  function boxLine(w, text) {
1210
1340
  const inner = w - 4;
1211
1341
  const padded = padRight(text, inner);
1212
- return " " + B("\u2551 ") + padded + B(" \u2551");
1342
+ return " " + ui.border("\u2502 ") + padded + ui.border(" \u2502");
1213
1343
  }
1214
1344
  function boxSep(w) {
1215
1345
  const inner = w - 4;
1216
- return " " + B("\u2560\u2550") + B("\u2550".repeat(inner)) + B("\u2550\u2563");
1346
+ return " " + ui.border("\u251C\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u2524");
1217
1347
  }
1218
1348
  function boxRow(w, label, value) {
1219
1349
  const inner = w - 4;
1220
- const labelPlain = stripAnsi(chalk4.bold(label));
1350
+ const labelPlain = stripAnsi(chalk3.bold(label));
1221
1351
  const visible = labelPlain.length + 1 + value.length;
1222
1352
  if (visible > inner) {
1223
1353
  const avail = inner - labelPlain.length - 2;
1224
1354
  const truncated = value.length > avail ? value.slice(0, avail - 1) + ".." : value;
1225
- return " " + G("\u2551 ") + chalk4.bold(label) + ": " + chalk4.yellow(truncated) + " ".repeat(Math.max(0, inner - labelPlain.length - 1 - truncated.length)) + G(" \u2551");
1355
+ return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(truncated) + " ".repeat(Math.max(0, inner - labelPlain.length - 1 - truncated.length)) + ui.border(" \u2502");
1226
1356
  }
1227
1357
  const right = inner - labelPlain.length - 1 - value.length;
1228
- return " " + G("\u2551 ") + chalk4.bold(label) + ": " + chalk4.yellow(value) + " ".repeat(right) + G(" \u2551");
1358
+ return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(value) + " ".repeat(right) + ui.border(" \u2502");
1229
1359
  }
1230
- var B, G;
1231
1360
  var init_box = __esm({
1232
1361
  "src/cli/box.ts"() {
1233
1362
  "use strict";
1234
- B = chalk4.hex("#8B6914");
1235
- G = chalk4.hex("#C4953A");
1363
+ init_theme();
1236
1364
  }
1237
1365
  });
1238
1366
 
1239
1367
  // src/exporter/summary.ts
1240
1368
  import fs3 from "fs/promises";
1241
1369
  import path4 from "path";
1242
- import chalk5 from "chalk";
1243
1370
  async function printSummary(exporter) {
1244
1371
  const w = maxWidth();
1245
1372
  const isSmall = w < 50;
@@ -1261,17 +1388,17 @@ async function printSummary(exporter) {
1261
1388
  count("data"),
1262
1389
  count("subpages")
1263
1390
  ]);
1264
- const G2 = chalk5.hex("#D4A017");
1265
- const G22 = chalk5.hex("#DAA520");
1266
- const C2 = chalk5.hex("#E0B040");
1267
- const Y = chalk5.yellow;
1268
- const O = chalk5.hex("#CC7722");
1269
- const Br = chalk5.hex("#B8860B");
1270
- const Gr = chalk5.gray;
1271
- const Gn = chalk5.green;
1391
+ const G = ui.primary;
1392
+ const G2 = ui.primarySoft;
1393
+ const C2 = ui.secondary;
1394
+ const Y = ui.accent;
1395
+ const O = ui.warning;
1396
+ const Br = ui.info;
1397
+ const Gr = ui.muted;
1398
+ const Gn = ui.success;
1272
1399
  const entries = [
1273
- ["styles/", styles, "CSS", G2],
1274
- ["scripts/vendor/", vendor, "JS vendor", G22],
1400
+ ["styles/", styles, "CSS", G],
1401
+ ["scripts/vendor/", vendor, "JS vendor", G2],
1275
1402
  ["scripts/modules/", scripts, "JS modules", C2],
1276
1403
  ["assets/images/", imgs, "images", Y],
1277
1404
  ["assets/videos/", videos, "videos", O],
@@ -1283,23 +1410,23 @@ async function printSummary(exporter) {
1283
1410
  console.log("");
1284
1411
  if (!isSmall) {
1285
1412
  console.log(boxTop(w));
1286
- console.log(boxLine(w, chalk5.bold.white(" Export Summary")));
1413
+ console.log(boxLine(w, `${ui.text.bold(" Export Summary")} ${chip("done")}`));
1287
1414
  console.log(boxSep(w));
1288
1415
  } else {
1289
- console.log(chalk5.bold.white(" Export Summary:"));
1416
+ console.log(ui.text.bold(" Export Summary:"));
1290
1417
  }
1291
1418
  for (const [label, cnt, type, color] of entries) {
1292
1419
  if (cnt === 0) continue;
1293
1420
  const inner = w - 4;
1294
1421
  const l = label.padEnd(16);
1295
1422
  const c = String(cnt).padStart(3);
1296
- const rowText = `${color(l)}${chalk5.white(c)} ${chalk5.gray(type)}`;
1423
+ const rowText = `${color(l)}${ui.text(c)} ${ui.muted(type)}`;
1297
1424
  const visible = 16 + 3 + 2 + type.length;
1298
1425
  const pad = Math.max(0, inner - visible);
1299
1426
  if (isSmall) {
1300
- console.log(` ${color(label)} ${chalk5.white(String(cnt))} ${chalk5.gray(type)}`);
1427
+ console.log(` ${color(label)} ${ui.text(String(cnt))} ${ui.muted(type)}`);
1301
1428
  } else {
1302
- console.log(" " + chalk5.hex("#C4953A")("\u2551 ") + rowText + " ".repeat(pad) + chalk5.hex("#C4953A")(" \u2551"));
1429
+ console.log(" " + ui.border("\u2502 ") + rowText + " ".repeat(pad) + ui.border(" \u2502"));
1303
1430
  }
1304
1431
  }
1305
1432
  if (!isSmall) {
@@ -1309,37 +1436,746 @@ async function printSummary(exporter) {
1309
1436
  const cdCmd = "cd " + path4.basename(exporter.outDir) + " && node serve.cjs";
1310
1437
  if (!isSmall) {
1311
1438
  console.log(boxTop(w));
1312
- console.log(boxLine(w, chalk5.bold.white(" To serve locally")));
1439
+ console.log(boxLine(w, ui.text.bold(" To serve locally")));
1313
1440
  console.log(boxSep(w));
1314
1441
  const inner = w - 6;
1315
1442
  const cmdLen = cdCmd.length;
1316
1443
  const pad = Math.max(0, inner - cmdLen);
1317
- console.log(" " + chalk5.hex("#C4953A")("\u2551 ") + G2(cdCmd) + " ".repeat(pad) + chalk5.hex("#9e9e9e")(" \u{1F5D0}") + chalk5.hex("#C4953A")(" \u2551"));
1444
+ console.log(
1445
+ " " + ui.border("\u2502 ") + G(cdCmd) + " ".repeat(pad) + ui.muted(" copy") + ui.border(" \u2502")
1446
+ );
1318
1447
  console.log(boxBot(w));
1319
1448
  } else {
1320
- console.log(chalk5.bold.white(" To serve locally:"));
1321
- console.log(` ${G2(cdCmd)}`);
1449
+ console.log(ui.text.bold(" To serve locally:"));
1450
+ console.log(` ${G(cdCmd)}`);
1322
1451
  }
1323
1452
  console.log("");
1324
- console.log(chalk5.gray(" NOTE: Must be served via HTTP for JS modules to work."));
1453
+ console.log(ui.muted(" note: must be served via HTTP for JS modules to work."));
1325
1454
  console.log("");
1326
1455
  }
1327
1456
  var init_summary = __esm({
1328
1457
  "src/exporter/summary.ts"() {
1329
1458
  "use strict";
1330
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("]")}`;
1629
+ }
1630
+ if (selected) {
1631
+ return `${ui.primary("\u203A")} ${ui.primary("[")} ${ui.text.bold(plain)} ${ui.primary("]")} ${ui.primary("\u2039")}`;
1632
+ }
1633
+ return `${ui.muted(" ")} ${ui.border("[")} ${ui.muted(plain)} ${ui.border("]")} ${ui.muted(" ")}`;
1634
+ }
1635
+ function enableInteractiveInput() {
1636
+ stdout.write("\x1B[?25l\x1B[?1000h\x1B[?1002h\x1B[?1003h\x1B[?1006h");
1637
+ }
1638
+ function disableMouse() {
1639
+ stdout.write("\x1B[?1003l\x1B[?1002l\x1B[?1000l\x1B[?1006l\x1B[?25h");
1640
+ }
1641
+ function parseMouseEvent(chunk) {
1642
+ const text = chunk.toString("utf-8");
1643
+ const match = text.match(/\x1B\[<(\d+);(\d+);(\d+)([mM])/);
1644
+ if (!match) return null;
1645
+ const code = Number(match[1]);
1646
+ const x = Number(match[2]);
1647
+ const y = Number(match[3]);
1648
+ const state = match[4];
1649
+ if (code === 64) return { kind: "wheel-up", x, y };
1650
+ if (code === 65) return { kind: "wheel-down", x, y };
1651
+ if (state === "m") return { kind: "click", x, y };
1652
+ if ((code & 32) === 32 || code === 35) return { kind: "hover", x, y };
1653
+ if ((code & 3) === 0) return { kind: "hover", x, y };
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
+ });
1680
+ }
1681
+ var init_select = __esm({
1682
+ "src/cli/select.ts"() {
1683
+ "use strict";
1684
+ init_box();
1685
+ init_theme();
1686
+ }
1687
+ });
1688
+
1689
+ // src/ai/prompt-assistant.ts
1690
+ import fs4 from "fs/promises";
1691
+ import path5 from "path";
1692
+ import { stdin as stdin2, stdout as stdout2 } from "process";
1693
+ import { spawn } from "child_process";
1694
+ async function runAiPromptAssistant(exporter) {
1695
+ if (!stdin2.isTTY || !stdout2.isTTY) return;
1696
+ printConvertPanel(exporter);
1697
+ const action = await select(
1698
+ "Framer Export AI Convert",
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", [
1707
+ "Choose a target stack, AI tool, and conversion situation.",
1708
+ "Mouse clicks are supported in the terminal when available.",
1709
+ "A detailed prompt file will be generated inside the export folder."
1710
+ ]);
1711
+ const targetOptions = [
1712
+ ...TARGETS.map((target2) => ({ label: target2.label, value: target2.id })),
1713
+ { label: "Customize with AI - BETA in development", value: "custom-ai", disabled: true }
1714
+ ];
1715
+ const targetId = await select("Choose target stack", targetOptions, 0);
1716
+ const aiToolId = await select(
1717
+ "Choose the AI coding tool",
1718
+ AI_TOOLS.map((tool) => ({ label: tool.label, value: tool.id })),
1719
+ 0
1720
+ );
1721
+ const goalId = await select(
1722
+ "Choose the conversion situation",
1723
+ GOALS.map((goal2) => ({ label: goal2.label, value: goal2.id })),
1724
+ 0
1725
+ );
1726
+ const target = TARGETS.find((item) => item.id === targetId) || TARGETS[0];
1727
+ const aiTool = AI_TOOLS.find((item) => item.id === aiToolId) || AI_TOOLS[0];
1728
+ const goal = GOALS.find((item) => item.id === goalId) || GOALS[0];
1729
+ const facts = await collectExportFacts(exporter);
1730
+ const prompt = buildConversionPrompt(target, aiTool, goal, facts);
1731
+ const aiDir = path5.join(exporter.outDir, "ai");
1732
+ const promptPath = path5.join(aiDir, `${aiTool.id}-${target.id}-${goal.id}-prompt.md`);
1733
+ await fs4.mkdir(aiDir, { recursive: true });
1734
+ await fs4.writeFile(promptPath, prompt, "utf-8");
1735
+ printPromptResult(promptPath, target, aiTool, goal);
1736
+ const promptAction = await select(
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") {
1745
+ try {
1746
+ await copyToClipboard(prompt);
1747
+ console.log(` ${ui.success("\u2713")} ${ui.text.bold("Prompt copied to clipboard")}
1748
+ `);
1749
+ } catch (error) {
1750
+ console.log(
1751
+ ` ${ui.warning("!")} ${ui.warning("Clipboard copy unavailable:")} ${ui.muted(error.message)}
1752
+ `
1753
+ );
1754
+ }
1755
+ }
1756
+ }
1757
+ async function collectExportFacts(exporter) {
1758
+ const counts = {};
1759
+ const rootEntries = await safeReadDir(exporter.outDir);
1760
+ for (const item of IMPORTANT_DIRS) {
1761
+ counts[item] = item === "index.html" ? await exists(path5.join(exporter.outDir, item)) ? 1 : 0 : await countEntries(path5.join(exporter.outDir, item));
1762
+ }
1763
+ return {
1764
+ sourceUrl: exporter.siteUrl,
1765
+ outputDir: exporter.outDir,
1766
+ platformName: exporter.platform.displayName,
1767
+ rootEntries,
1768
+ counts
1769
+ };
1770
+ }
1771
+ async function exists(filePath) {
1772
+ try {
1773
+ await fs4.access(filePath);
1774
+ return true;
1775
+ } catch {
1776
+ return false;
1777
+ }
1778
+ }
1779
+ async function safeReadDir(dir) {
1780
+ try {
1781
+ return (await fs4.readdir(dir)).sort();
1782
+ } catch {
1783
+ return [];
1784
+ }
1785
+ }
1786
+ async function countEntries(dir) {
1787
+ try {
1788
+ return (await fs4.readdir(dir)).length;
1789
+ } catch {
1790
+ return 0;
1791
+ }
1792
+ }
1793
+ function buttonLabel(label) {
1794
+ return `${ui.border("[")} ${ui.text.bold(label)} ${ui.border("]")}`;
1795
+ }
1796
+ function centerText2(text, width) {
1797
+ const visible = stripAnsi(text).length;
1798
+ if (visible >= width) return text;
1799
+ const left = Math.floor((width - visible) / 2);
1800
+ return " ".repeat(left) + text + " ".repeat(width - visible - left);
1801
+ }
1802
+ function printConvertPanel(exporter) {
1803
+ const w = maxWidth();
1804
+ const inner = w - 4;
1805
+ const rows = [
1806
+ centerText2(`${ui.text.bold("AI Convert")} ${ui.muted("BETA")}`, inner),
1807
+ centerText2(ui.muted("Generate a conversion prompt after the export."), inner),
1808
+ centerText2(`${buttonLabel("Convert")} ${ui.muted("or")} ${buttonLabel("Skip")}`, inner),
1809
+ centerText2(ui.muted(path5.basename(exporter.outDir)), inner)
1810
+ ];
1811
+ console.log("");
1812
+ console.log(boxTop(w));
1813
+ for (const row of rows) console.log(boxLine(w, row));
1814
+ console.log(boxBot(w));
1815
+ console.log("");
1816
+ }
1817
+ function printAssistantModal(title, lines) {
1818
+ const w = maxWidth();
1819
+ const inner = w - 4;
1820
+ console.log("");
1821
+ console.log(boxTop(w));
1822
+ console.log(boxLine(w, centerText2(`${ui.primary("\u25C6")} ${ui.text.bold(title)}`, inner)));
1823
+ console.log(boxSep(w));
1824
+ for (const line of lines) {
1825
+ console.log(boxLine(w, centerText2(ui.muted(line), inner)));
1826
+ }
1827
+ console.log(boxBot(w));
1828
+ console.log("");
1829
+ }
1830
+ function buildConversionPrompt(target, aiTool, goal, facts) {
1831
+ const rootEntries = facts.rootEntries.length ? facts.rootEntries.join(", ") : "No root entries detected";
1832
+ const exportDir = quoteForPrompt(facts.outputDir);
1833
+ const projectDir = quoteForPrompt(path5.join(facts.outputDir, target.projectDir));
1834
+ const scaffoldCommand = `cd ${exportDir}; ${target.scaffold}`;
1835
+ const sourceAssets = quoteForPrompt(path5.join(facts.outputDir, "assets", "*"));
1836
+ const destinationAssets = quoteForPrompt(
1837
+ path5.join(facts.outputDir, target.projectDir, target.staticDir, "assets")
1838
+ );
1839
+ const windowsAssetCopyCommand = `New-Item -ItemType Directory -Force -Path ${destinationAssets}; Copy-Item -Path ${sourceAssets} -Destination ${destinationAssets} -Recurse -Force`;
1840
+ const unixSourceAssets = quoteForPrompt(toPosixPath(path5.join(facts.outputDir, "assets")));
1841
+ const unixDestinationParent = quoteForPrompt(
1842
+ toPosixPath(path5.join(facts.outputDir, target.projectDir, target.staticDir))
1843
+ );
1844
+ const unixAssetCopyCommand = `mkdir -p ${unixDestinationParent} && cp -R ${unixSourceAssets} ${unixDestinationParent}/assets`;
1845
+ const directoryLines = IMPORTANT_DIRS.map(
1846
+ (dir) => `Inspect ${dir}: ${facts.counts[dir]} item(s) detected in the export.`
1847
+ );
1848
+ const promptLines = [
1849
+ `You are ${aiTool.agentName} working inside a local export created by Framer Export.`,
1850
+ "This brief was generated by the Framer Export AI Prompt Assistant BETA.",
1851
+ "Treat this beta prompt as a strict conversion checklist, not as permission to skip inspection.",
1852
+ "Your mission is to convert this exported static mirror into a clean production project.",
1853
+ `Target stack: ${target.stack}.`,
1854
+ `Selected AI coding tool: ${aiTool.displayName}.`,
1855
+ `Selected conversion situation: ${goal.label}.`,
1856
+ `Primary instruction for this situation: ${goal.instruction}`,
1857
+ `Main priority: ${goal.priority}`,
1858
+ `Source URL from the real export: ${facts.sourceUrl}`,
1859
+ `Detected platform from the real export: ${facts.platformName}`,
1860
+ `Export folder to inspect first: ${facts.outputDir}`,
1861
+ `Root entries actually present: ${rootEntries}`,
1862
+ `Create the converted project in: ${projectDir}`,
1863
+ `Recommended scaffold command: ${scaffoldCommand}`,
1864
+ `Expected important target files: ${target.entryFiles}`,
1865
+ `Routing guidance: ${target.routing}`,
1866
+ `Static assets destination for this stack: ${target.staticDir}/assets inside the converted project.`,
1867
+ `PowerShell command to copy exported assets after scaffolding: ${windowsAssetCopyCommand}`,
1868
+ `macOS/Linux command to copy exported assets after scaffolding: ${unixAssetCopyCommand}`,
1869
+ "Run the asset copy command; do not recreate, redownload, rename randomly, or replace real exported assets with placeholders.",
1870
+ "After copying assets, update every image, video, font, CSS url(), and script reference to the new local asset path.",
1871
+ "Do not rush. Take time to inspect the export before writing the final implementation.",
1872
+ "Do not invent brand names, copy, images, links, animations, colors, or sections.",
1873
+ "Use only real information found in index.html, CSS files, JavaScript files, data files, and assets.",
1874
+ "If a detail is missing, inspect more files instead of guessing.",
1875
+ "If a vendor script is minified or hard to understand, identify what behavior it provides before replacing it.",
1876
+ "Keep the final result professional, clean, responsive, and maintainable.",
1877
+ "Do not simplify the site because a section, animation, page, or layout is difficult.",
1878
+ "If something is hard, break it into smaller components and keep working until the result matches closely.",
1879
+ "For every exported page, aim for the closest possible pixel-perfect result: spacing, typography, images, viewport behavior, and motion.",
1880
+ "Do not merge multiple pages into one generic page unless the export proves they are duplicate routes.",
1881
+ "Do not replace complex exported sections with summaries, cards, screenshots, or placeholder blocks.",
1882
+ "If a page has visual depth, overlapping layers, sticky sections, galleries, or scroll effects, recreate those behaviors instead of flattening them.",
1883
+ "Start by listing the files and directories that matter for the conversion.",
1884
+ ...directoryLines,
1885
+ "Read index.html fully enough to understand page structure, metadata, linked assets, and scripts.",
1886
+ "Read the CSS files that define layout, typography, responsive rules, and visual details.",
1887
+ "Inspect scripts/vendor only to understand required interactions; do not blindly copy huge vendor bundles.",
1888
+ "Inspect scripts/modules for page-specific logic, animations, sliders, menus, and dynamic behavior.",
1889
+ "Inspect data files for CMS-like content, page data, configuration, or serialized props.",
1890
+ "Inspect assets/images and preserve the real image files that are actually used.",
1891
+ "Inspect assets/fonts and preserve font loading if the design depends on custom fonts.",
1892
+ "Inspect subpages if it contains exported pages; map them to routes only when they represent real pages.",
1893
+ "If subpages contains pages, convert each meaningful page with its own route and page component.",
1894
+ "For each converted page, compare against the original exported HTML route and adjust until it is visually close.",
1895
+ "Create a clean project structure instead of dumping everything into one component.",
1896
+ "Separate global layout, page sections, shared components, data helpers, and styles.",
1897
+ "Use semantic HTML for headings, navigation, buttons, forms, sections, and footer content.",
1898
+ "Preserve the original hierarchy of visible content unless there is a clear bug to fix.",
1899
+ "Preserve real URLs and links, but convert local asset paths to the new project structure.",
1900
+ "Move static assets into the target framework public/static asset location when appropriate.",
1901
+ "Do not keep broken CDN references if a local exported asset already exists.",
1902
+ "Do not hardcode absolute machine paths into source files; use framework-relative public asset paths after copying.",
1903
+ "Keep original filenames when possible so CSS and content references remain traceable.",
1904
+ "Do not leave unused analytics, editor badges, platform badges, or export-only scripts in the new app.",
1905
+ "Replace platform-specific runtime code with native framework components when possible.",
1906
+ "Keep interactions that users can see: menus, hover states, forms, sliders, animations, and scroll effects.",
1907
+ "If an interaction is too complex, implement a clean equivalent and document the difference briefly.",
1908
+ "Keep responsive behavior for desktop, tablet, and mobile.",
1909
+ "Check layout at small widths and avoid fixed desktop-only dimensions unless the original requires them.",
1910
+ "Use CSS variables or a clear theme file for colors, spacing, radius, shadows, and typography.",
1911
+ "Name components after their role: Hero, Header, FeatureGrid, Gallery, Pricing, Footer, and similar real sections.",
1912
+ "Avoid generic placeholder components if the exported site has specific section meaning.",
1913
+ "Use TypeScript types where they make the content or component props clearer.",
1914
+ "Do not add unnecessary libraries unless they replace a real exported behavior cleanly.",
1915
+ "If you add a library, explain why it is needed and where it is used.",
1916
+ "Keep package.json scripts standard: dev, build, preview, lint when available.",
1917
+ "Keep the build reproducible from a fresh install.",
1918
+ "After implementing, run the install/build/typecheck commands available for the target project.",
1919
+ "Fix any build errors instead of leaving TODOs.",
1920
+ "Open the generated app locally if possible and compare against the exported index.html visually.",
1921
+ "When there are subpages, compare every generated route against its matching exported file.",
1922
+ "Verify that every visible image loads from the new project.",
1923
+ "Verify that font rendering is close to the export.",
1924
+ "Verify that navigation and internal links work.",
1925
+ "Verify that responsive breakpoints do not overlap or hide important content.",
1926
+ "Verify that there are no console errors caused by missing assets or copied platform scripts.",
1927
+ "Keep accessibility basics: alt text when inferable, keyboard-reachable controls, visible focus states.",
1928
+ "Keep SEO basics: title, meta description if present, canonical only if it is correct, Open Graph when present.",
1929
+ "Do not fabricate SEO copy; reuse existing metadata or ask for missing copy if necessary.",
1930
+ "Commit to a small number of high-quality files rather than many noisy fragments.",
1931
+ "If a section repeats, extract a reusable component and data array.",
1932
+ "If a section is unique, keep it simple and local to the page.",
1933
+ "Document important migration decisions in a short README inside the converted project.",
1934
+ "The README must mention the source export folder, target stack, setup command, and known limitations.",
1935
+ "Do not delete the original export folder.",
1936
+ "Do not modify unrelated files outside the new converted project unless required for setup.",
1937
+ "If the worktree has existing changes, avoid reverting or overwriting them.",
1938
+ "Before large edits, inspect first and explain the conversion plan briefly.",
1939
+ "Then implement the conversion step by step until the app builds.",
1940
+ "Final answer must summarize what was converted, where the new project lives, and which checks passed.",
1941
+ "If something could not be converted, state the exact file or behavior and the reason.",
1942
+ "Quality bar: the result should feel like a real hand-built production app, not an automated scrape or simplified demo."
1943
+ ];
1944
+ return [
1945
+ `# ${aiTool.displayName} Conversion Prompt - ${target.label}`,
1946
+ "",
1947
+ "Status: BETA assistant output. Review the export carefully and follow the real files.",
1948
+ `Generated from real export: ${facts.outputDir}`,
1949
+ `AI tool: ${aiTool.displayName}`,
1950
+ `Target: ${target.label}`,
1951
+ `Situation: ${goal.label}`,
1952
+ "",
1953
+ promptLines.map((line, index) => `${String(index + 1).padStart(2, "0")}. ${line}`).join("\n"),
1954
+ ""
1955
+ ].join("\n");
1956
+ }
1957
+ function quoteForPrompt(value) {
1958
+ return `"${value.replace(/"/g, '\\"')}"`;
1959
+ }
1960
+ function toPosixPath(value) {
1961
+ return value.replace(/\\/g, "/");
1962
+ }
1963
+ async function copyToClipboard(text) {
1964
+ const commands = process.platform === "win32" ? [{ command: "clip", args: [] }] : process.platform === "darwin" ? [{ command: "pbcopy", args: [] }] : [
1965
+ { command: "wl-copy", args: [] },
1966
+ { command: "xclip", args: ["-selection", "clipboard"] },
1967
+ { command: "xsel", args: ["--clipboard", "--input"] }
1968
+ ];
1969
+ let lastError = null;
1970
+ for (const item of commands) {
1971
+ try {
1972
+ await pipeToCommand(item.command, item.args, text);
1973
+ return;
1974
+ } catch (error) {
1975
+ lastError = error;
1976
+ }
1977
+ }
1978
+ throw lastError || new Error("No clipboard command found");
1979
+ }
1980
+ function pipeToCommand(command, args, input) {
1981
+ return new Promise((resolve, reject) => {
1982
+ const child = spawn(command, args, { stdio: ["pipe", "ignore", "pipe"] });
1983
+ let stderr = "";
1984
+ let settled = false;
1985
+ const finish = (error) => {
1986
+ if (settled) return;
1987
+ settled = true;
1988
+ if (error) reject(error);
1989
+ else resolve();
1990
+ };
1991
+ child.stderr?.on("data", (chunk) => {
1992
+ stderr += chunk.toString();
1993
+ });
1994
+ child.on("error", finish);
1995
+ child.on("close", (code) => {
1996
+ if (code === 0) {
1997
+ finish();
1998
+ } else {
1999
+ finish(new Error(`${command} exited with ${code}${stderr ? `: ${stderr.trim()}` : ""}`));
2000
+ }
2001
+ });
2002
+ child.stdin?.end(input);
2003
+ });
2004
+ }
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
+ var IMPORTANT_DIRS, AI_TOOLS, TARGETS, GOALS;
2044
+ var init_prompt_assistant = __esm({
2045
+ "src/ai/prompt-assistant.ts"() {
2046
+ "use strict";
2047
+ init_select();
2048
+ init_box();
2049
+ init_theme();
2050
+ IMPORTANT_DIRS = [
2051
+ "index.html",
2052
+ "styles",
2053
+ "scripts/vendor",
2054
+ "scripts/modules",
2055
+ "assets/images",
2056
+ "assets/videos",
2057
+ "assets/fonts",
2058
+ "assets/misc",
2059
+ "data",
2060
+ "subpages"
2061
+ ];
2062
+ AI_TOOLS = [
2063
+ {
2064
+ id: "claude-code",
2065
+ label: "Claude Code",
2066
+ displayName: "Claude Code",
2067
+ agentName: "Claude Code"
2068
+ },
2069
+ {
2070
+ id: "codex",
2071
+ label: "Codex",
2072
+ displayName: "Codex",
2073
+ agentName: "Codex coding agent"
2074
+ },
2075
+ {
2076
+ id: "opencode",
2077
+ label: "OpenCode",
2078
+ displayName: "OpenCode",
2079
+ agentName: "OpenCode"
2080
+ },
2081
+ {
2082
+ id: "other-ai",
2083
+ label: "Other AI coding agent",
2084
+ displayName: "Other AI",
2085
+ agentName: "AI coding agent"
2086
+ }
2087
+ ];
2088
+ TARGETS = [
2089
+ {
2090
+ id: "react-vite",
2091
+ label: "React + Vite + TypeScript",
2092
+ stack: "React 18/19, TypeScript, Vite, CSS modules or plain CSS",
2093
+ projectDir: "converted-react-vite",
2094
+ staticDir: "public",
2095
+ scaffold: "npm create vite@latest converted-react-vite -- --template react-ts",
2096
+ entryFiles: "src/main.tsx, src/App.tsx, src/components/*, src/styles/*",
2097
+ routing: "Use React Router only if multiple exported pages exist in subpages/."
2098
+ },
2099
+ {
2100
+ id: "nextjs-app-router",
2101
+ label: "Next.js App Router",
2102
+ stack: "Next.js App Router, TypeScript, React Server Components where useful",
2103
+ projectDir: "converted-nextjs",
2104
+ staticDir: "public",
2105
+ scaffold: "npx create-next-app@latest converted-nextjs --ts --app --eslint",
2106
+ entryFiles: "app/page.tsx, app/layout.tsx, components/*, public/*",
2107
+ routing: "Map exported subpages to app routes and keep shared layout code reusable."
2108
+ },
2109
+ {
2110
+ id: "vue-vite",
2111
+ label: "Vue + Vite + TypeScript",
2112
+ stack: "Vue 3, TypeScript, Vite, single-file components",
2113
+ projectDir: "converted-vue-vite",
2114
+ staticDir: "public",
2115
+ scaffold: "npm create vite@latest converted-vue-vite -- --template vue-ts",
2116
+ entryFiles: "src/main.ts, src/App.vue, src/components/*.vue, src/styles/*",
2117
+ routing: "Use Vue Router only if multiple exported pages exist in subpages/."
2118
+ },
2119
+ {
2120
+ id: "sveltekit",
2121
+ label: "SvelteKit",
2122
+ stack: "SvelteKit, TypeScript, componentized routes and assets",
2123
+ projectDir: "converted-sveltekit",
2124
+ staticDir: "static",
2125
+ scaffold: "npm create svelte@latest converted-sveltekit",
2126
+ entryFiles: "src/routes/+page.svelte, src/lib/components/*, static/*",
2127
+ routing: "Create SvelteKit routes for meaningful exported pages when subpages/ exists."
2128
+ },
2129
+ {
2130
+ id: "astro",
2131
+ label: "Astro",
2132
+ stack: "Astro, TypeScript, island components only where interactivity is needed",
2133
+ projectDir: "converted-astro",
2134
+ staticDir: "public",
2135
+ scaffold: "npm create astro@latest converted-astro",
2136
+ entryFiles: "src/pages/index.astro, src/components/*, public/*",
2137
+ routing: "Use Astro pages for exported subpages and avoid unnecessary client JavaScript."
2138
+ }
2139
+ ];
2140
+ GOALS = [
2141
+ {
2142
+ id: "clean-rebuild",
2143
+ label: "Clean professional rebuild",
2144
+ instruction: "Rebuild the export as clean production code, not as a one-file HTML clone, while preserving the full page experience.",
2145
+ priority: "Readable structure, maintainability, complete pages, and faithful visual result."
2146
+ },
2147
+ {
2148
+ id: "pixel-perfect",
2149
+ label: "Pixel-perfect visual migration",
2150
+ instruction: "Prioritize pixel-perfect fidelity before refactoring anything aggressively, even when the layout is difficult.",
2151
+ priority: "Spacing, typography, responsive behavior, colors, media, animations, and page-by-page fidelity."
2152
+ },
2153
+ {
2154
+ id: "component-system",
2155
+ label: "Reusable component system",
2156
+ instruction: "Extract repeated UI blocks into reusable components with clean props without simplifying the original pages.",
2157
+ priority: "Components, layout primitives, naming, future editability, and complete section coverage."
2158
+ },
2159
+ {
2160
+ id: "performance-seo",
2161
+ label: "Performance and SEO rebuild",
2162
+ instruction: "Rebuild the site while reducing unused vendor code and improving SEO basics without losing visual fidelity.",
2163
+ priority: "Fast loading, semantic markup, metadata, accessibility, asset hygiene, and faithful pages."
2164
+ }
2165
+ ];
1331
2166
  }
1332
2167
  });
1333
2168
 
1334
2169
  // src/cli/cooking.ts
1335
- import chalk6 from "chalk";
1336
- var SHINE_WIDTH, FRAME_INTERVAL, DOT_PHASES, CookingSpinner;
2170
+ import chalk4 from "chalk";
2171
+ var SHINE_WIDTH, FRAME_INTERVAL, SPINNER_FRAMES, CookingSpinner;
1337
2172
  var init_cooking = __esm({
1338
2173
  "src/cli/cooking.ts"() {
1339
2174
  "use strict";
2175
+ init_theme();
1340
2176
  SHINE_WIDTH = 10;
1341
- FRAME_INTERVAL = 140;
1342
- DOT_PHASES = [" ", ". ", ".. ", "...", " ..", " .", " "];
2177
+ FRAME_INTERVAL = 80;
2178
+ SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1343
2179
  CookingSpinner = class {
1344
2180
  interval = null;
1345
2181
  frame = 0;
@@ -1377,11 +2213,11 @@ var init_cooking = __esm({
1377
2213
  }
1378
2214
  draw() {
1379
2215
  if (!this.active) return;
1380
- const dots = DOT_PHASES[this.frame % DOT_PHASES.length];
1381
- const shimmer = this.renderShimmer("Cooking");
1382
- const dotStr = chalk6.hex("#D4A017")(dots);
1383
- const phaseStr = this.phase ? ` ${chalk6.gray(this.limitLen(this.phase, 38))}` : "";
1384
- process.stdout.write(`\r\x1B[2K ${shimmer} ${dotStr}${phaseStr}`);
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}`);
1385
2221
  }
1386
2222
  renderShimmer(text) {
1387
2223
  let result = "";
@@ -1391,9 +2227,9 @@ var init_cooking = __esm({
1391
2227
  if (dist < SHINE_WIDTH) {
1392
2228
  const t = 1 - dist / SHINE_WIDTH;
1393
2229
  const g = Math.floor(160 + t * 95);
1394
- result += chalk6.rgb(g, g, g)(text[i]);
2230
+ result += chalk4.rgb(250, Math.min(255, g), Math.min(255, Math.floor(g * 0.75)))(text[i]);
1395
2231
  } else {
1396
- result += chalk6.rgb(160, 160, 160)(text[i]);
2232
+ result += ui.muted(text[i]);
1397
2233
  }
1398
2234
  }
1399
2235
  return result;
@@ -1412,9 +2248,9 @@ __export(exporter_exports, {
1412
2248
  FramerExporter: () => FramerExporter,
1413
2249
  deriveOutputName: () => deriveOutputName
1414
2250
  });
1415
- import fs4 from "fs/promises";
1416
- import path5 from "path";
1417
- import chalk7 from "chalk";
2251
+ import fs5 from "fs/promises";
2252
+ import path6 from "path";
2253
+ import chalk5 from "chalk";
1418
2254
  function deriveOutputName(url, platformName) {
1419
2255
  try {
1420
2256
  const parsed = new URL(url);
@@ -1446,8 +2282,10 @@ var init_exporter = __esm({
1446
2282
  init_download2();
1447
2283
  init_output();
1448
2284
  init_summary();
2285
+ init_prompt_assistant();
1449
2286
  init_platforms();
1450
2287
  init_cooking();
2288
+ init_theme();
1451
2289
  FramerExporter = class {
1452
2290
  siteUrl;
1453
2291
  outDir;
@@ -1473,12 +2311,16 @@ var init_exporter = __esm({
1473
2311
  }
1474
2312
  }
1475
2313
  async run(includeSubpages = false) {
1476
- console.log(chalk7.bold.hex("#D4A017")("\n Cooksite v4 - Full Mirror & Clean Export\n"));
1477
- info("Source : " + chalk7.underline(this.siteUrl));
1478
- info("Output : " + chalk7.yellow(this.outDir));
1479
- info("Platform : " + chalk7.hex("#D4A017")(this.platform.displayName));
2314
+ console.log(
2315
+ `
2316
+ ${ui.text.bold("Framer Export")} ${chip("mirror")} ${ui.muted("v4 pipeline")}
2317
+ `
2318
+ );
2319
+ info("Source : " + chalk5.underline(this.siteUrl));
2320
+ info("Output : " + ui.primary(this.outDir));
2321
+ info("Platform : " + ui.primary(this.platform.displayName));
1480
2322
  if (includeSubpages) {
1481
- info("Subpages : " + chalk7.green("enabled"));
2323
+ info("Subpages : " + ui.success("enabled"));
1482
2324
  }
1483
2325
  console.log("");
1484
2326
  this.cooking = new CookingSpinner();
@@ -1496,7 +2338,7 @@ var init_exporter = __esm({
1496
2338
  "data",
1497
2339
  "subpages"
1498
2340
  ]) {
1499
- await fs4.mkdir(path5.join(this.outDir, d), { recursive: true });
2341
+ await fs5.mkdir(path6.join(this.outDir, d), { recursive: true });
1500
2342
  }
1501
2343
  log("Output directory structure created");
1502
2344
  this.cooking.update("Fetching SSR HTML...");
@@ -1506,12 +2348,12 @@ var init_exporter = __esm({
1506
2348
  this.ssrHTML = buf.toString("utf-8");
1507
2349
  success("SSR HTML fetched (" + (this.ssrHTML.length / 1024).toFixed(1) + " KB)");
1508
2350
  } catch (e) {
1509
- log(chalk7.red("Could not fetch SSR HTML: " + e.message));
2351
+ log(chalk5.red("Could not fetch SSR HTML: " + e.message));
1510
2352
  }
1511
2353
  const htmlDetected = detectPlatform(this.siteUrl, this.ssrHTML);
1512
2354
  if (htmlDetected.name !== this.platform.name) {
1513
2355
  this.platform = htmlDetected;
1514
- log("Platform refined: " + chalk7.hex("#D4A017")(this.platform.displayName) + " (from HTML analysis)");
2356
+ log("Platform refined: " + ui.primary(this.platform.displayName) + " (from HTML analysis)");
1515
2357
  }
1516
2358
  await launchAndCapture(this);
1517
2359
  if (includeSubpages && this.page) {
@@ -1527,6 +2369,7 @@ var init_exporter = __esm({
1527
2369
  console.log("");
1528
2370
  success("Export complete!");
1529
2371
  await printSummary(this);
2372
+ await runAiPromptAssistant(this);
1530
2373
  }
1531
2374
  async crawlSubpages() {
1532
2375
  this.cooking?.update("Discovering sub-pages...");
@@ -1539,7 +2382,8 @@ var init_exporter = __esm({
1539
2382
  const hrefs = /* @__PURE__ */ new Set();
1540
2383
  for (const a of anchors) {
1541
2384
  const href = a.href;
1542
- if (!href || href.startsWith("javascript:") || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("#")) continue;
2385
+ if (!href || href.startsWith("javascript:") || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("#"))
2386
+ continue;
1543
2387
  try {
1544
2388
  const u = new URL(href);
1545
2389
  const h = u.hostname.replace(/^www\./, "");
@@ -1567,8 +2411,8 @@ var init_exporter = __esm({
1567
2411
  });
1568
2412
  const slug = this.deriveSlug(link, baseUrl);
1569
2413
  const filename = slug + ".html";
1570
- const filepath = path5.join(this.outDir, "subpages", filename);
1571
- await fs4.writeFile(filepath, html, "utf-8");
2414
+ const filepath = path6.join(this.outDir, "subpages", filename);
2415
+ await fs5.writeFile(filepath, html, "utf-8");
1572
2416
  log(" Saved: subpages/" + filename);
1573
2417
  } catch (e) {
1574
2418
  log(" Skipped " + link + ": " + e.message);
@@ -1590,142 +2434,39 @@ var init_exporter = __esm({
1590
2434
  }
1591
2435
  });
1592
2436
 
1593
- // src/cli/select.ts
1594
- import readline from "readline";
1595
- import { stdin, stdout } from "process";
1596
- import chalk8 from "chalk";
1597
- function stripAnsi2(s) {
1598
- return s.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
1599
- }
1600
- async function select(question, options, defaultIndex = 0) {
1601
- const isTTY = stdin.isTTY && stdout.isTTY;
1602
- if (!isTTY) {
1603
- return fallbackPrompt(question, options, defaultIndex);
1604
- }
1605
- return arrowSelect(question, options, defaultIndex);
1606
- }
1607
- async function arrowSelect(question, options, defaultIndex) {
1608
- return new Promise((resolve) => {
1609
- let selected = defaultIndex;
1610
- const render = (initial = false) => {
1611
- if (!initial) {
1612
- stdout.write(`\x1B[${options.length + 1}A`);
1613
- stdout.write("\x1B[J");
1614
- }
1615
- console.log(` ${chalk8.hex("#D4A017")("?")} ${chalk8.white.bold(question)}`);
1616
- for (let i = 0; i < options.length; i++) {
1617
- if (i === selected) {
1618
- console.log(` ${chalk8.hex("#D4A017")(">")} ${chalk8.white.bold(options[i].label)}`);
1619
- } else {
1620
- console.log(` ${chalk8.gray(options[i].label)}`);
1621
- }
1622
- }
1623
- };
1624
- render(true);
1625
- readline.emitKeypressEvents(stdin);
1626
- stdin.setRawMode(true);
1627
- const onKeypress = (_str, key) => {
1628
- if (!key) return;
1629
- if (key.name === "up" && selected > 0) {
1630
- selected--;
1631
- render();
1632
- } else if (key.name === "down" && selected < options.length - 1) {
1633
- selected++;
1634
- render();
1635
- } else if (key.name === "return") {
1636
- stdin.setRawMode(false);
1637
- stdin.removeListener("keypress", onKeypress);
1638
- stdin.pause();
1639
- stdout.write(`\x1B[${options.length + 1}A`);
1640
- stdout.write("\x1B[J");
1641
- console.log(` ${chalk8.green("\u2713")} ${chalk8.white.bold(question)} ${chalk8.hex("#D4A017")(stripAnsi2(options[selected].label))}
1642
- `);
1643
- resolve(options[selected].value);
1644
- } else if (key.ctrl && key.name === "c" || key.name === "escape") {
1645
- stdin.setRawMode(false);
1646
- stdin.removeListener("keypress", onKeypress);
1647
- process.exit(0);
1648
- }
1649
- };
1650
- stdin.resume();
1651
- stdin.on("keypress", onKeypress);
1652
- });
1653
- }
1654
- async function fallbackPrompt(question, options, defaultIndex) {
1655
- return new Promise((resolve) => {
1656
- const rl = readline.createInterface({ input: stdin, output: stdout });
1657
- console.log(` ${chalk8.hex("#D4A017")("?")} ${chalk8.white.bold(question)}
1658
- `);
1659
- for (let i = 0; i < options.length; i++) {
1660
- const marker = i === defaultIndex ? chalk8.green(" \u2605") : " ";
1661
- console.log(` ${chalk8.gray(`[${i + 1}]`)}${marker} ${chalk8.white(options[i].label)}`);
1662
- }
1663
- console.log("");
1664
- const def = String(defaultIndex + 1);
1665
- const ask = () => {
1666
- rl.question(` ${chalk8.hex("#D4A017")(">")} ${chalk8.gray(`Choose [1-${options.length}] (${def})`)}: `, (answer) => {
1667
- const trimmed = answer.trim();
1668
- if (!trimmed) {
1669
- rl.close();
1670
- const label = stripAnsi2(options[defaultIndex].label);
1671
- console.log(` ${chalk8.green("\u2713")} ${chalk8.hex("#D4A017")(label)}
1672
- `);
1673
- resolve(options[defaultIndex].value);
1674
- return;
1675
- }
1676
- const idx = parseInt(trimmed, 10);
1677
- if (idx >= 1 && idx <= options.length) {
1678
- rl.close();
1679
- const label = stripAnsi2(options[idx - 1].label);
1680
- console.log(` ${chalk8.green("\u2713")} ${chalk8.hex("#D4A017")(label)}
1681
- `);
1682
- resolve(options[idx - 1].value);
1683
- } else {
1684
- console.log(` ${chalk8.red("\u2717")} Enter 1-${options.length}
1685
- `);
1686
- ask();
1687
- }
1688
- });
1689
- };
1690
- ask();
1691
- });
1692
- }
1693
- var init_select = __esm({
1694
- "src/cli/select.ts"() {
1695
- "use strict";
1696
- }
1697
- });
1698
-
1699
2437
  // src/cli/setup.ts
1700
2438
  var setup_exports = {};
1701
2439
  __export(setup_exports, {
1702
2440
  runSetup: () => runSetup
1703
2441
  });
1704
2442
  import readline2 from "readline/promises";
1705
- import { stdin as stdin2, stdout as stdout2 } from "process";
1706
- import path6 from "path";
2443
+ import { stdin as stdin3, stdout as stdout3 } from "process";
2444
+ import path7 from "path";
1707
2445
  import { URL as URL4 } from "url";
1708
- import chalk9 from "chalk";
2446
+ import chalk6 from "chalk";
1709
2447
  function drawHeader(title) {
1710
2448
  const w = maxWidth();
1711
2449
  if (w < 50) {
1712
2450
  console.log(`
1713
- ${chalk9.hex("#D4A017")("\u25CF")} ${chalk9.bold.white(title)}`);
2451
+ ${bullet("\u25CF")} ${ui.text.bold(title)}`);
1714
2452
  return;
1715
2453
  }
1716
2454
  console.log(boxTop(w));
1717
- console.log(boxLine(w, chalk9.bold.white(" " + title)));
2455
+ console.log(boxLine(w, ui.text.bold(" " + title)));
1718
2456
  console.log(boxBot(w));
1719
2457
  console.log("");
1720
2458
  }
1721
2459
  async function runSetup(legacyMode = false) {
1722
2460
  showBanner();
1723
- console.log(chalk9.bold.white(" Welcome to Framer Export setup.\n"));
1724
- console.log(chalk9.gray(" Supports Framer, Webflow, and Wix sites.\n"));
1725
- const rl = readline2.createInterface({ input: stdin2, output: stdout2 });
2461
+ console.log(` ${ui.text.bold("Framer Export setup")} ${chip("interactive")}`);
2462
+ console.log(
2463
+ ` ${ui.muted("Export Framer, Webflow, and Wix sites into a clean local mirror.")}
2464
+ `
2465
+ );
2466
+ const rl = readline2.createInterface({ input: stdin3, output: stdout3 });
1726
2467
  const ask = async (question, defaultVal) => {
1727
- const suffix = defaultVal ? chalk9.gray(` (${defaultVal})`) : "";
1728
- const prompt = ` ${chalk9.hex("#D4A017")("?")} ${chalk9.white.bold(question)}${suffix} ${chalk9.gray(">")} `;
2468
+ const suffix = defaultVal ? chalk6.gray(` (${defaultVal})`) : "";
2469
+ const prompt = ` ${ui.primary("\u25CF")} ${ui.text.bold(question)}${suffix} ${ui.muted(">")} `;
1729
2470
  const answer = await rl.question(prompt);
1730
2471
  return answer.trim() || defaultVal || "";
1731
2472
  };
@@ -1737,42 +2478,54 @@ async function runSetup(legacyMode = false) {
1737
2478
  new URL4(input);
1738
2479
  siteUrl = input;
1739
2480
  } catch {
1740
- console.log(` ${chalk9.red("\u2717")} ${chalk9.red("Invalid URL. Enter a valid URL (https://...)")}
1741
- `);
2481
+ console.log(
2482
+ ` ${ui.error("\u2717")} ${ui.error("Invalid URL. Enter a valid URL (https://...)")}
2483
+ `
2484
+ );
1742
2485
  }
1743
2486
  }
1744
- console.log(` ${chalk9.green("\u2713")} ${chalk9.green("URL:")} ${chalk9.underline(siteUrl)}
2487
+ console.log(` ${ui.success("\u2713")} ${ui.success("URL:")} ${chalk6.underline(siteUrl)}
1745
2488
  `);
1746
2489
  drawHeader("Step 2 : Platform");
1747
2490
  const detected = detectPlatform(siteUrl);
1748
2491
  let platformName;
1749
2492
  if (legacyMode) {
1750
- console.log(` ${chalk9.blue("i")} Auto-detected: ${chalk9.hex("#D4A017")(detected.displayName)}`);
2493
+ console.log(` ${ui.info("i")} Auto-detected: ${ui.primary(detected.displayName)}`);
1751
2494
  const platformInput = await ask("Platform (framer/webflow/wix)", detected.name);
1752
2495
  platformName = ["framer", "webflow", "wix"].includes(platformInput) ? platformInput : detected.name;
1753
- console.log(` ${chalk9.green("\u2713")} ${chalk9.green("Platform:")} ${chalk9.hex("#D4A017")(platformName)}
2496
+ console.log(` ${ui.success("\u2713")} ${ui.success("Platform:")} ${ui.primary(platformName)}
1754
2497
  `);
1755
2498
  } else {
1756
2499
  rl.close();
1757
2500
  const platforms = [
1758
- { label: `Framer${detected.name === "framer" ? chalk9.gray(" (detected)") : ""}`, value: "framer" },
1759
- { label: `Webflow${detected.name === "webflow" ? chalk9.gray(" (detected)") : ""}`, value: "webflow" },
1760
- { label: `Wix${detected.name === "wix" ? chalk9.gray(" (detected)") : ""}`, value: "wix" }
2501
+ {
2502
+ label: `Framer${detected.name === "framer" ? chalk6.gray(" (detected)") : ""}`,
2503
+ value: "framer"
2504
+ },
2505
+ {
2506
+ label: `Webflow${detected.name === "webflow" ? chalk6.gray(" (detected)") : ""}`,
2507
+ value: "webflow"
2508
+ },
2509
+ { label: `Wix${detected.name === "wix" ? chalk6.gray(" (detected)") : ""}`, value: "wix" }
1761
2510
  ];
1762
2511
  const defaultIdx = ["framer", "webflow", "wix"].indexOf(detected.name);
1763
- platformName = await select("Select platform", platforms, Math.max(0, defaultIdx));
2512
+ platformName = await select(
2513
+ "Select platform",
2514
+ platforms,
2515
+ Math.max(0, defaultIdx)
2516
+ );
1764
2517
  }
1765
- const rl2 = legacyMode ? rl : readline2.createInterface({ input: stdin2, output: stdout2 });
2518
+ const rl2 = legacyMode ? rl : readline2.createInterface({ input: stdin3, output: stdout3 });
1766
2519
  const ask2 = async (question, defaultVal) => {
1767
- const suffix = defaultVal ? chalk9.gray(` (${defaultVal})`) : "";
1768
- const prompt = ` ${chalk9.hex("#D4A017")("?")} ${chalk9.white.bold(question)}${suffix} ${chalk9.gray(">")} `;
2520
+ const suffix = defaultVal ? chalk6.gray(` (${defaultVal})`) : "";
2521
+ const prompt = ` ${ui.primary("\u25CF")} ${ui.text.bold(question)}${suffix} ${ui.muted(">")} `;
1769
2522
  const answer = await rl2.question(prompt);
1770
2523
  return answer.trim() || defaultVal || "";
1771
2524
  };
1772
2525
  const derivedName = deriveOutputName(siteUrl, platformName);
1773
2526
  drawHeader("Step 3 : Output Directory");
1774
2527
  const outDir = await ask2("Output directory", "./" + derivedName);
1775
- console.log(` ${chalk9.green("\u2713")} ${chalk9.green("Output:")} ${chalk9.yellow(outDir)}
2528
+ console.log(` ${ui.success("\u2713")} ${ui.success("Output:")} ${ui.primary(outDir)}
1776
2529
  `);
1777
2530
  drawHeader("Step 4 : Options");
1778
2531
  let prettyPrint;
@@ -1781,15 +2534,19 @@ async function runSetup(legacyMode = false) {
1781
2534
  if (legacyMode) {
1782
2535
  const prettyAnswer = await ask2("Pretty-print JS files? (y/n)", "y");
1783
2536
  prettyPrint = prettyAnswer.toLowerCase().startsWith("y");
1784
- console.log(` ${chalk9.green("\u2713")} Pretty-print: ${prettyPrint ? chalk9.green("yes") : chalk9.red("no")}
1785
- `);
2537
+ console.log(
2538
+ ` ${ui.success("\u2713")} Pretty-print: ${prettyPrint ? ui.success("yes") : ui.error("no")}
2539
+ `
2540
+ );
1786
2541
  const subpagesAnswer = await ask2("Export sub-pages? (y/n)", "n");
1787
2542
  includeSubpages = subpagesAnswer.toLowerCase().startsWith("y");
1788
- console.log(` ${chalk9.green("\u2713")} Sub-pages: ${includeSubpages ? chalk9.green("yes") : chalk9.red("no")}
1789
- `);
2543
+ console.log(
2544
+ ` ${ui.success("\u2713")} Sub-pages: ${includeSubpages ? ui.success("yes") : ui.error("no")}
2545
+ `
2546
+ );
1790
2547
  const concurrencyAnswer = await ask2("Download concurrency", "12");
1791
2548
  concurrency = parseInt(concurrencyAnswer, 10) || 12;
1792
- console.log(` ${chalk9.green("\u2713")} Concurrency: ${chalk9.yellow(String(concurrency))}
2549
+ console.log(` ${ui.success("\u2713")} Concurrency: ${ui.primary(String(concurrency))}
1793
2550
  `);
1794
2551
  } else {
1795
2552
  rl2.close();
@@ -1798,33 +2555,40 @@ async function runSetup(legacyMode = false) {
1798
2555
  { label: "No", value: "no" }
1799
2556
  ]);
1800
2557
  prettyPrint = prettyVal === "yes";
1801
- const subpagesVal = await select("Export sub-pages?", [
1802
- { label: "No", value: "no" },
1803
- { label: "Yes, crawl and export", value: "yes" }
1804
- ], 0);
2558
+ const subpagesVal = await select(
2559
+ "Export sub-pages?",
2560
+ [
2561
+ { label: "No", value: "no" },
2562
+ { label: "Yes, crawl and export", value: "yes" }
2563
+ ],
2564
+ 0
2565
+ );
1805
2566
  includeSubpages = subpagesVal === "yes";
1806
- const concurrencyVal = await select("Download concurrency", [
1807
- { label: "6 (slow connection)", value: "6" },
1808
- { label: "12 (default)", value: "12" },
1809
- { label: "20 (fast connection)", value: "20" }
1810
- ], 1);
2567
+ const concurrencyVal = await select(
2568
+ "Download concurrency",
2569
+ [
2570
+ { label: "6 (slow connection)", value: "6" },
2571
+ { label: "12 (default)", value: "12" },
2572
+ { label: "20 (fast connection)", value: "20" }
2573
+ ],
2574
+ 1
2575
+ );
1811
2576
  concurrency = parseInt(concurrencyVal, 10);
1812
2577
  }
1813
2578
  const w = maxWidth();
1814
2579
  const isSmall = w < 50;
1815
- const G2 = chalk9.hex("#C4953A");
1816
- const B2 = chalk9.hex("#8B6914");
1817
2580
  console.log("");
1818
2581
  if (!isSmall) {
1819
2582
  console.log(boxTop(w));
1820
- console.log(boxLine(w, chalk9.bold.white(" Summary")));
2583
+ console.log(boxLine(w, ui.text.bold(" Summary")));
2584
+ console.log(boxSep(w));
1821
2585
  } else {
1822
- console.log(chalk9.bold.white(" Summary:"));
2586
+ console.log(ui.text.bold(" Summary:"));
1823
2587
  }
1824
2588
  for (const [label, value] of [
1825
2589
  ["URL", siteUrl],
1826
2590
  ["Platform", platformName],
1827
- ["Output", path6.resolve(outDir)],
2591
+ ["Output", path7.resolve(outDir)],
1828
2592
  ["Pretty-print", prettyPrint ? "yes" : "no"],
1829
2593
  ["Sub-pages", includeSubpages ? "yes" : "no"],
1830
2594
  ["Concurrency", String(concurrency)]
@@ -1849,14 +2613,14 @@ async function runSetup(legacyMode = false) {
1849
2613
  }
1850
2614
  if (!startExport) {
1851
2615
  console.log(`
1852
- ${chalk9.yellow("Export cancelled.")}
2616
+ ${ui.warning("Export cancelled.")}
1853
2617
  `);
1854
2618
  return;
1855
2619
  }
1856
2620
  console.log("");
1857
2621
  const { CFG: CFG2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1858
2622
  CFG2.concurrency = concurrency;
1859
- const exporter = new FramerExporter(siteUrl, path6.resolve(outDir), platformName);
2623
+ const exporter = new FramerExporter(siteUrl, path7.resolve(outDir), platformName);
1860
2624
  exporter.prettyPrint = prettyPrint;
1861
2625
  await exporter.run(includeSubpages);
1862
2626
  }
@@ -1868,24 +2632,30 @@ var init_setup = __esm({
1868
2632
  init_platforms();
1869
2633
  init_select();
1870
2634
  init_box();
2635
+ init_theme();
1871
2636
  }
1872
2637
  });
1873
2638
 
1874
2639
  // src/cli/index.ts
1875
2640
  init_package();
1876
- import path7 from "path";
2641
+ import path8 from "path";
1877
2642
  import { URL as URL5 } from "url";
1878
2643
 
1879
2644
  // src/cli/help.ts
1880
2645
  init_banner();
1881
- import chalk2 from "chalk";
2646
+ init_theme();
1882
2647
  function showHelp() {
1883
2648
  showBanner();
1884
- console.log(chalk2.white.bold(" USAGE\n"));
1885
- console.log(` ${chalk2.hex("#D4A017")("framer-export")} ${chalk2.yellow("<url>")} ${chalk2.gray("[output-dir]")}`);
1886
- console.log(` ${chalk2.hex("#D4A017")("fexport")} ${chalk2.yellow("<url>")} ${chalk2.gray("[output-dir]")}`);
2649
+ console.log(`${ui.text.bold(" USAGE")} ${chip("cli")}
2650
+ `);
2651
+ console.log(
2652
+ ` ${ui.primary("framer-export")} ${ui.warning("<url>")} ${ui.muted("[output-dir]")}`
2653
+ );
2654
+ console.log(
2655
+ ` ${ui.primary("fexport")} ${ui.warning("<url>")} ${ui.muted("[output-dir]")}`
2656
+ );
1887
2657
  console.log("");
1888
- console.log(chalk2.white.bold(" OPTIONS\n"));
2658
+ console.log(ui.text.bold(" OPTIONS\n"));
1889
2659
  const opts = [
1890
2660
  ["--setup", "Launch the interactive setup assistant"],
1891
2661
  ["--platform <p>", "Force platform: framer, webflow, wix"],
@@ -1894,27 +2664,39 @@ function showHelp() {
1894
2664
  ["--help, -h", "Show this help message"]
1895
2665
  ];
1896
2666
  for (const [flag, desc] of opts) {
1897
- console.log(` ${chalk2.green(flag.padEnd(18))} ${chalk2.white(desc)}`);
2667
+ console.log(` ${ui.success(flag.padEnd(18))} ${ui.text(desc)}`);
1898
2668
  }
1899
2669
  console.log("");
1900
- console.log(chalk2.white.bold(" SUPPORTED PLATFORMS\n"));
2670
+ console.log(ui.text.bold(" SUPPORTED PLATFORMS\n"));
1901
2671
  const platforms = [
1902
2672
  ["Framer", "Auto-detected via .framer.app / .framer.website URLs"],
1903
2673
  ["Webflow", "Auto-detected via .webflow.io URLs"],
1904
2674
  ["Wix", "Auto-detected via .wixsite.com URLs + HTML analysis"]
1905
2675
  ];
1906
2676
  for (const [name, desc] of platforms) {
1907
- console.log(` ${chalk2.hex("#D4A017")(name.padEnd(12))} ${chalk2.gray(desc)}`);
2677
+ console.log(` ${ui.primary(name.padEnd(12))} ${ui.muted(desc)}`);
1908
2678
  }
1909
2679
  console.log("");
1910
- console.log(chalk2.white.bold(" EXAMPLES\n"));
1911
- console.log(` ${chalk2.gray("$")} ${chalk2.hex("#D4A017")("framer-export")} ${chalk2.yellow("https://mysite.framer.app")}`);
1912
- console.log(` ${chalk2.gray("$")} ${chalk2.hex("#D4A017")("framer-export")} ${chalk2.yellow("https://mysite.webflow.io")}`);
1913
- console.log(` ${chalk2.gray("$")} ${chalk2.hex("#D4A017")("framer-export")} ${chalk2.yellow("https://user.wixsite.com/my-site")}`);
1914
- console.log(` ${chalk2.gray("$")} ${chalk2.hex("#D4A017")("framer-export")} ${chalk2.green("--platform webflow")} ${chalk2.yellow("https://custom.com")}`);
1915
- console.log(` ${chalk2.gray("$")} ${chalk2.hex("#D4A017")("framer-export")} ${chalk2.green("--subpages")} ${chalk2.yellow("https://mysite.com")}`);
1916
- console.log(` ${chalk2.gray("$")} ${chalk2.hex("#D4A017")("framer-export")} ${chalk2.green("--setup")}`);
1917
- console.log(` ${chalk2.gray("$")} ${chalk2.hex("#D4A017")("framer-export")} ${chalk2.green("--setup --legacy-mode")}`);
2680
+ console.log(ui.text.bold(" EXAMPLES\n"));
2681
+ console.log(
2682
+ ` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://mysite.framer.app")}`
2683
+ );
2684
+ console.log(
2685
+ ` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://mysite.webflow.io")}`
2686
+ );
2687
+ console.log(
2688
+ ` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.warning("https://user.wixsite.com/my-site")}`
2689
+ );
2690
+ console.log(
2691
+ ` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--platform webflow")} ${ui.warning("https://custom.com")}`
2692
+ );
2693
+ console.log(
2694
+ ` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--subpages")} ${ui.warning("https://mysite.com")}`
2695
+ );
2696
+ console.log(` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--setup")}`);
2697
+ console.log(
2698
+ ` ${ui.muted("$")} ${ui.primary("framer-export")} ${ui.success("--setup --legacy-mode")}`
2699
+ );
1918
2700
  console.log("");
1919
2701
  }
1920
2702
 
@@ -1953,6 +2735,7 @@ async function checkForUpdates(currentVersion) {
1953
2735
  }
1954
2736
 
1955
2737
  // src/cli/index.ts
2738
+ init_theme();
1956
2739
  var VERSION = package_default.version;
1957
2740
  function extractFlag(args, flag) {
1958
2741
  const idx = args.indexOf(flag);
@@ -1974,30 +2757,34 @@ async function main() {
1974
2757
  process.exit(0);
1975
2758
  }
1976
2759
  if (args.includes("--about")) {
1977
- const chalk10 = (await import("chalk")).default;
2760
+ const chalk7 = (await import("chalk")).default;
1978
2761
  showBanner();
1979
- console.log(` ${chalk10.white.bold("Framer Export")} ${chalk10.gray(`v${package_default.version}`)}`);
1980
- console.log(` ${chalk10.white(package_default.description)}
2762
+ console.log(` ${ui.text.bold("Framer Export")} ${ui.muted(`v${package_default.version}`)}`);
2763
+ console.log(` ${ui.text(package_default.description)}
1981
2764
  `);
1982
- console.log(` ${chalk10.white.bold("Author")} ${chalk10.hex("#D4A017")("Dany (danbenba)")}`);
1983
- console.log(` ${chalk10.white.bold("Portfolio")} ${chalk10.underline.hex("#D4A017")("https://github.com/danbenba")}`);
1984
- console.log(` ${chalk10.white.bold("GitHub")} ${chalk10.underline.hex("#D4A017")(package_default.repository.url.replace("git+", "").replace(".git", ""))}`);
1985
- console.log(` ${chalk10.white.bold("npm")} ${chalk10.underline.hex("#D4A017")(`https://www.npmjs.com/package/${package_default.name}`)}`);
1986
- console.log(` ${chalk10.white.bold("License")} ${chalk10.green(package_default.license)}`);
1987
- console.log(` ${chalk10.white.bold("Node")} ${chalk10.gray(`>=${package_default.engines.node}`)}`);
2765
+ console.log(` ${ui.text.bold("Author")} ${ui.primary("Dany (danbenba)")}`);
2766
+ console.log(
2767
+ ` ${ui.text.bold("Portfolio")} ${chalk7.underline.hex("#FAB283")("https://github.com/danbenba")}`
2768
+ );
2769
+ console.log(
2770
+ ` ${ui.text.bold("GitHub")} ${chalk7.underline.hex("#FAB283")(package_default.repository.url.replace("git+", "").replace(".git", ""))}`
2771
+ );
2772
+ console.log(
2773
+ ` ${ui.text.bold("npm")} ${chalk7.underline.hex("#FAB283")(`https://www.npmjs.com/package/${package_default.name}`)}`
2774
+ );
2775
+ console.log(` ${ui.text.bold("License")} ${ui.success(package_default.license)}`);
2776
+ console.log(` ${ui.text.bold("Node")} ${ui.muted(`>=${package_default.engines.node}`)}`);
1988
2777
  console.log("");
1989
2778
  process.exit(0);
1990
2779
  }
1991
2780
  checkForUpdates(VERSION).then((latest) => {
1992
2781
  if (!latest) return;
1993
- import("chalk").then((chalk10) => {
1994
- console.log("");
1995
- console.log(
1996
- ` ${chalk10.default.yellow("\u21B3")} Update available: ${chalk10.default.gray(VERSION)} \u2192 ${chalk10.default.green(latest)}`
1997
- );
1998
- console.log(` ${chalk10.default.hex("#D4A017")(" Run:")} ${chalk10.default.hex("#B8860B")("npm i -g framer-export@latest")}`);
1999
- console.log("");
2000
- });
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("");
2001
2788
  });
2002
2789
  if (args.includes("--help") || args.includes("-h")) {
2003
2790
  showHelp();
@@ -2022,9 +2809,8 @@ async function main() {
2022
2809
  try {
2023
2810
  new URL5(url);
2024
2811
  } catch {
2025
- const chalk10 = (await import("chalk")).default;
2026
- console.log(` ${chalk10.red("\u2717")} ${chalk10.red.bold("Invalid URL:")} ${chalk10.white(url)}`);
2027
- console.log(` ${chalk10.gray("Expected: https://yoursite.framer.app")}
2812
+ console.log(` ${ui.error("\u2717")} ${ui.error.bold("Invalid URL:")} ${ui.text(url)}`);
2813
+ console.log(` ${ui.muted("Expected: https://yoursite.framer.app")}
2028
2814
  `);
2029
2815
  process.exit(1);
2030
2816
  }
@@ -2034,12 +2820,16 @@ async function main() {
2034
2820
  const defaultDir = deriveOutputName2(url, detected);
2035
2821
  const out = args[1] || `./${defaultDir}`;
2036
2822
  try {
2037
- await new FramerExporter2(url, path7.resolve(out), platformOverride || void 0).run(includeSubpages);
2823
+ await new FramerExporter2(url, path8.resolve(out), platformOverride || void 0).run(
2824
+ includeSubpages
2825
+ );
2038
2826
  } catch (e) {
2039
- const chalk10 = (await import("chalk")).default;
2040
- console.log(`
2041
- ${chalk10.red("\u2717")} ${chalk10.red.bold("FAILED:")} ${chalk10.white(e.message)}`);
2042
- console.log(chalk10.gray(e.stack || ""));
2827
+ const chalk7 = (await import("chalk")).default;
2828
+ console.log(
2829
+ `
2830
+ ${ui.error("\u2717")} ${ui.error.bold("FAILED:")} ${ui.text(e.message)}`
2831
+ );
2832
+ console.log(chalk7.gray(e.stack || ""));
2043
2833
  process.exit(1);
2044
2834
  }
2045
2835
  }