@vizij/render 1.0.0 → 2.0.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 +195 -110
- package/dist/index.d.mts +183 -151
- package/dist/index.d.ts +183 -151
- package/dist/index.js +765 -1776
- package/dist/index.mjs +738 -1776
- package/package.json +11 -18
package/README.md
CHANGED
|
@@ -1,119 +1,209 @@
|
|
|
1
1
|
# @vizij/render
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Table of Contents
|
|
10
|
-
|
|
11
|
-
1. [Overview](#overview)
|
|
12
|
-
2. [Installation](#installation)
|
|
13
|
-
3. [Usage](#usage)
|
|
14
|
-
4. [Store & Hooks](#store--hooks)
|
|
15
|
-
5. [Controllers & Helpers](#controllers--helpers)
|
|
16
|
-
6. [Development & Testing](#development--testing)
|
|
17
|
-
7. [Publishing](#publishing)
|
|
18
|
-
8. [Related Packages](#related-packages)
|
|
3
|
+
> Vizij faces on a page — the page's canvas and the face slots over
|
|
4
|
+
> [`@vizij/runtime`](https://www.npmjs.com/package/@vizij/runtime)'s Bevy view —
|
|
5
|
+
> and the GLB loader, exporter and world model the authoring app edits.
|
|
19
6
|
|
|
20
7
|
---
|
|
21
8
|
|
|
22
9
|
## Overview
|
|
23
10
|
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
- **The page's canvas, `<VizijCanvas>`.** `@vizij/runtime` runs one Bevy App
|
|
12
|
+
per page, on one canvas. `<VizijCanvas>` renders it and mounts it once;
|
|
13
|
+
while it is shown, one animation-frame loop places each face over its slot
|
|
14
|
+
and hands each pointer pick to the slot it names.
|
|
15
|
+
- **A face slot, `<Vizij>`.** A `<div>` marking where one face draws on that
|
|
16
|
+
canvas. It renders no canvas of its own, so a page shows any number of faces
|
|
17
|
+
for the cost of one App. Until its face is drawn, it shows a loading view:
|
|
18
|
+
the runtime's download and start, then the face's own stages.
|
|
19
|
+
- **The loader and the exporter.** `loadGLTF`, `loadGLTFFromBlob` and their
|
|
20
|
+
`WithBundle` forms turn a rigged GLB into the world model; `buildSceneGraph`
|
|
21
|
+
and `exportScene` turn the world model back into GLB bytes. Both run in Node
|
|
22
|
+
as well as in a browser.
|
|
23
|
+
- **The world model and its store.** `useVizijStore` and its hooks hold the
|
|
24
|
+
world, its animatables and the element selection: the document the authoring
|
|
25
|
+
app edits.
|
|
26
|
+
|
|
27
|
+
A face's device — loading it, writing its keys, calling its methods — is
|
|
28
|
+
`@vizij/runtime`'s `Runtime`; [`@vizij/runtime-react`](../runtime-react/README.md)
|
|
29
|
+
binds one to a React tree.
|
|
28
30
|
|
|
29
31
|
---
|
|
30
32
|
|
|
31
33
|
## Installation
|
|
32
34
|
|
|
33
35
|
```bash
|
|
34
|
-
|
|
35
|
-
pnpm add @vizij/render three @react-three/fiber @react-three/drei zustand @vizij/utils
|
|
36
|
-
|
|
37
|
-
# npm
|
|
38
|
-
npm install @vizij/render three @react-three/fiber @react-three/drei zustand @vizij/utils
|
|
39
|
-
|
|
40
|
-
# yarn
|
|
41
|
-
yarn add @vizij/render three @react-three/fiber @react-three/drei zustand @vizij/utils
|
|
36
|
+
pnpm add @vizij/render @vizij/runtime three zustand @vizij/utils
|
|
42
37
|
```
|
|
43
38
|
|
|
44
|
-
Peer requirements:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- `@react-three/fiber >= 8`
|
|
49
|
-
- `@react-three/drei >= 9`
|
|
50
|
-
- `zustand >= 5`
|
|
51
|
-
- Optional UI integrations: `tailwindcss >= 4.1` (declared as a peer for theme utilities)
|
|
52
|
-
|
|
53
|
-
Ensure these versions align with the rest of your app to avoid duplicate React or Three.js instances.
|
|
39
|
+
Peer requirements: `@vizij/runtime ^8` (its `onInitProgress` feeds the
|
|
40
|
+
loading view), `react >= 18`, `three ^0.170` (the
|
|
41
|
+
loader and the exporter), `zustand ^5` (the store). `@vizij/runtime` is a peer
|
|
42
|
+
so that a page resolves one copy of it, and therefore one wasm module.
|
|
54
43
|
|
|
55
44
|
---
|
|
56
45
|
|
|
57
46
|
## Usage
|
|
58
47
|
|
|
59
48
|
```tsx
|
|
60
|
-
import {
|
|
61
|
-
import { useEffect } from "react";
|
|
62
|
-
|
|
63
|
-
export function VizijCanvas() {
|
|
64
|
-
const setDebug = useVizijStore((state) => state.setDebugState);
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
setDebug((debug) => ({ ...debug, showGrid: true }));
|
|
68
|
-
}, [setDebug]);
|
|
49
|
+
import { VizijCanvas, Vizij } from "@vizij/render";
|
|
69
50
|
|
|
51
|
+
export function Page() {
|
|
70
52
|
return (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
53
|
+
<>
|
|
54
|
+
<VizijCanvas />
|
|
55
|
+
<section>
|
|
56
|
+
<Vizij vizijId="quori" style={{ width: 480, height: 360 }} />
|
|
57
|
+
</section>
|
|
58
|
+
</>
|
|
77
59
|
);
|
|
78
60
|
}
|
|
79
61
|
```
|
|
80
62
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## Store & Hooks
|
|
86
|
-
|
|
87
|
-
- `useVizijStore(selector?)` – Access or mutate the renderer store with automatic subscription management.
|
|
88
|
-
- `useVizijStoreGetter`, `useVizijStoreSetter`, `useVizijStoreSubscription` – Fine-grained accessors when you need optimised reads/writes.
|
|
89
|
-
- `useFeatures()` – Inspect feature flags registered in the store.
|
|
90
|
-
- Store types (`VizijData`, `VizijActions`) are exported from `store-types` for strongly typed selectors.
|
|
63
|
+
The face itself is loaded under the same id, once the canvas is mounted:
|
|
91
64
|
|
|
92
|
-
|
|
65
|
+
```ts
|
|
66
|
+
import { canvasMounted } from "@vizij/render";
|
|
67
|
+
import { loadVizij } from "@vizij/runtime";
|
|
93
68
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- Controllers under `src/controllers` encapsulate input handling, camera logic, and other behaviours. Compose them with your own React components.
|
|
99
|
-
- `loadGLTF` / `loadGLTFFromBlob` load rig assets and extract the animatable metadata used by `@vizij/runtime-react`. A blob is parsed in memory, with nothing fetched.
|
|
100
|
-
- `buildSceneGraph(world, animatables, rootId)` builds the three.js graph of a world element with no renderer, and `exportScene(root, { bundle, animations })` encodes a graph as GLB bytes. Saving or uploading the bytes is the caller's job.
|
|
101
|
-
|
|
102
|
-
All exports are re-exported through `src/index.tsx`, so a simple `import { loadGLTF } from "@vizij/render"` works.
|
|
103
|
-
|
|
104
|
-
### Dual-format Vizij bundles
|
|
69
|
+
await canvasMounted();
|
|
70
|
+
const quori = await loadVizij("quori", glbBytes);
|
|
71
|
+
void quori.run();
|
|
72
|
+
```
|
|
105
73
|
|
|
106
|
-
|
|
74
|
+
`@vizij/runtime-react`'s `VizijRuntimeProvider` does this for a React tree.
|
|
75
|
+
|
|
76
|
+
### `<VizijCanvas options? className? style? />`
|
|
77
|
+
|
|
78
|
+
- Its first mount calls `@vizij/runtime`'s `mount` with `options` (every
|
|
79
|
+
face's defaults: `fit`, `zoom`, `background`, `ambient`, `unlit`). A second
|
|
80
|
+
`<VizijCanvas>`, a remount or React StrictMode's second effect shows the same
|
|
81
|
+
canvas and App; the options of the first mount stand for the page's
|
|
82
|
+
lifetime.
|
|
83
|
+
- `canvasMounted()` resolves with the canvas once it is mounted, which
|
|
84
|
+
`loadVizij` requires, and rejects if mounting failed.
|
|
85
|
+
- Default style: `position: fixed; inset: 0; pointer-events: none`, over the
|
|
86
|
+
page content. Faces then draw wherever their slots lie, whatever the
|
|
87
|
+
backgrounds of the sections between, and the page's own UI keeps the
|
|
88
|
+
pointer. Content that must draw over a face stacks above the canvas.
|
|
89
|
+
- `className` or `style` replace the default. A page that takes picks (an
|
|
90
|
+
authoring viewport) places the canvas itself and gives it pointer events.
|
|
91
|
+
|
|
92
|
+
The canvas's loop reads every slot's rectangle each frame and calls
|
|
93
|
+
`placeVizij` when it moved, which follows scrolling, resizes and layout shifts
|
|
94
|
+
alike. `placeVizij` and `drainPicks` act on the whole page, which is why they
|
|
95
|
+
have one caller.
|
|
96
|
+
|
|
97
|
+
### `<Vizij vizijId className? style? toneMapping? fit? zoom? bounds? showSafeArea? selection? onPick? loadingImage? loading? />`
|
|
98
|
+
|
|
99
|
+
| Prop | Effect |
|
|
100
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
101
|
+
| `vizijId` | The face the slot shows: the id it is loaded under. |
|
|
102
|
+
| `className`, `style` | Size and place the slot; the face draws over its border box. |
|
|
103
|
+
| `toneMapping` | The face's tone-mapping curve (`none`, `agx`, `aces`, `neutral`): `setView`. |
|
|
104
|
+
| `fit`, `zoom` | How the framed bounds fit the slot, and the magnification after the fit; the canvas's options when absent. |
|
|
105
|
+
| `bounds` | The world rectangle the face's camera frames, in place of the GLB's authored `rootBounds`. |
|
|
106
|
+
| `showSafeArea` | Outlines the framed bounds (`safeArea(vizijId)`) over the face, as a DOM overlay. |
|
|
107
|
+
| `selection` | The element ids whose outlines glow (`setSelection`). Absent, the slot leaves the face's selection as it is. |
|
|
108
|
+
| `onPick` | A press on the face: `{ vizijId, elementId }`, `elementId` `null` for a press that hit no element. |
|
|
109
|
+
| `loadingImage` | The default loading view's image, in place of Vizij's logo, which it ships; `null` for none. |
|
|
110
|
+
| `loading` | The slot's own loading view, `(loading: VizijLoading) => ReactNode`; `null` for none. |
|
|
111
|
+
|
|
112
|
+
The slot carries `data-vizij-id` and `data-vizij-ready`, `"true"` once the
|
|
113
|
+
face's scene shows its device's pose — what an e2e test waits on. When a slot
|
|
114
|
+
unmounts, its face is placed on an empty rectangle, where it draws nothing.
|
|
115
|
+
One slot shows a face at a time.
|
|
116
|
+
|
|
117
|
+
### The loading view
|
|
118
|
+
|
|
119
|
+
Until its face is first drawn, a slot shows a loading view over its border
|
|
120
|
+
box, centred: an image, Vizij's logo by default, then a progress bar and a
|
|
121
|
+
short label of the stage in the slot's text colour. The stages, in order:
|
|
122
|
+
|
|
123
|
+
| Stage | Label | Bar | Reported by |
|
|
124
|
+
| ---------- | -------------------------------------- | ------------- | -------------------------------------------- |
|
|
125
|
+
| `download` | Downloading the runtime: 3.1 of 9.8 MB | the bytes | `<VizijCanvas>`, from `onInitProgress` |
|
|
126
|
+
| `compile` | Compiling the runtime | indeterminate | `<VizijCanvas>`, from `onInitProgress` |
|
|
127
|
+
| `start` | Starting the runtime | indeterminate | `<VizijCanvas>`, from `onInitProgress` |
|
|
128
|
+
| `mount` | Starting the view | indeterminate | `<VizijCanvas>`, until `mount` resolves |
|
|
129
|
+
| `fetch` | Loading the face: 1.2 of 4.0 MB | the bytes | the face's loader, `setVizijProgress` |
|
|
130
|
+
| `load` | Loading the face | indeterminate | the face's loader; the default after `mount` |
|
|
131
|
+
| `draw` | Drawing the face | indeterminate | the face's loader |
|
|
132
|
+
| `failed` | Loading failed | none | `<VizijCanvas>` or the face's loader |
|
|
133
|
+
|
|
134
|
+
Without a known total, a byte count reads "3.1 MB" and its bar is
|
|
135
|
+
indeterminate; before any byte, the label has no count. A failure shows first;
|
|
136
|
+
the runtime's stages show before the face's, which wait on them. The view
|
|
137
|
+
hides once the canvas's loop reads the face ready (`data-vizij-ready="true"`),
|
|
138
|
+
and stays hidden through a reload, whose previous scene draws until the new
|
|
139
|
+
one is ready.
|
|
140
|
+
|
|
141
|
+
- **The face's stages** are reported by whoever loads the face, keyed by its
|
|
142
|
+
id: `setVizijProgress(vizijId, progress)`, with `progress` a
|
|
143
|
+
`VizijProgress` — `{ stage: "fetch", loaded, total }` (`total` `null` when
|
|
144
|
+
unknown), `{ stage: "load" }`, `{ stage: "draw" }` or
|
|
145
|
+
`{ stage: "failed", error }`. `@vizij/runtime-react`'s provider reports its
|
|
146
|
+
face. A page that calls `loadVizij` itself reports the same way, or leaves
|
|
147
|
+
its slot at "Loading the face" until the face is drawn. `null` says no face
|
|
148
|
+
is loaded under the id any more, and its slot shows the loading view again.
|
|
149
|
+
- **Its image:** `loadingImage` replaces Vizij's logo, which the view ships (a
|
|
150
|
+
small inline SVG in the logo's own colours); `null` leaves the image out.
|
|
151
|
+
- **Its whole element:** `loading` is called with a `VizijLoading` —
|
|
152
|
+
`{ stage, label, fraction }`, `fraction` from 0 to 1 for a byte count with
|
|
153
|
+
a known total and `null` otherwise — and its result is shown over the slot
|
|
154
|
+
in place of the default view. `loading={null}` shows none.
|
|
107
155
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
156
|
+
```tsx
|
|
157
|
+
<Vizij vizijId="quori" loadingImage={<img src="/logo.svg" alt="" />} />
|
|
158
|
+
<Vizij
|
|
159
|
+
vizijId="quori"
|
|
160
|
+
loading={({ label, fraction }) => (
|
|
161
|
+
<Spinner label={label} value={fraction ?? undefined} />
|
|
162
|
+
)}
|
|
163
|
+
/>
|
|
164
|
+
```
|
|
115
165
|
|
|
116
|
-
|
|
166
|
+
The default bar is a `role="progressbar"` named "Loading the face", its
|
|
167
|
+
`aria-valuetext` the label, and its `aria-valuenow` the percentage when the
|
|
168
|
+
stage has a fraction (indeterminate otherwise); a failure is a
|
|
169
|
+
`role="status"`. The view is a DOM overlay inside the slot, with pointer
|
|
170
|
+
events off, so it shows wherever the canvas draws nothing — the whole slot
|
|
171
|
+
until its face draws. `<VizijCanvas>` shows no loading view of its own: by default it
|
|
172
|
+
covers the whole page, and every slot already shows the runtime's stages.
|
|
173
|
+
|
|
174
|
+
### Loading and exporting
|
|
175
|
+
|
|
176
|
+
- `loadGLTF(url, aggressiveImport?, rootBounds?)`, `loadGLTFFromBlob` and
|
|
177
|
+
their `WithBundle` forms return the world, its animatables, the
|
|
178
|
+
`VIZIJ_bundle` and the GLB's animations. A blob is parsed in memory, with
|
|
179
|
+
nothing fetched.
|
|
180
|
+
- `buildSceneGraph(world, animatables, rootId)` builds the three.js graph of a
|
|
181
|
+
world element with no renderer; `exportScene(root, { bundle, animations })`
|
|
182
|
+
encodes it as GLB bytes. Every node carries its element's `RobotData`, and
|
|
183
|
+
its transform, morph weights and material factors are the element's
|
|
184
|
+
authored defaults.
|
|
185
|
+
- An ellipse or a rectangle exports as a node with no mesh, carrying its
|
|
186
|
+
`RobotData` — `type`, `width`, `height`, `fillColor`, `fillOpacity` — from
|
|
187
|
+
which the Bevy view draws it: a unit circle or square scaled by the width
|
|
188
|
+
and height, filled with the colour and opacity. Its stroke is not drawn.
|
|
189
|
+
- `extractVizijBundle(scene)` and `applyVizijBundle(scene, bundle)` read and
|
|
190
|
+
write the bundle on a three.js graph.
|
|
191
|
+
|
|
192
|
+
The `VIZIJ_bundle` extension follows the schema in
|
|
193
|
+
[`src/types/vizij-bundle.ts`](./src/types/vizij-bundle.ts): rig graphs, pose
|
|
194
|
+
configs, programs, stored clips and provenance hashes, beside glTF animations
|
|
195
|
+
that viewers which do not understand Vizij still play.
|
|
196
|
+
|
|
197
|
+
### The store
|
|
198
|
+
|
|
199
|
+
- `useVizijStore(selector)` reads the store of the nearest
|
|
200
|
+
`VizijContext.Provider`; `useVizijStoreGetter`, `useVizijStoreSetter` and
|
|
201
|
+
`useVizijStoreSubscription` are its fine-grained forms.
|
|
202
|
+
- `createVizijStore()` and `useDefaultVizijStore` create and hold one.
|
|
203
|
+
- It holds the world, its animatables, values keyed by namespace, the element
|
|
204
|
+
selection, and the edits the authoring app makes to them (`setVizij`,
|
|
205
|
+
`addWorldElements`, `setStaticFeature`, `createAnimatable`, …). Its types are
|
|
206
|
+
`VizijData` and `VizijActions`.
|
|
117
207
|
|
|
118
208
|
---
|
|
119
209
|
|
|
@@ -127,39 +217,34 @@ pnpm --filter "@vizij/render" lint
|
|
|
127
217
|
pnpm --filter "@vizij/render" size
|
|
128
218
|
```
|
|
129
219
|
|
|
130
|
-
|
|
220
|
+
The tests run on Node (`node --test`): the loader, the headless export round
|
|
221
|
+
trip of the shipped faces and of an ellipse and a rectangle, the canvas's frame
|
|
222
|
+
loop, and the loading view's stages, labels and overrides, against a stand-in of
|
|
223
|
+
`@vizij/runtime` (`tests/runtime-stand-in.mjs`). `size-limit` guards the bundle
|
|
224
|
+
size.
|
|
225
|
+
|
|
226
|
+
Faces are drawn by `@vizij/runtime`'s Bevy view, a peer dependency. `three` is
|
|
227
|
+
imported only by the loader (`src/functions/load-gltf.ts`,
|
|
228
|
+
`src/functions/gltf-loading/`) and the exporter
|
|
229
|
+
(`src/functions/build-scene-graph.ts`, `src/functions/export.ts`,
|
|
230
|
+
`src/functions/vizij-bundle.ts`); the rest imports its types at most. An API
|
|
231
|
+
change updates the apps that use the package and `@vizij/runtime-react` in the
|
|
232
|
+
same change.
|
|
131
233
|
|
|
132
234
|
---
|
|
133
235
|
|
|
134
236
|
## Publishing
|
|
135
237
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
pnpm changeset
|
|
142
|
-
pnpm version:packages
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
2. Validate locally:
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
pnpm install
|
|
149
|
-
pnpm --filter "@vizij/render" build
|
|
150
|
-
pnpm --filter "@vizij/render" test
|
|
151
|
-
pnpm --filter "@vizij/render" typecheck
|
|
152
|
-
pnpm --filter "@vizij/render" lint
|
|
153
|
-
pnpm --filter "@vizij/render" exec npm pack --dry-run
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
3. Merge the version bump to `main`. CI publishes any `@vizij/*` version not yet on npm (with provenance) — no release tag. See the root [README → Publishing & Versioning](../../../README.md#publishing--versioning).
|
|
238
|
+
A change carries a changeset (`pnpm changeset`). Merging to `main` updates the
|
|
239
|
+
Version Packages PR, and merging that publishes through
|
|
240
|
+
[`.github/workflows/publish-npm.yml`](../../../.github/workflows/publish-npm.yml).
|
|
241
|
+
See [`CONTRIBUTING.md` › Releasing](../../../CONTRIBUTING.md#releasing).
|
|
157
242
|
|
|
158
243
|
---
|
|
159
244
|
|
|
160
245
|
## Related Packages
|
|
161
246
|
|
|
162
|
-
- [`@vizij/runtime
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
247
|
+
- [`@vizij/runtime`](https://www.npmjs.com/package/@vizij/runtime) – the Bevy
|
|
248
|
+
view and each face's Arora device, in one wasm module.
|
|
249
|
+
- [`@vizij/runtime-react`](../runtime-react/README.md) – one face's lifecycle
|
|
250
|
+
in React and the fan-out of its store changes.
|