@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/tokens.mjs CHANGED
@@ -1,11 +1,385 @@
1
1
  import { clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
- import { createContext, useState, useRef, useEffect, useCallback, useMemo, useContext } from 'react';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { createContext, useRef, useEffect, useState, useCallback, useMemo, useContext } from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
5
  import { useTheme } from 'next-themes';
6
- import { Palette, Check, Sun, Moon, Monitor } from 'lucide-react';
6
+ import { Palette, Check, Sun, Moon, Monitor, Waves, Pause } from 'lucide-react';
7
7
 
8
- // src/tokens/cn.ts
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __esm = (fn, res) => function __init() {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ };
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+
18
+ // src/tokens/scene-tokens.ts
19
+ function parseColor(value) {
20
+ let m = value.match(/color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)/);
21
+ if (m?.[1] != null && m[2] != null && m[3] != null) {
22
+ return { r: +m[1], g: +m[2], b: +m[3] };
23
+ }
24
+ m = value.match(/rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)/);
25
+ if (m?.[1] != null && m[2] != null && m[3] != null) {
26
+ return { r: +m[1] / 255, g: +m[2] / 255, b: +m[3] / 255 };
27
+ }
28
+ return null;
29
+ }
30
+ function srgbToOklab({ r, g, b }) {
31
+ const lin = (c) => c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
32
+ const lr = lin(r);
33
+ const lg = lin(g);
34
+ const lb = lin(b);
35
+ const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
36
+ const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
37
+ const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
38
+ return {
39
+ L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
40
+ a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
41
+ b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
42
+ };
43
+ }
44
+ function oklabToSrgb(L, a, bb) {
45
+ const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * bb, 3);
46
+ const m = Math.pow(L - 0.1055613458 * a - 0.0638541728 * bb, 3);
47
+ const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * bb, 3);
48
+ const lr = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
49
+ const lg = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
50
+ const lb = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;
51
+ 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));
52
+ return { r: un(lr), g: un(lg), b: un(lb) };
53
+ }
54
+ function normalizeHue(c, isDark) {
55
+ const { L, a, b } = srgbToOklab(c);
56
+ const C = Math.hypot(a, b);
57
+ const targetL = isDark ? Math.min(0.75, Math.max(0.55, L)) : Math.min(0.95, Math.max(0.82, L + 0.25));
58
+ const scale = C > 1e-6 ? Math.min(C, 0.14) / C : 0;
59
+ return oklabToSrgb(targetL, a * scale, b * scale);
60
+ }
61
+ function readOcSceneTokens(host) {
62
+ if (typeof document === "undefined") return null;
63
+ const probe = document.createElement("div");
64
+ probe.setAttribute("aria-hidden", "true");
65
+ probe.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;pointer-events:none;";
66
+ const cells = SLOTS.map((slot) => {
67
+ const cell = document.createElement("span");
68
+ cell.style.color = `color-mix(in srgb, var(${slot}), var(${slot}))`;
69
+ probe.appendChild(cell);
70
+ return cell;
71
+ });
72
+ host.appendChild(probe);
73
+ try {
74
+ const parsed = cells.map((cell) => parseColor(getComputedStyle(cell).color));
75
+ const [orange, green, blue, violet, background] = parsed;
76
+ if (!orange || !green || !blue || !violet || !background) return null;
77
+ const root = document.documentElement;
78
+ return {
79
+ palette: [orange, green, blue, violet],
80
+ background,
81
+ isDark: root.classList.contains("dark"),
82
+ motionOff: root.getAttribute("data-oc-motion") === "off"
83
+ };
84
+ } finally {
85
+ host.removeChild(probe);
86
+ }
87
+ }
88
+ function subscribeOcTheme(cb) {
89
+ if (typeof document === "undefined") return () => {
90
+ };
91
+ let raf1 = 0;
92
+ let raf2 = 0;
93
+ const schedule = () => {
94
+ cancelAnimationFrame(raf1);
95
+ cancelAnimationFrame(raf2);
96
+ raf1 = requestAnimationFrame(() => {
97
+ raf2 = requestAnimationFrame(cb);
98
+ });
99
+ };
100
+ const mo = new MutationObserver(schedule);
101
+ mo.observe(document.documentElement, {
102
+ attributes: true,
103
+ attributeFilter: ["class", "data-oc-theme", "data-oc-motion"]
104
+ });
105
+ return () => {
106
+ mo.disconnect();
107
+ cancelAnimationFrame(raf1);
108
+ cancelAnimationFrame(raf2);
109
+ };
110
+ }
111
+ var SLOTS;
112
+ var init_scene_tokens = __esm({
113
+ "src/tokens/scene-tokens.ts"() {
114
+ SLOTS = ["--au-orange", "--au-green", "--au-blue", "--au-violet", "--background"];
115
+ }
116
+ });
117
+
118
+ // src/tokens/aurora-gl.ts
119
+ var aurora_gl_exports = {};
120
+ __export(aurora_gl_exports, {
121
+ mountAuroraGL: () => mountAuroraGL
122
+ });
123
+ function killFlagged() {
124
+ try {
125
+ const raw = localStorage.getItem(KILL_KEY);
126
+ if (!raw) return false;
127
+ if (Date.now() - Number(raw) > KILL_TTL_MS) {
128
+ localStorage.removeItem(KILL_KEY);
129
+ return false;
130
+ }
131
+ return true;
132
+ } catch {
133
+ return false;
134
+ }
135
+ }
136
+ function setKillFlag() {
137
+ try {
138
+ localStorage.setItem(KILL_KEY, String(Date.now()));
139
+ } catch {
140
+ }
141
+ }
142
+ function cypherFinish() {
143
+ const skin = document.documentElement.getAttribute("data-oc-theme");
144
+ return skin !== null && skin !== "ember";
145
+ }
146
+ function samplePalette(host) {
147
+ const tokens = readOcSceneTokens(host);
148
+ if (!tokens) return null;
149
+ return {
150
+ hues: tokens.palette.map((h) => normalizeHue(h, tokens.isDark)),
151
+ bg: tokens.background,
152
+ isDark: tokens.isDark,
153
+ finish: cypherFinish() ? 1 : 0
154
+ };
155
+ }
156
+ function mountAuroraGL(container) {
157
+ if (killFlagged()) return null;
158
+ if (getComputedStyle(container).getPropertyValue("--oc-aurora-gl").trim() === "off") {
159
+ return null;
160
+ }
161
+ const canvas = document.createElement("canvas");
162
+ canvas.className = "oc-aurora__gl";
163
+ const glMaybe = canvas.getContext("webgl2", {
164
+ antialias: false,
165
+ depth: false,
166
+ stencil: false,
167
+ alpha: false,
168
+ powerPreference: "low-power",
169
+ failIfMajorPerformanceCaveat: true
170
+ // SwiftShader/blocklist → CSS floor, free
171
+ });
172
+ if (!glMaybe) return null;
173
+ const gl = glMaybe;
174
+ const first = samplePalette(container);
175
+ if (!first) return null;
176
+ let prog;
177
+ const uni = {};
178
+ try {
179
+ const compile = (type, src) => {
180
+ const s = gl.createShader(type);
181
+ gl.shaderSource(s, src);
182
+ gl.compileShader(s);
183
+ if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error("compile");
184
+ return s;
185
+ };
186
+ prog = gl.createProgram();
187
+ gl.attachShader(prog, compile(gl.VERTEX_SHADER, VS));
188
+ gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, FS));
189
+ gl.linkProgram(prog);
190
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) throw new Error("link");
191
+ gl.useProgram(prog);
192
+ const buf = gl.createBuffer();
193
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
194
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
195
+ const loc = gl.getAttribLocation(prog, "p");
196
+ gl.enableVertexAttribArray(loc);
197
+ gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
198
+ for (const k of ["uRes", "uT", "uIsDark", "uFinish", "uMix", "uA0", "uA1", "uA2", "uA3", "uB0", "uB1", "uB2", "uB3", "uBg"]) {
199
+ uni[k] = gl.getUniformLocation(prog, k);
200
+ }
201
+ } catch {
202
+ return null;
203
+ }
204
+ container.appendChild(canvas);
205
+ let palFrom = first;
206
+ let palTo = first;
207
+ let mixStart = 0;
208
+ let raf = 0;
209
+ let lastFrame = 0;
210
+ let clock = 0;
211
+ let lastTick = 0;
212
+ let lastInput = performance.now();
213
+ let live = false;
214
+ let disposed = false;
215
+ let slowStreak = 0;
216
+ const setVec = (k, c) => gl.uniform3f(uni[k] ?? null, c.r, c.g, c.b);
217
+ function resize() {
218
+ const coarse = matchMedia("(pointer: coarse)").matches;
219
+ const dpr = Math.min(devicePixelRatio || 1, coarse ? 1.5 : 2) * 0.75;
220
+ const w = Math.max(1, Math.round(container.clientWidth * dpr));
221
+ const hgt = Math.max(1, Math.round(container.clientHeight * dpr));
222
+ if (canvas.width !== w || canvas.height !== hgt) {
223
+ canvas.width = w;
224
+ canvas.height = hgt;
225
+ gl.viewport(0, 0, w, hgt);
226
+ }
227
+ }
228
+ function draw(now) {
229
+ raf = requestAnimationFrame(draw);
230
+ if (now - lastFrame < FRAME_MS) return;
231
+ const frameGap = now - lastFrame;
232
+ lastFrame = now;
233
+ if (frameGap > FRAME_MS * 2.5) {
234
+ if (++slowStreak > 90) {
235
+ setKillFlag();
236
+ dispose();
237
+ return;
238
+ }
239
+ } else if (slowStreak > 0) slowStreak--;
240
+ const settled = now - lastInput > SETTLE_MS;
241
+ if (!settled) {
242
+ clock += Math.min(now - lastTick, 100) / 1e3;
243
+ }
244
+ lastTick = now;
245
+ if (settled && live && mixDone(now)) return;
246
+ resize();
247
+ const mix = Math.min(1, (now - mixStart) / CROSSFADE_MS);
248
+ gl.uniform2f(uni.uRes ?? null, canvas.width, canvas.height);
249
+ gl.uniform1f(uni.uT ?? null, clock);
250
+ gl.uniform1f(uni.uIsDark ?? null, palTo.isDark ? 1 : 0);
251
+ gl.uniform1f(uni.uFinish ?? null, palTo.finish);
252
+ gl.uniform1f(uni.uMix ?? null, mix);
253
+ setVec("uA0", palFrom.hues[0]);
254
+ setVec("uA1", palFrom.hues[1]);
255
+ setVec("uA2", palFrom.hues[2]);
256
+ setVec("uA3", palFrom.hues[3]);
257
+ setVec("uB0", palTo.hues[0]);
258
+ setVec("uB1", palTo.hues[1]);
259
+ setVec("uB2", palTo.hues[2]);
260
+ setVec("uB3", palTo.hues[3]);
261
+ setVec("uBg", palTo.bg);
262
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
263
+ if (!live) {
264
+ live = true;
265
+ container.classList.add("oc-aurora--gl-live");
266
+ }
267
+ }
268
+ function mixDone(now) {
269
+ return now - mixStart >= CROSSFADE_MS;
270
+ }
271
+ const onInput = () => {
272
+ lastInput = performance.now();
273
+ };
274
+ const onVisibility = () => {
275
+ if (document.hidden) {
276
+ cancelAnimationFrame(raf);
277
+ raf = 0;
278
+ } else if (!raf && !disposed) {
279
+ lastFrame = 0;
280
+ lastTick = performance.now();
281
+ raf = requestAnimationFrame(draw);
282
+ }
283
+ };
284
+ const onContextLost = (e) => {
285
+ e.preventDefault();
286
+ cancelAnimationFrame(raf);
287
+ raf = 0;
288
+ };
289
+ const onContextRestored = () => {
290
+ dispose();
291
+ };
292
+ const unsubscribeTheme = subscribeOcTheme(() => {
293
+ if (disposed) return;
294
+ const root = document.documentElement;
295
+ if (root.getAttribute("data-oc-motion") === "off") {
296
+ dispose();
297
+ return;
298
+ }
299
+ const next = samplePalette(container);
300
+ if (!next) return;
301
+ palFrom = { ...palTo, hues: [...palTo.hues] };
302
+ palTo = next;
303
+ mixStart = performance.now();
304
+ lastInput = performance.now();
305
+ });
306
+ window.addEventListener("pointermove", onInput, { passive: true });
307
+ window.addEventListener("scroll", onInput, { passive: true });
308
+ window.addEventListener("keydown", onInput, { passive: true });
309
+ document.addEventListener("visibilitychange", onVisibility);
310
+ canvas.addEventListener("webglcontextlost", onContextLost);
311
+ canvas.addEventListener("webglcontextrestored", onContextRestored);
312
+ function dispose() {
313
+ if (disposed) return;
314
+ disposed = true;
315
+ cancelAnimationFrame(raf);
316
+ unsubscribeTheme();
317
+ window.removeEventListener("pointermove", onInput);
318
+ window.removeEventListener("scroll", onInput);
319
+ window.removeEventListener("keydown", onInput);
320
+ document.removeEventListener("visibilitychange", onVisibility);
321
+ canvas.removeEventListener("webglcontextlost", onContextLost);
322
+ canvas.removeEventListener("webglcontextrestored", onContextRestored);
323
+ container.classList.remove("oc-aurora--gl-live");
324
+ canvas.remove();
325
+ const ext = gl.getExtension("WEBGL_lose_context");
326
+ if (ext) ext.loseContext();
327
+ }
328
+ resize();
329
+ mixStart = performance.now();
330
+ lastTick = performance.now();
331
+ raf = requestAnimationFrame(draw);
332
+ return dispose;
333
+ }
334
+ var KILL_KEY, KILL_TTL_MS, SETTLE_MS, FRAME_MS, CROSSFADE_MS, VS, FS;
335
+ var init_aurora_gl = __esm({
336
+ "src/tokens/aurora-gl.ts"() {
337
+ init_scene_tokens();
338
+ KILL_KEY = "oc-aurora-gl-kill:1";
339
+ KILL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
340
+ SETTLE_MS = 9e4;
341
+ FRAME_MS = 33;
342
+ CROSSFADE_MS = 250;
343
+ VS = `#version 300 es
344
+ in vec2 p;void main(){gl_Position=vec4(p,0.,1.);}`;
345
+ FS = `#version 300 es
346
+ precision highp float;
347
+ out vec4 o;
348
+ uniform vec2 uRes;uniform float uT,uIsDark,uFinish,uMix;
349
+ uniform vec3 uA0,uA1,uA2,uA3,uB0,uB1,uB2,uB3,uBg;
350
+ float h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
351
+ float n2(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);
352
+ 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);}
353
+ 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;}
354
+ float B2(vec2 a){a=floor(a);return fract(a.x/2.+a.y*a.y*.75);}
355
+ // OKLab mix (Ottosson) so skin crossfades never transit gray-brown.
356
+ vec3 l2s(vec3 c){return mix(12.92*c,1.055*pow(c,vec3(1./2.4))-.055,step(.0031308,c));}
357
+ vec3 s2l(vec3 c){return mix(c/12.92,pow((c+.055)/1.055,vec3(2.4)),step(.04045,c));}
358
+ vec3 okmix(vec3 a,vec3 b,float t){
359
+ vec3 la=s2l(a),lb=s2l(b);
360
+ mat3 M1=mat3(.4122214708,.2119034982,.0883024619,.5363325363,.6806995451,.2817188376,.0514459929,.1073969566,.6299787005);
361
+ mat3 M2=mat3(4.0767416621,-1.2684380046,-.0041960863,-3.3077115913,2.6097574011,-.7034186147,.2309699292,-.3413193965,1.707614701);
362
+ vec3 ka=pow(M1*la,vec3(1./3.)),kb=pow(M1*lb,vec3(1./3.));
363
+ vec3 k=mix(ka,kb,t);return clamp(l2s(M2*(k*k*k)),0.,1.);}
364
+ void main(){
365
+ vec2 uv=gl_FragCoord.xy/uRes;
366
+ vec2 p=(uv-.5)*vec2(uRes.x/uRes.y,1.)*1.9;
367
+ float t=uT*.018;
368
+ vec2 w=vec2(fbm(p+vec2(0.,t)),fbm(p+vec2(5.2,1.3)-t*.7));
369
+ float f=fbm(p+1.9*w+vec2(t*.5,-t*.3));
370
+ float g=fbm(p*.6+w*1.2+vec2(9.1,3.7));
371
+ vec3 c0=okmix(uA0,uB0,uMix),c1=okmix(uA1,uB1,uMix),c2=okmix(uA2,uB2,uMix),c3=okmix(uA3,uB3,uMix);
372
+ vec3 c=mix(mix(c0,c1,smoothstep(.25,.55,g)),mix(c2,c3,smoothstep(.45,.8,g)),smoothstep(.4,.7,g));
373
+ float e=smoothstep(.32,.9,f);
374
+ if(uFinish>.5){
375
+ float b=B2(.125*gl_FragCoord.xy)*.0625+B2(.25*gl_FragCoord.xy)*.25+B2(.5*gl_FragCoord.xy);
376
+ e=floor(e*5.+b*.8-.4)/5.;}
377
+ // container CSS already applies the mode-op \xD7 intensity opacity + radial mask
378
+ vec3 col=(uIsDark>.5)?clamp(uBg+c*e*1.15,0.,1.):mix(uBg,c,e);
379
+ col+=(fract(52.9829189*fract(dot(gl_FragCoord.xy,vec2(0.06711056,0.00583715))))-.5)/255.;
380
+ o=vec4(col,1.);}`;
381
+ }
382
+ });
9
383
  function cn(...inputs) {
10
384
  return twMerge(clsx(inputs));
11
385
  }
@@ -55,13 +429,85 @@ function resolveTheme(id) {
55
429
  return isKnownTheme(id) ? id : DEFAULT_OC_THEME;
56
430
  }
57
431
  var OC_SKIN_COOKIE = "oc_skin";
432
+ var OC_MOTION_COOKIE = "oc_motion";
58
433
  var BLOBS = [1, 2, 3, 4, 5];
59
- function OcAurora({ intensity, className }) {
434
+ function onIdle(cb, timeout) {
435
+ if (typeof requestIdleCallback === "function") {
436
+ const id2 = requestIdleCallback(cb, { timeout });
437
+ return () => cancelIdleCallback(id2);
438
+ }
439
+ const id = setTimeout(cb, timeout);
440
+ return () => clearTimeout(id);
441
+ }
442
+ function OcAurora({ intensity, gl = false, className }) {
443
+ const hostRef = useRef(null);
444
+ useEffect(() => {
445
+ if (!gl) return;
446
+ const host = hostRef.current;
447
+ if (!host) return;
448
+ let disposed = false;
449
+ let disposeScene = null;
450
+ let cancelIdle = null;
451
+ const prm = matchMedia("(prefers-reduced-motion: reduce)");
452
+ const forced = matchMedia("(forced-colors: active)");
453
+ function gatesPass() {
454
+ if (prm.matches || forced.matches) return false;
455
+ if (document.documentElement.getAttribute("data-oc-motion") === "off") return false;
456
+ const nav = navigator;
457
+ if (nav.connection?.saveData === true) return false;
458
+ if (typeof nav.deviceMemory === "number" && nav.deviceMemory <= 4) return false;
459
+ return true;
460
+ }
461
+ function boot() {
462
+ cancelIdle = onIdle(async () => {
463
+ if (disposed || disposeScene || !gatesPass()) return;
464
+ try {
465
+ const mod = await Promise.resolve().then(() => (init_aurora_gl(), aurora_gl_exports));
466
+ if (disposed || disposeScene || !gatesPass()) return;
467
+ disposeScene = mod.mountAuroraGL(host);
468
+ } catch {
469
+ }
470
+ }, 2e3);
471
+ }
472
+ function teardownScene() {
473
+ disposeScene?.();
474
+ disposeScene = null;
475
+ }
476
+ const onPrmChange = () => {
477
+ if (prm.matches) teardownScene();
478
+ else if (!disposeScene) boot();
479
+ };
480
+ prm.addEventListener?.("change", onPrmChange);
481
+ const mo = new MutationObserver(() => {
482
+ const off = document.documentElement.getAttribute("data-oc-motion") === "off";
483
+ if (!off && !disposeScene && !disposed) boot();
484
+ });
485
+ mo.observe(document.documentElement, {
486
+ attributes: true,
487
+ attributeFilter: ["data-oc-motion"]
488
+ });
489
+ if (document.readyState === "complete") boot();
490
+ else {
491
+ const onLoad = () => boot();
492
+ window.addEventListener("load", onLoad, { once: true });
493
+ }
494
+ return () => {
495
+ disposed = true;
496
+ cancelIdle?.();
497
+ prm.removeEventListener?.("change", onPrmChange);
498
+ mo.disconnect();
499
+ teardownScene();
500
+ };
501
+ }, [gl]);
60
502
  const style = intensity == null ? void 0 : { "--oc-aurora-intensity": String(intensity) };
61
- 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)) });
503
+ return /* @__PURE__ */ jsxs("div", { ref: hostRef, className: cn("oc-aurora", className), style, "aria-hidden": "true", children: [
504
+ BLOBS.map((n) => /* @__PURE__ */ jsx("div", { className: `oc-aurora__blob oc-aurora__blob-${n}` }, n)),
505
+ /* @__PURE__ */ jsx("div", { className: "oc-aurora__grain" })
506
+ ] });
62
507
  }
63
508
  var ONE_YEAR = 60 * 60 * 24 * 365;
64
509
  var ATTR = "data-oc-theme";
510
+ var MOTION_ATTR = "data-oc-motion";
65
511
  function readSkinCookie() {
66
512
  if (typeof document === "undefined") return null;
67
513
  const prefix = `${OC_SKIN_COOKIE}=`;
@@ -93,6 +539,38 @@ function applyAttr(skin) {
93
539
  if (typeof document === "undefined") return;
94
540
  document.documentElement.setAttribute(ATTR, skin);
95
541
  }
542
+ function readMotionCookie() {
543
+ if (typeof document === "undefined") return null;
544
+ const prefix = `${OC_MOTION_COOKIE}=`;
545
+ for (const part of document.cookie.split(";")) {
546
+ const trimmed = part.trim();
547
+ if (trimmed.startsWith(prefix)) {
548
+ const v = decodeURIComponent(trimmed.slice(prefix.length));
549
+ return v === "off" ? "off" : v === "on" ? "on" : null;
550
+ }
551
+ }
552
+ return null;
553
+ }
554
+ function writeMotionCookie(value) {
555
+ if (typeof document === "undefined") return;
556
+ const isLocal = location.hostname === "localhost" || location.hostname === "127.0.0.1";
557
+ const parts = [
558
+ `${OC_MOTION_COOKIE}=${encodeURIComponent(value)}`,
559
+ "Path=/",
560
+ `Max-Age=${ONE_YEAR}`,
561
+ "SameSite=Lax"
562
+ ];
563
+ if (!isLocal) {
564
+ parts.push("Domain=.ochk.io");
565
+ parts.push("Secure");
566
+ }
567
+ document.cookie = parts.join("; ");
568
+ }
569
+ function applyMotionAttr(motion) {
570
+ if (typeof document === "undefined") return;
571
+ if (motion === "off") document.documentElement.setAttribute(MOTION_ATTR, "off");
572
+ else document.documentElement.removeAttribute(MOTION_ATTR);
573
+ }
96
574
  function applySkinChrome(skin) {
97
575
  if (typeof window === "undefined") return;
98
576
  if (typeof window.__ocApplySkinChrome === "function") {
@@ -109,6 +587,7 @@ function OcThemeProvider({
109
587
  aurora = true
110
588
  }) {
111
589
  const [skin, setSkinState] = useState(() => resolveTheme(defaultSkin));
590
+ const [motion, setMotionState] = useState("on");
112
591
  const hydratedRef = useRef(false);
113
592
  useEffect(() => {
114
593
  if (hydratedRef.current) return;
@@ -117,6 +596,9 @@ function OcThemeProvider({
117
596
  applyAttr(fromCookie);
118
597
  applySkinChrome(fromCookie);
119
598
  setSkinState(fromCookie);
599
+ const motionCookie = readMotionCookie() ?? "on";
600
+ applyMotionAttr(motionCookie);
601
+ setMotionState(motionCookie);
120
602
  }, [defaultSkin]);
121
603
  useEffect(() => {
122
604
  function sync() {
@@ -126,6 +608,11 @@ function OcThemeProvider({
126
608
  applySkinChrome(fromCookie);
127
609
  setSkinState(fromCookie);
128
610
  }
611
+ const motionCookie = readMotionCookie() ?? "on";
612
+ if (motionCookie !== motion) {
613
+ applyMotionAttr(motionCookie);
614
+ setMotionState(motionCookie);
615
+ }
129
616
  }
130
617
  window.addEventListener("focus", sync);
131
618
  document.addEventListener("visibilitychange", sync);
@@ -133,7 +620,7 @@ function OcThemeProvider({
133
620
  window.removeEventListener("focus", sync);
134
621
  document.removeEventListener("visibilitychange", sync);
135
622
  };
136
- }, [skin]);
623
+ }, [skin, motion]);
137
624
  const setSkin = useCallback((id) => {
138
625
  const next = resolveTheme(id);
139
626
  applyAttr(next);
@@ -141,12 +628,24 @@ function OcThemeProvider({
141
628
  writeSkinCookie(next);
142
629
  setSkinState(next);
143
630
  }, []);
631
+ const setMotion = useCallback((value2) => {
632
+ const next = value2 === "off" ? "off" : "on";
633
+ applyMotionAttr(next);
634
+ writeMotionCookie(next);
635
+ setMotionState(next);
636
+ }, []);
144
637
  const value = useMemo(
145
- () => ({ skin, setSkin, themes: OC_THEMES }),
146
- [skin, setSkin]
638
+ () => ({ skin, setSkin, themes: OC_THEMES, motion, setMotion }),
639
+ [skin, setSkin, motion, setMotion]
147
640
  );
148
641
  return /* @__PURE__ */ jsxs(OcSkinContext.Provider, { value, children: [
149
- aurora !== false && /* @__PURE__ */ jsx(OcAurora, { intensity: typeof aurora === "object" ? aurora.intensity : void 0 }),
642
+ aurora !== false && /* @__PURE__ */ jsx(
643
+ OcAurora,
644
+ {
645
+ intensity: typeof aurora === "object" ? aurora.intensity : void 0,
646
+ gl: typeof aurora === "object" ? aurora.gl : void 0
647
+ }
648
+ ),
150
649
  children
151
650
  ] });
152
651
  }
@@ -157,9 +656,13 @@ function useOcSkin() {
157
656
  }
158
657
  return ctx;
159
658
  }
659
+ function useOcMotion() {
660
+ const { motion, setMotion } = useOcSkin();
661
+ return { motion, setMotion };
662
+ }
160
663
  function getOcThemeInitScript(defaultSkin = DEFAULT_OC_THEME) {
161
664
  const accents = Object.fromEntries(OC_THEMES.map((t) => [t.id, t.accent]));
162
- 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);}})();`;
665
+ 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){}})();`;
163
666
  }
164
667
  var COOKIE = "oc_theme";
165
668
  var ALLOWED = /* @__PURE__ */ new Set(["light", "dark", "system"]);
@@ -294,10 +797,12 @@ var MODES = [
294
797
  ];
295
798
  function AppearanceControls({ className }) {
296
799
  const { skin, setSkin, themes } = useOcSkin();
800
+ const { motion, setMotion } = useOcMotion();
297
801
  const { theme, setTheme } = useTheme();
298
802
  const [mounted, setMounted] = useState(false);
299
803
  useEffect(() => setMounted(true), []);
300
804
  const activeMode = mounted ? theme ?? "system" : null;
805
+ const activeMotion = mounted ? motion : null;
301
806
  return /* @__PURE__ */ jsxs("div", { className, children: [
302
807
  /* @__PURE__ */ jsx("div", { className: "label-mono text-muted-foreground px-3 pt-3 pb-2", children: "mode" }),
303
808
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1 px-2 pb-2", children: MODES.map(({ id, label, Icon }) => {
@@ -344,6 +849,32 @@ function AppearanceControls({ className }) {
344
849
  ]
345
850
  }
346
851
  ) }, t.id);
852
+ }) }),
853
+ /* @__PURE__ */ jsx("div", { className: "label-mono text-muted-foreground border-t px-3 pt-3 pb-2", children: "ambient motion" }),
854
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1 px-2 pb-3", children: [
855
+ { id: "on", label: "on", Icon: Waves },
856
+ { id: "off", label: "off", Icon: Pause }
857
+ ].map(({ id, label, Icon }) => {
858
+ const active = activeMotion === id;
859
+ return /* @__PURE__ */ jsxs(
860
+ "button",
861
+ {
862
+ type: "button",
863
+ role: "menuitemradio",
864
+ "aria-checked": active,
865
+ "aria-label": id === "off" ? "pause ambient motion" : "ambient motion on",
866
+ onClick: () => setMotion(id),
867
+ className: cn(
868
+ "flex flex-col items-center gap-1 rounded-md border py-2 text-[11px] transition-colors",
869
+ active ? "border-primary text-foreground bg-accent" : "border-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground"
870
+ ),
871
+ children: [
872
+ /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": true }),
873
+ label
874
+ ]
875
+ },
876
+ id
877
+ );
347
878
  }) })
348
879
  ] });
349
880
  }
@@ -400,6 +931,9 @@ function OcAppearanceMenu({
400
931
  ] });
401
932
  }
402
933
 
403
- export { AppearanceControls, DEFAULT_OC_THEME, OC_DEFAULT_ACCENT, OC_SKIN_COOKIE, OC_THEMES, OcAppearanceMenu, OcAurora, OcThemeBridge, OcThemePicker, OcThemeProvider, accentFor, cn, getOcThemeInitScript, isKnownTheme, resolveTheme, useOcSkin };
934
+ // src/tokens/index.ts
935
+ init_scene_tokens();
936
+
937
+ export { AppearanceControls, DEFAULT_OC_THEME, OC_DEFAULT_ACCENT, OC_MOTION_COOKIE, OC_SKIN_COOKIE, OC_THEMES, OcAppearanceMenu, OcAurora, OcThemeBridge, OcThemePicker, OcThemeProvider, accentFor, cn, getOcThemeInitScript, isKnownTheme, normalizeHue, readOcSceneTokens, resolveTheme, subscribeOcTheme, useOcMotion, useOcSkin };
404
938
  //# sourceMappingURL=tokens.mjs.map
405
939
  //# sourceMappingURL=tokens.mjs.map