frameshot-mcp 0.7.0 → 0.9.7
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/README.md +83 -69
- package/action.yml +114 -16
- package/dist/chunk-MEBQ7ZWA.js +1774 -0
- package/dist/chunk-VUYZHZBH.js +157 -0
- package/dist/cli.js +131 -133
- package/dist/index.js +519 -572
- package/dist/renderer.d.ts +17 -7
- package/dist/renderer.js +4 -6
- package/dist/stubs/gatsby.js +20 -0
- package/dist/stubs/next-font.js +9 -0
- package/dist/stubs/next-headers.js +20 -0
- package/dist/stubs/next-image.js +34 -0
- package/dist/stubs/next-link.js +25 -0
- package/dist/stubs/next-navigation.js +17 -0
- package/dist/stubs/next-router.js +19 -0
- package/dist/stubs/nuxt-app.js +37 -0
- package/dist/stubs/nuxt-imports.js +13 -0
- package/dist/stubs/qwik-city.js +33 -0
- package/dist/stubs/react-router.js +67 -0
- package/dist/stubs/server-only.js +2 -0
- package/dist/stubs/solid-router.js +27 -0
- package/dist/stubs/solid-start.js +18 -0
- package/dist/stubs/stubs/gatsby.js +20 -0
- package/dist/stubs/stubs/next-font.js +9 -0
- package/dist/stubs/stubs/next-headers.js +20 -0
- package/dist/stubs/stubs/next-image.js +34 -0
- package/dist/stubs/stubs/next-link.js +25 -0
- package/dist/stubs/stubs/next-navigation.js +17 -0
- package/dist/stubs/stubs/next-router.js +19 -0
- package/dist/stubs/stubs/nuxt-app.js +37 -0
- package/dist/stubs/stubs/nuxt-imports.js +13 -0
- package/dist/stubs/stubs/qwik-city.js +33 -0
- package/dist/stubs/stubs/react-router.js +67 -0
- package/dist/stubs/stubs/server-only.js +2 -0
- package/dist/stubs/stubs/solid-router.js +27 -0
- package/dist/stubs/stubs/solid-start.js +18 -0
- package/dist/stubs/stubs/sveltekit-environment.js +5 -0
- package/dist/stubs/stubs/sveltekit-navigation.js +11 -0
- package/dist/stubs/stubs/sveltekit-stores.js +15 -0
- package/dist/stubs/stubs/vike.js +11 -0
- package/dist/stubs/sveltekit-environment.js +5 -0
- package/dist/stubs/sveltekit-navigation.js +11 -0
- package/dist/stubs/sveltekit-stores.js +15 -0
- package/dist/stubs/vike.js +11 -0
- package/package.json +10 -4
- package/scripts/render-changed.mjs +140 -18
- package/dist/chunk-3LVWVDET.js +0 -849
- package/dist/chunk-47YJG5HR.js +0 -690
- package/dist/chunk-67JZQ6OI.js +0 -819
- package/dist/chunk-AZCGKIMU.js +0 -850
- package/dist/chunk-B3CLIGWU.js +0 -786
- package/dist/chunk-C6QSY4WR.js +0 -811
- package/dist/chunk-DX54PJKO.js +0 -603
- package/dist/chunk-EMCJGIMY.js +0 -984
- package/dist/chunk-FQNWGR62.js +0 -849
- package/dist/chunk-FTYTZW6D.js +0 -203
- package/dist/chunk-JGVKYXY2.js +0 -857
- package/dist/chunk-JYPEA4P2.js +0 -846
- package/dist/chunk-KHK35HDD.js +0 -855
- package/dist/chunk-Q7A3DLED.js +0 -848
- package/dist/chunk-SIA6XEHM.js +0 -811
- package/dist/chunk-ST35YDI6.js +0 -834
- package/dist/chunk-T5OBJK35.js +0 -855
- package/dist/chunk-U3GHS7KO.js +0 -837
- package/dist/chunk-WS2ASCD6.js +0 -683
- package/dist/chunk-WZMHVSUA.js +0 -847
- package/dist/chunk-ZZST6K7Y.js +0 -987
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuditUseCase,
|
|
3
|
+
BrowserPool,
|
|
4
|
+
CatalogUseCase,
|
|
5
|
+
DiffUseCase,
|
|
6
|
+
HtmlBuilder,
|
|
7
|
+
ImageComparator,
|
|
8
|
+
RenderUseCase,
|
|
9
|
+
ScreenshotUseCase,
|
|
10
|
+
SnapshotStore,
|
|
11
|
+
SnapshotUseCase,
|
|
12
|
+
ViteBundler
|
|
13
|
+
} from "./chunk-MEBQ7ZWA.js";
|
|
14
|
+
|
|
15
|
+
// src/use-cases/watch.ts
|
|
16
|
+
import { watch } from "chokidar";
|
|
17
|
+
var WatchUseCase = class {
|
|
18
|
+
constructor(renderUseCase) {
|
|
19
|
+
this.renderUseCase = renderUseCase;
|
|
20
|
+
}
|
|
21
|
+
renderUseCase;
|
|
22
|
+
sessions = /* @__PURE__ */ new Map();
|
|
23
|
+
latestRenders = /* @__PURE__ */ new Map();
|
|
24
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
25
|
+
// key: `${sessionId}:${filePath}`
|
|
26
|
+
nextId = 1;
|
|
27
|
+
start(patterns, props, callback) {
|
|
28
|
+
const id = `watch-${this.nextId++}`;
|
|
29
|
+
const watcher = watch(patterns, {
|
|
30
|
+
ignoreInitial: true,
|
|
31
|
+
// only watch future changes, not existing files on startup
|
|
32
|
+
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
|
|
33
|
+
});
|
|
34
|
+
watcher.on(
|
|
35
|
+
"change",
|
|
36
|
+
(path) => this.handleChange(id, path, props, callback)
|
|
37
|
+
);
|
|
38
|
+
const session = {
|
|
39
|
+
id,
|
|
40
|
+
patterns,
|
|
41
|
+
stop: () => {
|
|
42
|
+
watcher.close();
|
|
43
|
+
this.sessions.delete(id);
|
|
44
|
+
this.latestRenders.delete(id);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
this.sessions.set(id, session);
|
|
48
|
+
return session;
|
|
49
|
+
}
|
|
50
|
+
stop(sessionId) {
|
|
51
|
+
const session = this.sessions.get(sessionId);
|
|
52
|
+
if (!session) return false;
|
|
53
|
+
session.stop();
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
stopAll() {
|
|
57
|
+
const sessions = Array.from(this.sessions.values());
|
|
58
|
+
for (const session of sessions) {
|
|
59
|
+
session.stop();
|
|
60
|
+
}
|
|
61
|
+
this.latestRenders.clear();
|
|
62
|
+
}
|
|
63
|
+
activeSessions() {
|
|
64
|
+
return Array.from(this.sessions.values());
|
|
65
|
+
}
|
|
66
|
+
getLatestRender(sessionId) {
|
|
67
|
+
return this.latestRenders.get(sessionId);
|
|
68
|
+
}
|
|
69
|
+
async handleChange(sessionId, filePath, props, callback) {
|
|
70
|
+
const key = `${sessionId}:${filePath}`;
|
|
71
|
+
if (this.inFlight.has(key)) return;
|
|
72
|
+
this.inFlight.add(key);
|
|
73
|
+
const start = performance.now();
|
|
74
|
+
try {
|
|
75
|
+
const { results, mode } = await this.renderUseCase.renderFile(filePath, {
|
|
76
|
+
props,
|
|
77
|
+
autoFit: true,
|
|
78
|
+
fullPage: true,
|
|
79
|
+
engines: ["chromium"]
|
|
80
|
+
});
|
|
81
|
+
const result = results[0];
|
|
82
|
+
const event = {
|
|
83
|
+
sessionId,
|
|
84
|
+
filePath,
|
|
85
|
+
image: result.image,
|
|
86
|
+
width: result.width,
|
|
87
|
+
height: result.height,
|
|
88
|
+
elapsedMs: Math.round(performance.now() - start),
|
|
89
|
+
mode,
|
|
90
|
+
consoleErrors: result.consoleErrors
|
|
91
|
+
};
|
|
92
|
+
this.latestRenders.set(sessionId, event);
|
|
93
|
+
await callback(event);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
const event = {
|
|
96
|
+
sessionId,
|
|
97
|
+
filePath,
|
|
98
|
+
image: "",
|
|
99
|
+
width: 0,
|
|
100
|
+
height: 0,
|
|
101
|
+
elapsedMs: Math.round(performance.now() - start),
|
|
102
|
+
mode: "cdn",
|
|
103
|
+
consoleErrors: [],
|
|
104
|
+
error: err instanceof Error ? err.message : String(err)
|
|
105
|
+
};
|
|
106
|
+
this.latestRenders.set(sessionId, event);
|
|
107
|
+
await callback(event);
|
|
108
|
+
} finally {
|
|
109
|
+
this.inFlight.delete(key);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/container.ts
|
|
115
|
+
function createContainer() {
|
|
116
|
+
const pool = new BrowserPool();
|
|
117
|
+
const htmlBuilder = new HtmlBuilder();
|
|
118
|
+
const imageComparator = new ImageComparator();
|
|
119
|
+
const snapshotStore = new SnapshotStore();
|
|
120
|
+
const viteBundler = new ViteBundler();
|
|
121
|
+
const renderUseCase = new RenderUseCase(
|
|
122
|
+
pool,
|
|
123
|
+
htmlBuilder,
|
|
124
|
+
imageComparator,
|
|
125
|
+
viteBundler
|
|
126
|
+
);
|
|
127
|
+
const screenshotUseCase = new ScreenshotUseCase(pool);
|
|
128
|
+
const diffUseCase = new DiffUseCase(renderUseCase, imageComparator);
|
|
129
|
+
const auditUseCase = new AuditUseCase(pool, htmlBuilder);
|
|
130
|
+
const snapshotUseCase = new SnapshotUseCase(
|
|
131
|
+
snapshotStore,
|
|
132
|
+
renderUseCase,
|
|
133
|
+
diffUseCase
|
|
134
|
+
);
|
|
135
|
+
const catalogUseCase = new CatalogUseCase(renderUseCase);
|
|
136
|
+
const watchUseCase = new WatchUseCase(renderUseCase);
|
|
137
|
+
return {
|
|
138
|
+
pool,
|
|
139
|
+
viteBundler,
|
|
140
|
+
renderUseCase,
|
|
141
|
+
screenshotUseCase,
|
|
142
|
+
diffUseCase,
|
|
143
|
+
auditUseCase,
|
|
144
|
+
snapshotUseCase,
|
|
145
|
+
catalogUseCase,
|
|
146
|
+
watchUseCase,
|
|
147
|
+
async shutdown() {
|
|
148
|
+
watchUseCase.stopAll();
|
|
149
|
+
await viteBundler.shutdown();
|
|
150
|
+
await pool.shutdown();
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export {
|
|
156
|
+
createContainer
|
|
157
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
EXT_TO_FRAMEWORK
|
|
7
|
-
|
|
8
|
-
ImageComparator,
|
|
9
|
-
RenderUseCase,
|
|
10
|
-
ViteBundler
|
|
11
|
-
} from "./chunk-Q7A3DLED.js";
|
|
3
|
+
createContainer
|
|
4
|
+
} from "./chunk-VUYZHZBH.js";
|
|
5
|
+
import {
|
|
6
|
+
EXT_TO_FRAMEWORK
|
|
7
|
+
} from "./chunk-MEBQ7ZWA.js";
|
|
12
8
|
|
|
13
9
|
// src/cli.ts
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
10
|
+
import { mkdirSync } from "fs";
|
|
11
|
+
import { resolve as resolve2 } from "path";
|
|
12
|
+
|
|
13
|
+
// src/cli/commands.ts
|
|
14
|
+
import { execSync } from "child_process";
|
|
15
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
16
16
|
import { basename, extname, relative, resolve } from "path";
|
|
17
|
+
|
|
18
|
+
// src/cli/display.ts
|
|
17
19
|
var c = {
|
|
18
20
|
reset: "\x1B[0m",
|
|
19
21
|
bold: "\x1B[1m",
|
|
@@ -66,6 +68,15 @@ function spinner(text) {
|
|
|
66
68
|
function separator() {
|
|
67
69
|
log(` ${c.gray}${"\u2500".repeat(48)}${c.reset}`);
|
|
68
70
|
}
|
|
71
|
+
function renderBar(pct) {
|
|
72
|
+
const width = 30;
|
|
73
|
+
const filled = Math.round(pct / 100 * width);
|
|
74
|
+
const empty = width - filled;
|
|
75
|
+
return `${c.yellow}${"\u2588".repeat(filled)}${c.gray}${"\u2591".repeat(empty)}${c.reset} ${c.dim}${pct.toFixed(1)}%${c.reset}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/cli/image.ts
|
|
79
|
+
import { execFileSync } from "child_process";
|
|
69
80
|
function supportsItermImage() {
|
|
70
81
|
const term = process.env.TERM_PROGRAM ?? "";
|
|
71
82
|
return term === "iTerm.app" || term === "WezTerm" || !!process.env.TERM_PROGRAM_VERSION?.includes("iTerm") || term === "vscode";
|
|
@@ -121,117 +132,10 @@ function displayImage(base64, filePath) {
|
|
|
121
132
|
} catch {
|
|
122
133
|
}
|
|
123
134
|
}
|
|
124
|
-
function parseArgs(args) {
|
|
125
|
-
const command = args[0] ?? "help";
|
|
126
|
-
const positional = args[1] ?? "";
|
|
127
|
-
const flags = {
|
|
128
|
-
out: ".frameshot",
|
|
129
|
-
recursive: false
|
|
130
|
-
};
|
|
131
|
-
for (let i = 2; i < args.length; i++) {
|
|
132
|
-
const arg = args[i];
|
|
133
|
-
switch (arg) {
|
|
134
|
-
case "--props":
|
|
135
|
-
try {
|
|
136
|
-
flags.props = JSON.parse(args[++i]);
|
|
137
|
-
} catch {
|
|
138
|
-
error("Invalid --props JSON");
|
|
139
|
-
}
|
|
140
|
-
break;
|
|
141
|
-
case "--out":
|
|
142
|
-
case "-o":
|
|
143
|
-
flags.out = args[++i];
|
|
144
|
-
break;
|
|
145
|
-
case "--recursive":
|
|
146
|
-
case "-r":
|
|
147
|
-
flags.recursive = true;
|
|
148
|
-
break;
|
|
149
|
-
case "--width":
|
|
150
|
-
flags.width = Number.parseInt(args[++i], 10);
|
|
151
|
-
break;
|
|
152
|
-
case "--height":
|
|
153
|
-
flags.height = Number.parseInt(args[++i], 10);
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return { command, positional, flags };
|
|
158
|
-
}
|
|
159
|
-
var HELP = `
|
|
160
|
-
${brand()} ${c.dim}v0.7.0${c.reset}
|
|
161
|
-
${c.dim}Zero-config component screenshots \u2014 Vite + Playwright${c.reset}
|
|
162
|
-
|
|
163
|
-
${c.white}${c.bold}Commands${c.reset}
|
|
164
|
-
${c.blue}render${c.reset} ${c.dim}<file>${c.reset} Render a component to PNG
|
|
165
|
-
${c.blue}catalog${c.reset} ${c.dim}<dir>${c.reset} Render all components in a directory
|
|
166
|
-
${c.blue}diff${c.reset} ${c.dim}<file>${c.reset} Visual diff against git HEAD
|
|
167
135
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
${c.purple}--recursive${c.reset}, ${c.purple}-r${c.reset} Scan subdirectories
|
|
172
|
-
${c.purple}--width${c.reset} ${c.dim}<px>${c.reset} Viewport width ${c.gray}[auto]${c.reset}
|
|
173
|
-
${c.purple}--height${c.reset} ${c.dim}<px>${c.reset} Viewport height ${c.gray}[auto]${c.reset}
|
|
174
|
-
|
|
175
|
-
${c.white}${c.bold}Examples${c.reset}
|
|
176
|
-
${c.gray}$${c.reset} frameshot render src/Button.tsx
|
|
177
|
-
${c.gray}$${c.reset} frameshot render src/Card.tsx ${c.purple}--props${c.reset} '{"title":"Hi"}'
|
|
178
|
-
${c.gray}$${c.reset} frameshot catalog src/components/ ${c.purple}-r${c.reset}
|
|
179
|
-
${c.gray}$${c.reset} frameshot diff src/Header.tsx
|
|
180
|
-
|
|
181
|
-
${c.dim}Supports: React \xB7 Vue \xB7 Svelte \xB7 Tailwind \xB7 CSS Modules${c.reset}
|
|
182
|
-
${c.dim}Display: iTerm2 \xB7 Kitty \xB7 Sixel \xB7 fallback to open${c.reset}
|
|
183
|
-
|
|
184
|
-
`;
|
|
185
|
-
async function main() {
|
|
186
|
-
const { command, positional, flags } = parseArgs(process.argv.slice(2));
|
|
187
|
-
if (command === "help" || command === "--help" || command === "-h") {
|
|
188
|
-
process.stdout.write(HELP);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
if (!positional) {
|
|
192
|
-
error(
|
|
193
|
-
`Missing argument. Run ${c.dim}frameshot --help${c.reset} for usage.`
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
log(`
|
|
197
|
-
${brand()} ${c.dim}v0.7.0${c.reset}
|
|
198
|
-
`);
|
|
199
|
-
const pool = new BrowserPool();
|
|
200
|
-
const htmlBuilder = new HtmlBuilder();
|
|
201
|
-
const imageComparator = new ImageComparator();
|
|
202
|
-
const viteBundler = new ViteBundler();
|
|
203
|
-
const renderUseCase = new RenderUseCase(
|
|
204
|
-
pool,
|
|
205
|
-
htmlBuilder,
|
|
206
|
-
imageComparator,
|
|
207
|
-
viteBundler
|
|
208
|
-
);
|
|
209
|
-
const catalogUseCase = new CatalogUseCase(renderUseCase);
|
|
210
|
-
const diffUseCase = new DiffUseCase(renderUseCase, imageComparator);
|
|
211
|
-
const outDir = resolve(flags.out);
|
|
212
|
-
mkdirSync(outDir, { recursive: true });
|
|
213
|
-
try {
|
|
214
|
-
await pool.warmup(["chromium"]);
|
|
215
|
-
switch (command) {
|
|
216
|
-
case "render":
|
|
217
|
-
await cmdRender(renderUseCase, positional, flags, outDir);
|
|
218
|
-
break;
|
|
219
|
-
case "catalog":
|
|
220
|
-
await cmdCatalog(catalogUseCase, positional, flags, outDir);
|
|
221
|
-
break;
|
|
222
|
-
case "diff":
|
|
223
|
-
await cmdDiff(diffUseCase, positional, flags, outDir);
|
|
224
|
-
break;
|
|
225
|
-
default:
|
|
226
|
-
error(
|
|
227
|
-
`Unknown command '${command}'. Run ${c.dim}frameshot --help${c.reset} for usage.`
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
} finally {
|
|
231
|
-
await viteBundler.shutdown();
|
|
232
|
-
await pool.shutdown();
|
|
233
|
-
}
|
|
234
|
-
log("");
|
|
136
|
+
// src/cli/commands.ts
|
|
137
|
+
function getViewport(flags) {
|
|
138
|
+
return { width: flags.width ?? 1280, height: flags.height ?? 800 };
|
|
235
139
|
}
|
|
236
140
|
async function cmdRender(useCase, filePath, flags, outDir) {
|
|
237
141
|
const absPath = resolve(filePath);
|
|
@@ -253,10 +157,7 @@ async function cmdRender(useCase, filePath, flags, outDir) {
|
|
|
253
157
|
const start = performance.now();
|
|
254
158
|
const { results, mode } = await useCase.renderFile(absPath, {
|
|
255
159
|
props: flags.props,
|
|
256
|
-
viewport:
|
|
257
|
-
width: flags.width ?? 1280,
|
|
258
|
-
height: flags.height ?? 800
|
|
259
|
-
},
|
|
160
|
+
viewport: getViewport(flags),
|
|
260
161
|
autoFit,
|
|
261
162
|
fullPage: true,
|
|
262
163
|
engines: ["chromium"]
|
|
@@ -301,7 +202,7 @@ async function cmdCatalog(useCase, dirPath, flags, outDir) {
|
|
|
301
202
|
const s = spinner("Scanning & rendering components...");
|
|
302
203
|
const results = await useCase.renderCatalog(absDir, {
|
|
303
204
|
recursive: flags.recursive,
|
|
304
|
-
viewport:
|
|
205
|
+
viewport: getViewport(flags),
|
|
305
206
|
autoFit: flags.width === void 0 || flags.height === void 0
|
|
306
207
|
});
|
|
307
208
|
if (results.length === 0) {
|
|
@@ -349,7 +250,7 @@ async function cmdDiff(useCase, filePath, flags, outDir) {
|
|
|
349
250
|
baseCode = execSync(`git show HEAD:${gitRelPath}`, { encoding: "utf-8" });
|
|
350
251
|
} catch {
|
|
351
252
|
s.stop(`${c.red}Failed${c.reset}`);
|
|
352
|
-
|
|
253
|
+
throw new Error(
|
|
353
254
|
`Could not get HEAD version of ${relPath}. Is this file tracked by git?`
|
|
354
255
|
);
|
|
355
256
|
}
|
|
@@ -357,7 +258,7 @@ async function cmdDiff(useCase, filePath, flags, outDir) {
|
|
|
357
258
|
const ext = extname(filePath).toLowerCase();
|
|
358
259
|
const framework = EXT_TO_FRAMEWORK[ext] ?? "react";
|
|
359
260
|
const result = await useCase.diffComponent(baseCode, currentCode, framework, {
|
|
360
|
-
viewport:
|
|
261
|
+
viewport: getViewport(flags),
|
|
361
262
|
autoFit: flags.width === void 0 || flags.height === void 0
|
|
362
263
|
});
|
|
363
264
|
const elapsed = Math.round(performance.now() - start);
|
|
@@ -396,11 +297,108 @@ async function cmdDiff(useCase, filePath, flags, outDir) {
|
|
|
396
297
|
);
|
|
397
298
|
}
|
|
398
299
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
|
|
300
|
+
|
|
301
|
+
// src/cli.ts
|
|
302
|
+
function parseArgs(args) {
|
|
303
|
+
const command = args[0] ?? "help";
|
|
304
|
+
const positional = args[1] ?? "";
|
|
305
|
+
const flags = {
|
|
306
|
+
out: ".frameshot",
|
|
307
|
+
recursive: false
|
|
308
|
+
};
|
|
309
|
+
for (let i = 2; i < args.length; i++) {
|
|
310
|
+
const arg = args[i];
|
|
311
|
+
switch (arg) {
|
|
312
|
+
case "--props":
|
|
313
|
+
try {
|
|
314
|
+
flags.props = JSON.parse(args[++i]);
|
|
315
|
+
} catch {
|
|
316
|
+
error("Invalid --props JSON");
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
case "--out":
|
|
320
|
+
case "-o":
|
|
321
|
+
flags.out = args[++i];
|
|
322
|
+
break;
|
|
323
|
+
case "--recursive":
|
|
324
|
+
case "-r":
|
|
325
|
+
flags.recursive = true;
|
|
326
|
+
break;
|
|
327
|
+
case "--width":
|
|
328
|
+
flags.width = Number.parseInt(args[++i], 10);
|
|
329
|
+
break;
|
|
330
|
+
case "--height":
|
|
331
|
+
flags.height = Number.parseInt(args[++i], 10);
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return { command, positional, flags };
|
|
336
|
+
}
|
|
337
|
+
var HELP = `
|
|
338
|
+
${brand()} ${c.dim}v0.7.0${c.reset}
|
|
339
|
+
${c.dim}Zero-config component screenshots \u2014 Vite + Playwright${c.reset}
|
|
340
|
+
|
|
341
|
+
${c.white}${c.bold}Commands${c.reset}
|
|
342
|
+
${c.blue}render${c.reset} ${c.dim}<file>${c.reset} Render a component to PNG
|
|
343
|
+
${c.blue}catalog${c.reset} ${c.dim}<dir>${c.reset} Render all components in a directory
|
|
344
|
+
${c.blue}diff${c.reset} ${c.dim}<file>${c.reset} Visual diff against git HEAD
|
|
345
|
+
|
|
346
|
+
${c.white}${c.bold}Options${c.reset}
|
|
347
|
+
${c.purple}--props${c.reset} ${c.dim}'{...}'${c.reset} Component props (JSON)
|
|
348
|
+
${c.purple}--out${c.reset}, ${c.purple}-o${c.reset} ${c.dim}<dir>${c.reset} Output directory ${c.gray}[.frameshot]${c.reset}
|
|
349
|
+
${c.purple}--recursive${c.reset}, ${c.purple}-r${c.reset} Scan subdirectories
|
|
350
|
+
${c.purple}--width${c.reset} ${c.dim}<px>${c.reset} Viewport width ${c.gray}[auto]${c.reset}
|
|
351
|
+
${c.purple}--height${c.reset} ${c.dim}<px>${c.reset} Viewport height ${c.gray}[auto]${c.reset}
|
|
352
|
+
|
|
353
|
+
${c.white}${c.bold}Examples${c.reset}
|
|
354
|
+
${c.gray}$${c.reset} frameshot render src/Button.tsx
|
|
355
|
+
${c.gray}$${c.reset} frameshot render src/Card.tsx ${c.purple}--props${c.reset} '{"title":"Hi"}'
|
|
356
|
+
${c.gray}$${c.reset} frameshot catalog src/components/ ${c.purple}-r${c.reset}
|
|
357
|
+
${c.gray}$${c.reset} frameshot diff src/Header.tsx
|
|
358
|
+
|
|
359
|
+
${c.dim}Supports: React \xB7 Vue \xB7 Svelte \xB7 Tailwind \xB7 CSS Modules${c.reset}
|
|
360
|
+
${c.dim}Display: iTerm2 \xB7 Kitty \xB7 Sixel \xB7 fallback to open${c.reset}
|
|
361
|
+
|
|
362
|
+
`;
|
|
363
|
+
async function main() {
|
|
364
|
+
const { command, positional, flags } = parseArgs(process.argv.slice(2));
|
|
365
|
+
if (command === "help" || command === "--help" || command === "-h") {
|
|
366
|
+
process.stdout.write(HELP);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (!positional) {
|
|
370
|
+
error(
|
|
371
|
+
`Missing argument. Run ${c.dim}frameshot --help${c.reset} for usage.`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
log(`
|
|
375
|
+
${brand()} ${c.dim}v0.7.0${c.reset}
|
|
376
|
+
`);
|
|
377
|
+
const container = createContainer();
|
|
378
|
+
const { pool, renderUseCase, catalogUseCase, diffUseCase } = container;
|
|
379
|
+
const outDir = resolve2(flags.out);
|
|
380
|
+
mkdirSync(outDir, { recursive: true });
|
|
381
|
+
try {
|
|
382
|
+
await pool.warmup(["chromium"]);
|
|
383
|
+
switch (command) {
|
|
384
|
+
case "render":
|
|
385
|
+
await cmdRender(renderUseCase, positional, flags, outDir);
|
|
386
|
+
break;
|
|
387
|
+
case "catalog":
|
|
388
|
+
await cmdCatalog(catalogUseCase, positional, flags, outDir);
|
|
389
|
+
break;
|
|
390
|
+
case "diff":
|
|
391
|
+
await cmdDiff(diffUseCase, positional, flags, outDir);
|
|
392
|
+
break;
|
|
393
|
+
default:
|
|
394
|
+
error(
|
|
395
|
+
`Unknown command '${command}'. Run ${c.dim}frameshot --help${c.reset} for usage.`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
} finally {
|
|
399
|
+
await container.shutdown();
|
|
400
|
+
}
|
|
401
|
+
log("");
|
|
404
402
|
}
|
|
405
403
|
main().catch((e) => {
|
|
406
404
|
error(e.message ?? String(e));
|