frameshot-mcp 0.11.0 → 0.11.1
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
CHANGED
|
@@ -53,7 +53,7 @@ Two lines. Auto-detects changed components. Posts before/after/diff screenshots
|
|
|
53
53
|
- uses: actions/checkout@v7
|
|
54
54
|
with:
|
|
55
55
|
fetch-depth: 0
|
|
56
|
-
- uses: kamegoro/frameshot@v0.
|
|
56
|
+
- uses: kamegoro/frameshot@v0.11.0
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
<p align="center">
|
|
@@ -70,7 +70,7 @@ Changed `.tsx`, `.jsx`, `.vue`, `.svelte`, `.astro`, `.mdx` files detected autom
|
|
|
70
70
|
<summary>Options</summary>
|
|
71
71
|
|
|
72
72
|
```yaml
|
|
73
|
-
- uses: kamegoro/frameshot@v0.
|
|
73
|
+
- uses: kamegoro/frameshot@v0.11.0
|
|
74
74
|
with:
|
|
75
75
|
paths: "./src/components/*.tsx" # default: auto-detect
|
|
76
76
|
extensions: ".jsx,.tsx,.vue" # default: .jsx,.tsx,.vue,.svelte,.astro,.mdx
|
|
@@ -95,6 +95,32 @@ render_file("src/components/Dashboard.tsx")
|
|
|
95
95
|
|
|
96
96
|
Resolves your project's real imports, Tailwind config, CSS Modules, and path aliases — not a CDN polyfill.
|
|
97
97
|
|
|
98
|
+
### Works out of the box with 16 frameworks
|
|
99
|
+
|
|
100
|
+
| Meta-frameworks | Core libraries |
|
|
101
|
+
|---|---|
|
|
102
|
+
| Next.js, Nuxt, SvelteKit, SolidStart | React, Vue, Svelte |
|
|
103
|
+
| Remix / React Router 7, Gatsby | Solid, Preact |
|
|
104
|
+
| Qwik, Astro, Vike | Lit / Web Components |
|
|
105
|
+
|
|
106
|
+
Auto-detected from `package.json`. No config needed for the common case. Framework-specific imports (`next/navigation`, `$app/stores`, `useLoaderData`, …) are stubbed automatically so components render in isolation.
|
|
107
|
+
|
|
108
|
+
### Mock API calls
|
|
109
|
+
|
|
110
|
+
Components that fetch data from `/api/...` render in their loaded state instead of showing a spinner:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
render_file({
|
|
114
|
+
path: "src/components/UserList.tsx",
|
|
115
|
+
mock: {
|
|
116
|
+
"/api/users": [{ id: 1, name: "Alice" }],
|
|
117
|
+
"/api/posts/*": { status: 200, body: { data: [] } },
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Pattern syntax: path (`/api/users`), glob (`**/api/*`), or full URL.
|
|
123
|
+
|
|
98
124
|
---
|
|
99
125
|
|
|
100
126
|
## Using with Claude
|
|
@@ -351,7 +351,7 @@ var ProjectDetector = class {
|
|
|
351
351
|
|
|
352
352
|
// src/infrastructure/vite-bundler.ts
|
|
353
353
|
import { existsSync as existsSync4 } from "fs";
|
|
354
|
-
import { createRequire } from "module";
|
|
354
|
+
import { createRequire as createRequire2 } from "module";
|
|
355
355
|
import { dirname as dirname2, join as join18, resolve as resolve2 } from "path";
|
|
356
356
|
import { fileURLToPath } from "url";
|
|
357
357
|
|
|
@@ -360,6 +360,7 @@ import { join as join3 } from "path";
|
|
|
360
360
|
|
|
361
361
|
// src/infrastructure/adapters/helpers.ts
|
|
362
362
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
363
|
+
import { createRequire } from "module";
|
|
363
364
|
import { join as join2 } from "path";
|
|
364
365
|
function hasDep(pkgPath, names) {
|
|
365
366
|
if (!existsSync2(pkgPath)) return false;
|
|
@@ -388,6 +389,20 @@ async function loadPlugin(pkg, fnName) {
|
|
|
388
389
|
}
|
|
389
390
|
return [];
|
|
390
391
|
}
|
|
392
|
+
async function loadPluginFromProject(projectRoot, pkg, fnName) {
|
|
393
|
+
try {
|
|
394
|
+
const require2 = createRequire(join2(projectRoot, "package.json"));
|
|
395
|
+
const resolvedPath = require2.resolve(pkg);
|
|
396
|
+
const mod = await import(`file://${resolvedPath}`);
|
|
397
|
+
const candidate = fnName ? mod[fnName] : mod.default ?? mod;
|
|
398
|
+
if (typeof candidate === "function") {
|
|
399
|
+
const result = candidate();
|
|
400
|
+
return Array.isArray(result) ? result : [result];
|
|
401
|
+
}
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
return loadPlugin(pkg, fnName);
|
|
405
|
+
}
|
|
391
406
|
function loadReactPlugin() {
|
|
392
407
|
return loadPlugin("@vitejs/plugin-react");
|
|
393
408
|
}
|
|
@@ -417,13 +432,21 @@ var AstroAdapter = class {
|
|
|
417
432
|
if (hasInstalled(projectRoot, "react"))
|
|
418
433
|
plugins.push(...await loadReactPlugin());
|
|
419
434
|
if (hasInstalled(projectRoot, "vue"))
|
|
420
|
-
plugins.push(
|
|
435
|
+
plugins.push(
|
|
436
|
+
...await loadPluginFromProject(projectRoot, "@vitejs/plugin-vue")
|
|
437
|
+
);
|
|
421
438
|
if (hasInstalled(projectRoot, "svelte"))
|
|
422
439
|
plugins.push(
|
|
423
|
-
...await
|
|
440
|
+
...await loadPluginFromProject(
|
|
441
|
+
projectRoot,
|
|
442
|
+
"@sveltejs/vite-plugin-svelte",
|
|
443
|
+
"svelte"
|
|
444
|
+
)
|
|
424
445
|
);
|
|
425
446
|
if (hasInstalled(projectRoot, "solid-js"))
|
|
426
|
-
plugins.push(
|
|
447
|
+
plugins.push(
|
|
448
|
+
...await loadPluginFromProject(projectRoot, "vite-plugin-solid")
|
|
449
|
+
);
|
|
427
450
|
return plugins;
|
|
428
451
|
}
|
|
429
452
|
useFrameshotVite() {
|
|
@@ -482,15 +505,25 @@ var GenericAdapter = class {
|
|
|
482
505
|
if (hasInstalled(projectRoot, "react"))
|
|
483
506
|
plugins.push(...await loadReactPlugin());
|
|
484
507
|
if (hasInstalled(projectRoot, "vue"))
|
|
485
|
-
plugins.push(
|
|
508
|
+
plugins.push(
|
|
509
|
+
...await loadPluginFromProject(projectRoot, "@vitejs/plugin-vue")
|
|
510
|
+
);
|
|
486
511
|
if (hasInstalled(projectRoot, "svelte"))
|
|
487
512
|
plugins.push(
|
|
488
|
-
...await
|
|
513
|
+
...await loadPluginFromProject(
|
|
514
|
+
projectRoot,
|
|
515
|
+
"@sveltejs/vite-plugin-svelte",
|
|
516
|
+
"svelte"
|
|
517
|
+
)
|
|
489
518
|
);
|
|
490
519
|
if (hasInstalled(projectRoot, "solid-js"))
|
|
491
|
-
plugins.push(
|
|
520
|
+
plugins.push(
|
|
521
|
+
...await loadPluginFromProject(projectRoot, "vite-plugin-solid")
|
|
522
|
+
);
|
|
492
523
|
if (hasInstalled(projectRoot, "preact"))
|
|
493
|
-
plugins.push(
|
|
524
|
+
plugins.push(
|
|
525
|
+
...await loadPluginFromProject(projectRoot, "@preact/preset-vite")
|
|
526
|
+
);
|
|
494
527
|
return plugins;
|
|
495
528
|
}
|
|
496
529
|
useFrameshotVite() {
|
|
@@ -588,8 +621,8 @@ var NuxtAdapter = class {
|
|
|
588
621
|
getOptimizeDeps(_root) {
|
|
589
622
|
return ["vue"];
|
|
590
623
|
}
|
|
591
|
-
async getPlugins(
|
|
592
|
-
return
|
|
624
|
+
async getPlugins(root) {
|
|
625
|
+
return loadPluginFromProject(root, "@vitejs/plugin-vue");
|
|
593
626
|
}
|
|
594
627
|
useFrameshotVite() {
|
|
595
628
|
return true;
|
|
@@ -743,11 +776,15 @@ var SvelteAdapter = class {
|
|
|
743
776
|
getOptimizeDeps() {
|
|
744
777
|
return [];
|
|
745
778
|
}
|
|
746
|
-
async getPlugins(
|
|
747
|
-
return
|
|
779
|
+
async getPlugins(root) {
|
|
780
|
+
return loadPluginFromProject(
|
|
781
|
+
root,
|
|
782
|
+
"@sveltejs/vite-plugin-svelte",
|
|
783
|
+
"svelte"
|
|
784
|
+
);
|
|
748
785
|
}
|
|
749
|
-
useFrameshotVite() {
|
|
750
|
-
return
|
|
786
|
+
useFrameshotVite(projectRoot) {
|
|
787
|
+
return projectRoot ? !hasInstalled(projectRoot, "vite") : false;
|
|
751
788
|
}
|
|
752
789
|
skipProjectConfig() {
|
|
753
790
|
return true;
|
|
@@ -771,11 +808,15 @@ var SvelteKitAdapter = class {
|
|
|
771
808
|
getOptimizeDeps(_root) {
|
|
772
809
|
return [];
|
|
773
810
|
}
|
|
774
|
-
async getPlugins(
|
|
775
|
-
return
|
|
811
|
+
async getPlugins(root) {
|
|
812
|
+
return loadPluginFromProject(
|
|
813
|
+
root,
|
|
814
|
+
"@sveltejs/vite-plugin-svelte",
|
|
815
|
+
"svelte"
|
|
816
|
+
);
|
|
776
817
|
}
|
|
777
|
-
useFrameshotVite() {
|
|
778
|
-
return
|
|
818
|
+
useFrameshotVite(projectRoot) {
|
|
819
|
+
return projectRoot ? !hasInstalled(projectRoot, "vite") : false;
|
|
779
820
|
}
|
|
780
821
|
skipProjectConfig() {
|
|
781
822
|
return true;
|
|
@@ -861,8 +902,8 @@ var VueAdapter = class {
|
|
|
861
902
|
getOptimizeDeps() {
|
|
862
903
|
return ["vue"];
|
|
863
904
|
}
|
|
864
|
-
async getPlugins(
|
|
865
|
-
return
|
|
905
|
+
async getPlugins(root) {
|
|
906
|
+
return loadPluginFromProject(root, "@vitejs/plugin-vue");
|
|
866
907
|
}
|
|
867
908
|
useFrameshotVite() {
|
|
868
909
|
return true;
|
|
@@ -910,7 +951,7 @@ var ViteBundler = class {
|
|
|
910
951
|
const absPath = resolve2(filePath);
|
|
911
952
|
const project = options.projectRoot ? this.detector.detect(join18(options.projectRoot, "package.json")) : this.detector.detect(absPath);
|
|
912
953
|
const adapter = selectAdapter(project.root);
|
|
913
|
-
if (!project.hasVite && !adapter.useFrameshotVite()) {
|
|
954
|
+
if (!project.hasVite && !adapter.useFrameshotVite(project.root)) {
|
|
914
955
|
throw new Error(
|
|
915
956
|
`Vite not found in ${project.root}. Install vite: npm install -D vite`
|
|
916
957
|
);
|
|
@@ -988,9 +1029,10 @@ try {
|
|
|
988
1029
|
case "solid":
|
|
989
1030
|
return `${cssImport}
|
|
990
1031
|
import { render } from "solid-js/web";
|
|
1032
|
+
import { createComponent } from "solid-js";
|
|
991
1033
|
import Component from "${componentPath}";
|
|
992
1034
|
const props = ${propsJson};
|
|
993
|
-
render(() => Component
|
|
1035
|
+
render(() => createComponent(Component, props), document.getElementById("app"));
|
|
994
1036
|
`;
|
|
995
1037
|
case "preact":
|
|
996
1038
|
return `${cssImport}
|
|
@@ -1010,7 +1052,7 @@ document.getElementById("app").innerHTML = html;
|
|
|
1010
1052
|
async ensureServer(project, adapter) {
|
|
1011
1053
|
const existing = this.servers.get(project.root);
|
|
1012
1054
|
if (existing) return existing;
|
|
1013
|
-
const vite = adapter.useFrameshotVite() ? await this.importFrameshotVite() : await this.importVite(project.root);
|
|
1055
|
+
const vite = adapter.useFrameshotVite(project.root) ? await this.importFrameshotVite() : await this.importVite(project.root);
|
|
1014
1056
|
if (!vite) {
|
|
1015
1057
|
throw new Error(`Failed to import vite for ${project.root}`);
|
|
1016
1058
|
}
|
|
@@ -1108,7 +1150,7 @@ document.getElementById("app").innerHTML = html;
|
|
|
1108
1150
|
}
|
|
1109
1151
|
async importFrameshotVite() {
|
|
1110
1152
|
try {
|
|
1111
|
-
const requireFromHere =
|
|
1153
|
+
const requireFromHere = createRequire2(import.meta.url);
|
|
1112
1154
|
const vitePath = requireFromHere.resolve("vite");
|
|
1113
1155
|
const mod = await import(vitePath);
|
|
1114
1156
|
if (mod && typeof mod === "object" && "createServer" in mod) {
|
|
@@ -1127,7 +1169,7 @@ document.getElementById("app").innerHTML = html;
|
|
|
1127
1169
|
}
|
|
1128
1170
|
async importVite(projectRoot) {
|
|
1129
1171
|
try {
|
|
1130
|
-
const require2 =
|
|
1172
|
+
const require2 = createRequire2(join18(projectRoot, "package.json"));
|
|
1131
1173
|
const vitePath = require2.resolve("vite");
|
|
1132
1174
|
const mod = await import(vitePath);
|
|
1133
1175
|
if (mod && typeof mod === "object" && "createServer" in mod) {
|
|
@@ -1139,7 +1181,7 @@ document.getElementById("app").innerHTML = html;
|
|
|
1139
1181
|
}
|
|
1140
1182
|
hasPackage(projectRoot, name) {
|
|
1141
1183
|
try {
|
|
1142
|
-
const require2 =
|
|
1184
|
+
const require2 = createRequire2(join18(projectRoot, "package.json"));
|
|
1143
1185
|
require2.resolve(name);
|
|
1144
1186
|
return true;
|
|
1145
1187
|
} catch {
|
|
@@ -1475,13 +1517,12 @@ var RenderUseCase = class {
|
|
|
1475
1517
|
async renderFile(filePath, options = {}) {
|
|
1476
1518
|
const opts = this.resolveOptions(options);
|
|
1477
1519
|
const ext = extname2(filePath).toLowerCase();
|
|
1478
|
-
const
|
|
1520
|
+
const extFramework = EXT_TO_FRAMEWORK[ext] ?? "react";
|
|
1479
1521
|
let fallbackReason;
|
|
1480
1522
|
if (this.viteBundler) {
|
|
1481
1523
|
try {
|
|
1482
1524
|
const { url } = await this.viteBundler.getUrl(filePath, {
|
|
1483
1525
|
props: options.props,
|
|
1484
|
-
framework,
|
|
1485
1526
|
projectRoot: options.projectRoot
|
|
1486
1527
|
});
|
|
1487
1528
|
const results2 = await Promise.all(
|
|
@@ -1499,7 +1540,7 @@ var RenderUseCase = class {
|
|
|
1499
1540
|
fallbackReason = "Vite bundler not available";
|
|
1500
1541
|
}
|
|
1501
1542
|
const code = readFileSync3(filePath, "utf-8");
|
|
1502
|
-
const results = await this.render(code,
|
|
1543
|
+
const results = await this.render(code, extFramework, options);
|
|
1503
1544
|
return { results, mode: "cdn", fallbackReason };
|
|
1504
1545
|
}
|
|
1505
1546
|
async renderInteraction(code, framework, interactions, options = {}) {
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createContainer
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RXVC5ZDW.js";
|
|
5
5
|
import {
|
|
6
6
|
EXT_TO_FRAMEWORK
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SP7UAIQL.js";
|
|
8
8
|
|
|
9
9
|
// src/cli.ts
|
|
10
10
|
import { mkdirSync } from "fs";
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createContainer
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RXVC5ZDW.js";
|
|
5
5
|
import {
|
|
6
6
|
DEVICE_PRESETS,
|
|
7
7
|
EXT_TO_FRAMEWORK,
|
|
8
8
|
__export
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-SP7UAIQL.js";
|
|
10
10
|
|
|
11
11
|
// src/index.ts
|
|
12
12
|
import { execSync } from "child_process";
|
package/dist/renderer.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frameshot-mcp",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Zero-config visual testing for AI agents. Render project components with full Vite dependency resolution — no stories, no config.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|