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.8.0
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.8.0
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
@@ -10,7 +10,7 @@ import {
10
10
  SnapshotStore,
11
11
  SnapshotUseCase,
12
12
  ViteBundler
13
- } from "./chunk-FQ3BVCX7.js";
13
+ } from "./chunk-SP7UAIQL.js";
14
14
 
15
15
  // src/use-cases/watch.ts
16
16
  import { watch } from "chokidar";
@@ -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(...await loadPlugin("@vitejs/plugin-vue"));
435
+ plugins.push(
436
+ ...await loadPluginFromProject(projectRoot, "@vitejs/plugin-vue")
437
+ );
421
438
  if (hasInstalled(projectRoot, "svelte"))
422
439
  plugins.push(
423
- ...await loadPlugin("@sveltejs/vite-plugin-svelte", "svelte")
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(...await loadPlugin("vite-plugin-solid"));
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(...await loadPlugin("@vitejs/plugin-vue"));
508
+ plugins.push(
509
+ ...await loadPluginFromProject(projectRoot, "@vitejs/plugin-vue")
510
+ );
486
511
  if (hasInstalled(projectRoot, "svelte"))
487
512
  plugins.push(
488
- ...await loadPlugin("@sveltejs/vite-plugin-svelte", "svelte")
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(...await loadPlugin("vite-plugin-solid"));
520
+ plugins.push(
521
+ ...await loadPluginFromProject(projectRoot, "vite-plugin-solid")
522
+ );
492
523
  if (hasInstalled(projectRoot, "preact"))
493
- plugins.push(...await loadPlugin("@preact/preset-vite"));
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(_root) {
592
- return loadPlugin("@vitejs/plugin-vue");
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(_root) {
747
- return loadPlugin("@sveltejs/vite-plugin-svelte", "svelte");
779
+ async getPlugins(root) {
780
+ return loadPluginFromProject(
781
+ root,
782
+ "@sveltejs/vite-plugin-svelte",
783
+ "svelte"
784
+ );
748
785
  }
749
- useFrameshotVite() {
750
- return true;
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(_root) {
775
- return loadPlugin("@sveltejs/vite-plugin-svelte", "svelte");
811
+ async getPlugins(root) {
812
+ return loadPluginFromProject(
813
+ root,
814
+ "@sveltejs/vite-plugin-svelte",
815
+ "svelte"
816
+ );
776
817
  }
777
- useFrameshotVite() {
778
- return true;
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(_root) {
865
- return loadPlugin("@vitejs/plugin-vue");
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(props), document.getElementById("app"));
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 = createRequire(import.meta.url);
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 = createRequire(join18(projectRoot, "package.json"));
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 = createRequire(join18(projectRoot, "package.json"));
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 framework = EXT_TO_FRAMEWORK[ext] ?? "react";
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, framework, options);
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-AF64XFKX.js";
4
+ } from "./chunk-RXVC5ZDW.js";
5
5
  import {
6
6
  EXT_TO_FRAMEWORK
7
- } from "./chunk-FQ3BVCX7.js";
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-AF64XFKX.js";
4
+ } from "./chunk-RXVC5ZDW.js";
5
5
  import {
6
6
  DEVICE_PRESETS,
7
7
  EXT_TO_FRAMEWORK,
8
8
  __export
9
- } from "./chunk-FQ3BVCX7.js";
9
+ } from "./chunk-SP7UAIQL.js";
10
10
 
11
11
  // src/index.ts
12
12
  import { execSync } from "child_process";
package/dist/renderer.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  SnapshotStore,
14
14
  SnapshotUseCase,
15
15
  ViteBundler
16
- } from "./chunk-FQ3BVCX7.js";
16
+ } from "./chunk-SP7UAIQL.js";
17
17
  export {
18
18
  AuditUseCase,
19
19
  BrowserPool,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frameshot-mcp",
3
- "version": "0.11.0",
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",