framer-export 4.3.7 → 4.3.8

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.8",
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,67 @@ 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
+ var THEME, ui;
107
+ var init_theme = __esm({
108
+ "src/cli/theme.ts"() {
109
+ "use strict";
110
+ THEME = {
111
+ background: "#0A0A0A",
112
+ panel: "#141414",
113
+ element: "#1E1E1E",
114
+ border: "#484848",
115
+ borderActive: "#606060",
116
+ text: "#EEEEEE",
117
+ muted: "#808080",
118
+ primary: "#FAB283",
119
+ primarySoft: "#FFC09F",
120
+ secondary: "#5C9CF5",
121
+ accent: "#9D7CD8",
122
+ success: "#7FD88F",
123
+ warning: "#F5A742",
124
+ error: "#E06C75",
125
+ info: "#56B6C2"
126
+ };
127
+ ui = {
128
+ text: chalk.hex(THEME.text),
129
+ muted: chalk.hex(THEME.muted),
130
+ primary: chalk.hex(THEME.primary),
131
+ primarySoft: chalk.hex(THEME.primarySoft),
132
+ secondary: chalk.hex(THEME.secondary),
133
+ accent: chalk.hex(THEME.accent),
134
+ success: chalk.hex(THEME.success),
135
+ warning: chalk.hex(THEME.warning),
136
+ error: chalk.hex(THEME.error),
137
+ info: chalk.hex(THEME.info),
138
+ border: chalk.hex(THEME.border),
139
+ borderActive: chalk.hex(THEME.borderActive),
140
+ panel: chalk.hex(THEME.panel)
141
+ };
142
+ }
143
+ });
144
+
145
+ // src/cli/banner.ts
87
146
  function getWidth() {
88
147
  return process.stdout.columns || 80;
89
148
  }
@@ -92,33 +151,35 @@ function showBanner() {
92
151
  const isSmall = width < 65;
93
152
  if (isSmall) {
94
153
  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")}
154
+ ${ui.primary.bold("f-export")} ${ui.muted(`v${package_default.version}`)} ${chip("beta ui")}`);
155
+ console.log(` ${ui.text.bold("Framer Export")} ${ui.muted("for Framer, Webflow, and Wix")}
97
156
  `);
98
157
  return;
99
158
  }
100
- const gold = chalk.hex("#D4A017").bold;
101
- const gray = chalk.gray;
102
159
  console.log("");
103
160
  ASCII_ART.forEach((line) => {
104
- console.log(" " + gold(line));
161
+ console.log(" " + softGradient(line));
105
162
  });
106
163
  console.log("");
107
- console.log(` ${gray(`v${package_default.version}`)} ${chalk.white.bold("Framer \xB7 Webflow \xB7 Wix Exporter")}
108
- `);
164
+ console.log(
165
+ ` ${ui.muted(`v${package_default.version}`)} ${ui.text.bold("Framer Export")} ${chip("fexport")} ${ui.muted("local mirror exporter")}`
166
+ );
167
+ console.log(
168
+ ` ${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")}
169
+ `
170
+ );
109
171
  }
110
172
  var ASCII_ART;
111
173
  var init_banner = __esm({
112
174
  "src/cli/banner.ts"() {
113
175
  "use strict";
114
176
  init_package();
177
+ init_theme();
115
178
  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"
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 "
122
183
  ];
123
184
  }
124
185
  });
@@ -259,7 +320,7 @@ var init_download = __esm({
259
320
  });
260
321
 
261
322
  // src/logger/index.ts
262
- import chalk3 from "chalk";
323
+ import chalk2 from "chalk";
263
324
  function setCooking(spinner) {
264
325
  _cooking = spinner;
265
326
  }
@@ -275,64 +336,51 @@ var _cooking, T, LOG_PALETTE, _li, log, INFO_PALETTE, _ii, info, warn, success;
275
336
  var init_logger = __esm({
276
337
  "src/logger/index.ts"() {
277
338
  "use strict";
339
+ init_theme();
278
340
  _cooking = null;
279
341
  T = () => (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
280
342
  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)
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)
293
349
  ];
294
350
  _li = 0;
295
351
  log = (m) => {
296
352
  _li++;
297
353
  const c = LOG_PALETTE[_li % LOG_PALETTE.length];
298
- output(`${chalk3.hex("#6d6d6d")(`[${T()}]`)} ${chalk3.hex("#B8960A")("[LOG]")} ${c(trunc(m, 120))}`);
354
+ output(`${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.primary)("[log]")} ${c(trunc(m, 120))}`);
299
355
  };
300
356
  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)
357
+ (s) => chalk2.hex(THEME.info)(s),
358
+ (s) => chalk2.hex(THEME.secondary)(s),
359
+ (s) => chalk2.hex(THEME.primarySoft)(s)
307
360
  ];
308
361
  _ii = 0;
309
362
  info = (m) => {
310
363
  _ii++;
311
364
  const c = INFO_PALETTE[_ii % INFO_PALETTE.length];
312
- output(`${chalk3.hex("#6d6d6d")(`[${T()}]`)} ${chalk3.hex("#3AA89E").bold("[INFO]")} ${c(trunc(m, 120))}`);
365
+ output(`${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.info).bold("[info]")} ${c(trunc(m, 120))}`);
313
366
  };
314
367
  warn = (m) => {
315
368
  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)
369
+ (s) => chalk2.hex(THEME.warning)(s),
370
+ (s) => chalk2.hex(THEME.primary)(s)
320
371
  ];
321
372
  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))}`;
373
+ const line = `${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.warning).bold("[warn]")} ${c(trunc(m, 120))}`;
323
374
  if (_cooking) _cooking.log(line);
324
375
  else console.warn(line);
325
376
  };
326
377
  success = (m) => {
327
378
  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)
379
+ (s) => chalk2.hex(THEME.success)(s),
380
+ (s) => chalk2.hex(THEME.info)(s)
333
381
  ];
334
382
  const c = colors[Math.floor(Math.random() * colors.length)];
335
- output(`${chalk3.hex("#6d6d6d")(`[${T()}]`)} ${chalk3.greenBright("[OK]")} ${c(trunc(m, 120))}`);
383
+ output(`${chalk2.hex(THEME.muted)(`[${T()}]`)} ${chalk2.hex(THEME.success)("[ok]")} ${c(trunc(m, 120))}`);
336
384
  };
337
385
  }
338
386
  });
@@ -1186,7 +1234,7 @@ var init_output = __esm({
1186
1234
  });
1187
1235
 
1188
1236
  // src/cli/box.ts
1189
- import chalk4 from "chalk";
1237
+ import chalk3 from "chalk";
1190
1238
  function maxWidth() {
1191
1239
  return Math.min(process.stdout.columns || 80, 62);
1192
1240
  }
@@ -1195,51 +1243,45 @@ function padRight(text, w) {
1195
1243
  if (visible >= w) return text;
1196
1244
  return text + " ".repeat(w - visible);
1197
1245
  }
1198
- function stripAnsi(s) {
1199
- return s.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
1200
- }
1201
1246
  function boxTop(w) {
1202
1247
  const inner = w - 4;
1203
- return " " + B("\u2554\u2550") + B("\u2550".repeat(inner)) + B("\u2550\u2557");
1248
+ return " " + ui.border("\u256D\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256E");
1204
1249
  }
1205
1250
  function boxBot(w) {
1206
1251
  const inner = w - 4;
1207
- return " " + B("\u255A\u2550") + B("\u2550".repeat(inner)) + B("\u2550\u255D");
1252
+ return " " + ui.border("\u2570\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u256F");
1208
1253
  }
1209
1254
  function boxLine(w, text) {
1210
1255
  const inner = w - 4;
1211
1256
  const padded = padRight(text, inner);
1212
- return " " + B("\u2551 ") + padded + B(" \u2551");
1257
+ return " " + ui.border("\u2502 ") + padded + ui.border(" \u2502");
1213
1258
  }
1214
1259
  function boxSep(w) {
1215
1260
  const inner = w - 4;
1216
- return " " + B("\u2560\u2550") + B("\u2550".repeat(inner)) + B("\u2550\u2563");
1261
+ return " " + ui.border("\u251C\u2500") + ui.border("\u2500".repeat(inner)) + ui.border("\u2500\u2524");
1217
1262
  }
1218
1263
  function boxRow(w, label, value) {
1219
1264
  const inner = w - 4;
1220
- const labelPlain = stripAnsi(chalk4.bold(label));
1265
+ const labelPlain = stripAnsi(chalk3.bold(label));
1221
1266
  const visible = labelPlain.length + 1 + value.length;
1222
1267
  if (visible > inner) {
1223
1268
  const avail = inner - labelPlain.length - 2;
1224
1269
  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");
1270
+ return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(truncated) + " ".repeat(Math.max(0, inner - labelPlain.length - 1 - truncated.length)) + ui.border(" \u2502");
1226
1271
  }
1227
1272
  const right = inner - labelPlain.length - 1 - value.length;
1228
- return " " + G("\u2551 ") + chalk4.bold(label) + ": " + chalk4.yellow(value) + " ".repeat(right) + G(" \u2551");
1273
+ return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(value) + " ".repeat(right) + ui.border(" \u2502");
1229
1274
  }
1230
- var B, G;
1231
1275
  var init_box = __esm({
1232
1276
  "src/cli/box.ts"() {
1233
1277
  "use strict";
1234
- B = chalk4.hex("#8B6914");
1235
- G = chalk4.hex("#C4953A");
1278
+ init_theme();
1236
1279
  }
1237
1280
  });
1238
1281
 
1239
1282
  // src/exporter/summary.ts
1240
1283
  import fs3 from "fs/promises";
1241
1284
  import path4 from "path";
1242
- import chalk5 from "chalk";
1243
1285
  async function printSummary(exporter) {
1244
1286
  const w = maxWidth();
1245
1287
  const isSmall = w < 50;
@@ -1261,17 +1303,17 @@ async function printSummary(exporter) {
1261
1303
  count("data"),
1262
1304
  count("subpages")
1263
1305
  ]);
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;
1306
+ const G = ui.primary;
1307
+ const G2 = ui.primarySoft;
1308
+ const C2 = ui.secondary;
1309
+ const Y = ui.accent;
1310
+ const O = ui.warning;
1311
+ const Br = ui.info;
1312
+ const Gr = ui.muted;
1313
+ const Gn = ui.success;
1272
1314
  const entries = [
1273
- ["styles/", styles, "CSS", G2],
1274
- ["scripts/vendor/", vendor, "JS vendor", G22],
1315
+ ["styles/", styles, "CSS", G],
1316
+ ["scripts/vendor/", vendor, "JS vendor", G2],
1275
1317
  ["scripts/modules/", scripts, "JS modules", C2],
1276
1318
  ["assets/images/", imgs, "images", Y],
1277
1319
  ["assets/videos/", videos, "videos", O],
@@ -1283,23 +1325,23 @@ async function printSummary(exporter) {
1283
1325
  console.log("");
1284
1326
  if (!isSmall) {
1285
1327
  console.log(boxTop(w));
1286
- console.log(boxLine(w, chalk5.bold.white(" Export Summary")));
1328
+ console.log(boxLine(w, `${ui.text.bold(" Export Summary")} ${chip("done")}`));
1287
1329
  console.log(boxSep(w));
1288
1330
  } else {
1289
- console.log(chalk5.bold.white(" Export Summary:"));
1331
+ console.log(ui.text.bold(" Export Summary:"));
1290
1332
  }
1291
1333
  for (const [label, cnt, type, color] of entries) {
1292
1334
  if (cnt === 0) continue;
1293
1335
  const inner = w - 4;
1294
1336
  const l = label.padEnd(16);
1295
1337
  const c = String(cnt).padStart(3);
1296
- const rowText = `${color(l)}${chalk5.white(c)} ${chalk5.gray(type)}`;
1338
+ const rowText = `${color(l)}${ui.text(c)} ${ui.muted(type)}`;
1297
1339
  const visible = 16 + 3 + 2 + type.length;
1298
1340
  const pad = Math.max(0, inner - visible);
1299
1341
  if (isSmall) {
1300
- console.log(` ${color(label)} ${chalk5.white(String(cnt))} ${chalk5.gray(type)}`);
1342
+ console.log(` ${color(label)} ${ui.text(String(cnt))} ${ui.muted(type)}`);
1301
1343
  } else {
1302
- console.log(" " + chalk5.hex("#C4953A")("\u2551 ") + rowText + " ".repeat(pad) + chalk5.hex("#C4953A")(" \u2551"));
1344
+ console.log(" " + ui.border("\u2502 ") + rowText + " ".repeat(pad) + ui.border(" \u2502"));
1303
1345
  }
1304
1346
  }
1305
1347
  if (!isSmall) {
@@ -1309,37 +1351,584 @@ async function printSummary(exporter) {
1309
1351
  const cdCmd = "cd " + path4.basename(exporter.outDir) + " && node serve.cjs";
1310
1352
  if (!isSmall) {
1311
1353
  console.log(boxTop(w));
1312
- console.log(boxLine(w, chalk5.bold.white(" To serve locally")));
1354
+ console.log(boxLine(w, ui.text.bold(" To serve locally")));
1313
1355
  console.log(boxSep(w));
1314
1356
  const inner = w - 6;
1315
1357
  const cmdLen = cdCmd.length;
1316
1358
  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"));
1359
+ console.log(" " + ui.border("\u2502 ") + G(cdCmd) + " ".repeat(pad) + ui.muted(" copy") + ui.border(" \u2502"));
1318
1360
  console.log(boxBot(w));
1319
1361
  } else {
1320
- console.log(chalk5.bold.white(" To serve locally:"));
1321
- console.log(` ${G2(cdCmd)}`);
1362
+ console.log(ui.text.bold(" To serve locally:"));
1363
+ console.log(` ${G(cdCmd)}`);
1322
1364
  }
1323
1365
  console.log("");
1324
- console.log(chalk5.gray(" NOTE: Must be served via HTTP for JS modules to work."));
1366
+ console.log(ui.muted(" note: must be served via HTTP for JS modules to work."));
1325
1367
  console.log("");
1326
1368
  }
1327
1369
  var init_summary = __esm({
1328
1370
  "src/exporter/summary.ts"() {
1329
1371
  "use strict";
1330
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}`);
1461
+ }
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
+ });
1498
+ }
1499
+ var init_select = __esm({
1500
+ "src/cli/select.ts"() {
1501
+ "use strict";
1502
+ init_theme();
1503
+ }
1504
+ });
1505
+
1506
+ // src/ai/prompt-assistant.ts
1507
+ import fs4 from "fs/promises";
1508
+ import path5 from "path";
1509
+ import { stdin as stdin2, stdout as stdout2 } from "process";
1510
+ import { spawn } from "child_process";
1511
+ import chalk4 from "chalk";
1512
+ async function runAiPromptAssistant(exporter) {
1513
+ if (!stdin2.isTTY || !stdout2.isTTY) return;
1514
+ const targetOptions = [
1515
+ ...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" }
1518
+ ];
1519
+ console.log("");
1520
+ const targetId = await select("AI Prompt Assistant BETA: choose target stack", targetOptions, 0);
1521
+ if (targetId === "skip") return;
1522
+ const aiToolId = await select(
1523
+ "Choose the AI coding tool",
1524
+ AI_TOOLS.map((tool) => ({ label: tool.label, value: tool.id })),
1525
+ 0
1526
+ );
1527
+ const goalId = await select(
1528
+ "Choose the conversion situation",
1529
+ GOALS.map((goal2) => ({ label: goal2.label, value: goal2.id })),
1530
+ 0
1531
+ );
1532
+ const target = TARGETS.find((item) => item.id === targetId) || TARGETS[0];
1533
+ const aiTool = AI_TOOLS.find((item) => item.id === aiToolId) || AI_TOOLS[0];
1534
+ const goal = GOALS.find((item) => item.id === goalId) || GOALS[0];
1535
+ const facts = await collectExportFacts(exporter);
1536
+ const prompt = buildConversionPrompt(target, aiTool, goal, facts);
1537
+ const aiDir = path5.join(exporter.outDir, "ai");
1538
+ const promptPath = path5.join(aiDir, `${aiTool.id}-${target.id}-${goal.id}-prompt.md`);
1539
+ await fs4.mkdir(aiDir, { recursive: true });
1540
+ await fs4.writeFile(promptPath, prompt, "utf-8");
1541
+ printPromptResult(promptPath, target, aiTool, goal);
1542
+ const copyChoice = await select(
1543
+ "Copy prompt to clipboard?",
1544
+ [
1545
+ { label: "Copy prompt", value: "yes" },
1546
+ { label: "No, keep file only", value: "no" }
1547
+ ],
1548
+ 0
1549
+ );
1550
+ if (copyChoice === "yes") {
1551
+ try {
1552
+ await copyToClipboard(prompt);
1553
+ console.log(` ${chalk4.green("\u2713")} ${chalk4.white.bold("Prompt copied to clipboard")}
1554
+ `);
1555
+ } catch (error) {
1556
+ console.log(
1557
+ ` ${chalk4.yellow("!")} ${chalk4.yellow("Clipboard copy unavailable:")} ${chalk4.gray(error.message)}
1558
+ `
1559
+ );
1560
+ }
1561
+ }
1562
+ }
1563
+ async function collectExportFacts(exporter) {
1564
+ const counts = {};
1565
+ const rootEntries = await safeReadDir(exporter.outDir);
1566
+ for (const item of IMPORTANT_DIRS) {
1567
+ counts[item] = item === "index.html" ? await exists(path5.join(exporter.outDir, item)) ? 1 : 0 : await countEntries(path5.join(exporter.outDir, item));
1568
+ }
1569
+ return {
1570
+ sourceUrl: exporter.siteUrl,
1571
+ outputDir: exporter.outDir,
1572
+ platformName: exporter.platform.displayName,
1573
+ rootEntries,
1574
+ counts
1575
+ };
1576
+ }
1577
+ async function exists(filePath) {
1578
+ try {
1579
+ await fs4.access(filePath);
1580
+ return true;
1581
+ } catch {
1582
+ return false;
1583
+ }
1584
+ }
1585
+ async function safeReadDir(dir) {
1586
+ try {
1587
+ return (await fs4.readdir(dir)).sort();
1588
+ } catch {
1589
+ return [];
1590
+ }
1591
+ }
1592
+ async function countEntries(dir) {
1593
+ try {
1594
+ return (await fs4.readdir(dir)).length;
1595
+ } catch {
1596
+ return 0;
1597
+ }
1598
+ }
1599
+ function buildConversionPrompt(target, aiTool, goal, facts) {
1600
+ const rootEntries = facts.rootEntries.length ? facts.rootEntries.join(", ") : "No root entries detected";
1601
+ const exportDir = quoteForPrompt(facts.outputDir);
1602
+ const projectDir = quoteForPrompt(path5.join(facts.outputDir, target.projectDir));
1603
+ const scaffoldCommand = `cd ${exportDir}; ${target.scaffold}`;
1604
+ const sourceAssets = quoteForPrompt(path5.join(facts.outputDir, "assets", "*"));
1605
+ const destinationAssets = quoteForPrompt(
1606
+ path5.join(facts.outputDir, target.projectDir, target.staticDir, "assets")
1607
+ );
1608
+ const windowsAssetCopyCommand = `New-Item -ItemType Directory -Force -Path ${destinationAssets}; Copy-Item -Path ${sourceAssets} -Destination ${destinationAssets} -Recurse -Force`;
1609
+ const unixSourceAssets = quoteForPrompt(toPosixPath(path5.join(facts.outputDir, "assets")));
1610
+ const unixDestinationParent = quoteForPrompt(
1611
+ toPosixPath(path5.join(facts.outputDir, target.projectDir, target.staticDir))
1612
+ );
1613
+ const unixAssetCopyCommand = `mkdir -p ${unixDestinationParent} && cp -R ${unixSourceAssets} ${unixDestinationParent}/assets`;
1614
+ const directoryLines = IMPORTANT_DIRS.map(
1615
+ (dir) => `Inspect ${dir}: ${facts.counts[dir]} item(s) detected in the export.`
1616
+ );
1617
+ 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.",
1620
+ "Treat this beta prompt as a strict conversion checklist, not as permission to skip inspection.",
1621
+ "Your mission is to convert this exported static mirror into a clean production project.",
1622
+ `Target stack: ${target.stack}.`,
1623
+ `Selected AI coding tool: ${aiTool.displayName}.`,
1624
+ `Selected conversion situation: ${goal.label}.`,
1625
+ `Primary instruction for this situation: ${goal.instruction}`,
1626
+ `Main priority: ${goal.priority}`,
1627
+ `Source URL from the real export: ${facts.sourceUrl}`,
1628
+ `Detected platform from the real export: ${facts.platformName}`,
1629
+ `Export folder to inspect first: ${facts.outputDir}`,
1630
+ `Root entries actually present: ${rootEntries}`,
1631
+ `Create the converted project in: ${projectDir}`,
1632
+ `Recommended scaffold command: ${scaffoldCommand}`,
1633
+ `Expected important target files: ${target.entryFiles}`,
1634
+ `Routing guidance: ${target.routing}`,
1635
+ `Static assets destination for this stack: ${target.staticDir}/assets inside the converted project.`,
1636
+ `PowerShell command to copy exported assets after scaffolding: ${windowsAssetCopyCommand}`,
1637
+ `macOS/Linux command to copy exported assets after scaffolding: ${unixAssetCopyCommand}`,
1638
+ "Run the asset copy command; do not recreate, redownload, rename randomly, or replace real exported assets with placeholders.",
1639
+ "After copying assets, update every image, video, font, CSS url(), and script reference to the new local asset path.",
1640
+ "Do not rush. Take time to inspect the export before writing the final implementation.",
1641
+ "Do not invent brand names, copy, images, links, animations, colors, or sections.",
1642
+ "Use only real information found in index.html, CSS files, JavaScript files, data files, and assets.",
1643
+ "If a detail is missing, inspect more files instead of guessing.",
1644
+ "If a vendor script is minified or hard to understand, identify what behavior it provides before replacing it.",
1645
+ "Keep the final result professional, clean, responsive, and maintainable.",
1646
+ "Do not simplify the site because a section, animation, page, or layout is difficult.",
1647
+ "If something is hard, break it into smaller components and keep working until the result matches closely.",
1648
+ "For every exported page, aim for the closest possible pixel-perfect result: spacing, typography, images, viewport behavior, and motion.",
1649
+ "Do not merge multiple pages into one generic page unless the export proves they are duplicate routes.",
1650
+ "Do not replace complex exported sections with summaries, cards, screenshots, or placeholder blocks.",
1651
+ "If a page has visual depth, overlapping layers, sticky sections, galleries, or scroll effects, recreate those behaviors instead of flattening them.",
1652
+ "Start by listing the files and directories that matter for the conversion.",
1653
+ ...directoryLines,
1654
+ "Read index.html fully enough to understand page structure, metadata, linked assets, and scripts.",
1655
+ "Read the CSS files that define layout, typography, responsive rules, and visual details.",
1656
+ "Inspect scripts/vendor only to understand required interactions; do not blindly copy huge vendor bundles.",
1657
+ "Inspect scripts/modules for page-specific logic, animations, sliders, menus, and dynamic behavior.",
1658
+ "Inspect data files for CMS-like content, page data, configuration, or serialized props.",
1659
+ "Inspect assets/images and preserve the real image files that are actually used.",
1660
+ "Inspect assets/fonts and preserve font loading if the design depends on custom fonts.",
1661
+ "Inspect subpages if it contains exported pages; map them to routes only when they represent real pages.",
1662
+ "If subpages contains pages, convert each meaningful page with its own route and page component.",
1663
+ "For each converted page, compare against the original exported HTML route and adjust until it is visually close.",
1664
+ "Create a clean project structure instead of dumping everything into one component.",
1665
+ "Separate global layout, page sections, shared components, data helpers, and styles.",
1666
+ "Use semantic HTML for headings, navigation, buttons, forms, sections, and footer content.",
1667
+ "Preserve the original hierarchy of visible content unless there is a clear bug to fix.",
1668
+ "Preserve real URLs and links, but convert local asset paths to the new project structure.",
1669
+ "Move static assets into the target framework public/static asset location when appropriate.",
1670
+ "Do not keep broken CDN references if a local exported asset already exists.",
1671
+ "Do not hardcode absolute machine paths into source files; use framework-relative public asset paths after copying.",
1672
+ "Keep original filenames when possible so CSS and content references remain traceable.",
1673
+ "Do not leave unused analytics, editor badges, platform badges, or export-only scripts in the new app.",
1674
+ "Replace platform-specific runtime code with native framework components when possible.",
1675
+ "Keep interactions that users can see: menus, hover states, forms, sliders, animations, and scroll effects.",
1676
+ "If an interaction is too complex, implement a clean equivalent and document the difference briefly.",
1677
+ "Keep responsive behavior for desktop, tablet, and mobile.",
1678
+ "Check layout at small widths and avoid fixed desktop-only dimensions unless the original requires them.",
1679
+ "Use CSS variables or a clear theme file for colors, spacing, radius, shadows, and typography.",
1680
+ "Name components after their role: Hero, Header, FeatureGrid, Gallery, Pricing, Footer, and similar real sections.",
1681
+ "Avoid generic placeholder components if the exported site has specific section meaning.",
1682
+ "Use TypeScript types where they make the content or component props clearer.",
1683
+ "Do not add unnecessary libraries unless they replace a real exported behavior cleanly.",
1684
+ "If you add a library, explain why it is needed and where it is used.",
1685
+ "Keep package.json scripts standard: dev, build, preview, lint when available.",
1686
+ "Keep the build reproducible from a fresh install.",
1687
+ "After implementing, run the install/build/typecheck commands available for the target project.",
1688
+ "Fix any build errors instead of leaving TODOs.",
1689
+ "Open the generated app locally if possible and compare against the exported index.html visually.",
1690
+ "When there are subpages, compare every generated route against its matching exported file.",
1691
+ "Verify that every visible image loads from the new project.",
1692
+ "Verify that font rendering is close to the export.",
1693
+ "Verify that navigation and internal links work.",
1694
+ "Verify that responsive breakpoints do not overlap or hide important content.",
1695
+ "Verify that there are no console errors caused by missing assets or copied platform scripts.",
1696
+ "Keep accessibility basics: alt text when inferable, keyboard-reachable controls, visible focus states.",
1697
+ "Keep SEO basics: title, meta description if present, canonical only if it is correct, Open Graph when present.",
1698
+ "Do not fabricate SEO copy; reuse existing metadata or ask for missing copy if necessary.",
1699
+ "Commit to a small number of high-quality files rather than many noisy fragments.",
1700
+ "If a section repeats, extract a reusable component and data array.",
1701
+ "If a section is unique, keep it simple and local to the page.",
1702
+ "Document important migration decisions in a short README inside the converted project.",
1703
+ "The README must mention the source export folder, target stack, setup command, and known limitations.",
1704
+ "Do not delete the original export folder.",
1705
+ "Do not modify unrelated files outside the new converted project unless required for setup.",
1706
+ "If the worktree has existing changes, avoid reverting or overwriting them.",
1707
+ "Before large edits, inspect first and explain the conversion plan briefly.",
1708
+ "Then implement the conversion step by step until the app builds.",
1709
+ "Final answer must summarize what was converted, where the new project lives, and which checks passed.",
1710
+ "If something could not be converted, state the exact file or behavior and the reason.",
1711
+ "Quality bar: the result should feel like a real hand-built production app, not an automated scrape or simplified demo."
1712
+ ];
1713
+ return [
1714
+ `# ${aiTool.displayName} Conversion Prompt - ${target.label}`,
1715
+ "",
1716
+ "Status: BETA assistant output. Review the export carefully and follow the real files.",
1717
+ `Generated from real export: ${facts.outputDir}`,
1718
+ `AI tool: ${aiTool.displayName}`,
1719
+ `Target: ${target.label}`,
1720
+ `Situation: ${goal.label}`,
1721
+ "",
1722
+ promptLines.map((line, index) => `${String(index + 1).padStart(2, "0")}. ${line}`).join("\n"),
1723
+ ""
1724
+ ].join("\n");
1725
+ }
1726
+ function quoteForPrompt(value) {
1727
+ return `"${value.replace(/"/g, '\\"')}"`;
1728
+ }
1729
+ function toPosixPath(value) {
1730
+ return value.replace(/\\/g, "/");
1731
+ }
1732
+ async function copyToClipboard(text) {
1733
+ const commands = process.platform === "win32" ? [{ command: "clip", args: [] }] : process.platform === "darwin" ? [{ command: "pbcopy", args: [] }] : [
1734
+ { command: "wl-copy", args: [] },
1735
+ { command: "xclip", args: ["-selection", "clipboard"] },
1736
+ { command: "xsel", args: ["--clipboard", "--input"] }
1737
+ ];
1738
+ let lastError = null;
1739
+ for (const item of commands) {
1740
+ try {
1741
+ await pipeToCommand(item.command, item.args, text);
1742
+ return;
1743
+ } catch (error) {
1744
+ lastError = error;
1745
+ }
1746
+ }
1747
+ throw lastError || new Error("No clipboard command found");
1748
+ }
1749
+ function pipeToCommand(command, args, input) {
1750
+ return new Promise((resolve, reject) => {
1751
+ const child = spawn(command, args, { stdio: ["pipe", "ignore", "pipe"] });
1752
+ let stderr = "";
1753
+ let settled = false;
1754
+ const finish = (error) => {
1755
+ if (settled) return;
1756
+ settled = true;
1757
+ if (error) reject(error);
1758
+ else resolve();
1759
+ };
1760
+ child.stderr?.on("data", (chunk) => {
1761
+ stderr += chunk.toString();
1762
+ });
1763
+ child.on("error", finish);
1764
+ child.on("close", (code) => {
1765
+ if (code === 0) {
1766
+ finish();
1767
+ } else {
1768
+ finish(new Error(`${command} exited with ${code}${stderr ? `: ${stderr.trim()}` : ""}`));
1769
+ }
1770
+ });
1771
+ child.stdin?.end(input);
1772
+ });
1773
+ }
1774
+ function printPromptResult(promptPath, target, aiTool, goal) {
1775
+ const w = maxWidth();
1776
+ const isSmall = w < 50;
1777
+ const relPath = path5.relative(process.cwd(), promptPath) || promptPath;
1778
+ console.log("");
1779
+ if (!isSmall) {
1780
+ console.log(boxTop(w));
1781
+ console.log(boxLine(w, chalk4.bold.white(" AI Prompt Ready - BETA")));
1782
+ 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}`));
1787
+ console.log(boxBot(w));
1788
+ } 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)}`);
1794
+ }
1795
+ console.log("");
1796
+ }
1797
+ var IMPORTANT_DIRS, AI_TOOLS, TARGETS, GOALS;
1798
+ var init_prompt_assistant = __esm({
1799
+ "src/ai/prompt-assistant.ts"() {
1800
+ "use strict";
1801
+ init_select();
1802
+ init_box();
1803
+ IMPORTANT_DIRS = [
1804
+ "index.html",
1805
+ "styles",
1806
+ "scripts/vendor",
1807
+ "scripts/modules",
1808
+ "assets/images",
1809
+ "assets/videos",
1810
+ "assets/fonts",
1811
+ "assets/misc",
1812
+ "data",
1813
+ "subpages"
1814
+ ];
1815
+ AI_TOOLS = [
1816
+ {
1817
+ id: "claude-code",
1818
+ label: "Claude Code",
1819
+ displayName: "Claude Code",
1820
+ agentName: "Claude Code"
1821
+ },
1822
+ {
1823
+ id: "codex",
1824
+ label: "Codex",
1825
+ displayName: "Codex",
1826
+ agentName: "Codex coding agent"
1827
+ },
1828
+ {
1829
+ id: "opencode",
1830
+ label: "OpenCode",
1831
+ displayName: "OpenCode",
1832
+ agentName: "OpenCode"
1833
+ },
1834
+ {
1835
+ id: "other-ai",
1836
+ label: "Other AI coding agent",
1837
+ displayName: "Other AI",
1838
+ agentName: "AI coding agent"
1839
+ }
1840
+ ];
1841
+ TARGETS = [
1842
+ {
1843
+ id: "react-vite",
1844
+ label: "React + Vite + TypeScript",
1845
+ stack: "React 18/19, TypeScript, Vite, CSS modules or plain CSS",
1846
+ projectDir: "converted-react-vite",
1847
+ staticDir: "public",
1848
+ scaffold: "npm create vite@latest converted-react-vite -- --template react-ts",
1849
+ entryFiles: "src/main.tsx, src/App.tsx, src/components/*, src/styles/*",
1850
+ routing: "Use React Router only if multiple exported pages exist in subpages/."
1851
+ },
1852
+ {
1853
+ id: "nextjs-app-router",
1854
+ label: "Next.js App Router",
1855
+ stack: "Next.js App Router, TypeScript, React Server Components where useful",
1856
+ projectDir: "converted-nextjs",
1857
+ staticDir: "public",
1858
+ scaffold: "npx create-next-app@latest converted-nextjs --ts --app --eslint",
1859
+ entryFiles: "app/page.tsx, app/layout.tsx, components/*, public/*",
1860
+ routing: "Map exported subpages to app routes and keep shared layout code reusable."
1861
+ },
1862
+ {
1863
+ id: "vue-vite",
1864
+ label: "Vue + Vite + TypeScript",
1865
+ stack: "Vue 3, TypeScript, Vite, single-file components",
1866
+ projectDir: "converted-vue-vite",
1867
+ staticDir: "public",
1868
+ scaffold: "npm create vite@latest converted-vue-vite -- --template vue-ts",
1869
+ entryFiles: "src/main.ts, src/App.vue, src/components/*.vue, src/styles/*",
1870
+ routing: "Use Vue Router only if multiple exported pages exist in subpages/."
1871
+ },
1872
+ {
1873
+ id: "sveltekit",
1874
+ label: "SvelteKit",
1875
+ stack: "SvelteKit, TypeScript, componentized routes and assets",
1876
+ projectDir: "converted-sveltekit",
1877
+ staticDir: "static",
1878
+ scaffold: "npm create svelte@latest converted-sveltekit",
1879
+ entryFiles: "src/routes/+page.svelte, src/lib/components/*, static/*",
1880
+ routing: "Create SvelteKit routes for meaningful exported pages when subpages/ exists."
1881
+ },
1882
+ {
1883
+ id: "astro",
1884
+ label: "Astro",
1885
+ stack: "Astro, TypeScript, island components only where interactivity is needed",
1886
+ projectDir: "converted-astro",
1887
+ staticDir: "public",
1888
+ scaffold: "npm create astro@latest converted-astro",
1889
+ entryFiles: "src/pages/index.astro, src/components/*, public/*",
1890
+ routing: "Use Astro pages for exported subpages and avoid unnecessary client JavaScript."
1891
+ }
1892
+ ];
1893
+ GOALS = [
1894
+ {
1895
+ id: "clean-rebuild",
1896
+ label: "Clean professional rebuild",
1897
+ instruction: "Rebuild the export as clean production code, not as a one-file HTML clone, while preserving the full page experience.",
1898
+ priority: "Readable structure, maintainability, complete pages, and faithful visual result."
1899
+ },
1900
+ {
1901
+ id: "pixel-perfect",
1902
+ label: "Pixel-perfect visual migration",
1903
+ instruction: "Prioritize pixel-perfect fidelity before refactoring anything aggressively, even when the layout is difficult.",
1904
+ priority: "Spacing, typography, responsive behavior, colors, media, animations, and page-by-page fidelity."
1905
+ },
1906
+ {
1907
+ id: "component-system",
1908
+ label: "Reusable component system",
1909
+ instruction: "Extract repeated UI blocks into reusable components with clean props without simplifying the original pages.",
1910
+ priority: "Components, layout primitives, naming, future editability, and complete section coverage."
1911
+ },
1912
+ {
1913
+ id: "performance-seo",
1914
+ label: "Performance and SEO rebuild",
1915
+ instruction: "Rebuild the site while reducing unused vendor code and improving SEO basics without losing visual fidelity.",
1916
+ priority: "Fast loading, semantic markup, metadata, accessibility, asset hygiene, and faithful pages."
1917
+ }
1918
+ ];
1331
1919
  }
1332
1920
  });
1333
1921
 
1334
1922
  // src/cli/cooking.ts
1335
- import chalk6 from "chalk";
1336
- var SHINE_WIDTH, FRAME_INTERVAL, DOT_PHASES, CookingSpinner;
1923
+ import chalk5 from "chalk";
1924
+ var SHINE_WIDTH, FRAME_INTERVAL, SPINNER_FRAMES, CookingSpinner;
1337
1925
  var init_cooking = __esm({
1338
1926
  "src/cli/cooking.ts"() {
1339
1927
  "use strict";
1928
+ init_theme();
1340
1929
  SHINE_WIDTH = 10;
1341
- FRAME_INTERVAL = 140;
1342
- DOT_PHASES = [" ", ". ", ".. ", "...", " ..", " .", " "];
1930
+ FRAME_INTERVAL = 80;
1931
+ SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1343
1932
  CookingSpinner = class {
1344
1933
  interval = null;
1345
1934
  frame = 0;
@@ -1377,11 +1966,11 @@ var init_cooking = __esm({
1377
1966
  }
1378
1967
  draw() {
1379
1968
  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}`);
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}`);
1385
1974
  }
1386
1975
  renderShimmer(text) {
1387
1976
  let result = "";
@@ -1391,9 +1980,9 @@ var init_cooking = __esm({
1391
1980
  if (dist < SHINE_WIDTH) {
1392
1981
  const t = 1 - dist / SHINE_WIDTH;
1393
1982
  const g = Math.floor(160 + t * 95);
1394
- result += chalk6.rgb(g, g, g)(text[i]);
1983
+ result += chalk5.rgb(250, Math.min(255, g), Math.min(255, Math.floor(g * 0.75)))(text[i]);
1395
1984
  } else {
1396
- result += chalk6.rgb(160, 160, 160)(text[i]);
1985
+ result += ui.muted(text[i]);
1397
1986
  }
1398
1987
  }
1399
1988
  return result;
@@ -1412,9 +2001,9 @@ __export(exporter_exports, {
1412
2001
  FramerExporter: () => FramerExporter,
1413
2002
  deriveOutputName: () => deriveOutputName
1414
2003
  });
1415
- import fs4 from "fs/promises";
1416
- import path5 from "path";
1417
- import chalk7 from "chalk";
2004
+ import fs5 from "fs/promises";
2005
+ import path6 from "path";
2006
+ import chalk6 from "chalk";
1418
2007
  function deriveOutputName(url, platformName) {
1419
2008
  try {
1420
2009
  const parsed = new URL(url);
@@ -1446,8 +2035,10 @@ var init_exporter = __esm({
1446
2035
  init_download2();
1447
2036
  init_output();
1448
2037
  init_summary();
2038
+ init_prompt_assistant();
1449
2039
  init_platforms();
1450
2040
  init_cooking();
2041
+ init_theme();
1451
2042
  FramerExporter = class {
1452
2043
  siteUrl;
1453
2044
  outDir;
@@ -1473,12 +2064,14 @@ var init_exporter = __esm({
1473
2064
  }
1474
2065
  }
1475
2066
  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));
2067
+ console.log(`
2068
+ ${ui.text.bold("Framer Export")} ${chip("mirror")} ${ui.muted("v4 pipeline")}
2069
+ `);
2070
+ info("Source : " + chalk6.underline(this.siteUrl));
2071
+ info("Output : " + ui.primary(this.outDir));
2072
+ info("Platform : " + ui.primary(this.platform.displayName));
1480
2073
  if (includeSubpages) {
1481
- info("Subpages : " + chalk7.green("enabled"));
2074
+ info("Subpages : " + ui.success("enabled"));
1482
2075
  }
1483
2076
  console.log("");
1484
2077
  this.cooking = new CookingSpinner();
@@ -1496,7 +2089,7 @@ var init_exporter = __esm({
1496
2089
  "data",
1497
2090
  "subpages"
1498
2091
  ]) {
1499
- await fs4.mkdir(path5.join(this.outDir, d), { recursive: true });
2092
+ await fs5.mkdir(path6.join(this.outDir, d), { recursive: true });
1500
2093
  }
1501
2094
  log("Output directory structure created");
1502
2095
  this.cooking.update("Fetching SSR HTML...");
@@ -1506,12 +2099,14 @@ var init_exporter = __esm({
1506
2099
  this.ssrHTML = buf.toString("utf-8");
1507
2100
  success("SSR HTML fetched (" + (this.ssrHTML.length / 1024).toFixed(1) + " KB)");
1508
2101
  } catch (e) {
1509
- log(chalk7.red("Could not fetch SSR HTML: " + e.message));
2102
+ log(chalk6.red("Could not fetch SSR HTML: " + e.message));
1510
2103
  }
1511
2104
  const htmlDetected = detectPlatform(this.siteUrl, this.ssrHTML);
1512
2105
  if (htmlDetected.name !== this.platform.name) {
1513
2106
  this.platform = htmlDetected;
1514
- log("Platform refined: " + chalk7.hex("#D4A017")(this.platform.displayName) + " (from HTML analysis)");
2107
+ log(
2108
+ "Platform refined: " + ui.primary(this.platform.displayName) + " (from HTML analysis)"
2109
+ );
1515
2110
  }
1516
2111
  await launchAndCapture(this);
1517
2112
  if (includeSubpages && this.page) {
@@ -1527,6 +2122,7 @@ var init_exporter = __esm({
1527
2122
  console.log("");
1528
2123
  success("Export complete!");
1529
2124
  await printSummary(this);
2125
+ await runAiPromptAssistant(this);
1530
2126
  }
1531
2127
  async crawlSubpages() {
1532
2128
  this.cooking?.update("Discovering sub-pages...");
@@ -1539,7 +2135,8 @@ var init_exporter = __esm({
1539
2135
  const hrefs = /* @__PURE__ */ new Set();
1540
2136
  for (const a of anchors) {
1541
2137
  const href = a.href;
1542
- if (!href || href.startsWith("javascript:") || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("#")) continue;
2138
+ if (!href || href.startsWith("javascript:") || href.startsWith("mailto:") || href.startsWith("tel:") || href.startsWith("#"))
2139
+ continue;
1543
2140
  try {
1544
2141
  const u = new URL(href);
1545
2142
  const h = u.hostname.replace(/^www\./, "");
@@ -1567,8 +2164,8 @@ var init_exporter = __esm({
1567
2164
  });
1568
2165
  const slug = this.deriveSlug(link, baseUrl);
1569
2166
  const filename = slug + ".html";
1570
- const filepath = path5.join(this.outDir, "subpages", filename);
1571
- await fs4.writeFile(filepath, html, "utf-8");
2167
+ const filepath = path6.join(this.outDir, "subpages", filename);
2168
+ await fs5.writeFile(filepath, html, "utf-8");
1572
2169
  log(" Saved: subpages/" + filename);
1573
2170
  } catch (e) {
1574
2171
  log(" Skipped " + link + ": " + e.message);
@@ -1590,142 +2187,37 @@ var init_exporter = __esm({
1590
2187
  }
1591
2188
  });
1592
2189
 
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
2190
  // src/cli/setup.ts
1700
2191
  var setup_exports = {};
1701
2192
  __export(setup_exports, {
1702
2193
  runSetup: () => runSetup
1703
2194
  });
1704
2195
  import readline2 from "readline/promises";
1705
- import { stdin as stdin2, stdout as stdout2 } from "process";
1706
- import path6 from "path";
2196
+ import { stdin as stdin3, stdout as stdout3 } from "process";
2197
+ import path7 from "path";
1707
2198
  import { URL as URL4 } from "url";
1708
- import chalk9 from "chalk";
2199
+ import chalk7 from "chalk";
1709
2200
  function drawHeader(title) {
1710
2201
  const w = maxWidth();
1711
2202
  if (w < 50) {
1712
2203
  console.log(`
1713
- ${chalk9.hex("#D4A017")("\u25CF")} ${chalk9.bold.white(title)}`);
2204
+ ${bullet("\u25CF")} ${ui.text.bold(title)}`);
1714
2205
  return;
1715
2206
  }
1716
2207
  console.log(boxTop(w));
1717
- console.log(boxLine(w, chalk9.bold.white(" " + title)));
2208
+ console.log(boxLine(w, ui.text.bold(" " + title)));
1718
2209
  console.log(boxBot(w));
1719
2210
  console.log("");
1720
2211
  }
1721
2212
  async function runSetup(legacyMode = false) {
1722
2213
  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 });
2214
+ 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 });
1726
2218
  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(">")} `;
2219
+ const suffix = defaultVal ? chalk7.gray(` (${defaultVal})`) : "";
2220
+ const prompt = ` ${ui.primary("\u25CF")} ${ui.text.bold(question)}${suffix} ${ui.muted(">")} `;
1729
2221
  const answer = await rl.question(prompt);
1730
2222
  return answer.trim() || defaultVal || "";
1731
2223
  };
@@ -1737,42 +2229,42 @@ async function runSetup(legacyMode = false) {
1737
2229
  new URL4(input);
1738
2230
  siteUrl = input;
1739
2231
  } catch {
1740
- console.log(` ${chalk9.red("\u2717")} ${chalk9.red("Invalid URL. Enter a valid URL (https://...)")}
2232
+ console.log(` ${ui.error("\u2717")} ${ui.error("Invalid URL. Enter a valid URL (https://...)")}
1741
2233
  `);
1742
2234
  }
1743
2235
  }
1744
- console.log(` ${chalk9.green("\u2713")} ${chalk9.green("URL:")} ${chalk9.underline(siteUrl)}
2236
+ console.log(` ${ui.success("\u2713")} ${ui.success("URL:")} ${chalk7.underline(siteUrl)}
1745
2237
  `);
1746
2238
  drawHeader("Step 2 : Platform");
1747
2239
  const detected = detectPlatform(siteUrl);
1748
2240
  let platformName;
1749
2241
  if (legacyMode) {
1750
- console.log(` ${chalk9.blue("i")} Auto-detected: ${chalk9.hex("#D4A017")(detected.displayName)}`);
2242
+ console.log(` ${ui.info("i")} Auto-detected: ${ui.primary(detected.displayName)}`);
1751
2243
  const platformInput = await ask("Platform (framer/webflow/wix)", detected.name);
1752
2244
  platformName = ["framer", "webflow", "wix"].includes(platformInput) ? platformInput : detected.name;
1753
- console.log(` ${chalk9.green("\u2713")} ${chalk9.green("Platform:")} ${chalk9.hex("#D4A017")(platformName)}
2245
+ console.log(` ${ui.success("\u2713")} ${ui.success("Platform:")} ${ui.primary(platformName)}
1754
2246
  `);
1755
2247
  } else {
1756
2248
  rl.close();
1757
2249
  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" }
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" }
1761
2253
  ];
1762
2254
  const defaultIdx = ["framer", "webflow", "wix"].indexOf(detected.name);
1763
2255
  platformName = await select("Select platform", platforms, Math.max(0, defaultIdx));
1764
2256
  }
1765
- const rl2 = legacyMode ? rl : readline2.createInterface({ input: stdin2, output: stdout2 });
2257
+ const rl2 = legacyMode ? rl : readline2.createInterface({ input: stdin3, output: stdout3 });
1766
2258
  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(">")} `;
2259
+ const suffix = defaultVal ? chalk7.gray(` (${defaultVal})`) : "";
2260
+ const prompt = ` ${ui.primary("\u25CF")} ${ui.text.bold(question)}${suffix} ${ui.muted(">")} `;
1769
2261
  const answer = await rl2.question(prompt);
1770
2262
  return answer.trim() || defaultVal || "";
1771
2263
  };
1772
2264
  const derivedName = deriveOutputName(siteUrl, platformName);
1773
2265
  drawHeader("Step 3 : Output Directory");
1774
2266
  const outDir = await ask2("Output directory", "./" + derivedName);
1775
- console.log(` ${chalk9.green("\u2713")} ${chalk9.green("Output:")} ${chalk9.yellow(outDir)}
2267
+ console.log(` ${ui.success("\u2713")} ${ui.success("Output:")} ${ui.primary(outDir)}
1776
2268
  `);
1777
2269
  drawHeader("Step 4 : Options");
1778
2270
  let prettyPrint;
@@ -1781,15 +2273,15 @@ async function runSetup(legacyMode = false) {
1781
2273
  if (legacyMode) {
1782
2274
  const prettyAnswer = await ask2("Pretty-print JS files? (y/n)", "y");
1783
2275
  prettyPrint = prettyAnswer.toLowerCase().startsWith("y");
1784
- console.log(` ${chalk9.green("\u2713")} Pretty-print: ${prettyPrint ? chalk9.green("yes") : chalk9.red("no")}
2276
+ console.log(` ${ui.success("\u2713")} Pretty-print: ${prettyPrint ? ui.success("yes") : ui.error("no")}
1785
2277
  `);
1786
2278
  const subpagesAnswer = await ask2("Export sub-pages? (y/n)", "n");
1787
2279
  includeSubpages = subpagesAnswer.toLowerCase().startsWith("y");
1788
- console.log(` ${chalk9.green("\u2713")} Sub-pages: ${includeSubpages ? chalk9.green("yes") : chalk9.red("no")}
2280
+ console.log(` ${ui.success("\u2713")} Sub-pages: ${includeSubpages ? ui.success("yes") : ui.error("no")}
1789
2281
  `);
1790
2282
  const concurrencyAnswer = await ask2("Download concurrency", "12");
1791
2283
  concurrency = parseInt(concurrencyAnswer, 10) || 12;
1792
- console.log(` ${chalk9.green("\u2713")} Concurrency: ${chalk9.yellow(String(concurrency))}
2284
+ console.log(` ${ui.success("\u2713")} Concurrency: ${ui.primary(String(concurrency))}
1793
2285
  `);
1794
2286
  } else {
1795
2287
  rl2.close();
@@ -1812,19 +2304,18 @@ async function runSetup(legacyMode = false) {
1812
2304
  }
1813
2305
  const w = maxWidth();
1814
2306
  const isSmall = w < 50;
1815
- const G2 = chalk9.hex("#C4953A");
1816
- const B2 = chalk9.hex("#8B6914");
1817
2307
  console.log("");
1818
2308
  if (!isSmall) {
1819
2309
  console.log(boxTop(w));
1820
- console.log(boxLine(w, chalk9.bold.white(" Summary")));
2310
+ console.log(boxLine(w, ui.text.bold(" Summary")));
2311
+ console.log(boxSep(w));
1821
2312
  } else {
1822
- console.log(chalk9.bold.white(" Summary:"));
2313
+ console.log(ui.text.bold(" Summary:"));
1823
2314
  }
1824
2315
  for (const [label, value] of [
1825
2316
  ["URL", siteUrl],
1826
2317
  ["Platform", platformName],
1827
- ["Output", path6.resolve(outDir)],
2318
+ ["Output", path7.resolve(outDir)],
1828
2319
  ["Pretty-print", prettyPrint ? "yes" : "no"],
1829
2320
  ["Sub-pages", includeSubpages ? "yes" : "no"],
1830
2321
  ["Concurrency", String(concurrency)]
@@ -1849,14 +2340,14 @@ async function runSetup(legacyMode = false) {
1849
2340
  }
1850
2341
  if (!startExport) {
1851
2342
  console.log(`
1852
- ${chalk9.yellow("Export cancelled.")}
2343
+ ${ui.warning("Export cancelled.")}
1853
2344
  `);
1854
2345
  return;
1855
2346
  }
1856
2347
  console.log("");
1857
2348
  const { CFG: CFG2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1858
2349
  CFG2.concurrency = concurrency;
1859
- const exporter = new FramerExporter(siteUrl, path6.resolve(outDir), platformName);
2350
+ const exporter = new FramerExporter(siteUrl, path7.resolve(outDir), platformName);
1860
2351
  exporter.prettyPrint = prettyPrint;
1861
2352
  await exporter.run(includeSubpages);
1862
2353
  }
@@ -1868,24 +2359,26 @@ var init_setup = __esm({
1868
2359
  init_platforms();
1869
2360
  init_select();
1870
2361
  init_box();
2362
+ init_theme();
1871
2363
  }
1872
2364
  });
1873
2365
 
1874
2366
  // src/cli/index.ts
1875
2367
  init_package();
1876
- import path7 from "path";
2368
+ import path8 from "path";
1877
2369
  import { URL as URL5 } from "url";
1878
2370
 
1879
2371
  // src/cli/help.ts
1880
2372
  init_banner();
1881
- import chalk2 from "chalk";
2373
+ init_theme();
1882
2374
  function showHelp() {
1883
2375
  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]")}`);
2376
+ console.log(`${ui.text.bold(" USAGE")} ${chip("cli")}
2377
+ `);
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]")}`);
1887
2380
  console.log("");
1888
- console.log(chalk2.white.bold(" OPTIONS\n"));
2381
+ console.log(ui.text.bold(" OPTIONS\n"));
1889
2382
  const opts = [
1890
2383
  ["--setup", "Launch the interactive setup assistant"],
1891
2384
  ["--platform <p>", "Force platform: framer, webflow, wix"],
@@ -1894,27 +2387,27 @@ function showHelp() {
1894
2387
  ["--help, -h", "Show this help message"]
1895
2388
  ];
1896
2389
  for (const [flag, desc] of opts) {
1897
- console.log(` ${chalk2.green(flag.padEnd(18))} ${chalk2.white(desc)}`);
2390
+ console.log(` ${ui.success(flag.padEnd(18))} ${ui.text(desc)}`);
1898
2391
  }
1899
2392
  console.log("");
1900
- console.log(chalk2.white.bold(" SUPPORTED PLATFORMS\n"));
2393
+ console.log(ui.text.bold(" SUPPORTED PLATFORMS\n"));
1901
2394
  const platforms = [
1902
2395
  ["Framer", "Auto-detected via .framer.app / .framer.website URLs"],
1903
2396
  ["Webflow", "Auto-detected via .webflow.io URLs"],
1904
2397
  ["Wix", "Auto-detected via .wixsite.com URLs + HTML analysis"]
1905
2398
  ];
1906
2399
  for (const [name, desc] of platforms) {
1907
- console.log(` ${chalk2.hex("#D4A017")(name.padEnd(12))} ${chalk2.gray(desc)}`);
2400
+ console.log(` ${ui.primary(name.padEnd(12))} ${ui.muted(desc)}`);
1908
2401
  }
1909
2402
  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")}`);
2403
+ 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")}`);
2409
+ 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")}`);
1918
2411
  console.log("");
1919
2412
  }
1920
2413
 
@@ -1953,6 +2446,7 @@ async function checkForUpdates(currentVersion) {
1953
2446
  }
1954
2447
 
1955
2448
  // src/cli/index.ts
2449
+ init_theme();
1956
2450
  var VERSION = package_default.version;
1957
2451
  function extractFlag(args, flag) {
1958
2452
  const idx = args.indexOf(flag);
@@ -1974,30 +2468,26 @@ async function main() {
1974
2468
  process.exit(0);
1975
2469
  }
1976
2470
  if (args.includes("--about")) {
1977
- const chalk10 = (await import("chalk")).default;
2471
+ const chalk8 = (await import("chalk")).default;
1978
2472
  showBanner();
1979
- console.log(` ${chalk10.white.bold("Framer Export")} ${chalk10.gray(`v${package_default.version}`)}`);
1980
- console.log(` ${chalk10.white(package_default.description)}
2473
+ console.log(` ${ui.text.bold("Framer Export")} ${ui.muted(`v${package_default.version}`)}`);
2474
+ console.log(` ${ui.text(package_default.description)}
1981
2475
  `);
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}`)}`);
2476
+ 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}`)}`);
2480
+ console.log(` ${ui.text.bold("License")} ${ui.success(package_default.license)}`);
2481
+ console.log(` ${ui.text.bold("Node")} ${ui.muted(`>=${package_default.engines.node}`)}`);
1988
2482
  console.log("");
1989
2483
  process.exit(0);
1990
2484
  }
1991
2485
  checkForUpdates(VERSION).then((latest) => {
1992
2486
  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
- });
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("");
2001
2491
  });
2002
2492
  if (args.includes("--help") || args.includes("-h")) {
2003
2493
  showHelp();
@@ -2022,9 +2512,8 @@ async function main() {
2022
2512
  try {
2023
2513
  new URL5(url);
2024
2514
  } 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")}
2515
+ console.log(` ${ui.error("\u2717")} ${ui.error.bold("Invalid URL:")} ${ui.text(url)}`);
2516
+ console.log(` ${ui.muted("Expected: https://yoursite.framer.app")}
2028
2517
  `);
2029
2518
  process.exit(1);
2030
2519
  }
@@ -2034,12 +2523,12 @@ async function main() {
2034
2523
  const defaultDir = deriveOutputName2(url, detected);
2035
2524
  const out = args[1] || `./${defaultDir}`;
2036
2525
  try {
2037
- await new FramerExporter2(url, path7.resolve(out), platformOverride || void 0).run(includeSubpages);
2526
+ await new FramerExporter2(url, path8.resolve(out), platformOverride || void 0).run(includeSubpages);
2038
2527
  } catch (e) {
2039
- const chalk10 = (await import("chalk")).default;
2528
+ const chalk8 = (await import("chalk")).default;
2040
2529
  console.log(`
2041
- ${chalk10.red("\u2717")} ${chalk10.red.bold("FAILED:")} ${chalk10.white(e.message)}`);
2042
- console.log(chalk10.gray(e.stack || ""));
2530
+ ${ui.error("\u2717")} ${ui.error.bold("FAILED:")} ${ui.text(e.message)}`);
2531
+ console.log(chalk8.gray(e.stack || ""));
2043
2532
  process.exit(1);
2044
2533
  }
2045
2534
  }