@fusion.dev/helix 0.33.2 → 0.33.4
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/custom-elements.json +13 -1
- package/dist/aura/root/aura-root.d.ts +54 -0
- package/dist/aura/root/aura-root.js +17 -0
- package/package.json +6 -6
- package/types/jsx.d.ts +10 -0
- package/types/vue.d.ts +1 -1
package/custom-elements.json
CHANGED
|
@@ -4025,7 +4025,7 @@
|
|
|
4025
4025
|
"declarations": [
|
|
4026
4026
|
{
|
|
4027
4027
|
"kind": "class",
|
|
4028
|
-
"description": "One fragment shader over a full-screen triangle, with the palette read from\nthe tokens rather than given as colors. A background written with hex values\nis an asset that has to be re-cut when the brand moves; one written against\n`--helix-color-primary` is a consequence of it.\n\n**Every shape ramps through four token stops**, and the token layer inverts\neach: the page's background, `primary-subtle`, `primary`, then a peak. The\npeak is the foreground on a dark page and `primary-active` on a light one,\nbecause mixing a saturated hue toward near-black takes the chroma out of the\npixels carrying most of it. Which way the page runs is read off the ground's\nown luminance rather than asked of the mode, which is a private token.\n\n**No dependency, and no scene graph.** The geometry is three vertices derived\nfrom `gl_VertexID`, so there is no attribute, no buffer and no upload.\n\n**It stops when nobody is looking**, off screen and in a background tab, and\n`prefers-reduced-motion` gets one still frame rather than an empty box. A\ncontext the browser takes back, on a GPU reset or past its cap of about\nsixteen, stops it too, and the gradient shows until the context returns.\n\nStyled only, and with behavior, which the tier split does not otherwise\nallow: the behavior is drawing to a canvas, so there is no structure a\nconsumer could restyle and nothing for a headless twin to hold.",
|
|
4028
|
+
"description": "One fragment shader over a full-screen triangle, with the palette read from\nthe tokens rather than given as colors. A background written with hex values\nis an asset that has to be re-cut when the brand moves; one written against\n`--helix-color-primary` is a consequence of it.\n\n**Every shape ramps through four token stops**, and the token layer inverts\neach: the page's background, `primary-subtle`, `primary`, then a peak. The\npeak is the foreground on a dark page and `primary-active` on a light one,\nbecause mixing a saturated hue toward near-black takes the chroma out of the\npixels carrying most of it. Which way the page runs is read off the ground's\nown luminance rather than asked of the mode, which is a private token.\n\n**No dependency, and no scene graph.** The geometry is three vertices derived\nfrom `gl_VertexID`, so there is no attribute, no buffer and no upload.\n\n**It stops when nobody is looking**, off screen and in a background tab, and\n`prefers-reduced-motion` gets one still frame rather than an empty box. A\ncontext the browser takes back, on a GPU reset or past its cap of about\nsixteen, stops it too, and the gradient shows until the context returns.\n\n**It declines a renderer that draws on the CPU**, which is a headless browser\nand a machine with no usable GPU, and shows the gradient instead. The shader\ncosts about 450ms of first frame per element there against 2ms on a GPU, for\na field drawn from the same tokens the gradient already ramps.\n\nStyled only, and with behavior, which the tier split does not otherwise\nallow: the behavior is drawing to a canvas, so there is no structure a\nconsumer could restyle and nothing for a headless twin to hold.",
|
|
4029
4029
|
"name": "HelixAura",
|
|
4030
4030
|
"cssParts": [
|
|
4031
4031
|
{
|
|
@@ -4414,6 +4414,18 @@
|
|
|
4414
4414
|
}
|
|
4415
4415
|
}
|
|
4416
4416
|
},
|
|
4417
|
+
{
|
|
4418
|
+
"kind": "method",
|
|
4419
|
+
"name": "drawsInSoftware",
|
|
4420
|
+
"privacy": "private",
|
|
4421
|
+
"static": true,
|
|
4422
|
+
"return": {
|
|
4423
|
+
"type": {
|
|
4424
|
+
"text": "boolean"
|
|
4425
|
+
}
|
|
4426
|
+
},
|
|
4427
|
+
"description": "Whether WebGL here is drawn on the CPU, asked before one is drawn into.\n\n**The shader costs what a GPU makes it cheap to ask for.** The note at the\ntop of this file measures it: on Metal the browser shares the compiled\nprogram and each aura after the first draws its opening frame in about 2ms,\nwhile under SwiftShader nothing is shared and each one compiles the pipeline\nagain for about 450ms. Measured again 15 Sep 2026 on a page of 720 pixel\nauras, SwiftShader against the same page with this check in place: the worst\nframe gap went 568ms to 50ms with one aura, 1626ms to 34ms with three and\n3200ms to 42ms with six, and a page of three or six ran at 13 frames a\nsecond before and at the display's own rate after. What that buys is a field\na reader barely sees behind content, against a gradient ramped from the same\ntokens. So the software path takes the fallback.\n\n**The unmasked name is the only one that answers.** `gl.RENDERER` is masked\nto `WebKit WebGL` on Chromium, the same string on SwiftShader and on Metal,\nand `WEBGL_debug_renderer_info` is what lifts the mask.\n\n**An unreadable renderer draws**, which is the safe direction and not the\ntidy one. The extension is optional and is withheld deliberately: Firefox's\n`privacy.resistFingerprinting` and Safari's equivalent both drop it, and a\nbrowser may drop it at any time, since a GPU name is a fingerprinting\nsurface. Treating \"we cannot tell\" as software would take the shader away\nfrom every reader on a machine that hides its hardware, which is a real GPU\nfar more often than not. Treating it as hardware costs a machine that hides\na software renderer the 450ms, which is the cost the component has today.\n\n**A probe context of its own, and given straight back.** Asking the\nelement's own canvas would spend it: `build` below is written around a\ncanvas whose context was force-lost never yielding a working one again, and\nthe probe has to release what it took or a page of auras runs the browser\ninto its cap of about sixteen. One pixel by one, and released through\n`WEBGL_lose_context` the same way `drop` does.\n\n**The same attributes the real context asks for**, because they are part of\nthe question: `powerPreference` can select a different adapter, so a probe\nasking for a high-performance context would name a GPU the drawing one is\nnever given.\n\n**Asked per context rather than once for the page.** The answer is not\nfixed: a GPU process that crashes takes the page down to SwiftShader for\nthe rest of its life, and an aura rebuilding after that should get the\ncurrent answer rather than the one from before. It costs a context creation\nand two calls, measured 15 Sep 2026 at about 2ms under SwiftShader and 2ms\non Metal, against the 450ms and 135ms first draws it stands in front of, and\nit happens once per context rather than once per frame."
|
|
4428
|
+
},
|
|
4417
4429
|
{
|
|
4418
4430
|
"kind": "method",
|
|
4419
4431
|
"name": "build",
|
|
@@ -24,6 +24,11 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
|
|
|
24
24
|
* context the browser takes back, on a GPU reset or past its cap of about
|
|
25
25
|
* sixteen, stops it too, and the gradient shows until the context returns.
|
|
26
26
|
*
|
|
27
|
+
* **It declines a renderer that draws on the CPU**, which is a headless browser
|
|
28
|
+
* and a machine with no usable GPU, and shows the gradient instead. The shader
|
|
29
|
+
* costs about 450ms of first frame per element there against 2ms on a GPU, for
|
|
30
|
+
* a field drawn from the same tokens the gradient already ramps.
|
|
31
|
+
*
|
|
27
32
|
* Styled only, and with behavior, which the tier split does not otherwise
|
|
28
33
|
* allow: the behavior is drawing to a canvas, so there is no structure a
|
|
29
34
|
* consumer could restyle and nothing for a headless twin to hold.
|
|
@@ -274,6 +279,55 @@ export declare class HelixAura extends HelixElement {
|
|
|
274
279
|
protected render(): TemplateResult;
|
|
275
280
|
protected firstUpdated(changed: PropertyValues): void;
|
|
276
281
|
private start;
|
|
282
|
+
/**
|
|
283
|
+
* Whether WebGL here is drawn on the CPU, asked before one is drawn into.
|
|
284
|
+
*
|
|
285
|
+
* **The shader costs what a GPU makes it cheap to ask for.** The note at the
|
|
286
|
+
* top of this file measures it: on Metal the browser shares the compiled
|
|
287
|
+
* program and each aura after the first draws its opening frame in about 2ms,
|
|
288
|
+
* while under SwiftShader nothing is shared and each one compiles the pipeline
|
|
289
|
+
* again for about 450ms. Measured again 15 Sep 2026 on a page of 720 pixel
|
|
290
|
+
* auras, SwiftShader against the same page with this check in place: the worst
|
|
291
|
+
* frame gap went 568ms to 50ms with one aura, 1626ms to 34ms with three and
|
|
292
|
+
* 3200ms to 42ms with six, and a page of three or six ran at 13 frames a
|
|
293
|
+
* second before and at the display's own rate after. What that buys is a field
|
|
294
|
+
* a reader barely sees behind content, against a gradient ramped from the same
|
|
295
|
+
* tokens. So the software path takes the fallback.
|
|
296
|
+
*
|
|
297
|
+
* **The unmasked name is the only one that answers.** `gl.RENDERER` is masked
|
|
298
|
+
* to `WebKit WebGL` on Chromium, the same string on SwiftShader and on Metal,
|
|
299
|
+
* and `WEBGL_debug_renderer_info` is what lifts the mask.
|
|
300
|
+
*
|
|
301
|
+
* **An unreadable renderer draws**, which is the safe direction and not the
|
|
302
|
+
* tidy one. The extension is optional and is withheld deliberately: Firefox's
|
|
303
|
+
* `privacy.resistFingerprinting` and Safari's equivalent both drop it, and a
|
|
304
|
+
* browser may drop it at any time, since a GPU name is a fingerprinting
|
|
305
|
+
* surface. Treating "we cannot tell" as software would take the shader away
|
|
306
|
+
* from every reader on a machine that hides its hardware, which is a real GPU
|
|
307
|
+
* far more often than not. Treating it as hardware costs a machine that hides
|
|
308
|
+
* a software renderer the 450ms, which is the cost the component has today.
|
|
309
|
+
*
|
|
310
|
+
* **A probe context of its own, and given straight back.** Asking the
|
|
311
|
+
* element's own canvas would spend it: `build` below is written around a
|
|
312
|
+
* canvas whose context was force-lost never yielding a working one again, and
|
|
313
|
+
* the probe has to release what it took or a page of auras runs the browser
|
|
314
|
+
* into its cap of about sixteen. One pixel by one, and released through
|
|
315
|
+
* `WEBGL_lose_context` the same way `drop` does.
|
|
316
|
+
*
|
|
317
|
+
* **The same attributes the real context asks for**, because they are part of
|
|
318
|
+
* the question: `powerPreference` can select a different adapter, so a probe
|
|
319
|
+
* asking for a high-performance context would name a GPU the drawing one is
|
|
320
|
+
* never given.
|
|
321
|
+
*
|
|
322
|
+
* **Asked per context rather than once for the page.** The answer is not
|
|
323
|
+
* fixed: a GPU process that crashes takes the page down to SwiftShader for
|
|
324
|
+
* the rest of its life, and an aura rebuilding after that should get the
|
|
325
|
+
* current answer rather than the one from before. It costs a context creation
|
|
326
|
+
* and two calls, measured 15 Sep 2026 at about 2ms under SwiftShader and 2ms
|
|
327
|
+
* on Metal, against the 450ms and 135ms first draws it stands in front of, and
|
|
328
|
+
* it happens once per context rather than once per frame.
|
|
329
|
+
*/
|
|
330
|
+
private static drawsInSoftware;
|
|
277
331
|
/**
|
|
278
332
|
* The context and the program, from whatever canvas can still give one.
|
|
279
333
|
*
|
|
@@ -14,6 +14,7 @@ import { styles } from './aura-root.css.js';
|
|
|
14
14
|
const MAX_CANVAS_EDGE = 4096;
|
|
15
15
|
const MAX_CANVAS_PIXELS = 4_194_304;
|
|
16
16
|
const OPENING_PATIENCE = 500;
|
|
17
|
+
const SOFTWARE_RENDERER = /swiftshader|llvmpipe|softpipe|software rasterizer|basic render/i;
|
|
17
18
|
let HelixAura = class HelixAura extends HelixElement {
|
|
18
19
|
constructor() {
|
|
19
20
|
super(...arguments);
|
|
@@ -208,11 +209,27 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
208
209
|
this.open();
|
|
209
210
|
}
|
|
210
211
|
}
|
|
212
|
+
static drawsInSoftware() {
|
|
213
|
+
const probe = document.createElement('canvas');
|
|
214
|
+
probe.width = 1;
|
|
215
|
+
probe.height = 1;
|
|
216
|
+
const gl = probe.getContext('webgl2', HelixAura_1.CONTEXT);
|
|
217
|
+
if (!gl) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const debug = gl.getExtension('WEBGL_debug_renderer_info');
|
|
221
|
+
const name = debug ? gl.getParameter(debug.UNMASKED_RENDERER_WEBGL) : null;
|
|
222
|
+
gl.getExtension('WEBGL_lose_context')?.loseContext();
|
|
223
|
+
return typeof name === 'string' && SOFTWARE_RENDERER.test(name);
|
|
224
|
+
}
|
|
211
225
|
build() {
|
|
212
226
|
let canvas = this.renderRoot.querySelector('canvas');
|
|
213
227
|
if (!canvas) {
|
|
214
228
|
return false;
|
|
215
229
|
}
|
|
230
|
+
if (HelixAura_1.drawsInSoftware()) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
216
233
|
let gl = canvas.getContext('webgl2', HelixAura_1.CONTEXT);
|
|
217
234
|
if (gl?.isContextLost()) {
|
|
218
235
|
const fresh = document.createElement('canvas');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusion.dev/helix",
|
|
3
3
|
"description": "Styled component tier. Extends the headless tier with token-driven presentation.",
|
|
4
|
-
"version": "0.33.
|
|
4
|
+
"version": "0.33.4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ross Sheppard <ross.sheppard@fusion.dev>",
|
|
7
7
|
"repository": {
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
},
|
|
48
48
|
"bugs": "https://github.com/project-fusion/fusion/issues",
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@fusion.dev/helix-core": "0.33.
|
|
51
|
-
"@fusion.dev/helix-headless": "0.33.
|
|
52
|
-
"@fusion.dev/helix-styles": "0.33.
|
|
50
|
+
"@fusion.dev/helix-core": "0.33.4",
|
|
51
|
+
"@fusion.dev/helix-headless": "0.33.4",
|
|
52
|
+
"@fusion.dev/helix-styles": "0.33.4"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
56
|
-
"@fusion.dev/helix-cem": "0.33.
|
|
57
|
-
"@fusion.dev/helix-testing": "0.33.
|
|
56
|
+
"@fusion.dev/helix-cem": "0.33.4",
|
|
57
|
+
"@fusion.dev/helix-testing": "0.33.4",
|
|
58
58
|
"@fusion.dev/scripts": "0.0.0-dev",
|
|
59
59
|
"@happy-dom/global-registrator": "^20.0.0",
|
|
60
60
|
"@lit-labs/ssr": "^4.1.0",
|
package/types/jsx.d.ts
CHANGED
|
@@ -20395,6 +20395,11 @@ export type CustomElements = {
|
|
|
20395
20395
|
* context the browser takes back, on a GPU reset or past its cap of about
|
|
20396
20396
|
* sixteen, stops it too, and the gradient shows until the context returns.
|
|
20397
20397
|
*
|
|
20398
|
+
* **It declines a renderer that draws on the CPU**, which is a headless browser
|
|
20399
|
+
* and a machine with no usable GPU, and shows the gradient instead. The shader
|
|
20400
|
+
* costs about 450ms of first frame per element there against 2ms on a GPU, for
|
|
20401
|
+
* a field drawn from the same tokens the gradient already ramps.
|
|
20402
|
+
*
|
|
20398
20403
|
* Styled only, and with behavior, which the tier split does not otherwise
|
|
20399
20404
|
* allow: the behavior is drawing to a canvas, so there is no structure a
|
|
20400
20405
|
* consumer could restyle and nothing for a headless twin to hold.
|
|
@@ -39175,6 +39180,11 @@ export type CustomElementsSolidJs = {
|
|
|
39175
39180
|
* context the browser takes back, on a GPU reset or past its cap of about
|
|
39176
39181
|
* sixteen, stops it too, and the gradient shows until the context returns.
|
|
39177
39182
|
*
|
|
39183
|
+
* **It declines a renderer that draws on the CPU**, which is a headless browser
|
|
39184
|
+
* and a machine with no usable GPU, and shows the gradient instead. The shader
|
|
39185
|
+
* costs about 450ms of first frame per element there against 2ms on a GPU, for
|
|
39186
|
+
* a field drawn from the same tokens the gradient already ramps.
|
|
39187
|
+
*
|
|
39178
39188
|
* Styled only, and with behavior, which the tier split does not otherwise
|
|
39179
39189
|
* allow: the behavior is drawing to a canvas, so there is no structure a
|
|
39180
39190
|
* consumer could restyle and nothing for a headless twin to hold.
|
package/types/vue.d.ts
CHANGED
|
@@ -11197,7 +11197,7 @@ export interface WcToolkitVueGlobalComponents {
|
|
|
11197
11197
|
HelixAreaChartVueProps,
|
|
11198
11198
|
HelixAreaChartVueEvents
|
|
11199
11199
|
>;
|
|
11200
|
-
/** One fragment shader over a full-screen triangle, with the palette read from the tokens rather than given as colors. A background written with hex values is an asset that has to be re-cut when the brand moves; one written against '--helix-color-primary' is a consequence of it. **Every shape ramps through four token stops**, and the token layer inverts each: the page's background, 'primary-subtle', 'primary', then a peak. The peak is the foreground on a dark page and 'primary-active' on a light one, because mixing a saturated hue toward near-black takes the chroma out of the pixels carrying most of it. Which way the page runs is read off the ground's own luminance rather than asked of the mode, which is a private token. **No dependency, and no scene graph.** The geometry is three vertices derived from 'gl_VertexID', so there is no attribute, no buffer and no upload. **It stops when nobody is looking**, off screen and in a background tab, and 'prefers-reduced-motion' gets one still frame rather than an empty box. A context the browser takes back, on a GPU reset or past its cap of about sixteen, stops it too, and the gradient shows until the context returns. Styled only, and with behavior, which the tier split does not otherwise allow: the behavior is drawing to a canvas, so there is no structure a consumer could restyle and nothing for a headless twin to hold. */
|
|
11200
|
+
/** One fragment shader over a full-screen triangle, with the palette read from the tokens rather than given as colors. A background written with hex values is an asset that has to be re-cut when the brand moves; one written against '--helix-color-primary' is a consequence of it. **Every shape ramps through four token stops**, and the token layer inverts each: the page's background, 'primary-subtle', 'primary', then a peak. The peak is the foreground on a dark page and 'primary-active' on a light one, because mixing a saturated hue toward near-black takes the chroma out of the pixels carrying most of it. Which way the page runs is read off the ground's own luminance rather than asked of the mode, which is a private token. **No dependency, and no scene graph.** The geometry is three vertices derived from 'gl_VertexID', so there is no attribute, no buffer and no upload. **It stops when nobody is looking**, off screen and in a background tab, and 'prefers-reduced-motion' gets one still frame rather than an empty box. A context the browser takes back, on a GPU reset or past its cap of about sixteen, stops it too, and the gradient shows until the context returns. **It declines a renderer that draws on the CPU**, which is a headless browser and a machine with no usable GPU, and shows the gradient instead. The shader costs about 450ms of first frame per element there against 2ms on a GPU, for a field drawn from the same tokens the gradient already ramps. Styled only, and with behavior, which the tier split does not otherwise allow: the behavior is drawing to a canvas, so there is no structure a consumer could restyle and nothing for a headless twin to hold. */
|
|
11201
11201
|
"helix-aura": DefineCustomElement<
|
|
11202
11202
|
HelixAura,
|
|
11203
11203
|
HelixAuraVueProps,
|