framer-export 4.4.1 → 4.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +117 -102
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "framer-export",
|
|
18
|
-
version: "4.4.
|
|
18
|
+
version: "4.4.2",
|
|
19
19
|
description: "Export any Framer, Webflow, or Wix site into a fully working local mirror. Downloads all assets, strips badges, rewrites URLs, and pretty-prints JS.",
|
|
20
20
|
type: "module",
|
|
21
21
|
main: "dist/cli/index.js",
|
|
@@ -1603,12 +1603,13 @@ var init_template = __esm({
|
|
|
1603
1603
|
"src/server/template.ts"() {
|
|
1604
1604
|
"use strict";
|
|
1605
1605
|
SERVE_SCRIPT = `#!/usr/bin/env node
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1606
|
+
import http from 'node:http';
|
|
1607
|
+
import fs from 'node:fs';
|
|
1608
|
+
import path from 'node:path';
|
|
1609
|
+
import { fileURLToPath } from 'node:url';
|
|
1609
1610
|
|
|
1610
1611
|
const PORT = process.env.PORT || 3000;
|
|
1611
|
-
const ROOT =
|
|
1612
|
+
const ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
1612
1613
|
|
|
1613
1614
|
const MIME = {
|
|
1614
1615
|
'.html': 'text/html; charset=utf-8',
|
|
@@ -1679,6 +1680,7 @@ const ANSI = {
|
|
|
1679
1680
|
border: '\\x1b[38;2;72;72;72m',
|
|
1680
1681
|
success: '\\x1b[38;2;127;216;143m',
|
|
1681
1682
|
info: '\\x1b[38;2;86;182;194m',
|
|
1683
|
+
error: '\\x1b[38;2;224;108;117m',
|
|
1682
1684
|
};
|
|
1683
1685
|
|
|
1684
1686
|
function color(name, value) {
|
|
@@ -1692,23 +1694,13 @@ function frameLine(label, value) {
|
|
|
1692
1694
|
}
|
|
1693
1695
|
|
|
1694
1696
|
function drawServerUi() {
|
|
1695
|
-
const logo = [
|
|
1696
|
-
'\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ',
|
|
1697
|
-
'\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557',
|
|
1698
|
-
'\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D',
|
|
1699
|
-
'\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557',
|
|
1700
|
-
'\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D',
|
|
1701
|
-
'\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 ',
|
|
1702
|
-
];
|
|
1703
|
-
|
|
1704
|
-
console.log('');
|
|
1705
|
-
for (const line of logo) console.log(' ' + color('primary', line));
|
|
1706
1697
|
console.log('');
|
|
1707
1698
|
console.log(' ' + color('border', '\u256D\u2500' + '\u2500'.repeat(56) + '\u2500\u256E'));
|
|
1708
|
-
console.log(' ' + color('border', '\u2502 ') + color('text', ' Framer Export
|
|
1699
|
+
console.log(' ' + color('border', '\u2502 ') + color('text', ' Framer Export preview server') + ' '.repeat(26) + color('border', ' \u2502'));
|
|
1709
1700
|
console.log(' ' + color('border', '\u251C\u2500' + '\u2500'.repeat(56) + '\u2500\u2524'));
|
|
1710
1701
|
frameLine('URL', 'http://localhost:' + PORT);
|
|
1711
1702
|
frameLine('Root', ROOT);
|
|
1703
|
+
frameLine('Run', 'node serve.js');
|
|
1712
1704
|
frameLine('Mode', 'static mirror + SPA fallback');
|
|
1713
1705
|
console.log(' ' + color('border', '\u2570\u2500' + '\u2500'.repeat(56) + '\u2500\u256F'));
|
|
1714
1706
|
console.log('');
|
|
@@ -1717,9 +1709,12 @@ function drawServerUi() {
|
|
|
1717
1709
|
|
|
1718
1710
|
server.listen(PORT, () => {
|
|
1719
1711
|
const frames = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];
|
|
1712
|
+
const steps = ['Preparing server', 'Checking exported files', 'Binding local port', 'Serving static mirror'];
|
|
1720
1713
|
let i = 0;
|
|
1721
1714
|
const timer = setInterval(() => {
|
|
1722
|
-
|
|
1715
|
+
const step = steps[Math.min(steps.length - 1, Math.floor(i / 6))];
|
|
1716
|
+
const dots = color('error', '.'.repeat(i % 4).padEnd(3, ' '));
|
|
1717
|
+
process.stdout.write('\\r\\x1B[2K ' + color('primary', frames[i % frames.length]) + ' ' + color('text', step) + dots);
|
|
1723
1718
|
i++;
|
|
1724
1719
|
}, 80);
|
|
1725
1720
|
|
|
@@ -1727,7 +1722,7 @@ server.listen(PORT, () => {
|
|
|
1727
1722
|
clearInterval(timer);
|
|
1728
1723
|
process.stdout.write('\\r\\x1B[2K');
|
|
1729
1724
|
drawServerUi();
|
|
1730
|
-
},
|
|
1725
|
+
}, 1280);
|
|
1731
1726
|
});
|
|
1732
1727
|
`;
|
|
1733
1728
|
}
|
|
@@ -1893,8 +1888,13 @@ async function buildOutput(exporter) {
|
|
|
1893
1888
|
log("Writing index.html (" + (html.length / 1024).toFixed(1) + " KB)...");
|
|
1894
1889
|
await fs2.writeFile(path3.join(exporter.outDir, "index.html"), html);
|
|
1895
1890
|
success("index.html written");
|
|
1896
|
-
await fs2.writeFile(path3.join(exporter.outDir, "serve.
|
|
1897
|
-
log("serve.
|
|
1891
|
+
await fs2.writeFile(path3.join(exporter.outDir, "serve.js"), SERVE_SCRIPT);
|
|
1892
|
+
log("serve.js written");
|
|
1893
|
+
await fs2.writeFile(
|
|
1894
|
+
path3.join(exporter.outDir, "package.json"),
|
|
1895
|
+
JSON.stringify({ type: "module", scripts: { serve: "node serve.js" } }, null, 2) + "\n"
|
|
1896
|
+
);
|
|
1897
|
+
log("package.json written for serve.js");
|
|
1898
1898
|
success("Output build complete");
|
|
1899
1899
|
}
|
|
1900
1900
|
async function rewriteDownloadedFiles(exporter) {
|
|
@@ -2044,21 +2044,19 @@ async function printSummary(exporter) {
|
|
|
2044
2044
|
console.log(boxBot(w));
|
|
2045
2045
|
}
|
|
2046
2046
|
console.log("");
|
|
2047
|
-
const cdCmd = "cd " + path4.basename(exporter.outDir)
|
|
2047
|
+
const cdCmd = "cd " + path4.basename(exporter.outDir);
|
|
2048
|
+
const runCmd = "node serve.js";
|
|
2048
2049
|
if (!isSmall) {
|
|
2049
2050
|
console.log(boxTop(w));
|
|
2050
2051
|
console.log(boxLine(w, ui.text.bold(" To serve locally")));
|
|
2051
2052
|
console.log(boxSep(w));
|
|
2052
|
-
|
|
2053
|
-
|
|
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
|
-
);
|
|
2053
|
+
console.log(boxRow(w, "Open", cdCmd));
|
|
2054
|
+
console.log(boxRow(w, "Run", runCmd));
|
|
2058
2055
|
console.log(boxBot(w));
|
|
2059
2056
|
} else {
|
|
2060
2057
|
console.log(ui.text.bold(" To serve locally:"));
|
|
2061
2058
|
console.log(` ${G(cdCmd)}`);
|
|
2059
|
+
console.log(` ${G(runCmd)}`);
|
|
2062
2060
|
}
|
|
2063
2061
|
console.log("");
|
|
2064
2062
|
console.log(ui.muted(" note: must be served via HTTP for JS modules to work."));
|
|
@@ -2079,17 +2077,10 @@ import { stdin as stdin2, stdout as stdout2 } from "process";
|
|
|
2079
2077
|
import { spawn } from "child_process";
|
|
2080
2078
|
async function runAiPromptAssistant(exporter) {
|
|
2081
2079
|
if (!stdin2.isTTY || !stdout2.isTTY) return;
|
|
2082
|
-
|
|
2083
|
-
const
|
|
2084
|
-
|
|
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", [
|
|
2080
|
+
const serveCommand = buildServeCommand(exporter);
|
|
2081
|
+
const shouldConvert = await runExportCompletePrompt(exporter, serveCommand);
|
|
2082
|
+
if (!shouldConvert) return;
|
|
2083
|
+
printAssistantModal(`${ui.text.bold("AI conversion prompt")} ${ui.error("BETA")}`, [
|
|
2093
2084
|
"Choose a target stack, AI tool, and conversion situation.",
|
|
2094
2085
|
"Mouse clicks are supported in the terminal when available.",
|
|
2095
2086
|
"A detailed prompt file will be generated inside the export folder."
|
|
@@ -2118,16 +2109,8 @@ async function runAiPromptAssistant(exporter) {
|
|
|
2118
2109
|
const promptPath = path5.join(aiDir, `${aiTool.id}-${target.id}-${goal.id}-prompt.md`);
|
|
2119
2110
|
await fs4.mkdir(aiDir, { recursive: true });
|
|
2120
2111
|
await fs4.writeFile(promptPath, prompt, "utf-8");
|
|
2121
|
-
|
|
2122
|
-
|
|
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") {
|
|
2112
|
+
const copyPrompt = await runPromptReadyPrompt(promptPath, target, aiTool, goal);
|
|
2113
|
+
if (copyPrompt) {
|
|
2131
2114
|
try {
|
|
2132
2115
|
await copyToClipboard(prompt);
|
|
2133
2116
|
console.log(` ${ui.success("\u2713")} ${ui.text.bold("Prompt copied to clipboard")}
|
|
@@ -2185,27 +2168,97 @@ function centerText2(text, width) {
|
|
|
2185
2168
|
const left = Math.floor((width - visible) / 2);
|
|
2186
2169
|
return " ".repeat(left) + text + " ".repeat(width - visible - left);
|
|
2187
2170
|
}
|
|
2188
|
-
function
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2171
|
+
function buildServeCommand(exporter) {
|
|
2172
|
+
return `cd ${path5.basename(exporter.outDir)} && node serve.js`;
|
|
2173
|
+
}
|
|
2174
|
+
async function runExportCompletePrompt(exporter, serveCommand) {
|
|
2175
|
+
let copyServeCommand = false;
|
|
2176
|
+
while (true) {
|
|
2177
|
+
const action = await select(
|
|
2178
|
+
"Export complete",
|
|
2179
|
+
[
|
|
2180
|
+
{
|
|
2181
|
+
label: `${checkboxLabel(copyServeCommand)} Copy serve command when finishing`,
|
|
2182
|
+
value: "toggle-copy"
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
label: `${buttonLabel("Convert to AI code")} ${ui.error("BETA")}`,
|
|
2186
|
+
value: "convert"
|
|
2187
|
+
},
|
|
2188
|
+
{ label: buttonLabel("Finish"), value: "finish" }
|
|
2189
|
+
],
|
|
2190
|
+
2,
|
|
2191
|
+
{
|
|
2192
|
+
headerLines: [
|
|
2193
|
+
`Output: ${path5.basename(exporter.outDir)}`,
|
|
2194
|
+
`Run: ${serveCommand}`,
|
|
2195
|
+
"Use Convert to open the AI conversion assistant."
|
|
2196
|
+
],
|
|
2197
|
+
footer: "enter select \xB7 checkbox toggles copy \xB7 mouse hover/click"
|
|
2198
|
+
}
|
|
2199
|
+
);
|
|
2200
|
+
if (action === "toggle-copy") {
|
|
2201
|
+
copyServeCommand = !copyServeCommand;
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
if (action === "finish") {
|
|
2205
|
+
if (copyServeCommand) {
|
|
2206
|
+
try {
|
|
2207
|
+
await copyToClipboard(serveCommand);
|
|
2208
|
+
console.log(` ${ui.success("\u2713")} ${ui.text.bold("Serve command copied")}
|
|
2209
|
+
`);
|
|
2210
|
+
} catch (error) {
|
|
2211
|
+
console.log(
|
|
2212
|
+
` ${ui.warning("!")} ${ui.warning("Clipboard copy unavailable:")} ${ui.muted(error.message)}
|
|
2213
|
+
`
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
return false;
|
|
2218
|
+
}
|
|
2219
|
+
return true;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
async function runPromptReadyPrompt(promptPath, target, aiTool, goal) {
|
|
2223
|
+
const relPath = path5.relative(process.cwd(), promptPath) || promptPath;
|
|
2224
|
+
let copyPrompt = false;
|
|
2225
|
+
while (true) {
|
|
2226
|
+
const action = await select(
|
|
2227
|
+
`AI Prompt Ready ${ui.error("BETA")}`,
|
|
2228
|
+
[
|
|
2229
|
+
{
|
|
2230
|
+
label: `${checkboxLabel(copyPrompt)} Copy prompt when finishing`,
|
|
2231
|
+
value: "toggle-copy"
|
|
2232
|
+
},
|
|
2233
|
+
{ label: buttonLabel("Finish"), value: "finish" }
|
|
2234
|
+
],
|
|
2235
|
+
1,
|
|
2236
|
+
{
|
|
2237
|
+
headerLines: [
|
|
2238
|
+
`Tool: ${aiTool.displayName}`,
|
|
2239
|
+
`Stack: ${target.label}`,
|
|
2240
|
+
`Mode: ${goal.label}`,
|
|
2241
|
+
`File: ${relPath}`
|
|
2242
|
+
],
|
|
2243
|
+
footer: "enter finish \xB7 checkbox toggles copy \xB7 mouse hover/click"
|
|
2244
|
+
}
|
|
2245
|
+
);
|
|
2246
|
+
if (action === "toggle-copy") {
|
|
2247
|
+
copyPrompt = !copyPrompt;
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
return copyPrompt;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
function checkboxLabel(checked) {
|
|
2254
|
+
return checked ? ui.success("\u2611") : ui.muted("\u2610");
|
|
2202
2255
|
}
|
|
2203
2256
|
function printAssistantModal(title, lines) {
|
|
2204
2257
|
const w = maxWidth();
|
|
2205
2258
|
const inner = w - 4;
|
|
2206
2259
|
console.log("");
|
|
2207
2260
|
console.log(boxTop(w));
|
|
2208
|
-
console.log(boxLine(w, centerText2(`${ui.primary("\u25C6")} ${
|
|
2261
|
+
console.log(boxLine(w, centerText2(`${ui.primary("\u25C6")} ${title}`, inner)));
|
|
2209
2262
|
console.log(boxSep(w));
|
|
2210
2263
|
for (const line of lines) {
|
|
2211
2264
|
console.log(boxLine(w, centerText2(ui.muted(line), inner)));
|
|
@@ -2388,44 +2441,6 @@ function pipeToCommand(command, args, input) {
|
|
|
2388
2441
|
child.stdin?.end(input);
|
|
2389
2442
|
});
|
|
2390
2443
|
}
|
|
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
2444
|
var IMPORTANT_DIRS, AI_TOOLS, TARGETS, GOALS;
|
|
2430
2445
|
var init_prompt_assistant = __esm({
|
|
2431
2446
|
"src/ai/prompt-assistant.ts"() {
|