@vitreajs/vitrea 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,6 +35,13 @@ geometry kernel, the motion kernel, the DOM host layer and the WebGPU renderer
35
35
  are internal and bundled into them at publish time, so an app installs two
36
36
  packages and gets zero transitive runtime dependencies beyond React itself.
37
37
 
38
+ **TypeScript.** The published declarations resolve on their own, on any
39
+ TypeScript version, with no `types` entry and nothing extra installed —
40
+ including with `skipLibCheck: false`. The WebGPU type names the artifacts use
41
+ are declared inside them, and they merge with your own WebGPU types
42
+ (TypeScript 6's DOM lib, `@types/web`, or `@webgpu/types`) rather than
43
+ competing with them.
44
+
38
45
  ### Which package you actually import from
39
46
 
40
47
  | You are… | Install | Import |
@@ -93,6 +100,15 @@ sampling unit — one backdrop proxy, one blur, shared between its members:
93
100
  </GlassGroup>
94
101
  ```
95
102
 
103
+ **A surface has no intrinsic size.** vitrea declares neither position nor size
104
+ for one: it measures the box your CSS produced, once per frame, and fits the
105
+ material to it. The element itself is portalled into its plane's host layer,
106
+ which is a `position: absolute; inset: 0` overlay, so a surface places itself the
107
+ way any overlay child does — give it your own width, height and positioning, and
108
+ it will be exactly as big as you made it. This is also why press compression and
109
+ morph deformation are composed transforms rather than shape changes: a transform
110
+ cannot dirty the rect it is animating.
111
+
96
112
  And to see what the runtime actually resolved to, rather than what you asked for:
97
113
 
98
114
  ```tsx
@@ -139,8 +155,8 @@ interface GlassGroupState {
139
155
  analysis: "exact" | "hint" | "none";
140
156
  health: "ok" | "demoted";
141
157
  demotionReason?: "no-webgpu" | "no-backdrop-filter" | "tainted-source"
142
- | "incompatible-texture" | "device-lost" | "probe-failed"
143
- | "governor";
158
+ | "incompatible-texture" | "no-texture-supplied" | "device-lost"
159
+ | "probe-failed" | "governor";
144
160
  }
145
161
  ```
146
162
 
@@ -164,7 +180,7 @@ point.
164
180
 
165
181
  | Configuration | What you get |
166
182
  | --- | --- |
167
- | **texture + exact** — you register an image, video, canvas or procedural texture as the group's backdrop | Full refraction. Edge lensing visibly bends the backdrop; a larger surface lenses deeper than a small one over the same content. Luminance, variance and edge-density analysis run on the GPU. |
183
+ | **texture + exact** — you register an image, video or canvas as the group's backdrop | Full refraction. Edge lensing visibly bends the backdrop; a larger surface lenses deeper than a small one over the same content. Luminance, variance and edge-density analysis run on the GPU. |
168
184
  | **dom + hint** — arbitrary page content, plus a `hint` (or an estimator provider) | The browser compositor does the blur through a masked backdrop proxy; the GPU renders rim lensing, tint, glow and morphs. Adaptation comes from your hint. |
169
185
  | **dom + none** — arbitrary page content, no hint (the default) | Fixed regular material, geometry-driven rim and specular, foreground from tokens or `color-scheme`. |
170
186
 
@@ -173,6 +189,62 @@ There is a built-in best-effort estimator that reads known background colours an
173
189
  images where CORS permits, and it is documented as an estimator every place it
174
190
  appears — not as pixel analysis, because that is not what it is.
175
191
 
192
+ ### Registering a texture backdrop
193
+
194
+ The texture path is two steps, and it is two because of the purity law above:
195
+ `vitrea` may not hold an `HTMLImageElement`, so it cannot be the thing you hand
196
+ pixels to. The group **declares** the source; the root **supplies** it.
197
+
198
+ ```tsx
199
+ import { GlassGroup, GlassSurface, useGlassRoot } from "@vitreajs/vitrea-react";
200
+
201
+ function Hero() {
202
+ const root = useGlassRoot();
203
+
204
+ return (
205
+ <>
206
+ {/* 1. Declare. `configuredSource` stays "texture" through any demotion. */}
207
+ <GlassGroup id="hero" backdrop={{ kind: "texture", id: "hero" }}>
208
+ <GlassSurface radius={26} thickness={18}>…</GlassSurface>
209
+ </GlassGroup>
210
+
211
+ {/* 2. Supply. The id is what joins the two halves; the order does not
212
+ matter, and `setBackdropTexture` marks the source dirty itself, so
213
+ handing the pixels over is the whole wiring. */}
214
+ <img
215
+ src="/hero.jpg"
216
+ alt=""
217
+ onLoad={(event) =>
218
+ root?.setBackdropTexture("hero", { kind: "image", image: event.currentTarget })
219
+ }
220
+ />
221
+ </>
222
+ );
223
+ }
224
+ ```
225
+
226
+ The other two forms are `{ kind: "canvas", canvas }` and `{ kind: "video", video }`.
227
+ A video and a live canvas are re-imported on every frame that samples them; a
228
+ decoded image is imported once. Passing `undefined` withdraws a source's pixels.
229
+
230
+ Declaring a texture and never supplying one is not a silent hole: the group
231
+ resolves to `health: "demoted"` with `demotionReason: "no-texture-supplied"` and
232
+ keeps drawing tint, rim and glow. The readout names the missing half.
233
+
234
+ **Where the texture is placed.** The renderer maps the source over the **whole
235
+ viewport**, cover-fit — it fills the viewport and the overflow is cropped
236
+ symmetrically, the same geometry as `object-fit: cover` on a
237
+ `position: fixed; inset: 0` element. Not over the group, and not over the
238
+ surface.
239
+
240
+ This matters whenever your app paints the same image itself, which is the usual
241
+ case: the picture is on the page and the glass sits on top of it. The two
242
+ mappings have to agree. An `<img>` sized to a region, under a texture mapped to
243
+ the viewport, samples a different crop of the same file — and the mismatch
244
+ appears as the glass showing the wrong part of the picture, which reads
245
+ convincingly like a lensing artefact rather than a registration error. Paint your
246
+ copy viewport-sized and `object-fit: cover`, or accept that the two will differ.
247
+
176
248
  ### Tiers degrade within themselves before they switch
177
249
 
178
250
  The quality governor first reduces refraction resolution, adaptation cadence and
@@ -314,6 +386,68 @@ errors. Asking for the GPU tier is not the same as getting it, and
314
386
 
315
387
  ---
316
388
 
389
+ ## Testing your app
390
+
391
+ ### The readout is trustworthy; the environment may not be
392
+
393
+ `useGlassCapabilities()` reports what resolved. If it says
394
+ `demotionReason: "no-webgpu"`, that session genuinely had no WebGPU — the
395
+ readout is not the thing to doubt. What is worth doubting is the environment,
396
+ because one browser-automation default removes WebGPU without removing anything
397
+ you would notice.
398
+
399
+ **Playwright's bundled headless shell hands back a software adapter.** This repo
400
+ measures three different answers on one machine depending on how Chromium is
401
+ launched: the default headless shell resolves to a SwiftShader adapter, while
402
+ `channel: "chromium"` — the full browser binary — resolves to real hardware. A
403
+ suite that never asks for the channel runs entirely green on the CSS tier, and
404
+ every readout in it honestly says `no-webgpu`, which is what makes this the worst
405
+ kind of failure: nothing is broken, it is just the other tier, and the
406
+ instrumentation agrees with the wrong answer.
407
+
408
+ ```ts
409
+ // playwright.config.ts
410
+ projects: [
411
+ {
412
+ name: "chromium-gpu",
413
+ use: {
414
+ channel: "chromium",
415
+ launchOptions: {
416
+ args: ["--enable-unsafe-webgpu", "--enable-features=Vulkan,WebGPU"],
417
+ },
418
+ },
419
+ },
420
+ ],
421
+ ```
422
+
423
+ Two further requirements are not optional. `navigator.gpu` is undefined outside a
424
+ secure context, so serve the page over `http://localhost` — on `file://` and
425
+ `data:` URLs the absence reads exactly like "no WebGPU on this machine". And a
426
+ test that means to assert the GPU tier should **fail** when no adapter answers
427
+ rather than skip, and fail rather than quietly accept a software one; otherwise
428
+ it asserts nothing.
429
+
430
+ ### What is in the DOM differs by tier
431
+
432
+ The backdrop-proxy elements are not a general debugging landmark. A
433
+ `[data-vitrea-proxy]` element exists only where the GPU tier is sampling
434
+ arbitrary DOM:
435
+
436
+ | Resolved state | What the runtime puts in the DOM |
437
+ | --- | --- |
438
+ | `activeRenderer: "webgpu"`, `samplingBackend: "css-backdrop"` | one `[data-vitrea-proxy]` per group **per plane** it has members on |
439
+ | `activeRenderer: "webgpu"`, `samplingBackend: "gpu-texture"` | no proxy — the backdrop is a GPU texture, so there is nothing in the page to filter |
440
+ | `activeRenderer: "css"` | no proxies at all: the CSS tier applies `backdrop-filter` **in place**, on each glass host element |
441
+
442
+ So "find the proxy" is an assertion about one resolved state, not about the
443
+ dom-backdrop path in general — and its absence on the CSS tier is the design
444
+ rather than a fault, which is also why `probe-failed` can demote to that tier at
445
+ all: the very thing that failed is not on its path. To assert the CSS tier, read
446
+ the host element's own computed `backdrop-filter`, `background-color` and
447
+ `border-color`. To assert which tier you are on, read `useGlassCapabilities()`.
448
+
449
+ ---
450
+
317
451
  ## Accessibility
318
452
 
319
453
  Accessibility is policy in `vitrea`, applied by the host layer through media
package/dist/index.d.ts CHANGED
@@ -1,3 +1,19 @@
1
+ declare global {
2
+ interface GPUBuffer {}
3
+ interface GPUCommandEncoder {}
4
+ interface GPUComputePassTimestampWrites {}
5
+ interface GPUDevice {}
6
+ interface GPUExternalTexture {}
7
+ interface GPUExternalTextureDescriptor {}
8
+ interface GPUQueue {}
9
+ interface GPURenderPassTimestampWrites {}
10
+ interface GPUSupportedLimits {}
11
+ interface GPUTexture {}
12
+ interface GPUTextureDescriptor {}
13
+ interface GPUTextureView {}
14
+ }
15
+ type GPUTextureFormat = "bgra8unorm" | "rgba8unorm" | "rgba16float" | (string & {});
16
+
1
17
  /**
2
18
  * X8 — the shape channel set, and the vocabulary every other module speaks.
3
19
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitreajs/vitrea",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Framework-agnostic Liquid Glass material runtime: scene model, capability resolution, material policy, accessibility policy.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/SSFSKIM/designer",