@orangecheck/design 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import { clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
  import * as React2 from 'react';
4
- import { createContext, useState, useRef, useEffect, useCallback, useMemo, useContext, Fragment as Fragment$1, useId } from 'react';
4
+ import { createContext, useRef, useEffect, useState, useCallback, useMemo, useContext, Fragment as Fragment$1, useId } from 'react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import { useTheme } from 'next-themes';
7
- import { ChevronDown, Plus, Palette, Check, Sun, Moon, Monitor, CheckIcon, CircleIcon, X, XIcon, AlertTriangle, Pencil, ChevronLeft, ChevronRight, ChevronDownIcon, ChevronUpIcon, Maximize2, Copy, HelpCircle, Boxes, CornerDownLeft, Loader2, Menu } from 'lucide-react';
7
+ import { ChevronDown, Plus, Palette, Check, Sun, Moon, Monitor, Waves, Pause, CheckIcon, CircleIcon, X, XIcon, AlertTriangle, Pencil, ChevronLeft, ChevronRight, ChevronDownIcon, ChevronUpIcon, Maximize2, Copy, HelpCircle, Boxes, CornerDownLeft, Loader2, Menu } from 'lucide-react';
8
8
  import { Slot } from '@radix-ui/react-slot';
9
9
  import { cva } from 'class-variance-authority';
10
10
  import * as LabelPrimitive from '@radix-ui/react-label';
@@ -23,7 +23,381 @@ import Link6 from 'next/link';
23
23
  import { useRouter } from 'next/router';
24
24
  import { useOcSession, fetchOcLinkedIdentities } from '@orangecheck/auth-client';
25
25
 
26
- // src/tokens/cn.ts
26
+ var __defProp = Object.defineProperty;
27
+ var __getOwnPropNames = Object.getOwnPropertyNames;
28
+ var __esm = (fn, res) => function __init() {
29
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
30
+ };
31
+ var __export = (target, all) => {
32
+ for (var name in all)
33
+ __defProp(target, name, { get: all[name], enumerable: true });
34
+ };
35
+
36
+ // src/tokens/scene-tokens.ts
37
+ function parseColor(value) {
38
+ let m = value.match(/color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)/);
39
+ if (m?.[1] != null && m[2] != null && m[3] != null) {
40
+ return { r: +m[1], g: +m[2], b: +m[3] };
41
+ }
42
+ m = value.match(/rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)/);
43
+ if (m?.[1] != null && m[2] != null && m[3] != null) {
44
+ return { r: +m[1] / 255, g: +m[2] / 255, b: +m[3] / 255 };
45
+ }
46
+ return null;
47
+ }
48
+ function srgbToOklab({ r, g, b }) {
49
+ const lin = (c) => c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
50
+ const lr = lin(r);
51
+ const lg = lin(g);
52
+ const lb = lin(b);
53
+ const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
54
+ const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
55
+ const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
56
+ return {
57
+ L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
58
+ a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
59
+ b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
60
+ };
61
+ }
62
+ function oklabToSrgb(L, a, bb) {
63
+ const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * bb, 3);
64
+ const m = Math.pow(L - 0.1055613458 * a - 0.0638541728 * bb, 3);
65
+ const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * bb, 3);
66
+ const lr = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
67
+ const lg = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
68
+ const lb = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;
69
+ const un = (c) => Math.min(1, Math.max(0, c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055));
70
+ return { r: un(lr), g: un(lg), b: un(lb) };
71
+ }
72
+ function normalizeHue(c, isDark) {
73
+ const { L, a, b } = srgbToOklab(c);
74
+ const C = Math.hypot(a, b);
75
+ const targetL = isDark ? Math.min(0.75, Math.max(0.55, L)) : Math.min(0.95, Math.max(0.82, L + 0.25));
76
+ const scale = C > 1e-6 ? Math.min(C, 0.14) / C : 0;
77
+ return oklabToSrgb(targetL, a * scale, b * scale);
78
+ }
79
+ function readOcSceneTokens(host) {
80
+ if (typeof document === "undefined") return null;
81
+ const probe = document.createElement("div");
82
+ probe.setAttribute("aria-hidden", "true");
83
+ probe.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;pointer-events:none;";
84
+ const cells = SLOTS.map((slot) => {
85
+ const cell = document.createElement("span");
86
+ cell.style.color = `color-mix(in srgb, var(${slot}), var(${slot}))`;
87
+ probe.appendChild(cell);
88
+ return cell;
89
+ });
90
+ host.appendChild(probe);
91
+ try {
92
+ const parsed = cells.map((cell) => parseColor(getComputedStyle(cell).color));
93
+ const [orange, green, blue, violet, background] = parsed;
94
+ if (!orange || !green || !blue || !violet || !background) return null;
95
+ const root = document.documentElement;
96
+ return {
97
+ palette: [orange, green, blue, violet],
98
+ background,
99
+ isDark: root.classList.contains("dark"),
100
+ motionOff: root.getAttribute("data-oc-motion") === "off"
101
+ };
102
+ } finally {
103
+ host.removeChild(probe);
104
+ }
105
+ }
106
+ function subscribeOcTheme(cb) {
107
+ if (typeof document === "undefined") return () => {
108
+ };
109
+ let raf1 = 0;
110
+ let raf2 = 0;
111
+ const schedule = () => {
112
+ cancelAnimationFrame(raf1);
113
+ cancelAnimationFrame(raf2);
114
+ raf1 = requestAnimationFrame(() => {
115
+ raf2 = requestAnimationFrame(cb);
116
+ });
117
+ };
118
+ const mo = new MutationObserver(schedule);
119
+ mo.observe(document.documentElement, {
120
+ attributes: true,
121
+ attributeFilter: ["class", "data-oc-theme", "data-oc-motion"]
122
+ });
123
+ return () => {
124
+ mo.disconnect();
125
+ cancelAnimationFrame(raf1);
126
+ cancelAnimationFrame(raf2);
127
+ };
128
+ }
129
+ var SLOTS;
130
+ var init_scene_tokens = __esm({
131
+ "src/tokens/scene-tokens.ts"() {
132
+ SLOTS = ["--au-orange", "--au-green", "--au-blue", "--au-violet", "--background"];
133
+ }
134
+ });
135
+
136
+ // src/tokens/aurora-gl.ts
137
+ var aurora_gl_exports = {};
138
+ __export(aurora_gl_exports, {
139
+ mountAuroraGL: () => mountAuroraGL
140
+ });
141
+ function killFlagged() {
142
+ try {
143
+ const raw = localStorage.getItem(KILL_KEY);
144
+ if (!raw) return false;
145
+ if (Date.now() - Number(raw) > KILL_TTL_MS) {
146
+ localStorage.removeItem(KILL_KEY);
147
+ return false;
148
+ }
149
+ return true;
150
+ } catch {
151
+ return false;
152
+ }
153
+ }
154
+ function setKillFlag() {
155
+ try {
156
+ localStorage.setItem(KILL_KEY, String(Date.now()));
157
+ } catch {
158
+ }
159
+ }
160
+ function cypherFinish() {
161
+ const skin = document.documentElement.getAttribute("data-oc-theme");
162
+ return skin !== null && skin !== "ember";
163
+ }
164
+ function samplePalette(host) {
165
+ const tokens = readOcSceneTokens(host);
166
+ if (!tokens) return null;
167
+ return {
168
+ hues: tokens.palette.map((h) => normalizeHue(h, tokens.isDark)),
169
+ bg: tokens.background,
170
+ isDark: tokens.isDark,
171
+ finish: cypherFinish() ? 1 : 0
172
+ };
173
+ }
174
+ function mountAuroraGL(container) {
175
+ if (killFlagged()) return null;
176
+ if (getComputedStyle(container).getPropertyValue("--oc-aurora-gl").trim() === "off") {
177
+ return null;
178
+ }
179
+ const canvas = document.createElement("canvas");
180
+ canvas.className = "oc-aurora__gl";
181
+ const glMaybe = canvas.getContext("webgl2", {
182
+ antialias: false,
183
+ depth: false,
184
+ stencil: false,
185
+ alpha: false,
186
+ powerPreference: "low-power",
187
+ failIfMajorPerformanceCaveat: true
188
+ // SwiftShader/blocklist → CSS floor, free
189
+ });
190
+ if (!glMaybe) return null;
191
+ const gl = glMaybe;
192
+ const first = samplePalette(container);
193
+ if (!first) return null;
194
+ let prog;
195
+ const uni = {};
196
+ try {
197
+ const compile = (type, src) => {
198
+ const s = gl.createShader(type);
199
+ gl.shaderSource(s, src);
200
+ gl.compileShader(s);
201
+ if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error("compile");
202
+ return s;
203
+ };
204
+ prog = gl.createProgram();
205
+ gl.attachShader(prog, compile(gl.VERTEX_SHADER, VS));
206
+ gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, FS));
207
+ gl.linkProgram(prog);
208
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) throw new Error("link");
209
+ gl.useProgram(prog);
210
+ const buf = gl.createBuffer();
211
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
212
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
213
+ const loc = gl.getAttribLocation(prog, "p");
214
+ gl.enableVertexAttribArray(loc);
215
+ gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
216
+ for (const k of ["uRes", "uT", "uIsDark", "uFinish", "uMix", "uA0", "uA1", "uA2", "uA3", "uB0", "uB1", "uB2", "uB3", "uBg"]) {
217
+ uni[k] = gl.getUniformLocation(prog, k);
218
+ }
219
+ } catch {
220
+ return null;
221
+ }
222
+ container.appendChild(canvas);
223
+ let palFrom = first;
224
+ let palTo = first;
225
+ let mixStart = 0;
226
+ let raf = 0;
227
+ let lastFrame = 0;
228
+ let clock = 0;
229
+ let lastTick = 0;
230
+ let lastInput = performance.now();
231
+ let live = false;
232
+ let disposed = false;
233
+ let slowStreak = 0;
234
+ const setVec = (k, c) => gl.uniform3f(uni[k] ?? null, c.r, c.g, c.b);
235
+ function resize() {
236
+ const coarse = matchMedia("(pointer: coarse)").matches;
237
+ const dpr = Math.min(devicePixelRatio || 1, coarse ? 1.5 : 2) * 0.75;
238
+ const w = Math.max(1, Math.round(container.clientWidth * dpr));
239
+ const hgt = Math.max(1, Math.round(container.clientHeight * dpr));
240
+ if (canvas.width !== w || canvas.height !== hgt) {
241
+ canvas.width = w;
242
+ canvas.height = hgt;
243
+ gl.viewport(0, 0, w, hgt);
244
+ }
245
+ }
246
+ function draw(now) {
247
+ raf = requestAnimationFrame(draw);
248
+ if (now - lastFrame < FRAME_MS) return;
249
+ const frameGap = now - lastFrame;
250
+ lastFrame = now;
251
+ if (frameGap > FRAME_MS * 2.5) {
252
+ if (++slowStreak > 90) {
253
+ setKillFlag();
254
+ dispose();
255
+ return;
256
+ }
257
+ } else if (slowStreak > 0) slowStreak--;
258
+ const settled = now - lastInput > SETTLE_MS;
259
+ if (!settled) {
260
+ clock += Math.min(now - lastTick, 100) / 1e3;
261
+ }
262
+ lastTick = now;
263
+ if (settled && live && mixDone(now)) return;
264
+ resize();
265
+ const mix = Math.min(1, (now - mixStart) / CROSSFADE_MS);
266
+ gl.uniform2f(uni.uRes ?? null, canvas.width, canvas.height);
267
+ gl.uniform1f(uni.uT ?? null, clock);
268
+ gl.uniform1f(uni.uIsDark ?? null, palTo.isDark ? 1 : 0);
269
+ gl.uniform1f(uni.uFinish ?? null, palTo.finish);
270
+ gl.uniform1f(uni.uMix ?? null, mix);
271
+ setVec("uA0", palFrom.hues[0]);
272
+ setVec("uA1", palFrom.hues[1]);
273
+ setVec("uA2", palFrom.hues[2]);
274
+ setVec("uA3", palFrom.hues[3]);
275
+ setVec("uB0", palTo.hues[0]);
276
+ setVec("uB1", palTo.hues[1]);
277
+ setVec("uB2", palTo.hues[2]);
278
+ setVec("uB3", palTo.hues[3]);
279
+ setVec("uBg", palTo.bg);
280
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
281
+ if (!live) {
282
+ live = true;
283
+ container.classList.add("oc-aurora--gl-live");
284
+ }
285
+ }
286
+ function mixDone(now) {
287
+ return now - mixStart >= CROSSFADE_MS;
288
+ }
289
+ const onInput = () => {
290
+ lastInput = performance.now();
291
+ };
292
+ const onVisibility = () => {
293
+ if (document.hidden) {
294
+ cancelAnimationFrame(raf);
295
+ raf = 0;
296
+ } else if (!raf && !disposed) {
297
+ lastFrame = 0;
298
+ lastTick = performance.now();
299
+ raf = requestAnimationFrame(draw);
300
+ }
301
+ };
302
+ const onContextLost = (e) => {
303
+ e.preventDefault();
304
+ cancelAnimationFrame(raf);
305
+ raf = 0;
306
+ };
307
+ const onContextRestored = () => {
308
+ dispose();
309
+ };
310
+ const unsubscribeTheme = subscribeOcTheme(() => {
311
+ if (disposed) return;
312
+ const root = document.documentElement;
313
+ if (root.getAttribute("data-oc-motion") === "off") {
314
+ dispose();
315
+ return;
316
+ }
317
+ const next = samplePalette(container);
318
+ if (!next) return;
319
+ palFrom = { ...palTo, hues: [...palTo.hues] };
320
+ palTo = next;
321
+ mixStart = performance.now();
322
+ lastInput = performance.now();
323
+ });
324
+ window.addEventListener("pointermove", onInput, { passive: true });
325
+ window.addEventListener("scroll", onInput, { passive: true });
326
+ window.addEventListener("keydown", onInput, { passive: true });
327
+ document.addEventListener("visibilitychange", onVisibility);
328
+ canvas.addEventListener("webglcontextlost", onContextLost);
329
+ canvas.addEventListener("webglcontextrestored", onContextRestored);
330
+ function dispose() {
331
+ if (disposed) return;
332
+ disposed = true;
333
+ cancelAnimationFrame(raf);
334
+ unsubscribeTheme();
335
+ window.removeEventListener("pointermove", onInput);
336
+ window.removeEventListener("scroll", onInput);
337
+ window.removeEventListener("keydown", onInput);
338
+ document.removeEventListener("visibilitychange", onVisibility);
339
+ canvas.removeEventListener("webglcontextlost", onContextLost);
340
+ canvas.removeEventListener("webglcontextrestored", onContextRestored);
341
+ container.classList.remove("oc-aurora--gl-live");
342
+ canvas.remove();
343
+ const ext = gl.getExtension("WEBGL_lose_context");
344
+ if (ext) ext.loseContext();
345
+ }
346
+ resize();
347
+ mixStart = performance.now();
348
+ lastTick = performance.now();
349
+ raf = requestAnimationFrame(draw);
350
+ return dispose;
351
+ }
352
+ var KILL_KEY, KILL_TTL_MS, SETTLE_MS, FRAME_MS, CROSSFADE_MS, VS, FS;
353
+ var init_aurora_gl = __esm({
354
+ "src/tokens/aurora-gl.ts"() {
355
+ init_scene_tokens();
356
+ KILL_KEY = "oc-aurora-gl-kill:1";
357
+ KILL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
358
+ SETTLE_MS = 9e4;
359
+ FRAME_MS = 33;
360
+ CROSSFADE_MS = 250;
361
+ VS = `#version 300 es
362
+ in vec2 p;void main(){gl_Position=vec4(p,0.,1.);}`;
363
+ FS = `#version 300 es
364
+ precision highp float;
365
+ out vec4 o;
366
+ uniform vec2 uRes;uniform float uT,uIsDark,uFinish,uMix;
367
+ uniform vec3 uA0,uA1,uA2,uA3,uB0,uB1,uB2,uB3,uBg;
368
+ float h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
369
+ float n2(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);
370
+ return mix(mix(h(i),h(i+vec2(1,0)),f.x),mix(h(i+vec2(0,1)),h(i+vec2(1,1)),f.x),f.y);}
371
+ float fbm(vec2 p){float v=0.,a=.55;for(int i=0;i<3;i++){v+=a*n2(p);p=p*2.03+vec2(1.7,9.2);a*=.5;}return v;}
372
+ float B2(vec2 a){a=floor(a);return fract(a.x/2.+a.y*a.y*.75);}
373
+ // OKLab mix (Ottosson) so skin crossfades never transit gray-brown.
374
+ vec3 l2s(vec3 c){return mix(12.92*c,1.055*pow(c,vec3(1./2.4))-.055,step(.0031308,c));}
375
+ vec3 s2l(vec3 c){return mix(c/12.92,pow((c+.055)/1.055,vec3(2.4)),step(.04045,c));}
376
+ vec3 okmix(vec3 a,vec3 b,float t){
377
+ vec3 la=s2l(a),lb=s2l(b);
378
+ mat3 M1=mat3(.4122214708,.2119034982,.0883024619,.5363325363,.6806995451,.2817188376,.0514459929,.1073969566,.6299787005);
379
+ mat3 M2=mat3(4.0767416621,-1.2684380046,-.0041960863,-3.3077115913,2.6097574011,-.7034186147,.2309699292,-.3413193965,1.707614701);
380
+ vec3 ka=pow(M1*la,vec3(1./3.)),kb=pow(M1*lb,vec3(1./3.));
381
+ vec3 k=mix(ka,kb,t);return clamp(l2s(M2*(k*k*k)),0.,1.);}
382
+ void main(){
383
+ vec2 uv=gl_FragCoord.xy/uRes;
384
+ vec2 p=(uv-.5)*vec2(uRes.x/uRes.y,1.)*1.9;
385
+ float t=uT*.018;
386
+ vec2 w=vec2(fbm(p+vec2(0.,t)),fbm(p+vec2(5.2,1.3)-t*.7));
387
+ float f=fbm(p+1.9*w+vec2(t*.5,-t*.3));
388
+ float g=fbm(p*.6+w*1.2+vec2(9.1,3.7));
389
+ vec3 c0=okmix(uA0,uB0,uMix),c1=okmix(uA1,uB1,uMix),c2=okmix(uA2,uB2,uMix),c3=okmix(uA3,uB3,uMix);
390
+ vec3 c=mix(mix(c0,c1,smoothstep(.25,.55,g)),mix(c2,c3,smoothstep(.45,.8,g)),smoothstep(.4,.7,g));
391
+ float e=smoothstep(.32,.9,f);
392
+ if(uFinish>.5){
393
+ float b=B2(.125*gl_FragCoord.xy)*.0625+B2(.25*gl_FragCoord.xy)*.25+B2(.5*gl_FragCoord.xy);
394
+ e=floor(e*5.+b*.8-.4)/5.;}
395
+ // container CSS already applies the mode-op \xD7 intensity opacity + radial mask
396
+ vec3 col=(uIsDark>.5)?clamp(uBg+c*e*1.15,0.,1.):mix(uBg,c,e);
397
+ col+=(fract(52.9829189*fract(dot(gl_FragCoord.xy,vec2(0.06711056,0.00583715))))-.5)/255.;
398
+ o=vec4(col,1.);}`;
399
+ }
400
+ });
27
401
  function cn(...inputs) {
28
402
  return twMerge(clsx(inputs));
29
403
  }
@@ -73,13 +447,85 @@ function resolveTheme(id) {
73
447
  return isKnownTheme(id) ? id : DEFAULT_OC_THEME;
74
448
  }
75
449
  var OC_SKIN_COOKIE = "oc_skin";
450
+ var OC_MOTION_COOKIE = "oc_motion";
76
451
  var BLOBS = [1, 2, 3, 4, 5];
77
- function OcAurora({ intensity, className }) {
452
+ function onIdle(cb, timeout) {
453
+ if (typeof requestIdleCallback === "function") {
454
+ const id2 = requestIdleCallback(cb, { timeout });
455
+ return () => cancelIdleCallback(id2);
456
+ }
457
+ const id = setTimeout(cb, timeout);
458
+ return () => clearTimeout(id);
459
+ }
460
+ function OcAurora({ intensity, gl = false, className }) {
461
+ const hostRef = useRef(null);
462
+ useEffect(() => {
463
+ if (!gl) return;
464
+ const host = hostRef.current;
465
+ if (!host) return;
466
+ let disposed = false;
467
+ let disposeScene = null;
468
+ let cancelIdle = null;
469
+ const prm = matchMedia("(prefers-reduced-motion: reduce)");
470
+ const forced = matchMedia("(forced-colors: active)");
471
+ function gatesPass() {
472
+ if (prm.matches || forced.matches) return false;
473
+ if (document.documentElement.getAttribute("data-oc-motion") === "off") return false;
474
+ const nav = navigator;
475
+ if (nav.connection?.saveData === true) return false;
476
+ if (typeof nav.deviceMemory === "number" && nav.deviceMemory <= 4) return false;
477
+ return true;
478
+ }
479
+ function boot() {
480
+ cancelIdle = onIdle(async () => {
481
+ if (disposed || disposeScene || !gatesPass()) return;
482
+ try {
483
+ const mod = await Promise.resolve().then(() => (init_aurora_gl(), aurora_gl_exports));
484
+ if (disposed || disposeScene || !gatesPass()) return;
485
+ disposeScene = mod.mountAuroraGL(host);
486
+ } catch {
487
+ }
488
+ }, 2e3);
489
+ }
490
+ function teardownScene() {
491
+ disposeScene?.();
492
+ disposeScene = null;
493
+ }
494
+ const onPrmChange = () => {
495
+ if (prm.matches) teardownScene();
496
+ else if (!disposeScene) boot();
497
+ };
498
+ prm.addEventListener?.("change", onPrmChange);
499
+ const mo = new MutationObserver(() => {
500
+ const off = document.documentElement.getAttribute("data-oc-motion") === "off";
501
+ if (!off && !disposeScene && !disposed) boot();
502
+ });
503
+ mo.observe(document.documentElement, {
504
+ attributes: true,
505
+ attributeFilter: ["data-oc-motion"]
506
+ });
507
+ if (document.readyState === "complete") boot();
508
+ else {
509
+ const onLoad = () => boot();
510
+ window.addEventListener("load", onLoad, { once: true });
511
+ }
512
+ return () => {
513
+ disposed = true;
514
+ cancelIdle?.();
515
+ prm.removeEventListener?.("change", onPrmChange);
516
+ mo.disconnect();
517
+ teardownScene();
518
+ };
519
+ }, [gl]);
78
520
  const style = intensity == null ? void 0 : { "--oc-aurora-intensity": String(intensity) };
79
- return /* @__PURE__ */ jsx("div", { className: cn("oc-aurora", className), style, "aria-hidden": "true", children: BLOBS.map((n) => /* @__PURE__ */ jsx("div", { className: `oc-aurora__blob oc-aurora__blob-${n}` }, n)) });
521
+ return /* @__PURE__ */ jsxs("div", { ref: hostRef, className: cn("oc-aurora", className), style, "aria-hidden": "true", children: [
522
+ BLOBS.map((n) => /* @__PURE__ */ jsx("div", { className: `oc-aurora__blob oc-aurora__blob-${n}` }, n)),
523
+ /* @__PURE__ */ jsx("div", { className: "oc-aurora__grain" })
524
+ ] });
80
525
  }
81
526
  var ONE_YEAR = 60 * 60 * 24 * 365;
82
527
  var ATTR = "data-oc-theme";
528
+ var MOTION_ATTR = "data-oc-motion";
83
529
  function readSkinCookie() {
84
530
  if (typeof document === "undefined") return null;
85
531
  const prefix = `${OC_SKIN_COOKIE}=`;
@@ -111,6 +557,38 @@ function applyAttr(skin) {
111
557
  if (typeof document === "undefined") return;
112
558
  document.documentElement.setAttribute(ATTR, skin);
113
559
  }
560
+ function readMotionCookie() {
561
+ if (typeof document === "undefined") return null;
562
+ const prefix = `${OC_MOTION_COOKIE}=`;
563
+ for (const part of document.cookie.split(";")) {
564
+ const trimmed = part.trim();
565
+ if (trimmed.startsWith(prefix)) {
566
+ const v = decodeURIComponent(trimmed.slice(prefix.length));
567
+ return v === "off" ? "off" : v === "on" ? "on" : null;
568
+ }
569
+ }
570
+ return null;
571
+ }
572
+ function writeMotionCookie(value) {
573
+ if (typeof document === "undefined") return;
574
+ const isLocal = location.hostname === "localhost" || location.hostname === "127.0.0.1";
575
+ const parts = [
576
+ `${OC_MOTION_COOKIE}=${encodeURIComponent(value)}`,
577
+ "Path=/",
578
+ `Max-Age=${ONE_YEAR}`,
579
+ "SameSite=Lax"
580
+ ];
581
+ if (!isLocal) {
582
+ parts.push("Domain=.ochk.io");
583
+ parts.push("Secure");
584
+ }
585
+ document.cookie = parts.join("; ");
586
+ }
587
+ function applyMotionAttr(motion) {
588
+ if (typeof document === "undefined") return;
589
+ if (motion === "off") document.documentElement.setAttribute(MOTION_ATTR, "off");
590
+ else document.documentElement.removeAttribute(MOTION_ATTR);
591
+ }
114
592
  function applySkinChrome(skin) {
115
593
  if (typeof window === "undefined") return;
116
594
  if (typeof window.__ocApplySkinChrome === "function") {
@@ -127,6 +605,7 @@ function OcThemeProvider({
127
605
  aurora = true
128
606
  }) {
129
607
  const [skin, setSkinState] = useState(() => resolveTheme(defaultSkin));
608
+ const [motion, setMotionState] = useState("on");
130
609
  const hydratedRef = useRef(false);
131
610
  useEffect(() => {
132
611
  if (hydratedRef.current) return;
@@ -135,6 +614,9 @@ function OcThemeProvider({
135
614
  applyAttr(fromCookie);
136
615
  applySkinChrome(fromCookie);
137
616
  setSkinState(fromCookie);
617
+ const motionCookie = readMotionCookie() ?? "on";
618
+ applyMotionAttr(motionCookie);
619
+ setMotionState(motionCookie);
138
620
  }, [defaultSkin]);
139
621
  useEffect(() => {
140
622
  function sync() {
@@ -144,6 +626,11 @@ function OcThemeProvider({
144
626
  applySkinChrome(fromCookie);
145
627
  setSkinState(fromCookie);
146
628
  }
629
+ const motionCookie = readMotionCookie() ?? "on";
630
+ if (motionCookie !== motion) {
631
+ applyMotionAttr(motionCookie);
632
+ setMotionState(motionCookie);
633
+ }
147
634
  }
148
635
  window.addEventListener("focus", sync);
149
636
  document.addEventListener("visibilitychange", sync);
@@ -151,7 +638,7 @@ function OcThemeProvider({
151
638
  window.removeEventListener("focus", sync);
152
639
  document.removeEventListener("visibilitychange", sync);
153
640
  };
154
- }, [skin]);
641
+ }, [skin, motion]);
155
642
  const setSkin = useCallback((id) => {
156
643
  const next = resolveTheme(id);
157
644
  applyAttr(next);
@@ -159,12 +646,24 @@ function OcThemeProvider({
159
646
  writeSkinCookie(next);
160
647
  setSkinState(next);
161
648
  }, []);
649
+ const setMotion = useCallback((value2) => {
650
+ const next = value2 === "off" ? "off" : "on";
651
+ applyMotionAttr(next);
652
+ writeMotionCookie(next);
653
+ setMotionState(next);
654
+ }, []);
162
655
  const value = useMemo(
163
- () => ({ skin, setSkin, themes: OC_THEMES }),
164
- [skin, setSkin]
656
+ () => ({ skin, setSkin, themes: OC_THEMES, motion, setMotion }),
657
+ [skin, setSkin, motion, setMotion]
165
658
  );
166
659
  return /* @__PURE__ */ jsxs(OcSkinContext.Provider, { value, children: [
167
- aurora !== false && /* @__PURE__ */ jsx(OcAurora, { intensity: typeof aurora === "object" ? aurora.intensity : void 0 }),
660
+ aurora !== false && /* @__PURE__ */ jsx(
661
+ OcAurora,
662
+ {
663
+ intensity: typeof aurora === "object" ? aurora.intensity : void 0,
664
+ gl: typeof aurora === "object" ? aurora.gl : void 0
665
+ }
666
+ ),
168
667
  children
169
668
  ] });
170
669
  }
@@ -175,9 +674,13 @@ function useOcSkin() {
175
674
  }
176
675
  return ctx;
177
676
  }
677
+ function useOcMotion() {
678
+ const { motion, setMotion } = useOcSkin();
679
+ return { motion, setMotion };
680
+ }
178
681
  function getOcThemeInitScript(defaultSkin = DEFAULT_OC_THEME) {
179
682
  const accents = Object.fromEntries(OC_THEMES.map((t) => [t.id, t.accent]));
180
- return `(function(){var ACC=${JSON.stringify(accents)},DEF=${JSON.stringify(defaultSkin)},ORIG=${JSON.stringify(OC_DEFAULT_ACCENT)},fav=null;function acc(s){return ACC[s]||ACC[DEF]||ORIG;}window.__ocApplySkinChrome=function(s){try{var a=acc(s);var meta=document.querySelector('meta[name="theme-color"]');if(meta){meta.setAttribute('content',a);}var link=document.querySelector('link[rel~="icon"][type="image/svg+xml"]');if(!link){return;}var paint=function(svg){link.setAttribute('href','data:image/svg+xml,'+encodeURIComponent(svg.split(ORIG).join(a)));};if(fav!=null){paint(fav);return;}var h=link.getAttribute('href');if(!h||h.indexOf('data:')===0){return;}fetch(h).then(function(r){return r.text();}).then(function(svg){fav=svg;paint(svg);}).catch(function(){});}catch(e){}};try{var m=document.cookie.match(/(?:^|; )${OC_SKIN_COOKIE}=([^;]*)/);var s=m?decodeURIComponent(m[1]):DEF;if(!s){s=DEF;}document.documentElement.setAttribute('${ATTR}',s);window.__ocApplySkinChrome(s);}catch(e){document.documentElement.setAttribute('${ATTR}',DEF);}})();`;
683
+ return `(function(){var ACC=${JSON.stringify(accents)},DEF=${JSON.stringify(defaultSkin)},ORIG=${JSON.stringify(OC_DEFAULT_ACCENT)},fav=null;function acc(s){return ACC[s]||ACC[DEF]||ORIG;}window.__ocApplySkinChrome=function(s){try{var a=acc(s);var meta=document.querySelector('meta[name="theme-color"]');if(meta){meta.setAttribute('content',a);}var link=document.querySelector('link[rel~="icon"][type="image/svg+xml"]');if(!link){return;}var paint=function(svg){link.setAttribute('href','data:image/svg+xml,'+encodeURIComponent(svg.split(ORIG).join(a)));};if(fav!=null){paint(fav);return;}var h=link.getAttribute('href');if(!h||h.indexOf('data:')===0){return;}fetch(h).then(function(r){return r.text();}).then(function(svg){fav=svg;paint(svg);}).catch(function(){});}catch(e){}};try{var m=document.cookie.match(/(?:^|; )${OC_SKIN_COOKIE}=([^;]*)/);var s=m?decodeURIComponent(m[1]):DEF;if(!s){s=DEF;}document.documentElement.setAttribute('${ATTR}',s);window.__ocApplySkinChrome(s);}catch(e){document.documentElement.setAttribute('${ATTR}',DEF);}try{var mm=document.cookie.match(/(?:^|; )${OC_MOTION_COOKIE}=([^;]*)/);if(mm&&decodeURIComponent(mm[1])==='off'){document.documentElement.setAttribute('${MOTION_ATTR}','off');}}catch(e){}})();`;
181
684
  }
182
685
  var COOKIE = "oc_theme";
183
686
  var ALLOWED = /* @__PURE__ */ new Set(["light", "dark", "system"]);
@@ -312,10 +815,12 @@ var MODES = [
312
815
  ];
313
816
  function AppearanceControls({ className }) {
314
817
  const { skin, setSkin, themes } = useOcSkin();
818
+ const { motion, setMotion } = useOcMotion();
315
819
  const { theme, setTheme } = useTheme();
316
820
  const [mounted, setMounted] = useState(false);
317
821
  useEffect(() => setMounted(true), []);
318
822
  const activeMode = mounted ? theme ?? "system" : null;
823
+ const activeMotion = mounted ? motion : null;
319
824
  return /* @__PURE__ */ jsxs("div", { className, children: [
320
825
  /* @__PURE__ */ jsx("div", { className: "label-mono text-muted-foreground px-3 pt-3 pb-2", children: "mode" }),
321
826
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1 px-2 pb-2", children: MODES.map(({ id, label, Icon: Icon2 }) => {
@@ -362,6 +867,32 @@ function AppearanceControls({ className }) {
362
867
  ]
363
868
  }
364
869
  ) }, t.id);
870
+ }) }),
871
+ /* @__PURE__ */ jsx("div", { className: "label-mono text-muted-foreground border-t px-3 pt-3 pb-2", children: "ambient motion" }),
872
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1 px-2 pb-3", children: [
873
+ { id: "on", label: "on", Icon: Waves },
874
+ { id: "off", label: "off", Icon: Pause }
875
+ ].map(({ id, label, Icon: Icon2 }) => {
876
+ const active = activeMotion === id;
877
+ return /* @__PURE__ */ jsxs(
878
+ "button",
879
+ {
880
+ type: "button",
881
+ role: "menuitemradio",
882
+ "aria-checked": active,
883
+ "aria-label": id === "off" ? "pause ambient motion" : "ambient motion on",
884
+ onClick: () => setMotion(id),
885
+ className: cn(
886
+ "flex flex-col items-center gap-1 rounded-md border py-2 text-[11px] transition-colors",
887
+ active ? "border-primary text-foreground bg-accent" : "border-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground"
888
+ ),
889
+ children: [
890
+ /* @__PURE__ */ jsx(Icon2, { className: "size-4", "aria-hidden": true }),
891
+ label
892
+ ]
893
+ },
894
+ id
895
+ );
365
896
  }) })
366
897
  ] });
367
898
  }
@@ -417,6 +948,9 @@ function OcAppearanceMenu({
417
948
  )
418
949
  ] });
419
950
  }
951
+
952
+ // src/tokens/index.ts
953
+ init_scene_tokens();
420
954
  var buttonVariants = cva(
421
955
  "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
422
956
  {
@@ -4953,6 +5487,6 @@ function OcFamilyFooter({
4953
5487
  ] }) });
4954
5488
  }
4955
5489
 
4956
- export { AccentList, AccentNote, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertTitle, AlertWithAction, AlertWithCountdown, AppShell, AppearanceControls, Badge, Bar, BitcoinAddress, BrandBand, Button, Callout, Card, CheckList, Checkbox, ComparisonTable, ConfirmHost, CopyButton, DEFAULT_OC_THEME, DataRow, DefinitionList, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, EcosystemSwitcher, EmailCapture, EmptyState, ErrorBoundary, FAMILY_PROPERTIES, Faq, FeatureCard, HelpHint, IconBadge, Input, Label, LayoutSubHeader, MarketingHeading, Modal, NumberedStep, OC_DEFAULT_ACCENT, OC_SKIN_COOKIE, OC_THEMES, OcAccountMenu, OcAccountMenuView, OcAppBar, OcAppearanceMenu, OcAurora, OcDashboardHub, OcDashboardShell, OcFamilyFooter, OcLogoDropdown, OcPrimaryNav, OcThemeBridge, OcThemePicker, OcThemeProvider, Pagination, Panel, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PromptHost, QrCode, REFERENCE_BTC_USD, RadioGroup, RadioGroupItem, SITE_STATE_LABEL, SatsAmount, Section, SectionHeader, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, Sparkline, StatBlock, StatCard, StatGrid, StatTile, StatusPill, StepList, Surface, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeToggle, ThemeToggleLink, Toaster, Tooltip, TwoToneHeading, VerifiedChip, Working, WorkingPanel, accentFor, asOf, badgeVariants, buttonVariants, cn, confirm, explorerUrl, findFamilyProperty, formatSats, formatSatsCompact, getOcThemeInitScript, iconBadgeVariants, isKnownTheme, makeStatusPill, priceBoth, prompt, relativeTime, resolveTheme, satsToUsd, shortenAddress, surfaceVariants, usdToSats, useOcSkin, useSpotPrice };
5490
+ export { AccentList, AccentNote, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertTitle, AlertWithAction, AlertWithCountdown, AppShell, AppearanceControls, Badge, Bar, BitcoinAddress, BrandBand, Button, Callout, Card, CheckList, Checkbox, ComparisonTable, ConfirmHost, CopyButton, DEFAULT_OC_THEME, DataRow, DefinitionList, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, EcosystemSwitcher, EmailCapture, EmptyState, ErrorBoundary, FAMILY_PROPERTIES, Faq, FeatureCard, HelpHint, IconBadge, Input, Label, LayoutSubHeader, MarketingHeading, Modal, NumberedStep, OC_DEFAULT_ACCENT, OC_MOTION_COOKIE, OC_SKIN_COOKIE, OC_THEMES, OcAccountMenu, OcAccountMenuView, OcAppBar, OcAppearanceMenu, OcAurora, OcDashboardHub, OcDashboardShell, OcFamilyFooter, OcLogoDropdown, OcPrimaryNav, OcThemeBridge, OcThemePicker, OcThemeProvider, Pagination, Panel, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PromptHost, QrCode, REFERENCE_BTC_USD, RadioGroup, RadioGroupItem, SITE_STATE_LABEL, SatsAmount, Section, SectionHeader, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, Sparkline, StatBlock, StatCard, StatGrid, StatTile, StatusPill, StepList, Surface, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeToggle, ThemeToggleLink, Toaster, Tooltip, TwoToneHeading, VerifiedChip, Working, WorkingPanel, accentFor, asOf, badgeVariants, buttonVariants, cn, confirm, explorerUrl, findFamilyProperty, formatSats, formatSatsCompact, getOcThemeInitScript, iconBadgeVariants, isKnownTheme, makeStatusPill, normalizeHue, priceBoth, prompt, readOcSceneTokens, relativeTime, resolveTheme, satsToUsd, shortenAddress, subscribeOcTheme, surfaceVariants, usdToSats, useOcMotion, useOcSkin, useSpotPrice };
4957
5491
  //# sourceMappingURL=index.mjs.map
4958
5492
  //# sourceMappingURL=index.mjs.map