diff --git a/README.md b/README.md
index 3010859a..55f69044 100644
--- a/README.md
+++ b/README.md
@@ -56,19 +56,28 @@ Or edit your `package.json` manually:
## Examples
-Start with these foundational examples to learn the SDK:
-
-- [`examples/basic-server-vanillajs`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) — MCP server + MCP App using vanilla JS
-- [`examples/basic-server-react`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) — MCP server + MCP App using [React](https://github.com/facebook/react)
-- [`examples/basic-server-vue`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vue) — MCP server + MCP App using [Vue](https://github.com/vuejs/vue)
-- [`examples/basic-server-svelte`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-svelte) — MCP server + MCP App using [Svelte](https://github.com/sveltejs/svelte)
-- [`examples/basic-server-preact`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-preact) — MCP server + MCP App using [Preact](https://github.com/preactjs/preact)
-- [`examples/basic-server-solid`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-solid) — MCP server + MCP App using [Solid](https://github.com/solidjs/solid)
-- [`examples/basic-host`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) — MCP host application supporting MCP Apps
+
+| | | |
+|:---:|:---:|:---:|
+| [](examples/map-server) | [](examples/threejs-server) | [](examples/shadertoy-server) |
+| [**Map**](examples/map-server) | [**Three.js**](examples/threejs-server) | [**ShaderToy**](examples/shadertoy-server) |
+| [](examples/sheet-music-server) | [](examples/wiki-explorer-server) | [](examples/cohort-heatmap-server) |
+| [**Sheet Music**](examples/sheet-music-server) | [**Wiki Explorer**](examples/wiki-explorer-server) | [**Cohort Heatmap**](examples/cohort-heatmap-server) |
+| [](examples/scenario-modeler-server) | [](examples/budget-allocator-server) | [](examples/customer-segmentation-server) |
+| [**Scenario Modeler**](examples/scenario-modeler-server) | [**Budget Allocator**](examples/budget-allocator-server) | [**Customer Segmentation**](examples/customer-segmentation-server) |
+| [](examples/system-monitor-server) | [](examples/transcript-server) | [](examples/video-resource-server) |
+| [**System Monitor**](examples/system-monitor-server) | [**Transcript**](examples/transcript-server) | [**Video Resource**](examples/video-resource-server) |
+
+### Starter Templates
+
+| | |
+|:---:|:---|
+| [](examples/basic-server-react) | The same app built with different frameworks — pick your favorite!
[React](examples/basic-server-react) · [Vue](examples/basic-server-vue) · [Svelte](examples/basic-server-svelte) · [Preact](examples/basic-server-preact) · [Solid](examples/basic-server-solid) · [Vanilla JS](examples/basic-server-vanillajs) |
+
The [`examples/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples) directory contains additional demo apps showcasing real-world use cases.
-To run all examples together:
+To run all examples:
```bash
npm install
diff --git a/examples/basic-server-react/README.md b/examples/basic-server-react/README.md
index eb1864a7..c5be8e9d 100644
--- a/examples/basic-server-react/README.md
+++ b/examples/basic-server-react/README.md
@@ -1,5 +1,7 @@
# Example: Basic Server (React)
+
+
An MCP App example with a React UI.
> [!TIP]
diff --git a/examples/basic-server-react/grid-cell.png b/examples/basic-server-react/grid-cell.png
new file mode 100644
index 00000000..926846fe
Binary files /dev/null and b/examples/basic-server-react/grid-cell.png differ
diff --git a/examples/basic-server-react/screenshot.png b/examples/basic-server-react/screenshot.png
new file mode 100644
index 00000000..1c9df5ad
Binary files /dev/null and b/examples/basic-server-react/screenshot.png differ
diff --git a/examples/budget-allocator-server/grid-cell.png b/examples/budget-allocator-server/grid-cell.png
new file mode 100644
index 00000000..dee5a313
Binary files /dev/null and b/examples/budget-allocator-server/grid-cell.png differ
diff --git a/examples/budget-allocator-server/screenshot.png b/examples/budget-allocator-server/screenshot.png
new file mode 100644
index 00000000..1bd9783c
Binary files /dev/null and b/examples/budget-allocator-server/screenshot.png differ
diff --git a/examples/cohort-heatmap-server/grid-cell.png b/examples/cohort-heatmap-server/grid-cell.png
new file mode 100644
index 00000000..c1fe7041
Binary files /dev/null and b/examples/cohort-heatmap-server/grid-cell.png differ
diff --git a/examples/cohort-heatmap-server/screenshot.png b/examples/cohort-heatmap-server/screenshot.png
new file mode 100644
index 00000000..a8ad0e77
Binary files /dev/null and b/examples/cohort-heatmap-server/screenshot.png differ
diff --git a/examples/customer-segmentation-server/grid-cell.png b/examples/customer-segmentation-server/grid-cell.png
new file mode 100644
index 00000000..c0c19bf5
Binary files /dev/null and b/examples/customer-segmentation-server/grid-cell.png differ
diff --git a/examples/customer-segmentation-server/screenshot.png b/examples/customer-segmentation-server/screenshot.png
new file mode 100644
index 00000000..d891fbc6
Binary files /dev/null and b/examples/customer-segmentation-server/screenshot.png differ
diff --git a/examples/map-server/README.md b/examples/map-server/README.md
index c504eeab..ad9223dc 100644
--- a/examples/map-server/README.md
+++ b/examples/map-server/README.md
@@ -1,5 +1,7 @@
# Example: Interactive Map
+
+
Interactive 3D globe viewer using CesiumJS with OpenStreetMap tiles. Demonstrates geocoding integration and full MCP App capabilities.
## Features
diff --git a/examples/map-server/grid-cell.png b/examples/map-server/grid-cell.png
new file mode 100644
index 00000000..a5a85b9a
Binary files /dev/null and b/examples/map-server/grid-cell.png differ
diff --git a/examples/map-server/screenshot.png b/examples/map-server/screenshot.png
new file mode 100644
index 00000000..706d71e7
Binary files /dev/null and b/examples/map-server/screenshot.png differ
diff --git a/examples/scenario-modeler-server/grid-cell.png b/examples/scenario-modeler-server/grid-cell.png
new file mode 100644
index 00000000..d04569cf
Binary files /dev/null and b/examples/scenario-modeler-server/grid-cell.png differ
diff --git a/examples/scenario-modeler-server/screenshot.png b/examples/scenario-modeler-server/screenshot.png
new file mode 100644
index 00000000..0f4cf849
Binary files /dev/null and b/examples/scenario-modeler-server/screenshot.png differ
diff --git a/examples/shadertoy-server/README.md b/examples/shadertoy-server/README.md
index 0e85d47c..5f965cb8 100644
--- a/examples/shadertoy-server/README.md
+++ b/examples/shadertoy-server/README.md
@@ -50,7 +50,10 @@ _Tool input:_
```json
{
- "fragmentShader": "void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n fragColor = vec4(uv, 0.5 + 0.5*sin(iTime), 1.0);\n}"
+ "fragmentShader": "void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = fragCoord / iResolution.xy;
+ fragColor = vec4(uv, 0.5 + 0.5*sin(iTime), 1.0);
+}"
}
```
@@ -79,7 +82,22 @@ _Tool input:_
```json
{
- "fragmentShader": "void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;\n float segments = 6.0;\n float zoom = 1.0 + 0.3 * sin(iTime * 0.2);\n float angle = atan(uv.y, uv.x) + iTime * 0.3;\n float r = length(uv) * zoom;\n angle = mod(angle, 6.28 / segments);\n angle = abs(angle - 3.14 / segments);\n vec2 p = vec2(cos(angle), sin(angle)) * r;\n p += iTime * 0.1;\n float v = sin(p.x * 10.0) * sin(p.y * 10.0);\n v += sin(length(p) * 15.0 - iTime * 2.0);\n v += sin(p.x * 5.0 + p.y * 7.0 + iTime);\n vec3 col = 0.5 + 0.5 * cos(v * 2.0 + vec3(0.0, 2.0, 4.0) + iTime);\n fragColor = vec4(col, 1.0);\n}"
+ "fragmentShader": "void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
+ float segments = 6.0;
+ float zoom = 1.0 + 0.3 * sin(iTime * 0.2);
+ float angle = atan(uv.y, uv.x) + iTime * 0.3;
+ float r = length(uv) * zoom;
+ angle = mod(angle, 6.28 / segments);
+ angle = abs(angle - 3.14 / segments);
+ vec2 p = vec2(cos(angle), sin(angle)) * r;
+ p += iTime * 0.1;
+ float v = sin(p.x * 10.0) * sin(p.y * 10.0);
+ v += sin(length(p) * 15.0 - iTime * 2.0);
+ v += sin(p.x * 5.0 + p.y * 7.0 + iTime);
+ vec3 col = 0.5 + 0.5 * cos(v * 2.0 + vec3(0.0, 2.0, 4.0) + iTime);
+ fragColor = vec4(col, 1.0);
+}"
}
```
@@ -108,7 +126,22 @@ _Tool input:_
```json
{
- "fragmentShader": "void mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y * 2.5;\n vec2 mouse = (iMouse.xy / iResolution.xy - 0.5) * 2.0;\n vec2 c = mouse;\n vec2 z = uv;\n float iter = 0.0;\n for (int i = 0; i < 100; i++) {\n z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;\n if (dot(z, z) > 4.0) break;\n iter++;\n }\n float t = iter / 100.0;\n vec3 col = 0.5 + 0.5 * cos(3.0 + t * 6.28 * 2.0 + vec3(0.0, 0.6, 1.0));\n if (iter == 100.0) col = vec3(0.0);\n fragColor = vec4(col, 1.0);\n}"
+ "fragmentShader": "void mainImage(out vec4 fragColor, in vec2 fragCoord) {
+ vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y * 2.5;
+ vec2 mouse = (iMouse.xy / iResolution.xy - 0.5) * 2.0;
+ vec2 c = mouse;
+ vec2 z = uv;
+ float iter = 0.0;
+ for (int i = 0; i < 100; i++) {
+ z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
+ if (dot(z, z) > 4.0) break;
+ iter++;
+ }
+ float t = iter / 100.0;
+ vec3 col = 0.5 + 0.5 * cos(3.0 + t * 6.28 * 2.0 + vec3(0.0, 0.6, 1.0));
+ if (iter == 100.0) col = vec3(0.0);
+ fragColor = vec4(col, 1.0);
+}"
}
```
diff --git a/examples/shadertoy-server/grid-cell.png b/examples/shadertoy-server/grid-cell.png
new file mode 100644
index 00000000..4bed456a
Binary files /dev/null and b/examples/shadertoy-server/grid-cell.png differ
diff --git a/examples/shadertoy-server/screenshot.png b/examples/shadertoy-server/screenshot.png
new file mode 100644
index 00000000..9d90a4c8
Binary files /dev/null and b/examples/shadertoy-server/screenshot.png differ
diff --git a/examples/sheet-music-server/README.md b/examples/sheet-music-server/README.md
index 0eba9ff8..8f2f990c 100644
--- a/examples/sheet-music-server/README.md
+++ b/examples/sheet-music-server/README.md
@@ -38,7 +38,12 @@ When calling the `play-sheet-music` tool, provide ABC notation:
```json
{
- "abcNotation": "X:1\nT:C Major Scale\nM:4/4\nL:1/4\nK:C\nC D E F | G A B c |"
+ "abcNotation": "X:1
+T:C Major Scale
+M:4/4
+L:1/4
+K:C
+C D E F | G A B c |"
}
```
diff --git a/examples/sheet-music-server/grid-cell.png b/examples/sheet-music-server/grid-cell.png
new file mode 100644
index 00000000..b76374bb
Binary files /dev/null and b/examples/sheet-music-server/grid-cell.png differ
diff --git a/examples/sheet-music-server/screenshot.png b/examples/sheet-music-server/screenshot.png
new file mode 100644
index 00000000..046f1279
Binary files /dev/null and b/examples/sheet-music-server/screenshot.png differ
diff --git a/examples/system-monitor-server/grid-cell.png b/examples/system-monitor-server/grid-cell.png
new file mode 100644
index 00000000..b0ca8a10
Binary files /dev/null and b/examples/system-monitor-server/grid-cell.png differ
diff --git a/examples/system-monitor-server/screenshot.png b/examples/system-monitor-server/screenshot.png
new file mode 100644
index 00000000..e83d0ef9
Binary files /dev/null and b/examples/system-monitor-server/screenshot.png differ
diff --git a/examples/threejs-server/README.md b/examples/threejs-server/README.md
index 90a336fb..41e6d2f0 100644
--- a/examples/threejs-server/README.md
+++ b/examples/threejs-server/README.md
@@ -1,12 +1,8 @@
# Example: Three.js App
-Interactive 3D scene renderer using Three.js. Demonstrates streaming code preview and full MCP App integration.
+
-
-
-  |
-
-
+Interactive 3D scene renderer using Three.js. Demonstrates streaming code preview and full MCP App integration.
## Features
diff --git a/examples/threejs-server/grid-cell.png b/examples/threejs-server/grid-cell.png
new file mode 100644
index 00000000..438a6d17
Binary files /dev/null and b/examples/threejs-server/grid-cell.png differ
diff --git a/examples/threejs-server/screenshot.png b/examples/threejs-server/screenshot.png
new file mode 100644
index 00000000..7df32b76
Binary files /dev/null and b/examples/threejs-server/screenshot.png differ
diff --git a/examples/threejs-server/src/threejs-app.tsx b/examples/threejs-server/src/threejs-app.tsx
index 7877bd31..3fb50160 100644
--- a/examples/threejs-server/src/threejs-app.tsx
+++ b/examples/threejs-server/src/threejs-app.tsx
@@ -28,10 +28,19 @@ const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x00ff88 })
);
+// Start with an isometric-ish rotation to show 3 faces
+cube.rotation.x = 0.5;
+cube.rotation.y = 0.7;
scene.add(cube);
-scene.add(new THREE.DirectionalLight(0xffffff, 1));
-scene.add(new THREE.AmbientLight(0x404040));
+// Better lighting: key light + fill light + ambient
+const keyLight = new THREE.DirectionalLight(0xffffff, 1.2);
+keyLight.position.set(1, 1, 2);
+scene.add(keyLight);
+const fillLight = new THREE.DirectionalLight(0x8888ff, 0.4);
+fillLight.position.set(-1, 0, -1);
+scene.add(fillLight);
+scene.add(new THREE.AmbientLight(0x404040, 0.5));
camera.position.z = 3;
diff --git a/examples/transcript-server/README.md b/examples/transcript-server/README.md
index 057bc4fa..dae22280 100644
--- a/examples/transcript-server/README.md
+++ b/examples/transcript-server/README.md
@@ -1,5 +1,7 @@
# Transcript Server
+
+
An MCP App Server for live speech transcription using the Web Speech API.
## Features
diff --git a/examples/transcript-server/grid-cell.png b/examples/transcript-server/grid-cell.png
new file mode 100644
index 00000000..6895b337
Binary files /dev/null and b/examples/transcript-server/grid-cell.png differ
diff --git a/examples/transcript-server/screenshot.png b/examples/transcript-server/screenshot.png
new file mode 100644
index 00000000..3785b780
Binary files /dev/null and b/examples/transcript-server/screenshot.png differ
diff --git a/examples/video-resource-server/README.md b/examples/video-resource-server/README.md
index 4c6fbb57..252e6bbf 100644
--- a/examples/video-resource-server/README.md
+++ b/examples/video-resource-server/README.md
@@ -1,5 +1,7 @@
# Video Resource Server
+
+
Demonstrates serving binary content (video) via MCP resources using the base64 blob pattern.
## Quick Start
diff --git a/examples/video-resource-server/grid-cell.png b/examples/video-resource-server/grid-cell.png
new file mode 100644
index 00000000..9fb01f6a
Binary files /dev/null and b/examples/video-resource-server/grid-cell.png differ
diff --git a/examples/video-resource-server/screenshot.png b/examples/video-resource-server/screenshot.png
new file mode 100644
index 00000000..1e6e169d
Binary files /dev/null and b/examples/video-resource-server/screenshot.png differ
diff --git a/examples/wiki-explorer-server/grid-cell.png b/examples/wiki-explorer-server/grid-cell.png
new file mode 100644
index 00000000..b6d8fa59
Binary files /dev/null and b/examples/wiki-explorer-server/grid-cell.png differ
diff --git a/examples/wiki-explorer-server/screenshot.png b/examples/wiki-explorer-server/screenshot.png
new file mode 100644
index 00000000..291f1c12
Binary files /dev/null and b/examples/wiki-explorer-server/screenshot.png differ
diff --git a/package-lock.json b/package-lock.json
index 379526fe..d1232523 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28,6 +28,7 @@
"prettier": "^3.6.2",
"react": "^19.2.0",
"react-dom": "^19.2.0",
+ "sharp": "^0.34.5",
"ts-to-zod": "^5.1.0",
"tsx": "^4.21.0",
"typedoc": "^0.28.14",
@@ -70,7 +71,7 @@
},
"examples/basic-host": {
"name": "@modelcontextprotocol/ext-apps-basic-host",
- "version": "1.1.0",
+ "version": "0.4.0",
"dependencies": {
"@modelcontextprotocol/ext-apps": "../..",
"@modelcontextprotocol/sdk": "^1.24.0",
@@ -851,7 +852,6 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -1214,6 +1214,17 @@
"dev": true,
"license": "Apache-2.0"
},
+ "node_modules/@emnapi/runtime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@epic-web/invariant": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
@@ -1689,6 +1700,496 @@
"hono": "^4"
}
},
+ "node_modules/@img/colour": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
+ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -2566,7 +3067,6 @@
"integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
"debug": "^4.4.1",
@@ -2773,7 +3273,6 @@
"integrity": "sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -2798,7 +3297,6 @@
"integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -3201,7 +3699,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3527,7 +4024,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -4239,7 +4735,6 @@
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"dev": true,
"license": "ISC",
- "peer": true,
"engines": {
"node": ">=12"
}
@@ -4363,6 +4858,16 @@
"node": ">= 0.8"
}
},
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/devalue": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.1.tgz",
@@ -4714,7 +5219,6 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -6256,7 +6760,6 @@
"resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz",
"integrity": "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==",
"license": "MIT",
- "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
@@ -6352,7 +6855,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6451,7 +6953,6 @@
"integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -6573,7 +7074,6 @@
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz",
"integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=10"
}
@@ -6615,6 +7115,51 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -6819,7 +7364,6 @@
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.10.tgz",
"integrity": "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==",
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.1.0",
"seroval": "~1.3.0",
@@ -6998,7 +7542,6 @@
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.46.3.tgz",
"integrity": "sha512-Y5juST3x+/ySty5tYJCVWa6Corkxpt25bUZQHqOceg9xfMUtDsFx6rCsG6cYf1cA6vzDi66HIvaki0byZZX95A==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -7326,7 +7869,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -8076,7 +8618,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -8170,7 +8711,6 @@
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@@ -8347,7 +8887,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -8466,7 +9005,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz",
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.26",
"@vue/compiler-sfc": "3.5.26",
@@ -8639,7 +9177,6 @@
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"dev": true,
"license": "ISC",
- "peer": true,
"bin": {
"yaml": "bin.mjs"
},
@@ -8690,7 +9227,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/package.json b/package.json
index ebe9102f..e0b4dbba 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,7 @@
"prepare": "node scripts/setup-bun.mjs && npm run build && husky",
"docs": "typedoc",
"docs:watch": "typedoc --watch",
+ "generate:screenshots": "docker run --rm -v $(pwd):/work -w /work mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test tests/e2e/generate-grid-screenshots.spec.ts'",
"prettier": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --check",
"prettier:fix": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --write",
"check:versions": "node scripts/check-versions.mjs"
@@ -85,6 +86,7 @@
"prettier": "^3.6.2",
"react": "^19.2.0",
"react-dom": "^19.2.0",
+ "sharp": "^0.34.5",
"ts-to-zod": "^5.1.0",
"tsx": "^4.21.0",
"typedoc": "^0.28.14",
diff --git a/tests/e2e/generate-grid-screenshots.spec.ts b/tests/e2e/generate-grid-screenshots.spec.ts
new file mode 100644
index 00000000..c6863044
--- /dev/null
+++ b/tests/e2e/generate-grid-screenshots.spec.ts
@@ -0,0 +1,177 @@
+/**
+ * Generate 300x300 grid-cell.png screenshots for each example server.
+ *
+ * Usage:
+ * npm run generate:screenshots
+ *
+ * Output: examples//grid-cell.png (300x300, cropped top-aligned)
+ *
+ * For basic-server-* variants, only basic-server-react is included.
+ * integration-server is excluded (it's for E2E testing, same UI as basic-server-react).
+ */
+
+import { test, type Page } from "@playwright/test";
+import * as path from "path";
+import * as fs from "fs";
+import sharp from "sharp";
+
+const OUTPUT_SIZE = 300;
+const APP_WIDTH = 500;
+const DEFAULT_WAIT_MS = 5000;
+
+// Extra wait time for slow-loading servers (tiles, etc.)
+const EXTRA_WAIT_MS: Record = {
+ "map-server": 45000, // CesiumJS needs time for map tiles
+};
+
+// Servers to skip (screenshots maintained manually)
+const SKIP_SERVERS = new Set([
+ "video-resource", // Uses custom screenshot from PR comment
+]);
+
+// Server configurations (excludes integration-server which is for E2E testing)
+const SERVERS = [
+ {
+ key: "basic-react",
+ name: "Basic MCP App Server (React)",
+ dir: "basic-server-react",
+ },
+ {
+ key: "budget-allocator",
+ name: "Budget Allocator Server",
+ dir: "budget-allocator-server",
+ },
+ {
+ key: "cohort-heatmap",
+ name: "Cohort Heatmap Server",
+ dir: "cohort-heatmap-server",
+ },
+ {
+ key: "customer-segmentation",
+ name: "Customer Segmentation Server",
+ dir: "customer-segmentation-server",
+ },
+ { key: "map-server", name: "CesiumJS Map Server", dir: "map-server" },
+ {
+ key: "scenario-modeler",
+ name: "SaaS Scenario Modeler",
+ dir: "scenario-modeler-server",
+ },
+ { key: "shadertoy", name: "ShaderToy Server", dir: "shadertoy-server" },
+ {
+ key: "sheet-music",
+ name: "Sheet Music Server",
+ dir: "sheet-music-server",
+ },
+ {
+ key: "system-monitor",
+ name: "System Monitor Server",
+ dir: "system-monitor-server",
+ },
+ { key: "threejs", name: "Three.js Server", dir: "threejs-server" },
+ { key: "transcript", name: "Transcript Server", dir: "transcript-server" },
+ {
+ key: "video-resource",
+ name: "Video Resource Server",
+ dir: "video-resource-server",
+ },
+ { key: "wiki-explorer", name: "Wiki Explorer", dir: "wiki-explorer-server" },
+];
+
+/**
+ * Wait for the MCP App to load inside nested iframes.
+ */
+async function waitForAppLoad(page: Page) {
+ const outerFrame = page.frameLocator("iframe").nth(0);
+ await outerFrame
+ .locator("iframe")
+ .waitFor({ state: "visible", timeout: 60000 });
+}
+
+/**
+ * Load a server by selecting it from dropdown and clicking Call Tool.
+ */
+async function loadServer(page: Page, serverName: string) {
+ await page.goto("/");
+ await page
+ .locator("select")
+ .nth(0)
+ .waitFor({ state: "visible", timeout: 30000 });
+ await page.waitForTimeout(500);
+ await page.locator("select").nth(0).selectOption({ label: serverName });
+ await page.click('button:has-text("Call Tool")');
+ await waitForAppLoad(page);
+}
+
+/**
+ * Capture the app iframe content and save both:
+ * - screenshot.png: full-size raw screenshot of the iframe
+ * - grid-cell.png: 300x300 cropped thumbnail (top-aligned)
+ */
+async function captureAppScreenshot(page: Page, outputDir: string) {
+ // Get the inner app iframe element
+ const outerFrame = page.frameLocator("iframe").nth(0);
+ const innerIframe = outerFrame.locator("iframe").nth(0);
+
+ // Screenshot the inner iframe element
+ const screenshot = await innerIframe.screenshot();
+
+ // Save full-size screenshot
+ const screenshotPath = path.join(outputDir, "screenshot.png");
+ await sharp(screenshot).png().toFile(screenshotPath);
+
+ // Save 300x300 grid cell thumbnail (crop to fill, align top)
+ const gridCellPath = path.join(outputDir, "grid-cell.png");
+ await sharp(screenshot)
+ .resize(OUTPUT_SIZE, OUTPUT_SIZE, {
+ fit: "cover",
+ position: "top",
+ })
+ .png()
+ .toFile(gridCellPath);
+
+ return { screenshotPath, gridCellPath };
+}
+
+// Use a constrained viewport width for consistent app rendering
+test.use({ viewport: { width: APP_WIDTH, height: 600 } });
+
+// Increase test timeout for slow servers
+test.setTimeout(120000);
+
+// Generate screenshots for each server
+for (const server of SERVERS) {
+ test(`Generate grid-cell.png for ${server.dir}`, async ({ page }) => {
+ const examplesDir = path.join(process.cwd(), "examples");
+ const outputDir = path.join(examplesDir, server.dir);
+
+ // Skip if directory doesn't exist
+ if (!fs.existsSync(outputDir)) {
+ console.log(`⚠️ Skipping ${server.dir}: directory not found`);
+ test.skip();
+ return;
+ }
+
+ // Skip servers with manually maintained screenshots
+ if (SKIP_SERVERS.has(server.key)) {
+ console.log(`⏭️ Skipping ${server.dir}: manually maintained screenshot`);
+ test.skip();
+ return;
+ }
+
+ // Load the server
+ await loadServer(page, server.name);
+
+ // Wait for app to fully load (extra time for slow servers)
+ const waitMs = EXTRA_WAIT_MS[server.key] ?? DEFAULT_WAIT_MS;
+ console.log(`⏳ Waiting ${waitMs / 1000}s for ${server.dir}...`);
+ await page.waitForTimeout(waitMs);
+
+ // Capture and save both screenshot.png and grid-cell.png
+ const { screenshotPath, gridCellPath } = await captureAppScreenshot(
+ page,
+ outputDir,
+ );
+ console.log(`✅ Saved ${screenshotPath} + ${gridCellPath}`);
+ });
+}