@wildo-ai/presets-components-3d 1.1.6
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/LICENSE +34 -0
- package/dist/esm/frame/frame-3d-style.d.ts +156 -0
- package/dist/esm/frame/frame-3d-style.d.ts.map +1 -0
- package/dist/esm/frame/frame-3d-style.js +229 -0
- package/dist/esm/frame/frame-3d-style.js.map +1 -0
- package/dist/esm/frame/frame-appearance.d.ts +99 -0
- package/dist/esm/frame/frame-appearance.d.ts.map +1 -0
- package/dist/esm/frame/frame-appearance.js +207 -0
- package/dist/esm/frame/frame-appearance.js.map +1 -0
- package/dist/esm/frame/frame-canvas.d.ts +63 -0
- package/dist/esm/frame/frame-canvas.d.ts.map +1 -0
- package/dist/esm/frame/frame-canvas.js +134 -0
- package/dist/esm/frame/frame-canvas.js.map +1 -0
- package/dist/esm/frame/frame-canvas.lazy.d.ts +21 -0
- package/dist/esm/frame/frame-canvas.lazy.d.ts.map +1 -0
- package/dist/esm/frame/frame-canvas.lazy.js +31 -0
- package/dist/esm/frame/frame-canvas.lazy.js.map +1 -0
- package/dist/esm/frame/frame-color.d.ts +24 -0
- package/dist/esm/frame/frame-color.d.ts.map +1 -0
- package/dist/esm/frame/frame-color.js +52 -0
- package/dist/esm/frame/frame-color.js.map +1 -0
- package/dist/esm/frame/frame-driver.d.ts +60 -0
- package/dist/esm/frame/frame-driver.d.ts.map +1 -0
- package/dist/esm/frame/frame-driver.js +207 -0
- package/dist/esm/frame/frame-driver.js.map +1 -0
- package/dist/esm/frame/frame-renderer.d.ts +141 -0
- package/dist/esm/frame/frame-renderer.d.ts.map +1 -0
- package/dist/esm/frame/frame-renderer.js +635 -0
- package/dist/esm/frame/frame-renderer.js.map +1 -0
- package/dist/esm/frame/frame-shaders.d.ts +30 -0
- package/dist/esm/frame/frame-shaders.d.ts.map +1 -0
- package/dist/esm/frame/frame-shaders.js +97 -0
- package/dist/esm/frame/frame-shaders.js.map +1 -0
- package/dist/esm/frame/use-frame-appearance.d.ts +17 -0
- package/dist/esm/frame/use-frame-appearance.d.ts.map +1 -0
- package/dist/esm/frame/use-frame-appearance.js +38 -0
- package/dist/esm/frame/use-frame-appearance.js.map +1 -0
- package/dist/esm/index.d.ts +14 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/layout/header-first-shell-container.d.ts +22 -0
- package/dist/esm/layout/header-first-shell-container.d.ts.map +1 -0
- package/dist/esm/layout/header-first-shell-container.js +31 -0
- package/dist/esm/layout/header-first-shell-container.js.map +1 -0
- package/dist/esm/layout/layout-3d-definition.d.ts +54 -0
- package/dist/esm/layout/layout-3d-definition.d.ts.map +1 -0
- package/dist/esm/layout/layout-3d-definition.js +106 -0
- package/dist/esm/layout/layout-3d-definition.js.map +1 -0
- package/dist/esm/layout/layout-3d-transition-choreography.d.ts +30 -0
- package/dist/esm/layout/layout-3d-transition-choreography.d.ts.map +1 -0
- package/dist/esm/layout/layout-3d-transition-choreography.js +43 -0
- package/dist/esm/layout/layout-3d-transition-choreography.js.map +1 -0
- package/dist/esm/layout/layout-3d-vocabulary.d.ts +118 -0
- package/dist/esm/layout/layout-3d-vocabulary.d.ts.map +1 -0
- package/dist/esm/layout/layout-3d-vocabulary.js +104 -0
- package/dist/esm/layout/layout-3d-vocabulary.js.map +1 -0
- package/dist/esm/layout/shell-layout-placement.d.ts +26 -0
- package/dist/esm/layout/shell-layout-placement.d.ts.map +1 -0
- package/dist/esm/layout/shell-layout-placement.js +18 -0
- package/dist/esm/layout/shell-layout-placement.js.map +1 -0
- package/dist/esm/layout-3d-main-container.d.ts +73 -0
- package/dist/esm/layout-3d-main-container.d.ts.map +1 -0
- package/dist/esm/layout-3d-main-container.js +124 -0
- package/dist/esm/layout-3d-main-container.js.map +1 -0
- package/dist/esm/register-3d-layout.d.ts +40 -0
- package/dist/esm/register-3d-layout.d.ts.map +1 -0
- package/dist/esm/register-3d-layout.js +72 -0
- package/dist/esm/register-3d-layout.js.map +1 -0
- package/dist/esm/shell-region-measurement.d.ts +75 -0
- package/dist/esm/shell-region-measurement.d.ts.map +1 -0
- package/dist/esm/shell-region-measurement.js +75 -0
- package/dist/esm/shell-region-measurement.js.map +1 -0
- package/dist/esm/spatial/spatial-camera.d.ts +106 -0
- package/dist/esm/spatial/spatial-camera.d.ts.map +1 -0
- package/dist/esm/spatial/spatial-camera.js +151 -0
- package/dist/esm/spatial/spatial-camera.js.map +1 -0
- package/dist/esm/spatial/spatial-environment.d.ts +6 -0
- package/dist/esm/spatial/spatial-environment.d.ts.map +1 -0
- package/dist/esm/spatial/spatial-environment.js +166 -0
- package/dist/esm/spatial/spatial-environment.js.map +1 -0
- package/dist/esm/spatial/spatial-environment.lazy.d.ts +10 -0
- package/dist/esm/spatial/spatial-environment.lazy.d.ts.map +1 -0
- package/dist/esm/spatial/spatial-environment.lazy.js +13 -0
- package/dist/esm/spatial/spatial-environment.lazy.js.map +1 -0
- package/dist/esm/spatial/spatial-shell-container.d.ts +21 -0
- package/dist/esm/spatial/spatial-shell-container.d.ts.map +1 -0
- package/dist/esm/spatial/spatial-shell-container.js +231 -0
- package/dist/esm/spatial/spatial-shell-container.js.map +1 -0
- package/dist/esm/spatial/spatial-stage-store.d.ts +27 -0
- package/dist/esm/spatial/spatial-stage-store.d.ts.map +1 -0
- package/dist/esm/spatial/spatial-stage-store.js +33 -0
- package/dist/esm/spatial/spatial-stage-store.js.map +1 -0
- package/dist/esm/tokens/design-token-resolution.d.ts +110 -0
- package/dist/esm/tokens/design-token-resolution.d.ts.map +1 -0
- package/dist/esm/tokens/design-token-resolution.js +410 -0
- package/dist/esm/tokens/design-token-resolution.js.map +1 -0
- package/dist/esm/tokens/use-design-token-resolution.d.ts +22 -0
- package/dist/esm/tokens/use-design-token-resolution.d.ts.map +1 -0
- package/dist/esm/tokens/use-design-token-resolution.js +49 -0
- package/dist/esm/tokens/use-design-token-resolution.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { converter, parse } from 'culori';
|
|
2
|
+
import { DesignSystemToken, MaterialKind, ShellRegion } from '@wildo-ai/presets-components-models';
|
|
3
|
+
import { Frame3DSurfaceKind } from './frame-3d-style.js';
|
|
4
|
+
import { BLACK, WHITE, composite, contrastRatio, encodeSrgb, gradientPoints, mix, pushUntil, quantize } from './frame-color.js';
|
|
5
|
+
import { ResolvedDesignTokenKind } from '../tokens/design-token-resolution.js';
|
|
6
|
+
/**
|
|
7
|
+
* The lowest contrast any colour the frame can put behind text may have against `--foreground`: WCAG 2.x's
|
|
8
|
+
* AA minimum for normal text.
|
|
9
|
+
* spec-cite: WCAG 2.2 SC 1.4.3 Contrast (Minimum) — https://www.w3.org/TR/WCAG22/#contrast-minimum
|
|
10
|
+
*/
|
|
11
|
+
export const FRAME_MINIMUM_CONTRAST = 4.5;
|
|
12
|
+
/**
|
|
13
|
+
* What the derivation actually aims for: the minimum plus a margin for a GPU that TRUNCATES rather than rounds
|
|
14
|
+
* when it writes a blended value to the 8-bit framebuffer. The model already rounds every layer it writes
|
|
15
|
+
* ({@link quantize}); truncation moves a channel by at most one more level, which the margin covers.
|
|
16
|
+
*/
|
|
17
|
+
const FRAME_CONTRAST_TARGET = FRAME_MINIMUM_CONTRAST + 0.05;
|
|
18
|
+
const LINEAR_EASING = [0, 0, 1, 1];
|
|
19
|
+
const DURATION_TOKENS = [
|
|
20
|
+
DesignSystemToken.MOTION_DURATION_INSTANT,
|
|
21
|
+
DesignSystemToken.MOTION_DURATION_FAST,
|
|
22
|
+
DesignSystemToken.MOTION_DURATION_NORMAL,
|
|
23
|
+
DesignSystemToken.MOTION_DURATION_SLOW,
|
|
24
|
+
DesignSystemToken.MOTION_DURATION_SLOWER,
|
|
25
|
+
];
|
|
26
|
+
const toSrgb = converter('rgb');
|
|
27
|
+
/**
|
|
28
|
+
* Derive the frame's appearance from the active theme, or `null` when the foreground did not resolve — without
|
|
29
|
+
* it no colour can be shown to be readable, and a frame that cannot be shown readable is not drawn.
|
|
30
|
+
*
|
|
31
|
+
* **The contrast bound** is part of the derivation, not a check after it. Every colour text can sit on is
|
|
32
|
+
* composited the way the framebuffer blends — in sRGB-encoded space, each written layer rounded to 8 bits — and
|
|
33
|
+
* must reach the target against `--foreground`:
|
|
34
|
+
* - the background, at several points along its gradient;
|
|
35
|
+
* - the background under a shadow (shadows combine by MAXIMUM in the renderer, never by stacking, so one
|
|
36
|
+
* shadow at its full opacity is the darkest a point can be);
|
|
37
|
+
* - the frost over either, along its own gradient;
|
|
38
|
+
* - the rim at its HOVER opacity over the frost.
|
|
39
|
+
*
|
|
40
|
+
* A style value that would break it is WEAKENED — a colour moved toward the pole with the most contrast, a
|
|
41
|
+
* shadow or rim made fainter — and the bound is never relaxed. A style is a preference; the bound is a guarantee.
|
|
42
|
+
*/
|
|
43
|
+
export function deriveFrameAppearance(inputs) {
|
|
44
|
+
const colorOf = (token) => tokenColor(inputs.resolutions.get(token));
|
|
45
|
+
const foreground = colorOf(DesignSystemToken.FOREGROUND);
|
|
46
|
+
if (!foreground)
|
|
47
|
+
return null;
|
|
48
|
+
// The pole is black or white, whichever contrasts more with the foreground; bounded colours move toward it.
|
|
49
|
+
const pole = contrastRatio(foreground, BLACK) >= contrastRatio(foreground, WHITE) ? BLACK : WHITE;
|
|
50
|
+
const readable = (color) => contrastRatio(quantize(color), foreground) >= FRAME_CONTRAST_TARGET;
|
|
51
|
+
const glass = inputs.navigationMaterial?.kind === MaterialKind.GLASS ? inputs.navigationMaterial : undefined;
|
|
52
|
+
// A material's light angle is measured against the DOM's y-DOWN axis (the engine's edge response reads it
|
|
53
|
+
// against image-space normals); the frame's is y-UP, so the vertical component flips sign.
|
|
54
|
+
const lightAngleDeg = glass ? -glass.optics.lightAngleDeg : inputs.style.lightAngleDeg;
|
|
55
|
+
// ── Background ── (nothing is drawn, not even a slab, when its base colour does not resolve: frost over an
|
|
56
|
+
// unknown base cannot be bounded.)
|
|
57
|
+
const base = colorOf(inputs.style.background.baseToken);
|
|
58
|
+
const glowColor = colorOf(inputs.style.background.glowToken) ?? base;
|
|
59
|
+
let background = null;
|
|
60
|
+
if (base && glowColor) {
|
|
61
|
+
const from = pushUntil(base, pole, readable);
|
|
62
|
+
const to = pushUntil(mix(base, glowColor, clamp01(inputs.style.background.glow)), pole, (c) => gradientPoints(from, c).every(readable));
|
|
63
|
+
background = { from, to };
|
|
64
|
+
}
|
|
65
|
+
const backgroundStops = background ? gradientPoints(background.from, background.to).map(quantize) : [];
|
|
66
|
+
const light = colorOf(inputs.style.lightToken) ?? WHITE;
|
|
67
|
+
const radiusPx = sizeOf(inputs.resolutions.get(DesignSystemToken.RADIUS)) ?? 0;
|
|
68
|
+
const borderWidthPx = sizeOf(inputs.resolutions.get(DesignSystemToken.BORDER_WIDTH)) ?? 1;
|
|
69
|
+
const tintOf = (style) => (style.tintColor !== undefined ? cssColor(style.tintColor) : colorOf(style.tintToken));
|
|
70
|
+
const drawn = (style) => style.surface !== Frame3DSurfaceKind.NONE && tintOf(style) !== null && backgroundStops.length > 0;
|
|
71
|
+
// ── Pass 1: shadows ── Each is black, made fainter until the background under it stays readable. A shadow
|
|
72
|
+
// spreads beyond its own slab — under a NEIGHBOUR's slab too — and the renderer combines overlapping shadows by
|
|
73
|
+
// their maximum, so every slab's frost must be readable over the background under the STRONGEST shadow of any
|
|
74
|
+
// slab, not only under its own.
|
|
75
|
+
const shadowAlphaOf = (style) => {
|
|
76
|
+
let alpha = clamp01(style.shadow.opacity);
|
|
77
|
+
while (alpha > 0 && !backgroundStops.every((stop) => readable(composite(BLACK, alpha, stop))))
|
|
78
|
+
alpha = Math.max(0, alpha - 0.02);
|
|
79
|
+
return alpha;
|
|
80
|
+
};
|
|
81
|
+
const shadowAlphas = new Map();
|
|
82
|
+
for (const region of Object.values(ShellRegion)) {
|
|
83
|
+
const style = inputs.style.regions[region];
|
|
84
|
+
shadowAlphas.set(region, drawn(style) ? shadowAlphaOf(style) : 0);
|
|
85
|
+
}
|
|
86
|
+
const strongestShadow = Math.max(0, ...shadowAlphas.values());
|
|
87
|
+
const underlays = [...backgroundStops, ...backgroundStops.map((stop) => quantize(composite(BLACK, strongestShadow, stop)))];
|
|
88
|
+
// ── Pass 2: every slab's frost and rim, bounded over all of those underlays ──
|
|
89
|
+
const slab = (region, style) => {
|
|
90
|
+
const tint = tintOf(style);
|
|
91
|
+
if (!drawn(style) || !tint)
|
|
92
|
+
return invisibleSlab();
|
|
93
|
+
const shadowAlpha = shadowAlphas.get(region) ?? 0;
|
|
94
|
+
// Frost: the lit side moves toward the theme's light colour; in dark mode by the style's dark-mode share.
|
|
95
|
+
const fillAlpha = clamp01((glass ? glass.optics.tintOpacity : style.frost) * tintAlpha(style, inputs.resolutions));
|
|
96
|
+
const litAmount = clamp01(style.gradient) * (inputs.isDark ? clamp01(inputs.style.darkModeLightStrength) : 1);
|
|
97
|
+
const frostReadable = (fill) => underlays.every((under) => readable(composite(fill, fillAlpha, under)));
|
|
98
|
+
const fillFrom = pushUntil(tint, pole, frostReadable);
|
|
99
|
+
const fillTo = pushUntil(mix(fillFrom, light, litAmount), pole, (c) => gradientPoints(fillFrom, c).every(frostReadable));
|
|
100
|
+
const frostUnderlays = gradientPoints(fillFrom, fillTo).flatMap((fill) => underlays.map((under) => ({ fill, under })));
|
|
101
|
+
// Rim: at its HOVER opacity, over every frost point — one layer with the frost, as the shader writes it —
|
|
102
|
+
// made fainter (rest and hover together) until readable.
|
|
103
|
+
const rim = colorOf(style.rim.token) ?? foreground;
|
|
104
|
+
// A glass material states its rest highlight; the hover emphasis keeps the style's own hover-to-rest ratio.
|
|
105
|
+
const styleRatio = style.rim.opacity > 0 ? style.rim.hoverOpacity / style.rim.opacity : 1;
|
|
106
|
+
const rimRest = clamp01(glass ? glass.optics.edgeHighlightOpacity : style.rim.opacity);
|
|
107
|
+
const rimHover = clamp01(glass ? glass.optics.edgeHighlightOpacity * styleRatio : style.rim.hoverOpacity);
|
|
108
|
+
let rimScale = 1;
|
|
109
|
+
const rimReadable = (scale) => frostUnderlays.every(({ fill, under }) => readable(composite(rim, rimHover * scale, composite(fill, fillAlpha, under))));
|
|
110
|
+
while (rimScale > 0 && !rimReadable(rimScale))
|
|
111
|
+
rimScale = Math.max(0, rimScale - 0.02);
|
|
112
|
+
return {
|
|
113
|
+
visible: true,
|
|
114
|
+
fillFrom,
|
|
115
|
+
fillTo,
|
|
116
|
+
fillAlpha,
|
|
117
|
+
rim,
|
|
118
|
+
rimAlpha: rimRest * rimScale,
|
|
119
|
+
rimHoverAlpha: rimHover * rimScale,
|
|
120
|
+
rimWidthPx: glass ? glass.optics.rimWidthDevicePx / Math.max(1, inputs.devicePixelRatio) : borderWidthPx * Math.max(0, style.rim.widthScale),
|
|
121
|
+
radiusPx: radiusPx * Math.max(0, style.radiusScale),
|
|
122
|
+
shadowOffsetXPx: style.shadow.offsetXPx,
|
|
123
|
+
shadowOffsetYPx: style.shadow.offsetYPx,
|
|
124
|
+
shadowBlurPx: Math.max(0, style.shadow.blurPx),
|
|
125
|
+
shadowAlpha,
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
const regions = {};
|
|
129
|
+
for (const region of Object.values(ShellRegion))
|
|
130
|
+
regions[region] = slab(region, inputs.style.regions[region]);
|
|
131
|
+
const reduced = inputs.prefersReducedMotion;
|
|
132
|
+
const slowestSeconds = Math.max(0, ...DURATION_TOKENS.map((token) => secondsOf(inputs.resolutions.get(token))));
|
|
133
|
+
return {
|
|
134
|
+
foreground,
|
|
135
|
+
background,
|
|
136
|
+
lightAngleDeg,
|
|
137
|
+
regions,
|
|
138
|
+
transitionSeconds: reduced ? 0 : secondsOf(inputs.resolutions.get(DesignSystemToken.MOTION_DURATION_NORMAL)),
|
|
139
|
+
transitionEasing: easingOf(inputs.resolutions.get(DesignSystemToken.MOTION_EASING_DEFAULT)),
|
|
140
|
+
hoverSeconds: secondsOf(inputs.resolutions.get(DesignSystemToken.MOTION_DURATION_FAST)),
|
|
141
|
+
hoverEasing: easingOf(inputs.resolutions.get(DesignSystemToken.MOTION_EASING_SPRING)),
|
|
142
|
+
hoverSpring: springOf(inputs.surfaceGestureTransition),
|
|
143
|
+
hoverResponse: !reduced,
|
|
144
|
+
transitionCeilingSeconds: slowestSeconds > 0 ? slowestSeconds * 1.5 : 1.5,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A motion.dev gesture transition, when it is a spring. motion.dev's defaults fill what it omits: stiffness 100,
|
|
149
|
+
* damping 10, mass 1. Anything that is not `type: 'spring'` (a tween) leaves the hover to the motion tokens.
|
|
150
|
+
*/
|
|
151
|
+
function springOf(transition) {
|
|
152
|
+
if (!transition || transition.type !== 'spring')
|
|
153
|
+
return null;
|
|
154
|
+
const number = (key, fallback) => (typeof transition[key] === 'number' && Number.isFinite(transition[key]) ? transition[key] : fallback);
|
|
155
|
+
const spring = { stiffness: number('stiffness', 100), damping: number('damping', 10), mass: number('mass', 1) };
|
|
156
|
+
return spring.stiffness > 0 && spring.mass > 0 && spring.damping >= 0 ? spring : null;
|
|
157
|
+
}
|
|
158
|
+
function clamp01(value) {
|
|
159
|
+
return Math.min(1, Math.max(0, value));
|
|
160
|
+
}
|
|
161
|
+
function tokenColor(resolution) {
|
|
162
|
+
if (!resolution || !('resolved' in resolution) || resolution.resolved.kind !== ResolvedDesignTokenKind.COLOR)
|
|
163
|
+
return null;
|
|
164
|
+
const [r, g, b] = resolution.resolved.linearRgb;
|
|
165
|
+
return [encodeSrgb(r), encodeSrgb(g), encodeSrgb(b)];
|
|
166
|
+
}
|
|
167
|
+
/** A token colour's own alpha multiplies the frost: a translucent token is a lighter tint, not an opaque one. */
|
|
168
|
+
function tintAlpha(style, resolutions) {
|
|
169
|
+
if (style.tintColor !== undefined)
|
|
170
|
+
return parse(style.tintColor)?.alpha ?? 1;
|
|
171
|
+
const resolution = resolutions.get(style.tintToken);
|
|
172
|
+
return resolution && 'resolved' in resolution && resolution.resolved.kind === ResolvedDesignTokenKind.COLOR ? resolution.resolved.alpha : 1;
|
|
173
|
+
}
|
|
174
|
+
function cssColor(value) {
|
|
175
|
+
const parsed = parse(value);
|
|
176
|
+
if (!parsed)
|
|
177
|
+
return null;
|
|
178
|
+
const rgb = toSrgb(parsed);
|
|
179
|
+
return [clamp01(Number(rgb.r ?? 0)), clamp01(Number(rgb.g ?? 0)), clamp01(Number(rgb.b ?? 0))];
|
|
180
|
+
}
|
|
181
|
+
function sizeOf(resolution) {
|
|
182
|
+
return resolution && 'resolved' in resolution && resolution.resolved.kind === ResolvedDesignTokenKind.SIZE ? resolution.resolved.px : null;
|
|
183
|
+
}
|
|
184
|
+
function secondsOf(resolution) {
|
|
185
|
+
return resolution && 'resolved' in resolution && resolution.resolved.kind === ResolvedDesignTokenKind.DURATION ? Math.max(0, resolution.resolved.seconds) : 0;
|
|
186
|
+
}
|
|
187
|
+
function easingOf(resolution) {
|
|
188
|
+
return resolution && 'resolved' in resolution && resolution.resolved.kind === ResolvedDesignTokenKind.EASING ? resolution.resolved.controlPoints : LINEAR_EASING;
|
|
189
|
+
}
|
|
190
|
+
function invisibleSlab() {
|
|
191
|
+
return {
|
|
192
|
+
visible: false,
|
|
193
|
+
fillFrom: BLACK,
|
|
194
|
+
fillTo: BLACK,
|
|
195
|
+
fillAlpha: 0,
|
|
196
|
+
rim: BLACK,
|
|
197
|
+
rimAlpha: 0,
|
|
198
|
+
rimHoverAlpha: 0,
|
|
199
|
+
rimWidthPx: 0,
|
|
200
|
+
radiusPx: 0,
|
|
201
|
+
shadowOffsetXPx: 0,
|
|
202
|
+
shadowOffsetYPx: 0,
|
|
203
|
+
shadowBlurPx: 0,
|
|
204
|
+
shadowAlpha: 0,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=frame-appearance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-appearance.js","sourceRoot":"","sources":["../../../../src/frame/frame-appearance.ts"],"names":[],"mappings":"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","sourcesContent":["import { converter, parse } from 'culori';\nimport { DesignSystemToken, MaterialKind, ShellRegion, type MaterialProfile } from '@wildo-ai/presets-components-models';\n\nimport { Frame3DSurfaceKind, type Frame3DRegionStyle, type Frame3DThemeStyle } from './frame-3d-style';\nimport { BLACK, WHITE, composite, contrastRatio, encodeSrgb, gradientPoints, mix, pushUntil, quantize, type FrameColor } from './frame-color';\nimport { ResolvedDesignTokenKind, type DesignTokenResolution } from '../tokens/design-token-resolution';\n\nexport type { FrameColor } from './frame-color';\n\n/** How one region kind's slab is drawn, in frame units (CSS pixels, sRGB-encoded colours). */\nexport interface FrameSlabAppearance {\n /** False when the style draws nothing here, or a colour it needs did not resolve. */\n visible: boolean;\n /** The shaded side of the frost. */\n fillFrom: FrameColor;\n /** The lit side of the frost. */\n fillTo: FrameColor;\n fillAlpha: number;\n rim: FrameColor;\n rimAlpha: number;\n rimHoverAlpha: number;\n rimWidthPx: number;\n radiusPx: number;\n shadowOffsetXPx: number;\n shadowOffsetYPx: number;\n shadowBlurPx: number;\n shadowAlpha: number;\n}\n\n/** A cubic-Bézier easing, as its four control points. */\nexport type FrameEasing = readonly [number, number, number, number];\n\n/** A damped spring, as motion.dev's gesture `transition` declares one. */\nexport interface FrameSpring {\n stiffness: number;\n damping: number;\n mass: number;\n}\n\n/**\n * Everything the frame draws, as plain data. It is computed in the DOM tree from the theme and handed to the\n * canvas as props, so the canvas holds meshes only and reads no context (#1823).\n */\nexport interface FrameAppearance {\n /** The `--foreground` the bound was computed against. A change of it (a mode switch) is applied at once. */\n foreground: FrameColor;\n /** The in-scene background, shaded side then lit side. */\n background: { from: FrameColor; to: FrameColor } | null;\n /** The light direction in the FRAME's convention: degrees, 0 = from the right, 90 = from the top (y up). */\n lightAngleDeg: number;\n regions: Record<ShellRegion, FrameSlabAppearance>;\n /** A theme change cross-fades over this. 0 under reduced motion. */\n transitionSeconds: number;\n transitionEasing: FrameEasing;\n /** The rim's hover emphasis animates over this, when no spring is declared. */\n hoverSeconds: number;\n hoverEasing: FrameEasing;\n /** The theme's surface gesture spring: when declared, the rim's hover emphasis follows it instead. */\n hoverSpring: FrameSpring | null;\n /** False under reduced motion: the frame is static, so the rim does not respond to hover at all. */\n hoverResponse: boolean;\n /**\n * How long a shell CSS transition may run before the frame stops following it without a `transitionend`:\n * the slowest motion-duration token, with half again as margin. The shell's own transitions use those tokens.\n */\n transitionCeilingSeconds: number;\n}\n\nexport interface FrameAppearanceInputs {\n resolutions: ReadonlyMap<DesignSystemToken, DesignTokenResolution>;\n style: Frame3DThemeStyle;\n /** The theme's navigation material (`MaterialProfileRole.GLASS_NAVIGATION`), when it has one. */\n navigationMaterial: MaterialProfile | undefined;\n /** The theme's surface gesture `transition` (motion.dev), when its surface recipe declares one. */\n surfaceGestureTransition: Readonly<Record<string, string | number>> | undefined;\n isDark: boolean;\n prefersReducedMotion: boolean;\n /** Converts the material's device-pixel rim width into CSS pixels. */\n devicePixelRatio: number;\n}\n\n/**\n * The lowest contrast any colour the frame can put behind text may have against `--foreground`: WCAG 2.x's\n * AA minimum for normal text.\n * spec-cite: WCAG 2.2 SC 1.4.3 Contrast (Minimum) — https://www.w3.org/TR/WCAG22/#contrast-minimum\n */\nexport const FRAME_MINIMUM_CONTRAST = 4.5;\n\n/**\n * What the derivation actually aims for: the minimum plus a margin for a GPU that TRUNCATES rather than rounds\n * when it writes a blended value to the 8-bit framebuffer. The model already rounds every layer it writes\n * ({@link quantize}); truncation moves a channel by at most one more level, which the margin covers.\n */\nconst FRAME_CONTRAST_TARGET = FRAME_MINIMUM_CONTRAST + 0.05;\n\nconst LINEAR_EASING: FrameEasing = [0, 0, 1, 1];\nconst DURATION_TOKENS = [\n DesignSystemToken.MOTION_DURATION_INSTANT,\n DesignSystemToken.MOTION_DURATION_FAST,\n DesignSystemToken.MOTION_DURATION_NORMAL,\n DesignSystemToken.MOTION_DURATION_SLOW,\n DesignSystemToken.MOTION_DURATION_SLOWER,\n];\nconst toSrgb = converter('rgb');\n\n/**\n * Derive the frame's appearance from the active theme, or `null` when the foreground did not resolve — without\n * it no colour can be shown to be readable, and a frame that cannot be shown readable is not drawn.\n *\n * **The contrast bound** is part of the derivation, not a check after it. Every colour text can sit on is\n * composited the way the framebuffer blends — in sRGB-encoded space, each written layer rounded to 8 bits — and\n * must reach the target against `--foreground`:\n * - the background, at several points along its gradient;\n * - the background under a shadow (shadows combine by MAXIMUM in the renderer, never by stacking, so one\n * shadow at its full opacity is the darkest a point can be);\n * - the frost over either, along its own gradient;\n * - the rim at its HOVER opacity over the frost.\n *\n * A style value that would break it is WEAKENED — a colour moved toward the pole with the most contrast, a\n * shadow or rim made fainter — and the bound is never relaxed. A style is a preference; the bound is a guarantee.\n */\nexport function deriveFrameAppearance(inputs: FrameAppearanceInputs): FrameAppearance | null {\n const colorOf = (token: DesignSystemToken): FrameColor | null => tokenColor(inputs.resolutions.get(token));\n const foreground = colorOf(DesignSystemToken.FOREGROUND);\n if (!foreground) return null;\n // The pole is black or white, whichever contrasts more with the foreground; bounded colours move toward it.\n const pole = contrastRatio(foreground, BLACK) >= contrastRatio(foreground, WHITE) ? BLACK : WHITE;\n const readable = (color: FrameColor): boolean => contrastRatio(quantize(color), foreground) >= FRAME_CONTRAST_TARGET;\n\n const glass = inputs.navigationMaterial?.kind === MaterialKind.GLASS ? inputs.navigationMaterial : undefined;\n // A material's light angle is measured against the DOM's y-DOWN axis (the engine's edge response reads it\n // against image-space normals); the frame's is y-UP, so the vertical component flips sign.\n const lightAngleDeg = glass ? -glass.optics.lightAngleDeg : inputs.style.lightAngleDeg;\n\n // ── Background ── (nothing is drawn, not even a slab, when its base colour does not resolve: frost over an\n // unknown base cannot be bounded.)\n const base = colorOf(inputs.style.background.baseToken);\n const glowColor = colorOf(inputs.style.background.glowToken) ?? base;\n let background: { from: FrameColor; to: FrameColor } | null = null;\n if (base && glowColor) {\n const from = pushUntil(base, pole, readable);\n const to = pushUntil(mix(base, glowColor, clamp01(inputs.style.background.glow)), pole, (c) => gradientPoints(from, c).every(readable));\n background = { from, to };\n }\n const backgroundStops = background ? gradientPoints(background.from, background.to).map(quantize) : [];\n const light = colorOf(inputs.style.lightToken) ?? WHITE;\n\n const radiusPx = sizeOf(inputs.resolutions.get(DesignSystemToken.RADIUS)) ?? 0;\n const borderWidthPx = sizeOf(inputs.resolutions.get(DesignSystemToken.BORDER_WIDTH)) ?? 1;\n\n const tintOf = (style: Frame3DRegionStyle): FrameColor | null => (style.tintColor !== undefined ? cssColor(style.tintColor) : colorOf(style.tintToken));\n const drawn = (style: Frame3DRegionStyle): boolean => style.surface !== Frame3DSurfaceKind.NONE && tintOf(style) !== null && backgroundStops.length > 0;\n\n // ── Pass 1: shadows ── Each is black, made fainter until the background under it stays readable. A shadow\n // spreads beyond its own slab — under a NEIGHBOUR's slab too — and the renderer combines overlapping shadows by\n // their maximum, so every slab's frost must be readable over the background under the STRONGEST shadow of any\n // slab, not only under its own.\n const shadowAlphaOf = (style: Frame3DRegionStyle): number => {\n let alpha = clamp01(style.shadow.opacity);\n while (alpha > 0 && !backgroundStops.every((stop) => readable(composite(BLACK, alpha, stop)))) alpha = Math.max(0, alpha - 0.02);\n return alpha;\n };\n const shadowAlphas = new Map<ShellRegion, number>();\n for (const region of Object.values(ShellRegion)) {\n const style = inputs.style.regions[region];\n shadowAlphas.set(region, drawn(style) ? shadowAlphaOf(style) : 0);\n }\n const strongestShadow = Math.max(0, ...shadowAlphas.values());\n const underlays = [...backgroundStops, ...backgroundStops.map((stop) => quantize(composite(BLACK, strongestShadow, stop)))];\n\n // ── Pass 2: every slab's frost and rim, bounded over all of those underlays ──\n const slab = (region: ShellRegion, style: Frame3DRegionStyle): FrameSlabAppearance => {\n const tint = tintOf(style);\n if (!drawn(style) || !tint) return invisibleSlab();\n const shadowAlpha = shadowAlphas.get(region) ?? 0;\n\n // Frost: the lit side moves toward the theme's light colour; in dark mode by the style's dark-mode share.\n const fillAlpha = clamp01((glass ? glass.optics.tintOpacity : style.frost) * tintAlpha(style, inputs.resolutions));\n const litAmount = clamp01(style.gradient) * (inputs.isDark ? clamp01(inputs.style.darkModeLightStrength) : 1);\n const frostReadable = (fill: FrameColor): boolean => underlays.every((under) => readable(composite(fill, fillAlpha, under)));\n const fillFrom = pushUntil(tint, pole, frostReadable);\n const fillTo = pushUntil(mix(fillFrom, light, litAmount), pole, (c) => gradientPoints(fillFrom, c).every(frostReadable));\n const frostUnderlays = gradientPoints(fillFrom, fillTo).flatMap((fill) => underlays.map((under) => ({ fill, under })));\n\n // Rim: at its HOVER opacity, over every frost point — one layer with the frost, as the shader writes it —\n // made fainter (rest and hover together) until readable.\n const rim = colorOf(style.rim.token) ?? foreground;\n // A glass material states its rest highlight; the hover emphasis keeps the style's own hover-to-rest ratio.\n const styleRatio = style.rim.opacity > 0 ? style.rim.hoverOpacity / style.rim.opacity : 1;\n const rimRest = clamp01(glass ? glass.optics.edgeHighlightOpacity : style.rim.opacity);\n const rimHover = clamp01(glass ? glass.optics.edgeHighlightOpacity * styleRatio : style.rim.hoverOpacity);\n let rimScale = 1;\n const rimReadable = (scale: number): boolean => frostUnderlays.every(({ fill, under }) => readable(composite(rim, rimHover * scale, composite(fill, fillAlpha, under))));\n while (rimScale > 0 && !rimReadable(rimScale)) rimScale = Math.max(0, rimScale - 0.02);\n\n return {\n visible: true,\n fillFrom,\n fillTo,\n fillAlpha,\n rim,\n rimAlpha: rimRest * rimScale,\n rimHoverAlpha: rimHover * rimScale,\n rimWidthPx: glass ? glass.optics.rimWidthDevicePx / Math.max(1, inputs.devicePixelRatio) : borderWidthPx * Math.max(0, style.rim.widthScale),\n radiusPx: radiusPx * Math.max(0, style.radiusScale),\n shadowOffsetXPx: style.shadow.offsetXPx,\n shadowOffsetYPx: style.shadow.offsetYPx,\n shadowBlurPx: Math.max(0, style.shadow.blurPx),\n shadowAlpha,\n };\n };\n\n const regions = {} as Record<ShellRegion, FrameSlabAppearance>;\n for (const region of Object.values(ShellRegion)) regions[region] = slab(region, inputs.style.regions[region]);\n\n const reduced = inputs.prefersReducedMotion;\n const slowestSeconds = Math.max(0, ...DURATION_TOKENS.map((token) => secondsOf(inputs.resolutions.get(token))));\n return {\n foreground,\n background,\n lightAngleDeg,\n regions,\n transitionSeconds: reduced ? 0 : secondsOf(inputs.resolutions.get(DesignSystemToken.MOTION_DURATION_NORMAL)),\n transitionEasing: easingOf(inputs.resolutions.get(DesignSystemToken.MOTION_EASING_DEFAULT)),\n hoverSeconds: secondsOf(inputs.resolutions.get(DesignSystemToken.MOTION_DURATION_FAST)),\n hoverEasing: easingOf(inputs.resolutions.get(DesignSystemToken.MOTION_EASING_SPRING)),\n hoverSpring: springOf(inputs.surfaceGestureTransition),\n hoverResponse: !reduced,\n transitionCeilingSeconds: slowestSeconds > 0 ? slowestSeconds * 1.5 : 1.5,\n };\n}\n\n/**\n * A motion.dev gesture transition, when it is a spring. motion.dev's defaults fill what it omits: stiffness 100,\n * damping 10, mass 1. Anything that is not `type: 'spring'` (a tween) leaves the hover to the motion tokens.\n */\nfunction springOf(transition: Readonly<Record<string, string | number>> | undefined): FrameSpring | null {\n if (!transition || transition.type !== 'spring') return null;\n const number = (key: string, fallback: number): number => (typeof transition[key] === 'number' && Number.isFinite(transition[key]) ? (transition[key] as number) : fallback);\n const spring = { stiffness: number('stiffness', 100), damping: number('damping', 10), mass: number('mass', 1) };\n return spring.stiffness > 0 && spring.mass > 0 && spring.damping >= 0 ? spring : null;\n}\n\nfunction clamp01(value: number): number {\n return Math.min(1, Math.max(0, value));\n}\n\nfunction tokenColor(resolution: DesignTokenResolution | undefined): FrameColor | null {\n if (!resolution || !('resolved' in resolution) || resolution.resolved.kind !== ResolvedDesignTokenKind.COLOR) return null;\n const [r, g, b] = resolution.resolved.linearRgb;\n return [encodeSrgb(r), encodeSrgb(g), encodeSrgb(b)];\n}\n\n/** A token colour's own alpha multiplies the frost: a translucent token is a lighter tint, not an opaque one. */\nfunction tintAlpha(style: Frame3DRegionStyle, resolutions: ReadonlyMap<DesignSystemToken, DesignTokenResolution>): number {\n if (style.tintColor !== undefined) return parse(style.tintColor)?.alpha ?? 1;\n const resolution = resolutions.get(style.tintToken);\n return resolution && 'resolved' in resolution && resolution.resolved.kind === ResolvedDesignTokenKind.COLOR ? resolution.resolved.alpha : 1;\n}\n\nfunction cssColor(value: string): FrameColor | null {\n const parsed = parse(value);\n if (!parsed) return null;\n const rgb = toSrgb(parsed);\n return [clamp01(Number(rgb.r ?? 0)), clamp01(Number(rgb.g ?? 0)), clamp01(Number(rgb.b ?? 0))];\n}\n\nfunction sizeOf(resolution: DesignTokenResolution | undefined): number | null {\n return resolution && 'resolved' in resolution && resolution.resolved.kind === ResolvedDesignTokenKind.SIZE ? resolution.resolved.px : null;\n}\n\nfunction secondsOf(resolution: DesignTokenResolution | undefined): number {\n return resolution && 'resolved' in resolution && resolution.resolved.kind === ResolvedDesignTokenKind.DURATION ? Math.max(0, resolution.resolved.seconds) : 0;\n}\n\nfunction easingOf(resolution: DesignTokenResolution | undefined): FrameEasing {\n return resolution && 'resolved' in resolution && resolution.resolved.kind === ResolvedDesignTokenKind.EASING ? resolution.resolved.controlPoints : LINEAR_EASING;\n}\n\nfunction invisibleSlab(): FrameSlabAppearance {\n return {\n visible: false,\n fillFrom: BLACK,\n fillTo: BLACK,\n fillAlpha: 0,\n rim: BLACK,\n rimAlpha: 0,\n rimHoverAlpha: 0,\n rimWidthPx: 0,\n radiusPx: 0,\n shadowOffsetXPx: 0,\n shadowOffsetYPx: 0,\n shadowBlurPx: 0,\n shadowAlpha: 0,\n };\n}\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type CanvasProps, type EventManager } from '@react-three/fiber';
|
|
3
|
+
import { type FrameTransitionTrigger } from './frame-renderer';
|
|
4
|
+
import type { FrameAppearance } from './frame-appearance';
|
|
5
|
+
import { Layout3DCamera, type Layout3DFrameChoreography, type Layout3DZoneFocusTransition } from '../layout/layout-3d-vocabulary';
|
|
6
|
+
/**
|
|
7
|
+
* Every `<Canvas>` setting the frame depends on, in one place so a test pins them.
|
|
8
|
+
*
|
|
9
|
+
* - `flat`: no tone mapping. The shaders write sRGB-encoded colours as given; tone mapping would move every
|
|
10
|
+
* colour after the contrast bound was checked.
|
|
11
|
+
* - `frameloop: 'never'`: fiber renders nothing on its own. The scene renders when a region moved, when the
|
|
12
|
+
* appearance changed, and per animation frame only while something animates — so an idle shell costs zero frames.
|
|
13
|
+
* - `powerPreference: 'low-power'`: a decorative frame must not wake a discrete GPU.
|
|
14
|
+
*/
|
|
15
|
+
export declare const FRAME_CANVAS_SETTINGS: {
|
|
16
|
+
flat: true;
|
|
17
|
+
frameloop: "never";
|
|
18
|
+
gl: {
|
|
19
|
+
alpha: true;
|
|
20
|
+
antialias: true;
|
|
21
|
+
powerPreference: "low-power";
|
|
22
|
+
};
|
|
23
|
+
events: () => EventManager<HTMLElement>;
|
|
24
|
+
style: {
|
|
25
|
+
pointerEvents: "none";
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export interface FrameCanvasProps {
|
|
29
|
+
/** Computed in the DOM tree (`useFrameAppearance`); the canvas reads no context of its own. */
|
|
30
|
+
appearance: FrameAppearance | null;
|
|
31
|
+
/** The layout's camera. */
|
|
32
|
+
camera: Layout3DCamera;
|
|
33
|
+
/** Whether the layout's transitions cross-fade an appearance change (`crossFadesAppearance`). */
|
|
34
|
+
crossFadeAppearance: boolean;
|
|
35
|
+
/** What the layout plays on a navigation (#1826). */
|
|
36
|
+
navigationTransition: Layout3DFrameChoreography;
|
|
37
|
+
/** What the layout plays when the sidebar collapses, expands or is resized (#1826). */
|
|
38
|
+
sidebarResizeTransition: Layout3DFrameChoreography;
|
|
39
|
+
/** What the layout shows for the focused zone (#1826). */
|
|
40
|
+
zoneFocusTransition: Layout3DZoneFocusTransition;
|
|
41
|
+
/** The content's identity, from the navigation state: a change plays the navigation transition. */
|
|
42
|
+
navigationSignature: string;
|
|
43
|
+
/** The navigation state's focused zone, or null. */
|
|
44
|
+
focusedZone: string | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The `<Canvas>` camera for a layout's camera. Exhaustive over {@link Layout3DCamera}: a camera added for #1826
|
|
48
|
+
* fails to compile here until the frame can draw through it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function canvasCameraSettings(camera: Layout3DCamera): Pick<CanvasProps, 'orthographic' | 'camera'>;
|
|
51
|
+
/**
|
|
52
|
+
* The 3D frame (#1823): an in-scene background derived from the theme tokens, and a frosted slab under every
|
|
53
|
+
* shell region, aligned to the region's box. Everything inside the regions stays the 2D shell's HTML; this
|
|
54
|
+
* canvas draws only what is behind them, and holds meshes only.
|
|
55
|
+
*/
|
|
56
|
+
export default function FrameCanvas({ camera, ...scene }: FrameCanvasProps): React.JSX.Element;
|
|
57
|
+
/**
|
|
58
|
+
* Counts each started transition on the canvas element, as `data-frame-3d-transitions-<trigger>`. The frame's only
|
|
59
|
+
* observable surface for WHICH choreography ran: a live probe (`scripts/presets-3d-live-probe`) reads it, because
|
|
60
|
+
* counting draws cannot tell a choreography from a hover or a re-layout. Writes a number, nothing else.
|
|
61
|
+
*/
|
|
62
|
+
export declare function countTransition(canvas: HTMLElement, trigger: FrameTransitionTrigger): void;
|
|
63
|
+
//# sourceMappingURL=frame-canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-canvas.d.ts","sourceRoot":"","sources":["../../../../src/frame/frame-canvas.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AACvD,OAAO,EAAoB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAI3F,OAAO,EAAiB,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,KAAK,yBAAyB,EAAE,KAAK,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AASlI;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;kBAXJ,YAAY,CAAC,WAAW,CAAC;;;;CAiBd,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,2BAA2B;IAC3B,MAAM,EAAE,cAAc,CAAC;IACvB,iGAAiG;IACjG,mBAAmB,EAAE,OAAO,CAAC;IAC7B,qDAAqD;IACrD,oBAAoB,EAAE,yBAAyB,CAAC;IAChD,uFAAuF;IACvF,uBAAuB,EAAE,yBAAyB,CAAC;IACnD,0DAA0D;IAC1D,mBAAmB,EAAE,2BAA2B,CAAC;IACjD,mGAAmG;IACnG,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,GAAG,QAAQ,CAAC,CAazG;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAQ7F;AAuFD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAG1F"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useLayoutEffect, useRef } from 'react';
|
|
3
|
+
import { Canvas, useThree } from '@react-three/fiber';
|
|
4
|
+
import { createFrameDriver } from './frame-driver.js';
|
|
5
|
+
import { FrameRenderer } from './frame-renderer.js';
|
|
6
|
+
import { Layout3DCamera } from '../layout/layout-3d-vocabulary.js';
|
|
7
|
+
/**
|
|
8
|
+
* The frame receives no pointer events and handles none. fiber's wrapper would otherwise set
|
|
9
|
+
* `pointer-events: auto` inline and attach its listeners, making a full-viewport canvas a hit target wherever
|
|
10
|
+
* the shell root does not cover it. Hover is read from the DOCUMENT instead (see {@link FrameScene}).
|
|
11
|
+
*/
|
|
12
|
+
const NO_POINTER_EVENTS = () => ({ enabled: false, priority: 0 });
|
|
13
|
+
/**
|
|
14
|
+
* Every `<Canvas>` setting the frame depends on, in one place so a test pins them.
|
|
15
|
+
*
|
|
16
|
+
* - `flat`: no tone mapping. The shaders write sRGB-encoded colours as given; tone mapping would move every
|
|
17
|
+
* colour after the contrast bound was checked.
|
|
18
|
+
* - `frameloop: 'never'`: fiber renders nothing on its own. The scene renders when a region moved, when the
|
|
19
|
+
* appearance changed, and per animation frame only while something animates — so an idle shell costs zero frames.
|
|
20
|
+
* - `powerPreference: 'low-power'`: a decorative frame must not wake a discrete GPU.
|
|
21
|
+
*/
|
|
22
|
+
export const FRAME_CANVAS_SETTINGS = {
|
|
23
|
+
flat: true,
|
|
24
|
+
frameloop: 'never',
|
|
25
|
+
gl: { alpha: true, antialias: true, powerPreference: 'low-power' },
|
|
26
|
+
events: NO_POINTER_EVENTS,
|
|
27
|
+
style: { pointerEvents: 'none' },
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The `<Canvas>` camera for a layout's camera. Exhaustive over {@link Layout3DCamera}: a camera added for #1826
|
|
31
|
+
* fails to compile here until the frame can draw through it.
|
|
32
|
+
*/
|
|
33
|
+
export function canvasCameraSettings(camera) {
|
|
34
|
+
switch (camera) {
|
|
35
|
+
case Layout3DCamera.FLAT_ORTHOGRAPHIC:
|
|
36
|
+
// Orthographic with zoom 1: one scene unit is one CSS pixel, so a slab lies exactly on its region's box.
|
|
37
|
+
return { orthographic: true, camera: { position: [0, 0, 10], zoom: 1 } };
|
|
38
|
+
case Layout3DCamera.SPATIAL_PERSPECTIVE:
|
|
39
|
+
// The spatial layout draws its own environment through its own camera (`spatial/`); the frame is never mounted.
|
|
40
|
+
throw new Error('The 3D frame does not draw a spatial layout: its placement renders the spatial environment itself.');
|
|
41
|
+
default: {
|
|
42
|
+
const unsupported = camera;
|
|
43
|
+
throw new Error(`The 3D frame has no camera '${String(unsupported)}'.`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The 3D frame (#1823): an in-scene background derived from the theme tokens, and a frosted slab under every
|
|
49
|
+
* shell region, aligned to the region's box. Everything inside the regions stays the 2D shell's HTML; this
|
|
50
|
+
* canvas draws only what is behind them, and holds meshes only.
|
|
51
|
+
*/
|
|
52
|
+
export default function FrameCanvas({ camera, ...scene }) {
|
|
53
|
+
return (_jsx("div", { "aria-hidden": "true", "data-frame-3d": "frame", className: "fixed inset-0 pointer-events-none", children: _jsx(Canvas, { ...FRAME_CANVAS_SETTINGS, ...canvasCameraSettings(camera), children: _jsx(FrameScene, { ...scene }) }) }));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The scene host: creates the renderer and the scheduling driver (`createFrameDriver`, which decides WHEN to draw)
|
|
57
|
+
* and forwards the appearance prop to it. Everything about timing lives in the driver, where it is unit-tested.
|
|
58
|
+
*/
|
|
59
|
+
function FrameScene({ appearance, crossFadeAppearance, navigationTransition, sidebarResizeTransition, zoneFocusTransition, navigationSignature, focusedZone, }) {
|
|
60
|
+
const groupRef = useRef(null);
|
|
61
|
+
const gl = useThree((state) => state.gl);
|
|
62
|
+
const scene = useThree((state) => state.scene);
|
|
63
|
+
const camera = useThree((state) => state.camera);
|
|
64
|
+
const get = useThree((state) => state.get);
|
|
65
|
+
const size = useThree((state) => state.size);
|
|
66
|
+
const dpr = useThree((state) => state.viewport.dpr);
|
|
67
|
+
const driverRef = useRef(null);
|
|
68
|
+
// The latest inputs, so a driver re-created for a new renderer or camera starts from them (not from nothing): its
|
|
69
|
+
// first navigation signature only records, so a re-creation never plays a navigation that did not happen.
|
|
70
|
+
const appearanceRef = useRef(appearance);
|
|
71
|
+
const navigationRef = useRef(navigationSignature);
|
|
72
|
+
const focusedZoneRef = useRef(focusedZone);
|
|
73
|
+
useLayoutEffect(() => {
|
|
74
|
+
const group = groupRef.current;
|
|
75
|
+
if (!group)
|
|
76
|
+
return undefined;
|
|
77
|
+
const renderer = new FrameRenderer(group, { navigation: navigationTransition, sidebarResize: sidebarResizeTransition, zoneFocus: zoneFocusTransition });
|
|
78
|
+
const driver = createFrameDriver({
|
|
79
|
+
renderer,
|
|
80
|
+
renderScene: () => gl.render(scene, camera),
|
|
81
|
+
// The canvas' own size from fiber's store: the number the camera frustum was fitted to.
|
|
82
|
+
viewport: () => ({ width: get().size.width, height: get().size.height }),
|
|
83
|
+
canvas: gl.domElement,
|
|
84
|
+
document,
|
|
85
|
+
requestFrame: (callback) => requestAnimationFrame(callback),
|
|
86
|
+
cancelFrame: (handle) => cancelAnimationFrame(handle),
|
|
87
|
+
now: () => performance.now(),
|
|
88
|
+
createResizeObserver: (callback) => new ResizeObserver(callback),
|
|
89
|
+
createMutationObserver: (callback) => new MutationObserver(callback),
|
|
90
|
+
crossFadeAppearance,
|
|
91
|
+
onTransitionStarted: (trigger) => countTransition(gl.domElement, trigger),
|
|
92
|
+
}, appearanceRef.current);
|
|
93
|
+
driver.setNavigation(navigationRef.current);
|
|
94
|
+
driver.setFocusedZone(focusedZoneRef.current);
|
|
95
|
+
driverRef.current = driver;
|
|
96
|
+
return () => {
|
|
97
|
+
driver.dispose();
|
|
98
|
+
renderer.dispose();
|
|
99
|
+
driverRef.current = null;
|
|
100
|
+
};
|
|
101
|
+
}, [gl, scene, camera, get, crossFadeAppearance, navigationTransition, sidebarResizeTransition, zoneFocusTransition]);
|
|
102
|
+
// The appearance changed (theme, mode, reduced motion). Declared AFTER the scene effect, so the driver exists.
|
|
103
|
+
useLayoutEffect(() => {
|
|
104
|
+
if (appearanceRef.current === appearance)
|
|
105
|
+
return;
|
|
106
|
+
appearanceRef.current = appearance;
|
|
107
|
+
driverRef.current?.setAppearance(appearance);
|
|
108
|
+
}, [appearance]);
|
|
109
|
+
// The content or the focused zone changed (#1826). After the scene effect, so the driver exists.
|
|
110
|
+
useLayoutEffect(() => {
|
|
111
|
+
navigationRef.current = navigationSignature;
|
|
112
|
+
driverRef.current?.setNavigation(navigationSignature);
|
|
113
|
+
}, [navigationSignature]);
|
|
114
|
+
useLayoutEffect(() => {
|
|
115
|
+
focusedZoneRef.current = focusedZone;
|
|
116
|
+
driverRef.current?.setFocusedZone(focusedZone);
|
|
117
|
+
}, [focusedZone]);
|
|
118
|
+
// The canvas resized, or its pixel ratio changed: fiber has re-fitted the camera and cleared the drawing buffer,
|
|
119
|
+
// so redraw even if every region is where it was.
|
|
120
|
+
useLayoutEffect(() => {
|
|
121
|
+
driverRef.current?.redraw();
|
|
122
|
+
}, [size.width, size.height, dpr]);
|
|
123
|
+
return _jsx("group", { ref: groupRef });
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Counts each started transition on the canvas element, as `data-frame-3d-transitions-<trigger>`. The frame's only
|
|
127
|
+
* observable surface for WHICH choreography ran: a live probe (`scripts/presets-3d-live-probe`) reads it, because
|
|
128
|
+
* counting draws cannot tell a choreography from a hover or a re-layout. Writes a number, nothing else.
|
|
129
|
+
*/
|
|
130
|
+
export function countTransition(canvas, trigger) {
|
|
131
|
+
const attribute = `data-frame-3d-transitions-${trigger}`;
|
|
132
|
+
canvas.setAttribute(attribute, String(Number(canvas.getAttribute(attribute) ?? '0') + 1));
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=frame-canvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-canvas.js","sourceRoot":"","sources":["../../../../src/frame/frame-canvas.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useLayoutEffect, useRef } from 'react';\nimport { Canvas, useThree, type CanvasProps, type EventManager } from '@react-three/fiber';\nimport { Group } from 'three';\n\nimport { createFrameDriver, type FrameDriver } from './frame-driver';\nimport { FrameRenderer, type FrameTransitionTrigger } from './frame-renderer';\nimport type { FrameAppearance } from './frame-appearance';\nimport { Layout3DCamera, type Layout3DFrameChoreography, type Layout3DZoneFocusTransition } from '../layout/layout-3d-vocabulary';\n\n/**\n * The frame receives no pointer events and handles none. fiber's wrapper would otherwise set\n * `pointer-events: auto` inline and attach its listeners, making a full-viewport canvas a hit target wherever\n * the shell root does not cover it. Hover is read from the DOCUMENT instead (see {@link FrameScene}).\n */\nconst NO_POINTER_EVENTS = (): EventManager<HTMLElement> => ({ enabled: false, priority: 0 });\n\n/**\n * Every `<Canvas>` setting the frame depends on, in one place so a test pins them.\n *\n * - `flat`: no tone mapping. The shaders write sRGB-encoded colours as given; tone mapping would move every\n * colour after the contrast bound was checked.\n * - `frameloop: 'never'`: fiber renders nothing on its own. The scene renders when a region moved, when the\n * appearance changed, and per animation frame only while something animates — so an idle shell costs zero frames.\n * - `powerPreference: 'low-power'`: a decorative frame must not wake a discrete GPU.\n */\nexport const FRAME_CANVAS_SETTINGS = {\n flat: true,\n frameloop: 'never',\n gl: { alpha: true, antialias: true, powerPreference: 'low-power' },\n events: NO_POINTER_EVENTS,\n style: { pointerEvents: 'none' },\n} satisfies Omit<CanvasProps, 'children'>;\n\nexport interface FrameCanvasProps {\n /** Computed in the DOM tree (`useFrameAppearance`); the canvas reads no context of its own. */\n appearance: FrameAppearance | null;\n /** The layout's camera. */\n camera: Layout3DCamera;\n /** Whether the layout's transitions cross-fade an appearance change (`crossFadesAppearance`). */\n crossFadeAppearance: boolean;\n /** What the layout plays on a navigation (#1826). */\n navigationTransition: Layout3DFrameChoreography;\n /** What the layout plays when the sidebar collapses, expands or is resized (#1826). */\n sidebarResizeTransition: Layout3DFrameChoreography;\n /** What the layout shows for the focused zone (#1826). */\n zoneFocusTransition: Layout3DZoneFocusTransition;\n /** The content's identity, from the navigation state: a change plays the navigation transition. */\n navigationSignature: string;\n /** The navigation state's focused zone, or null. */\n focusedZone: string | null;\n}\n\n/**\n * The `<Canvas>` camera for a layout's camera. Exhaustive over {@link Layout3DCamera}: a camera added for #1826\n * fails to compile here until the frame can draw through it.\n */\nexport function canvasCameraSettings(camera: Layout3DCamera): Pick<CanvasProps, 'orthographic' | 'camera'> {\n switch (camera) {\n case Layout3DCamera.FLAT_ORTHOGRAPHIC:\n // Orthographic with zoom 1: one scene unit is one CSS pixel, so a slab lies exactly on its region's box.\n return { orthographic: true, camera: { position: [0, 0, 10], zoom: 1 } };\n case Layout3DCamera.SPATIAL_PERSPECTIVE:\n // The spatial layout draws its own environment through its own camera (`spatial/`); the frame is never mounted.\n throw new Error('The 3D frame does not draw a spatial layout: its placement renders the spatial environment itself.');\n default: {\n const unsupported: never = camera;\n throw new Error(`The 3D frame has no camera '${String(unsupported)}'.`);\n }\n }\n}\n\n/**\n * The 3D frame (#1823): an in-scene background derived from the theme tokens, and a frosted slab under every\n * shell region, aligned to the region's box. Everything inside the regions stays the 2D shell's HTML; this\n * canvas draws only what is behind them, and holds meshes only.\n */\nexport default function FrameCanvas({ camera, ...scene }: FrameCanvasProps): React.JSX.Element {\n return (\n <div aria-hidden=\"true\" data-frame-3d=\"frame\" className=\"fixed inset-0 pointer-events-none\">\n <Canvas {...FRAME_CANVAS_SETTINGS} {...canvasCameraSettings(camera)}>\n <FrameScene {...scene} />\n </Canvas>\n </div>\n );\n}\n\n/**\n * The scene host: creates the renderer and the scheduling driver (`createFrameDriver`, which decides WHEN to draw)\n * and forwards the appearance prop to it. Everything about timing lives in the driver, where it is unit-tested.\n */\nfunction FrameScene({\n appearance,\n crossFadeAppearance,\n navigationTransition,\n sidebarResizeTransition,\n zoneFocusTransition,\n navigationSignature,\n focusedZone,\n}: Omit<FrameCanvasProps, 'camera'>): React.JSX.Element {\n const groupRef = useRef<Group>(null);\n const gl = useThree((state) => state.gl);\n const scene = useThree((state) => state.scene);\n const camera = useThree((state) => state.camera);\n const get = useThree((state) => state.get);\n const size = useThree((state) => state.size);\n const dpr = useThree((state) => state.viewport.dpr);\n const driverRef = useRef<FrameDriver | null>(null);\n // The latest inputs, so a driver re-created for a new renderer or camera starts from them (not from nothing): its\n // first navigation signature only records, so a re-creation never plays a navigation that did not happen.\n const appearanceRef = useRef(appearance);\n const navigationRef = useRef(navigationSignature);\n const focusedZoneRef = useRef(focusedZone);\n\n useLayoutEffect(() => {\n const group = groupRef.current;\n if (!group) return undefined;\n const renderer = new FrameRenderer(group, { navigation: navigationTransition, sidebarResize: sidebarResizeTransition, zoneFocus: zoneFocusTransition });\n const driver = createFrameDriver(\n {\n renderer,\n renderScene: () => gl.render(scene, camera),\n // The canvas' own size from fiber's store: the number the camera frustum was fitted to.\n viewport: () => ({ width: get().size.width, height: get().size.height }),\n canvas: gl.domElement,\n document,\n requestFrame: (callback) => requestAnimationFrame(callback),\n cancelFrame: (handle) => cancelAnimationFrame(handle),\n now: () => performance.now(),\n createResizeObserver: (callback) => new ResizeObserver(callback),\n createMutationObserver: (callback) => new MutationObserver(callback),\n crossFadeAppearance,\n onTransitionStarted: (trigger) => countTransition(gl.domElement, trigger),\n },\n appearanceRef.current,\n );\n driver.setNavigation(navigationRef.current);\n driver.setFocusedZone(focusedZoneRef.current);\n driverRef.current = driver;\n return () => {\n driver.dispose();\n renderer.dispose();\n driverRef.current = null;\n };\n }, [gl, scene, camera, get, crossFadeAppearance, navigationTransition, sidebarResizeTransition, zoneFocusTransition]);\n\n // The appearance changed (theme, mode, reduced motion). Declared AFTER the scene effect, so the driver exists.\n useLayoutEffect(() => {\n if (appearanceRef.current === appearance) return;\n appearanceRef.current = appearance;\n driverRef.current?.setAppearance(appearance);\n }, [appearance]);\n\n // The content or the focused zone changed (#1826). After the scene effect, so the driver exists.\n useLayoutEffect(() => {\n navigationRef.current = navigationSignature;\n driverRef.current?.setNavigation(navigationSignature);\n }, [navigationSignature]);\n useLayoutEffect(() => {\n focusedZoneRef.current = focusedZone;\n driverRef.current?.setFocusedZone(focusedZone);\n }, [focusedZone]);\n\n // The canvas resized, or its pixel ratio changed: fiber has re-fitted the camera and cleared the drawing buffer,\n // so redraw even if every region is where it was.\n useLayoutEffect(() => {\n driverRef.current?.redraw();\n }, [size.width, size.height, dpr]);\n\n return <group ref={groupRef} />;\n}\n\n/**\n * Counts each started transition on the canvas element, as `data-frame-3d-transitions-<trigger>`. The frame's only\n * observable surface for WHICH choreography ran: a live probe (`scripts/presets-3d-live-probe`) reads it, because\n * counting draws cannot tell a choreography from a hover or a re-layout. Writes a number, nothing else.\n */\nexport function countTransition(canvas: HTMLElement, trigger: FrameTransitionTrigger): void {\n const attribute = `data-frame-3d-transitions-${trigger}`;\n canvas.setAttribute(attribute, String(Number(canvas.getAttribute(attribute) ?? '0') + 1));\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { FrameCanvasProps } from './frame-canvas';
|
|
3
|
+
/**
|
|
4
|
+
* The boundary contains every THROWN failure of the decorative frame — a chunk that did not arrive, a WebGL
|
|
5
|
+
* context the browser refused to create (fiber rethrows that during render) — to the frame's own slot. It is
|
|
6
|
+
* failure containment, not a fallback mode: the 2D shell beside it never depended on the frame, so the
|
|
7
|
+
* application keeps every region and simply shows no frame. There is deliberately no retry: a browser that
|
|
8
|
+
* refused WebGL once refuses it again, and a retry loop is how a decoration becomes a CPU cost.
|
|
9
|
+
*
|
|
10
|
+
* A context LOST after creation is not an error and no boundary sees it: it is a `webglcontextlost` event,
|
|
11
|
+
* which the canvas handles itself (it stops drawing, and redraws on `webglcontextrestored`).
|
|
12
|
+
*
|
|
13
|
+
* The boundary logs the failure itself (with the door name), so no `onError` is passed: a second line would
|
|
14
|
+
* report the same failure twice.
|
|
15
|
+
*
|
|
16
|
+
* Memoised: its props are primitives and the appearance value, and the navigation context the frame slot reads
|
|
17
|
+
* changes far more often than they do (the content area's width, on every frame of a sidebar animation), so an
|
|
18
|
+
* unchanged frame never re-renders the canvas.
|
|
19
|
+
*/
|
|
20
|
+
export declare const FrameCanvasDoor: React.NamedExoticComponent<FrameCanvasProps>;
|
|
21
|
+
//# sourceMappingURL=frame-canvas.lazy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-canvas.lazy.d.ts","sourceRoot":"","sources":["../../../../src/frame/frame-canvas.lazy.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAI9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAUvD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,eAAe,8CAQ1B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { Suspense, lazy } from 'react';
|
|
3
|
+
import { LazyDoorErrorBoundary } from '@wildo-ai/saas-frontend-lib';
|
|
4
|
+
/**
|
|
5
|
+
* The lazy door of the 3D frame: three.js and @react-three/fiber load only when a 3D main container actually
|
|
6
|
+
* mounts, so an application that declares this library but has not reached its shell (the sign-in screens, a
|
|
7
|
+
* public page) downloads none of it. `check:lazy-boundaries` keeps it a door: the module behind it may be
|
|
8
|
+
* imported statically only here (and from tests).
|
|
9
|
+
*/
|
|
10
|
+
const LazyFrameCanvas = lazy(() => import('./frame-canvas.js'));
|
|
11
|
+
/**
|
|
12
|
+
* The boundary contains every THROWN failure of the decorative frame — a chunk that did not arrive, a WebGL
|
|
13
|
+
* context the browser refused to create (fiber rethrows that during render) — to the frame's own slot. It is
|
|
14
|
+
* failure containment, not a fallback mode: the 2D shell beside it never depended on the frame, so the
|
|
15
|
+
* application keeps every region and simply shows no frame. There is deliberately no retry: a browser that
|
|
16
|
+
* refused WebGL once refuses it again, and a retry loop is how a decoration becomes a CPU cost.
|
|
17
|
+
*
|
|
18
|
+
* A context LOST after creation is not an error and no boundary sees it: it is a `webglcontextlost` event,
|
|
19
|
+
* which the canvas handles itself (it stops drawing, and redraws on `webglcontextrestored`).
|
|
20
|
+
*
|
|
21
|
+
* The boundary logs the failure itself (with the door name), so no `onError` is passed: a second line would
|
|
22
|
+
* report the same failure twice.
|
|
23
|
+
*
|
|
24
|
+
* Memoised: its props are primitives and the appearance value, and the navigation context the frame slot reads
|
|
25
|
+
* changes far more often than they do (the content area's width, on every frame of a sidebar animation), so an
|
|
26
|
+
* unchanged frame never re-renders the canvas.
|
|
27
|
+
*/
|
|
28
|
+
export const FrameCanvasDoor = React.memo(function FrameCanvasDoor(props) {
|
|
29
|
+
return (_jsx(LazyDoorErrorBoundary, { doorName: "presets-components-3d/frame", fallback: null, children: _jsx(Suspense, { fallback: null, children: _jsx(LazyFrameCanvas, { ...props }) }) }));
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=frame-canvas.lazy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-canvas.lazy.js","sourceRoot":"","sources":["../../../../src/frame/frame-canvas.lazy.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAE9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAIpE;;;;;GAKG;AACH,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAE7D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,eAAe,CAAC,KAAuB;IACxF,OAAO,CACL,KAAC,qBAAqB,IAAC,QAAQ,EAAC,6BAA6B,EAAC,QAAQ,EAAE,IAAI,YAC1E,KAAC,QAAQ,IAAC,QAAQ,EAAE,IAAI,YACtB,KAAC,eAAe,OAAK,KAAK,GAAI,GACrB,GACW,CACzB,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import React, { Suspense, lazy } from 'react';\n\nimport { LazyDoorErrorBoundary } from '@wildo-ai/saas-frontend-lib';\n\nimport type { FrameCanvasProps } from './frame-canvas';\n\n/**\n * The lazy door of the 3D frame: three.js and @react-three/fiber load only when a 3D main container actually\n * mounts, so an application that declares this library but has not reached its shell (the sign-in screens, a\n * public page) downloads none of it. `check:lazy-boundaries` keeps it a door: the module behind it may be\n * imported statically only here (and from tests).\n */\nconst LazyFrameCanvas = lazy(() => import('./frame-canvas'));\n\n/**\n * The boundary contains every THROWN failure of the decorative frame — a chunk that did not arrive, a WebGL\n * context the browser refused to create (fiber rethrows that during render) — to the frame's own slot. It is\n * failure containment, not a fallback mode: the 2D shell beside it never depended on the frame, so the\n * application keeps every region and simply shows no frame. There is deliberately no retry: a browser that\n * refused WebGL once refuses it again, and a retry loop is how a decoration becomes a CPU cost.\n *\n * A context LOST after creation is not an error and no boundary sees it: it is a `webglcontextlost` event,\n * which the canvas handles itself (it stops drawing, and redraws on `webglcontextrestored`).\n *\n * The boundary logs the failure itself (with the door name), so no `onError` is passed: a second line would\n * report the same failure twice.\n *\n * Memoised: its props are primitives and the appearance value, and the navigation context the frame slot reads\n * changes far more often than they do (the content area's width, on every frame of a sidebar animation), so an\n * unchanged frame never re-renders the canvas.\n */\nexport const FrameCanvasDoor = React.memo(function FrameCanvasDoor(props: FrameCanvasProps): React.JSX.Element {\n return (\n <LazyDoorErrorBoundary doorName=\"presets-components-3d/frame\" fallback={null}>\n <Suspense fallback={null}>\n <LazyFrameCanvas {...props} />\n </Suspense>\n </LazyDoorErrorBoundary>\n );\n});\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame's colour arithmetic (#1823). INTERNAL to the package: the root barrel does not export it. It is the
|
|
3
|
+
* model of what the shaders write, and the appearance derivation and its tests share it; it is not an API.
|
|
4
|
+
*/
|
|
5
|
+
/** A colour as the frame draws it: sRGB-ENCODED channels in 0–1, exactly what lands in the framebuffer. */
|
|
6
|
+
export type FrameColor = readonly [number, number, number];
|
|
7
|
+
export declare const BLACK: FrameColor;
|
|
8
|
+
export declare const WHITE: FrameColor;
|
|
9
|
+
/** WCAG relative luminance of an sRGB-encoded colour. spec-cite: WCAG 2.2 — "relative luminance" definition — https://www.w3.org/TR/WCAG22/#dfn-relative-luminance */
|
|
10
|
+
export declare function relativeLuminance(color: FrameColor): number;
|
|
11
|
+
/** WCAG contrast ratio between two sRGB-encoded colours. spec-cite: WCAG 2.2 — "contrast ratio" definition — https://www.w3.org/TR/WCAG22/#dfn-contrast-ratio */
|
|
12
|
+
export declare function contrastRatio(a: FrameColor, b: FrameColor): number;
|
|
13
|
+
/** `top` at `alpha` over an opaque `bottom`, blended in sRGB-encoded space as the framebuffer does. */
|
|
14
|
+
export declare function composite(top: FrameColor, alpha: number, bottom: FrameColor): FrameColor;
|
|
15
|
+
export declare function mix(a: FrameColor, b: FrameColor, t: number): FrameColor;
|
|
16
|
+
/** The points of a gradient the bound checks. */
|
|
17
|
+
export declare function gradientPoints(from: FrameColor, to: FrameColor): FrameColor[];
|
|
18
|
+
/** Move `color` toward `pole` in small steps until `ok` holds; the pole itself if nothing short of it does. */
|
|
19
|
+
export declare function pushUntil(color: FrameColor, pole: FrameColor, ok: (c: FrameColor) => boolean): FrameColor;
|
|
20
|
+
export declare function decodeSrgb(channel: number): number;
|
|
21
|
+
export declare function encodeSrgb(channel: number): number;
|
|
22
|
+
/** The colour as the 8-bit framebuffer stores it: each channel rounded to the nearest of 256 levels. */
|
|
23
|
+
export declare function quantize(color: FrameColor): FrameColor;
|
|
24
|
+
//# sourceMappingURL=frame-color.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-color.d.ts","sourceRoot":"","sources":["../../../../src/frame/frame-color.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,2GAA2G;AAC3G,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAE3D,eAAO,MAAM,KAAK,EAAE,UAAsB,CAAC;AAC3C,eAAO,MAAM,KAAK,EAAE,UAAsB,CAAC;AAM3C,sKAAsK;AACtK,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAG3D;AAED,iKAAiK;AACjK,wBAAgB,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,MAAM,CAIlE;AAED,uGAAuG;AACvG,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CAExF;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAEvE;AAED,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,UAAU,EAAE,CAE7E;AAED,+GAA+G;AAC/G,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,OAAO,GAAG,UAAU,CAMzG;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,wGAAwG;AACxG,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAEtD"}
|