framer-export 4.4.1 → 4.4.3

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.4.1",
18
+ version: "4.4.3",
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",
@@ -215,7 +215,8 @@ async function showLoadingIntro(version) {
215
215
  const title = renderShinyText(`Framer Export v${version}`, frame, {
216
216
  baseColor: "#969696",
217
217
  shineColor: "#ffffff",
218
- shineWidth: 18
218
+ shineWidth: 18,
219
+ speed: 1.2
219
220
  });
220
221
  const dots = ".".repeat(frame % 4).padEnd(3, " ");
221
222
  process.stdout.write(
@@ -237,8 +238,9 @@ function renderShinyText(text, frame, options = {}) {
237
238
  const baseColor = options.baseColor || "#808080";
238
239
  const shineColor = options.shineColor || "#ffffff";
239
240
  const shineWidth = options.shineWidth || SHINE_WIDTH;
241
+ const speed = options.speed ?? SHINE_SPEED;
240
242
  const travel = text.length + shineWidth * 2;
241
- const pos = frame * SHINE_SPEED % travel - shineWidth;
243
+ const pos = frame * speed % travel - shineWidth;
242
244
  const base = hexToRgb(baseColor);
243
245
  const shine = hexToRgb(shineColor);
244
246
  let result = "";
@@ -338,9 +340,66 @@ function maxWidth() {
338
340
  return Math.min(process.stdout.columns || 80, 76);
339
341
  }
340
342
  function padRight(text, w) {
341
- const visible = stripAnsi(text).length;
342
- if (visible >= w) return text;
343
- return text + " ".repeat(w - visible);
343
+ const fitted = fitAnsi(text, w);
344
+ const visible = stripAnsi(fitted).length;
345
+ if (visible >= w) return fitted;
346
+ return fitted + " ".repeat(w - visible);
347
+ }
348
+ function fitAnsi(text, width) {
349
+ if (stripAnsi(text).length <= width) return text;
350
+ if (width <= 2) return ".".repeat(Math.max(0, width));
351
+ let out = "";
352
+ let visible = 0;
353
+ let i = 0;
354
+ const limit = width - 2;
355
+ while (i < text.length && visible < limit) {
356
+ if (text[i] === "\x1B") {
357
+ const match = text.slice(i).match(/^\x1B\[[0-9;]*[a-zA-Z]/);
358
+ if (match) {
359
+ out += match[0];
360
+ i += match[0].length;
361
+ continue;
362
+ }
363
+ }
364
+ out += text[i];
365
+ visible++;
366
+ i++;
367
+ }
368
+ return out + "..\x1B[0m";
369
+ }
370
+ function fitValue(value, width) {
371
+ if (value.length <= width) return value;
372
+ const pathLike = value.includes("\\") || value.includes("/");
373
+ if (pathLike) {
374
+ const shortened = shortenPath(value, width);
375
+ if (shortened.length <= width) return shortened;
376
+ }
377
+ if (width <= 3) return ".".repeat(Math.max(0, width));
378
+ return value.slice(0, width - 2) + "..";
379
+ }
380
+ function shortenPath(value, width) {
381
+ if (width <= 12) {
382
+ return width <= 3 ? ".".repeat(Math.max(0, width)) : value.slice(0, width - 2) + "..";
383
+ }
384
+ const separator = value.includes("\\") ? "\\" : "/";
385
+ const parts = value.split(/[\\/]+/).filter(Boolean);
386
+ const prefix = value.startsWith(separator) ? separator : "";
387
+ const shortened = parts.map((part, index) => {
388
+ const isLast = index === parts.length - 1;
389
+ if (isLast) return part;
390
+ if (/^[A-Za-z]:$/.test(part)) return part;
391
+ return part.length > 5 ? part.slice(0, 3) + ".." : part;
392
+ }).join(separator);
393
+ const withPrefix = prefix + shortened;
394
+ if (withPrefix.length <= width) return withPrefix;
395
+ const last = parts.at(-1) || value;
396
+ const first = parts.slice(0, -1).map((part) => /^[A-Za-z]:$/.test(part) ? part : part.slice(0, 3) + "..");
397
+ const compact = prefix + [...first, last].join(separator);
398
+ if (compact.length <= width) return compact;
399
+ const tailSpace = Math.max(8, Math.floor(width * 0.45));
400
+ const headSpace = Math.max(0, width - tailSpace - 4);
401
+ const middle = compact.slice(0, headSpace) + separator + ".." + separator + compact.slice(-tailSpace);
402
+ return middle.length <= width ? middle : compact.slice(0, width - 2) + "..";
344
403
  }
345
404
  function boxTop(w) {
346
405
  const inner = w - 4;
@@ -379,14 +438,10 @@ function panelSep(w) {
379
438
  function boxRow(w, label, value) {
380
439
  const inner = w - 4;
381
440
  const labelPlain = stripAnsi(chalk3.bold(label));
382
- const visible = labelPlain.length + 1 + value.length;
383
- if (visible > inner) {
384
- const avail = inner - labelPlain.length - 2;
385
- const truncated = value.length > avail ? value.slice(0, avail - 1) + ".." : value;
386
- return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(truncated) + " ".repeat(Math.max(0, inner - labelPlain.length - 1 - truncated.length)) + ui.border(" \u2502");
387
- }
388
- const right = inner - labelPlain.length - 1 - value.length;
389
- return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(value) + " ".repeat(right) + ui.border(" \u2502");
441
+ const avail = Math.max(0, inner - labelPlain.length - 2);
442
+ const fittedValue = fitValue(value, avail);
443
+ const right = Math.max(0, inner - labelPlain.length - 2 - fittedValue.length);
444
+ return " " + ui.border("\u2502 ") + chalk3.bold(label) + ": " + ui.primary(fittedValue) + " ".repeat(right) + ui.border(" \u2502");
390
445
  }
391
446
  var init_box = __esm({
392
447
  "src/cli/box.ts"() {
@@ -1603,12 +1658,13 @@ var init_template = __esm({
1603
1658
  "src/server/template.ts"() {
1604
1659
  "use strict";
1605
1660
  SERVE_SCRIPT = `#!/usr/bin/env node
1606
- const http = require('http');
1607
- const fs = require('fs');
1608
- const path = require('path');
1661
+ import http from 'node:http';
1662
+ import fs from 'node:fs';
1663
+ import path from 'node:path';
1664
+ import { fileURLToPath } from 'node:url';
1609
1665
 
1610
1666
  const PORT = process.env.PORT || 3000;
1611
- const ROOT = __dirname;
1667
+ const ROOT = path.dirname(fileURLToPath(import.meta.url));
1612
1668
 
1613
1669
  const MIME = {
1614
1670
  '.html': 'text/html; charset=utf-8',
@@ -1679,37 +1735,53 @@ const ANSI = {
1679
1735
  border: '\\x1b[38;2;72;72;72m',
1680
1736
  success: '\\x1b[38;2;127;216;143m',
1681
1737
  info: '\\x1b[38;2;86;182;194m',
1738
+ accent: '\\x1b[38;2;157;124;216m',
1739
+ warning: '\\x1b[38;2;245;167;66m',
1740
+ command: '\\x1b[38;2;255;192;159m',
1741
+ error: '\\x1b[38;2;224;108;117m',
1682
1742
  };
1683
1743
 
1684
1744
  function color(name, value) {
1685
1745
  return ANSI[name] + value + ANSI.reset;
1686
1746
  }
1687
1747
 
1688
- function frameLine(label, value) {
1689
- const text = ' ' + label.padEnd(10) + value;
1690
- const pad = Math.max(0, 56 - text.length);
1691
- console.log(' ' + color('border', '\u2502 ') + color('muted', label.padEnd(10)) + color('text', value) + ' '.repeat(pad) + color('border', ' \u2502'));
1748
+ function fitValue(value, width) {
1749
+ const text = String(value);
1750
+ if (text.length <= width) return text;
1751
+ if ((text.includes('\\\\') || text.includes('/')) && width > 12) {
1752
+ const separator = text.includes('\\\\') ? '\\\\' : '/';
1753
+ const prefix = text.startsWith(separator) ? separator : '';
1754
+ const parts = text.split(/[\\\\/]+/).filter(Boolean);
1755
+ const compact = prefix + parts.map((part, index) => {
1756
+ if (index === parts.length - 1) return part;
1757
+ if (/^[A-Za-z]:$/.test(part)) return part;
1758
+ return part.length > 5 ? part.slice(0, 3) + '..' : part;
1759
+ }).join(separator);
1760
+ if (compact.length <= width) return compact;
1761
+ const tail = Math.max(8, Math.floor(width * 0.45));
1762
+ const head = Math.max(0, width - tail - 4);
1763
+ return compact.slice(0, head) + separator + '..' + separator + compact.slice(-tail);
1764
+ }
1765
+ return width <= 3 ? '.'.repeat(Math.max(0, width)) : text.slice(0, width - 2) + '..';
1692
1766
  }
1693
1767
 
1694
- function drawServerUi() {
1695
- const logo = [
1696
- '\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ',
1697
- '\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557',
1698
- '\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D',
1699
- '\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557',
1700
- '\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D',
1701
- '\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 ',
1702
- ];
1768
+ function frameLine(label, value, colorName = 'text') {
1769
+ const labelText = label.padEnd(10);
1770
+ const valueWidth = Math.max(0, 56 - labelText.length);
1771
+ const shown = fitValue(value, valueWidth);
1772
+ const pad = Math.max(0, 56 - labelText.length - shown.length);
1773
+ console.log(' ' + color('border', '\u2502 ') + color('muted', labelText) + color(colorName, shown) + ' '.repeat(pad) + color('border', ' \u2502'));
1774
+ }
1703
1775
 
1704
- console.log('');
1705
- for (const line of logo) console.log(' ' + color('primary', line));
1776
+ function drawServerUi() {
1706
1777
  console.log('');
1707
1778
  console.log(' ' + color('border', '\u256D\u2500' + '\u2500'.repeat(56) + '\u2500\u256E'));
1708
- console.log(' ' + color('border', '\u2502 ') + color('text', ' Framer Export local server') + ' '.repeat(28) + color('border', ' \u2502'));
1779
+ console.log(' ' + color('border', '\u2502 ') + color('text', ' Framer Export preview server') + ' '.repeat(26) + color('border', ' \u2502'));
1709
1780
  console.log(' ' + color('border', '\u251C\u2500' + '\u2500'.repeat(56) + '\u2500\u2524'));
1710
- frameLine('URL', 'http://localhost:' + PORT);
1711
- frameLine('Root', ROOT);
1712
- frameLine('Mode', 'static mirror + SPA fallback');
1781
+ frameLine('URL', 'http://localhost:' + PORT, 'primary');
1782
+ frameLine('Root', ROOT, 'soft');
1783
+ frameLine('Run', 'node serve.js', 'command');
1784
+ frameLine('Mode', 'static mirror + SPA fallback', 'accent');
1713
1785
  console.log(' ' + color('border', '\u2570\u2500' + '\u2500'.repeat(56) + '\u2500\u256F'));
1714
1786
  console.log('');
1715
1787
  console.log(' ' + color('success', 'ready') + color('muted', ' press Ctrl+C to stop') + '\\n');
@@ -1717,9 +1789,14 @@ function drawServerUi() {
1717
1789
 
1718
1790
  server.listen(PORT, () => {
1719
1791
  const frames = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];
1792
+ const steps = ['Preparing server', 'Checking exported files', 'Binding local port', 'Serving static mirror'];
1793
+ const stepColors = ['text', 'info', 'warning', 'success'];
1720
1794
  let i = 0;
1721
1795
  const timer = setInterval(() => {
1722
- process.stdout.write('\\r\\x1B[2K ' + color('primary', frames[i % frames.length]) + ' ' + color('muted', 'starting Framer Export server'));
1796
+ const stepIndex = Math.min(steps.length - 1, Math.floor(i / 6));
1797
+ const step = steps[stepIndex];
1798
+ const dots = color('error', '.'.repeat(i % 4).padEnd(3, ' '));
1799
+ process.stdout.write('\\r\\x1B[2K ' + color('primary', frames[i % frames.length]) + ' ' + color(stepColors[stepIndex], step) + dots);
1723
1800
  i++;
1724
1801
  }, 80);
1725
1802
 
@@ -1727,7 +1804,7 @@ server.listen(PORT, () => {
1727
1804
  clearInterval(timer);
1728
1805
  process.stdout.write('\\r\\x1B[2K');
1729
1806
  drawServerUi();
1730
- }, 720);
1807
+ }, 1280);
1731
1808
  });
1732
1809
  `;
1733
1810
  }
@@ -1893,8 +1970,13 @@ async function buildOutput(exporter) {
1893
1970
  log("Writing index.html (" + (html.length / 1024).toFixed(1) + " KB)...");
1894
1971
  await fs2.writeFile(path3.join(exporter.outDir, "index.html"), html);
1895
1972
  success("index.html written");
1896
- await fs2.writeFile(path3.join(exporter.outDir, "serve.cjs"), SERVE_SCRIPT);
1897
- log("serve.cjs written");
1973
+ await fs2.writeFile(path3.join(exporter.outDir, "serve.js"), SERVE_SCRIPT);
1974
+ log("serve.js written");
1975
+ await fs2.writeFile(
1976
+ path3.join(exporter.outDir, "package.json"),
1977
+ JSON.stringify({ type: "module", scripts: { serve: "node serve.js" } }, null, 2) + "\n"
1978
+ );
1979
+ log("package.json written for serve.js");
1898
1980
  success("Output build complete");
1899
1981
  }
1900
1982
  async function rewriteDownloadedFiles(exporter) {
@@ -2044,21 +2126,19 @@ async function printSummary(exporter) {
2044
2126
  console.log(boxBot(w));
2045
2127
  }
2046
2128
  console.log("");
2047
- const cdCmd = "cd " + path4.basename(exporter.outDir) + " && node serve.cjs";
2129
+ const cdCmd = "cd " + path4.basename(exporter.outDir);
2130
+ const runCmd = "node serve.js";
2048
2131
  if (!isSmall) {
2049
2132
  console.log(boxTop(w));
2050
2133
  console.log(boxLine(w, ui.text.bold(" To serve locally")));
2051
2134
  console.log(boxSep(w));
2052
- const inner = w - 6;
2053
- const cmdLen = cdCmd.length;
2054
- const pad = Math.max(0, inner - cmdLen);
2055
- console.log(
2056
- " " + ui.border("\u2502 ") + G(cdCmd) + " ".repeat(pad) + ui.muted(" copy") + ui.border(" \u2502")
2057
- );
2135
+ console.log(boxRow(w, "Open", cdCmd));
2136
+ console.log(boxRow(w, "Run", runCmd));
2058
2137
  console.log(boxBot(w));
2059
2138
  } else {
2060
2139
  console.log(ui.text.bold(" To serve locally:"));
2061
2140
  console.log(` ${G(cdCmd)}`);
2141
+ console.log(` ${G(runCmd)}`);
2062
2142
  }
2063
2143
  console.log("");
2064
2144
  console.log(ui.muted(" note: must be served via HTTP for JS modules to work."));
@@ -2079,17 +2159,10 @@ import { stdin as stdin2, stdout as stdout2 } from "process";
2079
2159
  import { spawn } from "child_process";
2080
2160
  async function runAiPromptAssistant(exporter) {
2081
2161
  if (!stdin2.isTTY || !stdout2.isTTY) return;
2082
- printConvertPanel(exporter);
2083
- const action = await select(
2084
- "Framer Export AI Convert",
2085
- [
2086
- { label: `${buttonLabel("Convert")} open AI prompt modal`, value: "convert" },
2087
- { label: `${buttonLabel("Skip")} finish export`, value: "skip" }
2088
- ],
2089
- 0
2090
- );
2091
- if (action === "skip") return;
2092
- printAssistantModal("AI conversion prompt", [
2162
+ const serveCommand = buildServeCommand(exporter);
2163
+ const shouldConvert = await runExportCompletePrompt(exporter, serveCommand);
2164
+ if (!shouldConvert) return;
2165
+ printAssistantModal(`${ui.text.bold("AI conversion prompt")} ${ui.error("BETA")}`, [
2093
2166
  "Choose a target stack, AI tool, and conversion situation.",
2094
2167
  "Mouse clicks are supported in the terminal when available.",
2095
2168
  "A detailed prompt file will be generated inside the export folder."
@@ -2118,16 +2191,8 @@ async function runAiPromptAssistant(exporter) {
2118
2191
  const promptPath = path5.join(aiDir, `${aiTool.id}-${target.id}-${goal.id}-prompt.md`);
2119
2192
  await fs4.mkdir(aiDir, { recursive: true });
2120
2193
  await fs4.writeFile(promptPath, prompt, "utf-8");
2121
- printPromptResult(promptPath, target, aiTool, goal);
2122
- const promptAction = await select(
2123
- "Prompt actions",
2124
- [
2125
- { label: `${buttonLabel("Copy prompt")} clipboard`, value: "copy" },
2126
- { label: `${buttonLabel("Done")} keep file only`, value: "done" }
2127
- ],
2128
- 0
2129
- );
2130
- if (promptAction === "copy") {
2194
+ const copyPrompt = await runPromptReadyPrompt(promptPath, target, aiTool, goal);
2195
+ if (copyPrompt) {
2131
2196
  try {
2132
2197
  await copyToClipboard(prompt);
2133
2198
  console.log(` ${ui.success("\u2713")} ${ui.text.bold("Prompt copied to clipboard")}
@@ -2185,27 +2250,97 @@ function centerText2(text, width) {
2185
2250
  const left = Math.floor((width - visible) / 2);
2186
2251
  return " ".repeat(left) + text + " ".repeat(width - visible - left);
2187
2252
  }
2188
- function printConvertPanel(exporter) {
2189
- const w = maxWidth();
2190
- const inner = w - 4;
2191
- const rows = [
2192
- centerText2(`${ui.text.bold("AI Convert")} ${ui.muted("BETA")}`, inner),
2193
- centerText2(ui.muted("Generate a conversion prompt after the export."), inner),
2194
- centerText2(`${buttonLabel("Convert")} ${ui.muted("or")} ${buttonLabel("Skip")}`, inner),
2195
- centerText2(ui.muted(path5.basename(exporter.outDir)), inner)
2196
- ];
2197
- console.log("");
2198
- console.log(boxTop(w));
2199
- for (const row of rows) console.log(boxLine(w, row));
2200
- console.log(boxBot(w));
2201
- console.log("");
2253
+ function buildServeCommand(exporter) {
2254
+ return `cd ${path5.basename(exporter.outDir)} && node serve.js`;
2255
+ }
2256
+ async function runExportCompletePrompt(exporter, serveCommand) {
2257
+ let copyServeCommand = true;
2258
+ while (true) {
2259
+ const action = await select(
2260
+ "Export complete",
2261
+ [
2262
+ {
2263
+ label: `${checkboxLabel(copyServeCommand)} Copy serve command when finishing`,
2264
+ value: "toggle-copy"
2265
+ },
2266
+ {
2267
+ label: `${buttonLabel("Convert to AI code")} ${ui.error("BETA")}`,
2268
+ value: "convert"
2269
+ },
2270
+ { label: buttonLabel("Finish"), value: "finish" }
2271
+ ],
2272
+ 2,
2273
+ {
2274
+ headerLines: [
2275
+ `Output: ${path5.basename(exporter.outDir)}`,
2276
+ `Run: ${serveCommand}`,
2277
+ "Use Convert to open the AI conversion assistant."
2278
+ ],
2279
+ footer: "enter select \xB7 checkbox toggles copy \xB7 mouse hover/click"
2280
+ }
2281
+ );
2282
+ if (action === "toggle-copy") {
2283
+ copyServeCommand = !copyServeCommand;
2284
+ continue;
2285
+ }
2286
+ if (action === "finish") {
2287
+ if (copyServeCommand) {
2288
+ try {
2289
+ await copyToClipboard(serveCommand);
2290
+ console.log(` ${ui.success("\u2713")} ${ui.text.bold("Serve command copied")}
2291
+ `);
2292
+ } catch (error) {
2293
+ console.log(
2294
+ ` ${ui.warning("!")} ${ui.warning("Clipboard copy unavailable:")} ${ui.muted(error.message)}
2295
+ `
2296
+ );
2297
+ }
2298
+ }
2299
+ return false;
2300
+ }
2301
+ return true;
2302
+ }
2303
+ }
2304
+ async function runPromptReadyPrompt(promptPath, target, aiTool, goal) {
2305
+ const relPath = path5.relative(process.cwd(), promptPath) || promptPath;
2306
+ let copyPrompt = false;
2307
+ while (true) {
2308
+ const action = await select(
2309
+ `AI Prompt Ready ${ui.error("BETA")}`,
2310
+ [
2311
+ {
2312
+ label: `${checkboxLabel(copyPrompt)} Copy prompt when finishing`,
2313
+ value: "toggle-copy"
2314
+ },
2315
+ { label: buttonLabel("Finish"), value: "finish" }
2316
+ ],
2317
+ 1,
2318
+ {
2319
+ headerLines: [
2320
+ `Tool: ${aiTool.displayName}`,
2321
+ `Stack: ${target.label}`,
2322
+ `Mode: ${goal.label}`,
2323
+ `File: ${relPath}`
2324
+ ],
2325
+ footer: "enter finish \xB7 checkbox toggles copy \xB7 mouse hover/click"
2326
+ }
2327
+ );
2328
+ if (action === "toggle-copy") {
2329
+ copyPrompt = !copyPrompt;
2330
+ continue;
2331
+ }
2332
+ return copyPrompt;
2333
+ }
2334
+ }
2335
+ function checkboxLabel(checked) {
2336
+ return checked ? ui.success("\u2611") : ui.muted("\u2610");
2202
2337
  }
2203
2338
  function printAssistantModal(title, lines) {
2204
2339
  const w = maxWidth();
2205
2340
  const inner = w - 4;
2206
2341
  console.log("");
2207
2342
  console.log(boxTop(w));
2208
- console.log(boxLine(w, centerText2(`${ui.primary("\u25C6")} ${ui.text.bold(title)}`, inner)));
2343
+ console.log(boxLine(w, centerText2(`${ui.primary("\u25C6")} ${title}`, inner)));
2209
2344
  console.log(boxSep(w));
2210
2345
  for (const line of lines) {
2211
2346
  console.log(boxLine(w, centerText2(ui.muted(line), inner)));
@@ -2388,44 +2523,6 @@ function pipeToCommand(command, args, input) {
2388
2523
  child.stdin?.end(input);
2389
2524
  });
2390
2525
  }
2391
- function printPromptResult(promptPath, target, aiTool, goal) {
2392
- const w = maxWidth();
2393
- const isSmall = w < 50;
2394
- const inner = w - 4;
2395
- const relPath = path5.relative(process.cwd(), promptPath) || promptPath;
2396
- console.log("");
2397
- if (!isSmall) {
2398
- console.log(boxTop(w));
2399
- console.log(
2400
- boxLine(
2401
- w,
2402
- centerText2(
2403
- `${ui.success("\u2713")} ${ui.text.bold("AI Prompt Ready")} ${ui.muted("BETA")}`,
2404
- inner
2405
- )
2406
- )
2407
- );
2408
- console.log(boxSep(w));
2409
- console.log(
2410
- boxLine(w, centerText2(`${ui.muted("Tool")} ${ui.primary(aiTool.displayName)}`, inner))
2411
- );
2412
- console.log(boxLine(w, centerText2(`${ui.muted("Stack")} ${ui.primary(target.label)}`, inner)));
2413
- console.log(boxLine(w, centerText2(`${ui.muted("Mode")} ${ui.primary(goal.label)}`, inner)));
2414
- console.log(boxLine(w, centerText2(`${ui.muted("File")} ${ui.primary(relPath)}`, inner)));
2415
- console.log(boxSep(w));
2416
- console.log(
2417
- boxLine(w, centerText2(`${buttonLabel("Copy prompt")} ${buttonLabel("Done")}`, inner))
2418
- );
2419
- console.log(boxBot(w));
2420
- } else {
2421
- console.log(ui.text.bold(" AI Prompt Ready - BETA"));
2422
- console.log(` Tool: ${ui.primary(aiTool.displayName)}`);
2423
- console.log(` Stack: ${ui.primary(target.label)}`);
2424
- console.log(` Situation: ${ui.primary(goal.label)}`);
2425
- console.log(` File: ${ui.primary(relPath)}`);
2426
- }
2427
- console.log("");
2428
- }
2429
2526
  var IMPORTANT_DIRS, AI_TOOLS, TARGETS, GOALS;
2430
2527
  var init_prompt_assistant = __esm({
2431
2528
  "src/ai/prompt-assistant.ts"() {