vectorvesper 2.5.0 → 2.7.0
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 +113 -113
- package/dist/{chunk-5YSVDVLK.js → chunk-BI3LEGXE.js} +1 -1
- package/dist/{chunk-SFP5K3ZO.js → chunk-ZFHVMCR6.js} +1 -1
- package/dist/hooks.json +115 -6
- package/dist/index.js +19 -16
- package/dist/{mcp-QHRDHAME.js → mcp-RHYAXOSU.js} +3 -3
- package/dist/{server-ENS44LZV.js → server-DVFJRSZW.js} +138 -7
- package/package.json +81 -81
package/README.md
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
BANNER: host a wide image (≈1280×640) at an ABSOLUTE url — relative paths do
|
|
3
|
-
NOT render on npmjs.com — then uncomment this block:
|
|
4
|
-
|
|
5
|
-
<p align="center">
|
|
6
|
-
<a href="https://vectorvesper.dev">
|
|
7
|
-
<img src="https://vectorvesper.dev/banner.png" alt="Vector Vesper" width="100%" />
|
|
8
|
-
</a>
|
|
9
|
-
</p>
|
|
10
|
-
-->
|
|
11
|
-
|
|
12
|
-
<h1 align="center">Vector Vesper</h1>
|
|
13
|
-
|
|
14
|
-
<p align="center">
|
|
15
|
-
Production-grade WebGL, 3D, and motion components for React. Copy directly into your codebase and own the code.
|
|
16
|
-
</p>
|
|
17
|
-
|
|
18
|
-
<p align="center">
|
|
19
|
-
<a href="https://www.npmjs.com/package/vectorvesper"><img src="https://img.shields.io/npm/v/vectorvesper?color=7EACB5&label=npm" alt="npm version" /></a>
|
|
20
|
-
<a href="https://github.com/vectorvesper/vv-components/blob/main/LICENSE"><img src="https://img.shields.io/github/license/vectorvesper/vv-components?color=7EACB5&label=license" alt="license" /></a>
|
|
21
|
-
<a href="https://vectorvesper.dev"><img src="https://img.shields.io/badge/docs-vectorvesper.dev-7EACB5" alt="docs" /></a>
|
|
22
|
-
</p>
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
> Vector Vesper is a CLI you run with `npx`, not a package you install. Start with `npx vectorvesper init` below.
|
|
27
|
-
|
|
28
|
-
**Vector Vesper** is a motion toolkit for React consisting of 3D animations, shaders, and advanced motion components.
|
|
29
|
-
|
|
30
|
-
The CLI writes the component code directly into your repository. You have full control over the math, the shaders, and the styles.
|
|
31
|
-
|
|
32
|
-
## Quick Start
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# Initialize project configuration
|
|
36
|
-
npx vectorvesper init
|
|
37
|
-
|
|
38
|
-
# Add a visual component
|
|
39
|
-
npx vectorvesper add video-card
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
The CLI auto-detects your workspace, installs required animation dependencies (such as `three`, `@react-three/fiber`, or `gsap`), and writes the code to `@/components/vv/`.
|
|
43
|
-
|
|
44
|
-
```tsx
|
|
45
|
-
import { VideoCard } from "@/components/vv/video-card";
|
|
46
|
-
|
|
47
|
-
export default function Page() {
|
|
48
|
-
return (
|
|
49
|
-
<div className="h-screen w-full">
|
|
50
|
-
<VideoCard src="/clip.mp4" />
|
|
51
|
-
</div>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## MCP server — motion advice for your coding agent
|
|
57
|
-
|
|
58
|
-
The same CLI runs as an [MCP](https://modelcontextprotocol.io) server, so Claude Code, Cursor, Copilot and friends can ask about motion before they write it.
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
npx vectorvesper mcp install
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
That configures every supported editor it finds. Restart the editor and run `npx vectorvesper mcp status` to confirm. No account or token is needed — the runtime, its contracts and the linter are free.
|
|
65
|
-
|
|
66
|
-
**Why bother.** Bad motion code does not error. TypeScript passes, the build is green, and the page janks on a mid-range phone six weeks later with nothing connecting the two. An agent has no feedback signal for the one quality dimension that matters here, so this server gives it three:
|
|
67
|
-
|
|
68
|
-
- **`plan_motion`** — describe what you are building and get told whether it needs a primitive **at all**, which ones, in what order, and when that advice would be wrong. It says "use CSS, not this" for a hover state, a fade on mount, a spinner or a one-time scroll reveal, because those are compositor features that cost no main-thread time. Roughly a fifth of its answers recommend the platform over this library.
|
|
69
|
-
- **`get_hook` / `get_pattern` / `get_component`** — the contract, not the source. Does this need a client boundary, does it own the element's transform, what does it conflict with, which frame lane does it run in, and when should you not use it.
|
|
70
|
-
- **`check_motion`** — a static linter for the failure class that ships silently: a private `requestAnimationFrame` loop outside the shared budget, a discarded unsubscribe, `setState` in a frame callback, layout read in the write lane, one transform with two owners, a `<Canvas>` prop that quietly overrules the adapter that set it, a WebGL scene with nothing watching for a lost context, motion with no reduced-motion guard.
|
|
71
|
-
|
|
72
|
-
Prefer to wire it by hand? Add this to your client's MCP config:
|
|
73
|
-
|
|
74
|
-
```json
|
|
75
|
-
{
|
|
76
|
-
"mcpServers": {
|
|
77
|
-
"vectorvesper": {
|
|
78
|
-
"command": "npx",
|
|
79
|
-
"args": ["-y", "vectorvesper", "mcp"]
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Full setup notes and the per-client config paths: [vectorvesper.dev/docs/mcp](https://vectorvesper.dev/docs/mcp).
|
|
86
|
-
|
|
87
|
-
## CLI Command Reference
|
|
88
|
-
|
|
89
|
-
| Command | Description |
|
|
90
|
-
| --- | --- |
|
|
91
|
-
| `init` | Detect project setup and write `vv.config.json` |
|
|
92
|
-
| `add <slug>` | Add a component and auto-install its dependencies |
|
|
93
|
-
| `list` | List all available components in the registry |
|
|
94
|
-
| `update [slug]` | Update installed components to the latest version |
|
|
95
|
-
| `diff [slug]` | Check local files against registry updates |
|
|
96
|
-
| `remove <slug>` | Safely remove a component's files from the project |
|
|
97
|
-
| `mcp install [client]` | Configure the MCP server for your editor(s) |
|
|
98
|
-
| `mcp status` | Check which clients are wired up |
|
|
99
|
-
| `mcp` | Run the MCP server on stdio (what the editor invokes) |
|
|
100
|
-
| `info` | Output workspace diagnostics for troubleshooting |
|
|
101
|
-
|
|
102
|
-
Works out of the box with **npm, pnpm, yarn, and bun** by checking your lockfile.
|
|
103
|
-
|
|
104
|
-
## Links
|
|
105
|
-
|
|
106
|
-
* **Documentation & Guides:** [vectorvesper.dev/docs](https://vectorvesper.dev/docs)
|
|
107
|
-
* **Visual Component Catalog:** [vectorvesper.dev/components](https://vectorvesper.dev/components)
|
|
108
|
-
* **GitHub Repository:** [github.com/vectorvesper/vv-components](https://github.com/vectorvesper/vv-components)
|
|
109
|
-
* **Report Issues:** [github.com/vectorvesper/vv-components/issues](https://github.com/vectorvesper/vv-components/issues)
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
Licensed under the MIT License for open-source components. Premium/commercial components are subject to the Vector Vesper terms of service.
|
|
1
|
+
<!--
|
|
2
|
+
BANNER: host a wide image (≈1280×640) at an ABSOLUTE url — relative paths do
|
|
3
|
+
NOT render on npmjs.com — then uncomment this block:
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://vectorvesper.dev">
|
|
7
|
+
<img src="https://vectorvesper.dev/banner.png" alt="Vector Vesper" width="100%" />
|
|
8
|
+
</a>
|
|
9
|
+
</p>
|
|
10
|
+
-->
|
|
11
|
+
|
|
12
|
+
<h1 align="center">Vector Vesper</h1>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
Production-grade WebGL, 3D, and motion components for React. Copy directly into your codebase and own the code.
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="https://www.npmjs.com/package/vectorvesper"><img src="https://img.shields.io/npm/v/vectorvesper?color=7EACB5&label=npm" alt="npm version" /></a>
|
|
20
|
+
<a href="https://github.com/vectorvesper/vv-components/blob/main/LICENSE"><img src="https://img.shields.io/github/license/vectorvesper/vv-components?color=7EACB5&label=license" alt="license" /></a>
|
|
21
|
+
<a href="https://vectorvesper.dev"><img src="https://img.shields.io/badge/docs-vectorvesper.dev-7EACB5" alt="docs" /></a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
> Vector Vesper is a CLI you run with `npx`, not a package you install. Start with `npx vectorvesper init` below.
|
|
27
|
+
|
|
28
|
+
**Vector Vesper** is a motion toolkit for React consisting of 3D animations, shaders, and advanced motion components.
|
|
29
|
+
|
|
30
|
+
The CLI writes the component code directly into your repository. You have full control over the math, the shaders, and the styles.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Initialize project configuration
|
|
36
|
+
npx vectorvesper init
|
|
37
|
+
|
|
38
|
+
# Add a visual component
|
|
39
|
+
npx vectorvesper add video-card
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The CLI auto-detects your workspace, installs required animation dependencies (such as `three`, `@react-three/fiber`, or `gsap`), and writes the code to `@/components/vv/`.
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { VideoCard } from "@/components/vv/video-card";
|
|
46
|
+
|
|
47
|
+
export default function Page() {
|
|
48
|
+
return (
|
|
49
|
+
<div className="h-screen w-full">
|
|
50
|
+
<VideoCard src="/clip.mp4" />
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## MCP server — motion advice for your coding agent
|
|
57
|
+
|
|
58
|
+
The same CLI runs as an [MCP](https://modelcontextprotocol.io) server, so Claude Code, Cursor, Copilot and friends can ask about motion before they write it.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx vectorvesper mcp install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
That configures every supported editor it finds. Restart the editor and run `npx vectorvesper mcp status` to confirm. No account or token is needed — the runtime, its contracts and the linter are free.
|
|
65
|
+
|
|
66
|
+
**Why bother.** Bad motion code does not error. TypeScript passes, the build is green, and the page janks on a mid-range phone six weeks later with nothing connecting the two. An agent has no feedback signal for the one quality dimension that matters here, so this server gives it three:
|
|
67
|
+
|
|
68
|
+
- **`plan_motion`** — describe what you are building and get told whether it needs a primitive **at all**, which ones, in what order, and when that advice would be wrong. It says "use CSS, not this" for a hover state, a fade on mount, a spinner or a one-time scroll reveal, because those are compositor features that cost no main-thread time. Roughly a fifth of its answers recommend the platform over this library.
|
|
69
|
+
- **`get_hook` / `get_pattern` / `get_component`** — the contract, not the source. Does this need a client boundary, does it own the element's transform, what does it conflict with, which frame lane does it run in, and when should you not use it.
|
|
70
|
+
- **`check_motion`** — a static linter for the failure class that ships silently: a private `requestAnimationFrame` loop outside the shared budget, a discarded unsubscribe, `setState` in a frame callback, layout read in the write lane, one transform with two owners, a `<Canvas>` prop that quietly overrules the adapter that set it, a WebGL scene with nothing watching for a lost context, motion with no reduced-motion guard.
|
|
71
|
+
|
|
72
|
+
Prefer to wire it by hand? Add this to your client's MCP config:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"mcpServers": {
|
|
77
|
+
"vectorvesper": {
|
|
78
|
+
"command": "npx",
|
|
79
|
+
"args": ["-y", "vectorvesper", "mcp"]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Full setup notes and the per-client config paths: [vectorvesper.dev/docs/mcp](https://vectorvesper.dev/docs/mcp).
|
|
86
|
+
|
|
87
|
+
## CLI Command Reference
|
|
88
|
+
|
|
89
|
+
| Command | Description |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `init` | Detect project setup and write `vv.config.json` |
|
|
92
|
+
| `add <slug>` | Add a component and auto-install its dependencies |
|
|
93
|
+
| `list` | List all available components in the registry |
|
|
94
|
+
| `update [slug]` | Update installed components to the latest version |
|
|
95
|
+
| `diff [slug]` | Check local files against registry updates |
|
|
96
|
+
| `remove <slug>` | Safely remove a component's files from the project |
|
|
97
|
+
| `mcp install [client]` | Configure the MCP server for your editor(s) |
|
|
98
|
+
| `mcp status` | Check which clients are wired up |
|
|
99
|
+
| `mcp` | Run the MCP server on stdio (what the editor invokes) |
|
|
100
|
+
| `info` | Output workspace diagnostics for troubleshooting |
|
|
101
|
+
|
|
102
|
+
Works out of the box with **npm, pnpm, yarn, and bun** by checking your lockfile.
|
|
103
|
+
|
|
104
|
+
## Links
|
|
105
|
+
|
|
106
|
+
* **Documentation & Guides:** [vectorvesper.dev/docs](https://vectorvesper.dev/docs)
|
|
107
|
+
* **Visual Component Catalog:** [vectorvesper.dev/components](https://vectorvesper.dev/components)
|
|
108
|
+
* **GitHub Repository:** [github.com/vectorvesper/vv-components](https://github.com/vectorvesper/vv-components)
|
|
109
|
+
* **Report Issues:** [github.com/vectorvesper/vv-components/issues](https://github.com/vectorvesper/vv-components/issues)
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
Licensed under the MIT License for open-source components. Premium/commercial components are subject to the Vector Vesper terms of service.
|
|
@@ -130,7 +130,7 @@ var VVConfigSchema = z.object({
|
|
|
130
130
|
})
|
|
131
131
|
});
|
|
132
132
|
var VVGlobalConfigSchema = z.object({
|
|
133
|
-
// Auth —
|
|
133
|
+
// Auth — access token for paid components
|
|
134
134
|
auth: z.object({
|
|
135
135
|
token: z.string().optional(),
|
|
136
136
|
email: z.string().optional(),
|
package/dist/hooks.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "1.0.0",
|
|
3
|
-
"engineVersion": "4.0.
|
|
4
|
-
"generatedAt": "2026-
|
|
3
|
+
"engineVersion": "4.0.1",
|
|
4
|
+
"generatedAt": "2026-09-09T11:54:49.193Z",
|
|
5
5
|
"contractLevel": "full",
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
@@ -3686,7 +3686,14 @@
|
|
|
3686
3686
|
"simulation"
|
|
3687
3687
|
],
|
|
3688
3688
|
"allOf": [],
|
|
3689
|
-
"unless": [
|
|
3689
|
+
"unless": [
|
|
3690
|
+
"lottie",
|
|
3691
|
+
"dotlottie",
|
|
3692
|
+
"rive",
|
|
3693
|
+
"spline",
|
|
3694
|
+
"countdown",
|
|
3695
|
+
"stopwatch"
|
|
3696
|
+
],
|
|
3690
3697
|
"minHits": 1,
|
|
3691
3698
|
"priority": 100,
|
|
3692
3699
|
"verdict": "custom",
|
|
@@ -3962,6 +3969,94 @@
|
|
|
3962
3969
|
],
|
|
3963
3970
|
"notWhen": "The problem is not memory but frames. If the scene is janky rather than crashing, start with `useFramePressure` to find out what is actually eating the frame before optimising anything."
|
|
3964
3971
|
},
|
|
3972
|
+
{
|
|
3973
|
+
"id": "clock-and-interval",
|
|
3974
|
+
"when": [
|
|
3975
|
+
"countdown",
|
|
3976
|
+
"count down",
|
|
3977
|
+
"timer",
|
|
3978
|
+
"stopwatch",
|
|
3979
|
+
"every second",
|
|
3980
|
+
"every few seconds",
|
|
3981
|
+
"clock",
|
|
3982
|
+
"poll",
|
|
3983
|
+
"polling",
|
|
3984
|
+
"auto refresh",
|
|
3985
|
+
"refresh every",
|
|
3986
|
+
"elapsed time"
|
|
3987
|
+
],
|
|
3988
|
+
"allOf": [],
|
|
3989
|
+
"unless": [
|
|
3990
|
+
"scrub",
|
|
3991
|
+
"shader",
|
|
3992
|
+
"canvas",
|
|
3993
|
+
"frame",
|
|
3994
|
+
"particle",
|
|
3995
|
+
"scroll"
|
|
3996
|
+
],
|
|
3997
|
+
"minHits": 1,
|
|
3998
|
+
"priority": 885,
|
|
3999
|
+
"verdict": "no-runtime",
|
|
4000
|
+
"headline": "A clock is not a frame loop. Use a timer.",
|
|
4001
|
+
"because": "A value that changes once a second needs one update a second. Subscribing it to the frame loop runs the callback sixty to a hundred and twenty times to produce the same digit, and every one of those wakes the scheduler, occupies the budget, and shows up in devtools as work with nothing to show for it. Frame loops are for values that change every frame.",
|
|
4002
|
+
"use": [],
|
|
4003
|
+
"components": [],
|
|
4004
|
+
"instead": "`setInterval`, or a `setTimeout` chain when the cadence has to drift-correct. Compute the displayed value from `Date.now()` against a fixed start rather than by counting ticks — intervals are throttled in background tabs, so a counter that adds one per tick silently runs slow while a computed one stays right.",
|
|
4005
|
+
"watchFor": [],
|
|
4006
|
+
"notWhen": "The number itself animates rather than stepping. A total easing to a new value on the shared loop is `useNumberTicker`, and that genuinely is per-frame work."
|
|
4007
|
+
},
|
|
4008
|
+
{
|
|
4009
|
+
"id": "third-party-animation-runtime",
|
|
4010
|
+
"when": [
|
|
4011
|
+
"lottie",
|
|
4012
|
+
"dotlottie",
|
|
4013
|
+
"rive",
|
|
4014
|
+
"spline",
|
|
4015
|
+
"vanta",
|
|
4016
|
+
"after effects"
|
|
4017
|
+
],
|
|
4018
|
+
"allOf": [],
|
|
4019
|
+
"unless": [],
|
|
4020
|
+
"minHits": 1,
|
|
4021
|
+
"priority": 880,
|
|
4022
|
+
"verdict": "no-runtime",
|
|
4023
|
+
"headline": "It brings its own renderer and its own frame loop. Do not wrap it in ours.",
|
|
4024
|
+
"because": "These players own their playback clock end to end. Driving one from a frame callback gives the page two schedulers arguing over the same animation: ours decides the work should shed under load, theirs keeps advancing regardless, and the result is worse than either alone. There is no per-frame work of yours here to coordinate.",
|
|
4025
|
+
"use": [],
|
|
4026
|
+
"components": [],
|
|
4027
|
+
"instead": "Let the player drive itself, and use its own API to pause it when it is off screen. What is worth governing is the mount, not the loop: these bundles are large and their first parse lands wherever you put it.",
|
|
4028
|
+
"watchFor": [
|
|
4029
|
+
"client-boundary",
|
|
4030
|
+
"missing-cleanup"
|
|
4031
|
+
],
|
|
4032
|
+
"notWhen": "The mount cost is the problem rather than the playback. `useSafeToMount` holds a heavy subtree until the frame can afford it, and `useSceneGate` adds the off-screen and recovery decisions on top. Both govern when the player appears without touching how it animates."
|
|
4033
|
+
},
|
|
4034
|
+
{
|
|
4035
|
+
"id": "list-virtualisation",
|
|
4036
|
+
"when": [
|
|
4037
|
+
"virtualisation",
|
|
4038
|
+
"virtualization",
|
|
4039
|
+
"virtualised list",
|
|
4040
|
+
"virtualized list",
|
|
4041
|
+
"infinite scroll",
|
|
4042
|
+
"windowing",
|
|
4043
|
+
"react-window",
|
|
4044
|
+
"long list",
|
|
4045
|
+
"render thousands of rows"
|
|
4046
|
+
],
|
|
4047
|
+
"allOf": [],
|
|
4048
|
+
"unless": [],
|
|
4049
|
+
"minHits": 1,
|
|
4050
|
+
"priority": 870,
|
|
4051
|
+
"verdict": "no-runtime",
|
|
4052
|
+
"headline": "That is a windowing problem, not a motion one.",
|
|
4053
|
+
"because": "The cost is the number of mounted DOM nodes and the reconciliation over them, which is React work rather than frame work. Nothing here reduces a node count, and a frame budget cannot govern a render that has already been committed. Reaching for a scheduler because a list feels slow treats the symptom furthest from the cause.",
|
|
4054
|
+
"use": [],
|
|
4055
|
+
"components": [],
|
|
4056
|
+
"instead": "A windowing library — TanStack Virtual or react-window — so only the visible rows exist. Measure with the browser profiler first: an unmemoised row component is more often the cause than the row count.",
|
|
4057
|
+
"watchFor": [],
|
|
4058
|
+
"notWhen": "Each row runs its own frame-driven effect. A thousand rows each with a private `requestAnimationFrame` loop is a scheduling problem, and one `useTick` per row on the shared conductor is what fixes it."
|
|
4059
|
+
},
|
|
3965
4060
|
{
|
|
3966
4061
|
"id": "per-frame-work",
|
|
3967
4062
|
"when": [
|
|
@@ -3973,7 +4068,11 @@
|
|
|
3973
4068
|
"game loop",
|
|
3974
4069
|
"ticker",
|
|
3975
4070
|
"frame loop",
|
|
3976
|
-
"custom effect"
|
|
4071
|
+
"custom effect",
|
|
4072
|
+
"morph",
|
|
4073
|
+
"tween",
|
|
4074
|
+
"over time",
|
|
4075
|
+
"frame by frame"
|
|
3977
4076
|
],
|
|
3978
4077
|
"allOf": [],
|
|
3979
4078
|
"unless": [],
|
|
@@ -4009,13 +4108,23 @@
|
|
|
4009
4108
|
"follow the mouse",
|
|
4010
4109
|
"track the cursor",
|
|
4011
4110
|
"track the mouse",
|
|
4012
|
-
"chase the cursor"
|
|
4111
|
+
"chase the cursor",
|
|
4112
|
+
"as you scroll",
|
|
4113
|
+
"on scroll",
|
|
4114
|
+
"scroll down",
|
|
4115
|
+
"scroll progress",
|
|
4116
|
+
"scroll offset",
|
|
4117
|
+
"sticky header",
|
|
4118
|
+
"shrink on scroll",
|
|
4119
|
+
"scroll indicator"
|
|
4013
4120
|
],
|
|
4014
4121
|
"allOf": [],
|
|
4015
4122
|
"unless": [
|
|
4016
4123
|
"trail",
|
|
4017
4124
|
"magnetic",
|
|
4018
|
-
"parallax"
|
|
4125
|
+
"parallax",
|
|
4126
|
+
"reveal",
|
|
4127
|
+
"scrollytelling"
|
|
4019
4128
|
],
|
|
4020
4129
|
"minHits": 1,
|
|
4021
4130
|
"priority": 470,
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
detectProject,
|
|
4
4
|
fetchComponent,
|
|
5
5
|
fetchRegistryIndex
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BI3LEGXE.js";
|
|
7
7
|
import {
|
|
8
8
|
getAuthToken,
|
|
9
9
|
getGlobalConfigPath,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
setVerbose,
|
|
16
16
|
validateSlug,
|
|
17
17
|
writeConfig
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-ZFHVMCR6.js";
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
import { Command } from "commander";
|
|
@@ -295,7 +295,7 @@ async function listCommand() {
|
|
|
295
295
|
console.log(`
|
|
296
296
|
${pc2.green(String(freeCount))} free components${proCount > 0 ? ` \xB7 ${pc2.magenta(String(proCount))} pro components` : ""}`);
|
|
297
297
|
if (proCount > 0 && !hasAuth) {
|
|
298
|
-
console.log(` \u{1F511} ${pc2.dim("Unlock pro components:")} ${pc2.cyan("npx vectorvesper login <
|
|
298
|
+
console.log(` \u{1F511} ${pc2.dim("Unlock pro components:")} ${pc2.cyan("npx vectorvesper login <token>")} ${pc2.dim("\xB7")} ${pc2.cyan("https://vectorvesper.dev/pricing")}`);
|
|
299
299
|
}
|
|
300
300
|
console.log(`
|
|
301
301
|
\u{1F449} Run ${pc2.bold(pc2.cyan("npx vectorvesper add <slug>"))} to add a component to your project.
|
|
@@ -303,7 +303,8 @@ async function listCommand() {
|
|
|
303
303
|
} catch (error) {
|
|
304
304
|
const message = error instanceof Error ? error.message : String(error);
|
|
305
305
|
spinner.fail(pc2.red(`Failed to fetch components list: ${message}`));
|
|
306
|
-
process.
|
|
306
|
+
process.exitCode = 1;
|
|
307
|
+
return;
|
|
307
308
|
}
|
|
308
309
|
}
|
|
309
310
|
|
|
@@ -758,7 +759,7 @@ async function addCommand(slug, options = {}) {
|
|
|
758
759
|
} catch (error) {
|
|
759
760
|
const message = error instanceof Error ? error.message : String(error);
|
|
760
761
|
spinner.fail(pc5.red(`Failed to resolve component "${slug}": ${message}`));
|
|
761
|
-
process.
|
|
762
|
+
process.exitCode = 1;
|
|
762
763
|
return;
|
|
763
764
|
}
|
|
764
765
|
let transpileWarnings = [];
|
|
@@ -780,7 +781,7 @@ async function addCommand(slug, options = {}) {
|
|
|
780
781
|
} catch (error) {
|
|
781
782
|
const message = error instanceof Error ? error.message : String(error);
|
|
782
783
|
spinner.fail(pc5.red(message));
|
|
783
|
-
process.
|
|
784
|
+
process.exitCode = 1;
|
|
784
785
|
return;
|
|
785
786
|
}
|
|
786
787
|
const filesToWrite = [];
|
|
@@ -856,7 +857,7 @@ async function addCommand(slug, options = {}) {
|
|
|
856
857
|
} catch (error) {
|
|
857
858
|
const message = error instanceof Error ? error.message : String(error);
|
|
858
859
|
spinner.fail(pc5.red(`Failed to write files: ${message}`));
|
|
859
|
-
process.
|
|
860
|
+
process.exitCode = 1;
|
|
860
861
|
return;
|
|
861
862
|
}
|
|
862
863
|
try {
|
|
@@ -1018,7 +1019,7 @@ async function updateCommand(slug, options = {}) {
|
|
|
1018
1019
|
if (updatedComponents.length > 0) {
|
|
1019
1020
|
await handleDependencies(updatedComponents, projectRoot, projectInfo, options);
|
|
1020
1021
|
}
|
|
1021
|
-
if (failed.length > 0) process.
|
|
1022
|
+
if (failed.length > 0) process.exitCode = 1;
|
|
1022
1023
|
}
|
|
1023
1024
|
|
|
1024
1025
|
// src/commands/remove.ts
|
|
@@ -1122,7 +1123,7 @@ Remove ${slug}
|
|
|
1122
1123
|
import fs7 from "fs";
|
|
1123
1124
|
import path7 from "path";
|
|
1124
1125
|
import pc8 from "picocolors";
|
|
1125
|
-
var CLI_VERSION = true ? "2.
|
|
1126
|
+
var CLI_VERSION = true ? "2.7.0" : "0.0.0-dev";
|
|
1126
1127
|
async function infoCommand() {
|
|
1127
1128
|
console.log(pc8.bold(pc8.cyan("\nVector Vesper Diagnostics\n")));
|
|
1128
1129
|
const projectInfo = detectProject();
|
|
@@ -1255,7 +1256,8 @@ Component Diff: ${slug}`)));
|
|
|
1255
1256
|
} catch (error) {
|
|
1256
1257
|
const message = error instanceof Error ? error.message : String(error);
|
|
1257
1258
|
spinner.fail(pc9.red(`Failed to fetch remote component details: ${message}`));
|
|
1258
|
-
process.
|
|
1259
|
+
process.exitCode = 1;
|
|
1260
|
+
return;
|
|
1259
1261
|
}
|
|
1260
1262
|
} else {
|
|
1261
1263
|
const spinner = ora4({
|
|
@@ -1320,7 +1322,8 @@ ${pc9.green("\u2714")} All components are up to date!
|
|
|
1320
1322
|
} catch (error) {
|
|
1321
1323
|
const message = error instanceof Error ? error.message : String(error);
|
|
1322
1324
|
spinner.fail(pc9.red(`Failed to check updates: ${message}`));
|
|
1323
|
-
process.
|
|
1325
|
+
process.exitCode = 1;
|
|
1326
|
+
return;
|
|
1324
1327
|
}
|
|
1325
1328
|
}
|
|
1326
1329
|
}
|
|
@@ -1420,7 +1423,7 @@ async function whoamiCommand() {
|
|
|
1420
1423
|
}
|
|
1421
1424
|
|
|
1422
1425
|
// src/index.ts
|
|
1423
|
-
var version = true ? "2.
|
|
1426
|
+
var version = true ? "2.7.0" : "0.0.0-dev";
|
|
1424
1427
|
var program = new Command();
|
|
1425
1428
|
program.name("vv").description("Vector Vesper CLI \u2014 add visual components to your React project").version(version).option("--verbose", "Show detailed debug output").hook("preAction", (thisCommand) => {
|
|
1426
1429
|
const opts = thisCommand.opts();
|
|
@@ -1449,20 +1452,20 @@ program.command("info").description("Show project diagnostics and list installed
|
|
|
1449
1452
|
program.command("diff [slug]").description("Check for updates against the component registry").action(async (slug) => {
|
|
1450
1453
|
await diffCommand(slug);
|
|
1451
1454
|
});
|
|
1452
|
-
program.command("login <
|
|
1455
|
+
program.command("login <token>").description("Authenticate with an access token for pro components").action(async (key) => {
|
|
1453
1456
|
await loginCommand(key);
|
|
1454
1457
|
});
|
|
1455
|
-
program.command("logout"
|
|
1458
|
+
program.command("logout").description("Clear the stored access token").action(async () => {
|
|
1456
1459
|
await logoutCommand();
|
|
1457
1460
|
});
|
|
1458
|
-
program.command("whoami"
|
|
1461
|
+
program.command("whoami").description("Show current authentication status").action(async () => {
|
|
1459
1462
|
await whoamiCommand();
|
|
1460
1463
|
});
|
|
1461
1464
|
program.command("mcp [action] [client]").description(
|
|
1462
1465
|
"Run as an MCP server (stdio) for AI coding agents. `vv mcp install` wires it into your editor; `vv mcp status` checks the setup."
|
|
1463
1466
|
).option("--stdio", "Force server mode even from an interactive terminal").option("-g, --global", "Write user-level config instead of project-level (install)").action(
|
|
1464
1467
|
async (action, client, options) => {
|
|
1465
|
-
const { mcpCommand } = await import("./mcp-
|
|
1468
|
+
const { mcpCommand } = await import("./mcp-RHYAXOSU.js");
|
|
1466
1469
|
await mcpCommand(action, { ...options, client });
|
|
1467
1470
|
}
|
|
1468
1471
|
);
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-CPZKZO3A.js";
|
|
4
4
|
import {
|
|
5
5
|
getAuthToken
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZFHVMCR6.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/mcp.ts
|
|
9
9
|
import pc from "picocolors";
|
|
@@ -146,7 +146,7 @@ function detectClients(cwd = process.cwd()) {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
// src/commands/mcp.ts
|
|
149
|
-
var VERSION = true ? "2.
|
|
149
|
+
var VERSION = true ? "2.7.0" : "0.0.0-dev";
|
|
150
150
|
var CONFIG_SNIPPET = `{
|
|
151
151
|
"mcpServers": {
|
|
152
152
|
"vectorvesper": {
|
|
@@ -293,7 +293,7 @@ ${pc.red("\u2717")} Unknown argument "${action}" for \`vv mcp\`.
|
|
|
293
293
|
return;
|
|
294
294
|
}
|
|
295
295
|
protectStdout();
|
|
296
|
-
const { startMcpServer } = await import("./server-
|
|
296
|
+
const { startMcpServer } = await import("./server-DVFJRSZW.js");
|
|
297
297
|
try {
|
|
298
298
|
await startMcpServer();
|
|
299
299
|
} catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
detectProject,
|
|
3
3
|
fetchRegistryIndex
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BI3LEGXE.js";
|
|
5
5
|
import {
|
|
6
6
|
findHook,
|
|
7
7
|
findPattern,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-CPZKZO3A.js";
|
|
10
10
|
import {
|
|
11
11
|
getAuthToken
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZFHVMCR6.js";
|
|
13
13
|
|
|
14
14
|
// src/mcp/server.ts
|
|
15
15
|
import { z } from "zod";
|
|
@@ -148,6 +148,10 @@ function collect(ast, traverse) {
|
|
|
148
148
|
canvases: [],
|
|
149
149
|
rawGlContexts: [],
|
|
150
150
|
handlesContextLoss: false,
|
|
151
|
+
gpuDevices: [],
|
|
152
|
+
gpuLostHandlers: [],
|
|
153
|
+
mentionsDestroyedReason: false,
|
|
154
|
+
webgpuClassChecks: [],
|
|
151
155
|
gatingObserver: []
|
|
152
156
|
};
|
|
153
157
|
const reactSetters = collectReactSetters(ast, traverse);
|
|
@@ -194,6 +198,16 @@ function collect(ast, traverse) {
|
|
|
194
198
|
}
|
|
195
199
|
return false;
|
|
196
200
|
};
|
|
201
|
+
const memberPath = (n) => {
|
|
202
|
+
if (!n || typeof n.type !== "string") return null;
|
|
203
|
+
if (n.type === "Identifier") return n.name;
|
|
204
|
+
if (n.type === "MemberExpression" && !n.computed) {
|
|
205
|
+
const obj = memberPath(n.object);
|
|
206
|
+
const prop = n.property?.type === "Identifier" ? n.property.name : null;
|
|
207
|
+
return obj && prop ? `${obj}.${prop}` : null;
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
};
|
|
197
211
|
const isLatched = (setterPath) => {
|
|
198
212
|
let node = setterPath;
|
|
199
213
|
while (node) {
|
|
@@ -203,6 +217,10 @@ function collect(ast, traverse) {
|
|
|
203
217
|
const collect2 = (n) => {
|
|
204
218
|
if (!n || typeof n.type !== "string") return;
|
|
205
219
|
if (n.type === "Identifier") guards.add(n.name);
|
|
220
|
+
if (n.type === "MemberExpression") {
|
|
221
|
+
const p = memberPath(n);
|
|
222
|
+
if (p) guards.add(p);
|
|
223
|
+
}
|
|
206
224
|
for (const key of Object.keys(n)) {
|
|
207
225
|
const v = n[key];
|
|
208
226
|
if (Array.isArray(v)) v.forEach(collect2);
|
|
@@ -213,7 +231,8 @@ function collect(ast, traverse) {
|
|
|
213
231
|
let latched = false;
|
|
214
232
|
const check = (inner) => {
|
|
215
233
|
const target = inner.node.left ?? inner.node.argument;
|
|
216
|
-
|
|
234
|
+
const name = memberPath(target);
|
|
235
|
+
if (name && guards.has(name)) latched = true;
|
|
217
236
|
};
|
|
218
237
|
if (typeof node.traverse === "function") {
|
|
219
238
|
node.traverse({ AssignmentExpression: check, UpdateExpression: check });
|
|
@@ -303,6 +322,25 @@ function collect(ast, traverse) {
|
|
|
303
322
|
);
|
|
304
323
|
if (lane === "input" || lane === "update" || lane === "render") scanLaneDiscipline(cb, lane, via);
|
|
305
324
|
};
|
|
325
|
+
const isDetachedProbeCanvas = (p, node) => {
|
|
326
|
+
const obj = node.callee?.object;
|
|
327
|
+
if (!obj || obj.type !== "Identifier") return false;
|
|
328
|
+
const binding = typeof p.scope?.getBinding === "function" ? p.scope.getBinding(obj.name) : null;
|
|
329
|
+
if (!binding) return false;
|
|
330
|
+
const decl = binding.path?.node;
|
|
331
|
+
const init = decl?.type === "VariableDeclarator" ? decl.init : null;
|
|
332
|
+
if (!init || init.type !== "CallExpression") return false;
|
|
333
|
+
if (calleeName(init.callee) !== "createElement") return false;
|
|
334
|
+
const tag = init.arguments?.[0];
|
|
335
|
+
if (tag?.type !== "StringLiteral" || tag.value !== "canvas") return false;
|
|
336
|
+
const INERT = /* @__PURE__ */ new Set(["getContext", "width", "height"]);
|
|
337
|
+
return (binding.referencePaths ?? []).every((ref) => {
|
|
338
|
+
const parent = ref.parentPath?.node;
|
|
339
|
+
if (!parent) return false;
|
|
340
|
+
const isMember = parent.type === "MemberExpression" || parent.type === "OptionalMemberExpression";
|
|
341
|
+
return isMember && parent.object === ref.node && INERT.has(parent.property?.name);
|
|
342
|
+
});
|
|
343
|
+
};
|
|
306
344
|
const handleCall = (p) => {
|
|
307
345
|
const node = p.node;
|
|
308
346
|
const name = calleeName(node.callee);
|
|
@@ -320,6 +358,35 @@ function collect(ast, traverse) {
|
|
|
320
358
|
case "cancelAnimationFrame":
|
|
321
359
|
f.hasCancelRaf = true;
|
|
322
360
|
break;
|
|
361
|
+
// Telling RendererHealth about a context change IS handling it, and it is
|
|
362
|
+
// the half `webglcontextlost`-string detection cannot see.
|
|
363
|
+
//
|
|
364
|
+
// This is keyed on the call rather than on an import name on purpose.
|
|
365
|
+
// Projects routinely re-export the runtime through their own barrel — our
|
|
366
|
+
// own docs site imports `getRendererHealth` from `@/motion`, which is a
|
|
367
|
+
// file that does `export * from "@vectorvesper/motion/react"`. Every
|
|
368
|
+
// check that asks "is this name in `vvImports`" is blind to that, so the
|
|
369
|
+
// scene-gate page reported having no context-loss handling three lines
|
|
370
|
+
// above the `reportLost()` that handles it. Found 2026-09-09.
|
|
371
|
+
//
|
|
372
|
+
// Deliberately loose: only `reportLost`/`reportHealthy`, not
|
|
373
|
+
// `getRendererHealth`, because reading health for a readout is not
|
|
374
|
+
// handling. Being loose here can only suppress a finding, and a missed
|
|
375
|
+
// warning is the failure this file prefers over a false one.
|
|
376
|
+
case "reportLost":
|
|
377
|
+
case "reportHealthy":
|
|
378
|
+
f.handlesContextLoss = true;
|
|
379
|
+
break;
|
|
380
|
+
case "requestDevice":
|
|
381
|
+
f.gpuDevices.push(line);
|
|
382
|
+
break;
|
|
383
|
+
case "then": {
|
|
384
|
+
const obj = node.callee?.object;
|
|
385
|
+
if ((obj?.property?.name ?? obj?.property?.value) === "lost") {
|
|
386
|
+
f.gpuLostHandlers.push(line);
|
|
387
|
+
}
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
323
390
|
case "setInterval":
|
|
324
391
|
f.setInterval.push(line);
|
|
325
392
|
break;
|
|
@@ -350,9 +417,9 @@ function collect(ast, traverse) {
|
|
|
350
417
|
break;
|
|
351
418
|
case "getContext": {
|
|
352
419
|
const kind = node.arguments?.[0];
|
|
353
|
-
if (kind?.type
|
|
354
|
-
|
|
355
|
-
|
|
420
|
+
if (kind?.type !== "StringLiteral" || isDetachedProbeCanvas(p, node)) break;
|
|
421
|
+
if (/^(webgl2?|experimental-webgl)$/.test(kind.value)) f.rawGlContexts.push(line);
|
|
422
|
+
if (kind.value === "webgpu") f.gpuDevices.push(line);
|
|
356
423
|
break;
|
|
357
424
|
}
|
|
358
425
|
case "kill":
|
|
@@ -411,6 +478,7 @@ function collect(ast, traverse) {
|
|
|
411
478
|
const v = p.node.value;
|
|
412
479
|
if (v.includes("prefers-reduced-motion")) f.hasReducedMotionGuard = true;
|
|
413
480
|
if (v === "webglcontextlost" || v === "webglcontextrestored") f.handlesContextLoss = true;
|
|
481
|
+
if (v === "destroyed") f.mentionsDestroyedReason = true;
|
|
414
482
|
if (/\binfinite\b/.test(v) && /\b\d+(?:\.\d+)?m?s\b/.test(v)) {
|
|
415
483
|
f.looping.push({ line: p.node.loc?.start.line ?? 0, kind: "CSS `infinite` animation" });
|
|
416
484
|
}
|
|
@@ -484,6 +552,22 @@ function collect(ast, traverse) {
|
|
|
484
552
|
if (n && /(?:Geometry|Material|Texture|RenderTarget)$/.test(n)) {
|
|
485
553
|
f.threeDisposable.push(p.node.loc?.start.line ?? 0);
|
|
486
554
|
}
|
|
555
|
+
if (n === "WebGPURenderer") f.gpuDevices.push(p.node.loc?.start.line ?? 0);
|
|
556
|
+
},
|
|
557
|
+
/**
|
|
558
|
+
* `x instanceof WebGPURenderer`, which is the wrong question.
|
|
559
|
+
*
|
|
560
|
+
* three's `Renderer.init` catches a WebGPU failure and swaps in a WebGL
|
|
561
|
+
* backend, so the class says WebGPU while the canvas is WebGL. Branching on
|
|
562
|
+
* it to decide how to notice a lost context leaves the fallback with no
|
|
563
|
+
* handling at all, silently.
|
|
564
|
+
*/
|
|
565
|
+
BinaryExpression(p) {
|
|
566
|
+
if (p.node.operator !== "instanceof") return;
|
|
567
|
+
const right = p.node.right;
|
|
568
|
+
if (right?.type === "Identifier" && /^WebGPURenderer$/.test(right.name)) {
|
|
569
|
+
f.webgpuClassChecks.push(p.node.loc?.start.line ?? 0);
|
|
570
|
+
}
|
|
487
571
|
},
|
|
488
572
|
CallExpression(p) {
|
|
489
573
|
handleCall(p);
|
|
@@ -686,6 +770,50 @@ function checkContextLoss(f, file) {
|
|
|
686
770
|
}
|
|
687
771
|
];
|
|
688
772
|
}
|
|
773
|
+
function checkGpuDeviceLoss(f, file) {
|
|
774
|
+
if (f.gpuDevices.length === 0) return [];
|
|
775
|
+
const handled = f.gpuLostHandlers.length > 0 || f.vvImports.has("watchGPUDevice") || f.vvImports.has("useRenderQuality");
|
|
776
|
+
if (handled) return [];
|
|
777
|
+
return [
|
|
778
|
+
{
|
|
779
|
+
rule: "webgpu-device-loss",
|
|
780
|
+
severity: "warning",
|
|
781
|
+
file,
|
|
782
|
+
line: f.gpuDevices[0],
|
|
783
|
+
message: "This file takes a WebGPU device and nothing watches `device.lost`. A WebGPU canvas never fires `webglcontextlost`, so a listener written for WebGL will not hear this one. When the device goes the scene stops drawing with nothing logged.",
|
|
784
|
+
fix: "Pass the device to `watchGPUDevice(device)` from `@vectorvesper/motion`, and key the scene on a scene gate's `generation` so the loss rebuilds it against a device you request fresh. Through React Three Fiber, spreading `useRenderQuality`'s props does both.",
|
|
785
|
+
nudge: 'Call `get_pattern("lazy-3d-section")` for the whole wiring.'
|
|
786
|
+
}
|
|
787
|
+
];
|
|
788
|
+
}
|
|
789
|
+
function checkGpuDestroyReason(f, file) {
|
|
790
|
+
if (f.gpuLostHandlers.length === 0) return [];
|
|
791
|
+
if (f.mentionsDestroyedReason) return [];
|
|
792
|
+
if (f.vvImports.has("watchGPUDevice")) return [];
|
|
793
|
+
return [
|
|
794
|
+
{
|
|
795
|
+
rule: "webgpu-destroy-not-a-loss",
|
|
796
|
+
severity: "warning",
|
|
797
|
+
file,
|
|
798
|
+
line: f.gpuLostHandlers[0],
|
|
799
|
+
message: 'This handler treats every `device.lost` resolution as a failure. It also resolves when your own code calls `device.destroy()`, where `reason` is `"destroyed"` \u2014 so unmounting a scene reports a loss, which rebuilds the page, which unmounts more scenes.',
|
|
800
|
+
fix: 'Return early when `info?.reason === "destroyed"`, or use `watchGPUDevice(device)`, which does it and coalesces simultaneous losses into one rebuild.'
|
|
801
|
+
}
|
|
802
|
+
];
|
|
803
|
+
}
|
|
804
|
+
function checkWebgpuClassCheck(f, file) {
|
|
805
|
+
if (f.webgpuClassChecks.length === 0) return [];
|
|
806
|
+
return [
|
|
807
|
+
{
|
|
808
|
+
rule: "webgpu-renderer-class-check",
|
|
809
|
+
severity: "info",
|
|
810
|
+
file,
|
|
811
|
+
line: f.webgpuClassChecks[0],
|
|
812
|
+
message: "`instanceof WebGPURenderer` does not tell you the renderer is drawing with WebGPU. three catches a failed WebGPU init and swaps in a WebGL backend, keeping the same object, so this branch can send a WebGL canvas down the WebGPU path.",
|
|
813
|
+
fix: "Ask the backend rather than the class: `renderer.backend?.isWebGPUBackend` with the device checked for a `lost` promise. Better still, wire both paths \u2014 the `webglcontextlost` listener is inert on a real WebGPU canvas and is exactly what saves the fallback case."
|
|
814
|
+
}
|
|
815
|
+
];
|
|
816
|
+
}
|
|
689
817
|
function checkHandRolledGate(f, file) {
|
|
690
818
|
const ownsGpu = f.usesR3F && f.canvases.length > 0 || f.rawGlContexts.length > 0;
|
|
691
819
|
if (!ownsGpu) return [];
|
|
@@ -759,6 +887,9 @@ async function analyzeSource(file, source, framework) {
|
|
|
759
887
|
...checkReducedMotion(facts, file),
|
|
760
888
|
...checkRenderQualityProps(facts, file),
|
|
761
889
|
...checkContextLoss(facts, file),
|
|
890
|
+
...checkGpuDeviceLoss(facts, file),
|
|
891
|
+
...checkGpuDestroyReason(facts, file),
|
|
892
|
+
...checkWebgpuClassCheck(facts, file),
|
|
762
893
|
...checkHandRolledGate(facts, file)
|
|
763
894
|
].sort((a, b) => a.line - b.line);
|
|
764
895
|
return { file, findings };
|
|
@@ -975,7 +1106,7 @@ function formatPlan({ building, using }) {
|
|
|
975
1106
|
}
|
|
976
1107
|
|
|
977
1108
|
// src/mcp/server.ts
|
|
978
|
-
var VERSION = true ? "2.
|
|
1109
|
+
var VERSION = true ? "2.7.0" : "0.0.0-dev";
|
|
979
1110
|
function text(body) {
|
|
980
1111
|
return { content: [{ type: "text", text: body }] };
|
|
981
1112
|
}
|
package/package.json
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vectorvesper",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Motion runtime and WebGL/R3F component CLI for React, with an MCP server that tells your coding agent which primitive to use, how to wire it, and when not to.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"main": "./dist/index.js",
|
|
8
|
-
"bin": {
|
|
9
|
-
"vectorvesper": "dist/index.js",
|
|
10
|
-
"vv": "dist/index.js"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"dist",
|
|
14
|
-
"package.json"
|
|
15
|
-
],
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": ">=18"
|
|
18
|
-
},
|
|
19
|
-
"repository": {
|
|
20
|
-
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/vectorvesper/vv-components.git"
|
|
22
|
-
},
|
|
23
|
-
"bugs": {
|
|
24
|
-
"url": "https://github.com/vectorvesper/vv-components/issues"
|
|
25
|
-
},
|
|
26
|
-
"homepage": "https://vectorvesper.dev",
|
|
27
|
-
"keywords": [
|
|
28
|
-
"mcp",
|
|
29
|
-
"model-context-protocol",
|
|
30
|
-
"mcp-server",
|
|
31
|
-
"ai",
|
|
32
|
-
"agent",
|
|
33
|
-
"claude",
|
|
34
|
-
"cursor",
|
|
35
|
-
"copilot",
|
|
36
|
-
"react",
|
|
37
|
-
"webgl",
|
|
38
|
-
"three",
|
|
39
|
-
"threejs",
|
|
40
|
-
"react-three-fiber",
|
|
41
|
-
"components",
|
|
42
|
-
"cli",
|
|
43
|
-
"ui",
|
|
44
|
-
"animation",
|
|
45
|
-
"motion",
|
|
46
|
-
"gsap",
|
|
47
|
-
"shader",
|
|
48
|
-
"interactive",
|
|
49
|
-
"performance",
|
|
50
|
-
"requestanimationframe"
|
|
51
|
-
],
|
|
52
|
-
"scripts": {
|
|
53
|
-
"build": "tsup",
|
|
54
|
-
"dev": "tsup --watch",
|
|
55
|
-
"typecheck": "tsc --noEmit",
|
|
56
|
-
"test": "vitest run",
|
|
57
|
-
"test:watch": "vitest",
|
|
58
|
-
"test:mcp": "node scripts/mcp-e2e/harness.mjs --local",
|
|
59
|
-
"test:mcp:published": "node scripts/mcp-e2e/harness.mjs",
|
|
60
|
-
"postbuild": "node scripts/bundle-manifest.mjs",
|
|
61
|
-
"prepublishOnly": "npm run build"
|
|
62
|
-
},
|
|
63
|
-
"dependencies": {
|
|
64
|
-
"@babel/core": "^7.24.0",
|
|
65
|
-
"@babel/preset-typescript": "^7.24.0",
|
|
66
|
-
"@clack/prompts": "^0.7.0",
|
|
67
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
68
|
-
"commander": "^12.0.0",
|
|
69
|
-
"ora": "^8.0.1",
|
|
70
|
-
"picocolors": "^1.0.0",
|
|
71
|
-
"zod": "^3.22.4"
|
|
72
|
-
},
|
|
73
|
-
"devDependencies": {
|
|
74
|
-
"@types/babel__core": "^7.20.5",
|
|
75
|
-
"@types/node": "^25.9.2",
|
|
76
|
-
"tsup": "^8.0.2",
|
|
77
|
-
"typescript": "^6.0.3",
|
|
78
|
-
"vitest": "^2.1.8"
|
|
79
|
-
},
|
|
80
|
-
"mcpName": "dev.vectorvesper/motion"
|
|
81
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "vectorvesper",
|
|
3
|
+
"version": "2.7.0",
|
|
4
|
+
"description": "Motion runtime and WebGL/R3F component CLI for React, with an MCP server that tells your coding agent which primitive to use, how to wire it, and when not to.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"vectorvesper": "dist/index.js",
|
|
10
|
+
"vv": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"package.json"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/vectorvesper/vv-components.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/vectorvesper/vv-components/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://vectorvesper.dev",
|
|
27
|
+
"keywords": [
|
|
28
|
+
"mcp",
|
|
29
|
+
"model-context-protocol",
|
|
30
|
+
"mcp-server",
|
|
31
|
+
"ai",
|
|
32
|
+
"agent",
|
|
33
|
+
"claude",
|
|
34
|
+
"cursor",
|
|
35
|
+
"copilot",
|
|
36
|
+
"react",
|
|
37
|
+
"webgl",
|
|
38
|
+
"three",
|
|
39
|
+
"threejs",
|
|
40
|
+
"react-three-fiber",
|
|
41
|
+
"components",
|
|
42
|
+
"cli",
|
|
43
|
+
"ui",
|
|
44
|
+
"animation",
|
|
45
|
+
"motion",
|
|
46
|
+
"gsap",
|
|
47
|
+
"shader",
|
|
48
|
+
"interactive",
|
|
49
|
+
"performance",
|
|
50
|
+
"requestanimationframe"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"dev": "tsup --watch",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"test:mcp": "node scripts/mcp-e2e/harness.mjs --local",
|
|
59
|
+
"test:mcp:published": "node scripts/mcp-e2e/harness.mjs",
|
|
60
|
+
"postbuild": "node scripts/bundle-manifest.mjs",
|
|
61
|
+
"prepublishOnly": "npm run build"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@babel/core": "^7.24.0",
|
|
65
|
+
"@babel/preset-typescript": "^7.24.0",
|
|
66
|
+
"@clack/prompts": "^0.7.0",
|
|
67
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
68
|
+
"commander": "^12.0.0",
|
|
69
|
+
"ora": "^8.0.1",
|
|
70
|
+
"picocolors": "^1.0.0",
|
|
71
|
+
"zod": "^3.22.4"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@types/babel__core": "^7.20.5",
|
|
75
|
+
"@types/node": "^25.9.2",
|
|
76
|
+
"tsup": "^8.0.2",
|
|
77
|
+
"typescript": "^6.0.3",
|
|
78
|
+
"vitest": "^2.1.8"
|
|
79
|
+
},
|
|
80
|
+
"mcpName": "dev.vectorvesper/motion"
|
|
81
|
+
}
|