@almadar/ui 6.54.0 → 6.56.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.
Files changed (114) hide show
  1. package/dist/{AwaitingSkeletonContext-Bh399p1x.d.ts → AwaitingSkeletonContext-DPgW__WV.d.ts} +3 -1
  2. package/dist/{Canvas3DHost-B-y2TNij.d.ts → Canvas3DHost-DfAhg6EH.d.ts} +2 -0
  3. package/dist/Typography-7PDT62PG.js +5 -0
  4. package/dist/{UISlotRenderer-QUEG6WFM.js → UISlotRenderer-QTHFT6Z4.js} +6 -6
  5. package/dist/avl/index.cjs +3385 -1883
  6. package/dist/avl/index.js +45 -737
  7. package/dist/{chunk-ERXIHZW3.js → chunk-D2E44ODB.js} +29 -15
  8. package/dist/{chunk-FJDS4SVD.js → chunk-F4ZTPKZL.js} +3 -3
  9. package/dist/{chunk-U3CGKPOK.js → chunk-FQM37C56.js} +136 -107
  10. package/dist/{chunk-4UP6BZ4R.js → chunk-HCSJHRC4.js} +1 -1
  11. package/dist/{chunk-N2V4LHFQ.js → chunk-HRCR53OW.js} +10 -1
  12. package/dist/{chunk-O7OLVVBR.js → chunk-Q47ZN2DW.js} +24 -22
  13. package/dist/{chunk-4HC5HRHB.js → chunk-QGH4NAQO.js} +2 -2
  14. package/dist/{chunk-MCKFWPT2.js → chunk-RNY5LJXJ.js} +1 -1
  15. package/dist/{chunk-Z7JWXIRE.js → chunk-SGJHVPHC.js} +2 -2
  16. package/dist/{chunk-6O37FCX4.js → chunk-V3FGTQUE.js} +37 -2
  17. package/dist/{chunk-U2FGMHQP.js → chunk-XZKGRSIO.js} +3334 -1796
  18. package/dist/{chunk-Z5IYJU44.js → chunk-Y53Q55H5.js} +521 -24
  19. package/dist/components/index.cjs +5194 -3008
  20. package/dist/components/index.d.ts +117 -52
  21. package/dist/components/index.js +13 -13
  22. package/dist/context/index.cjs +36 -1
  23. package/dist/context/index.d.ts +1 -1
  24. package/dist/context/index.js +1 -1
  25. package/dist/design-classes/index.cjs +29 -15
  26. package/dist/design-classes/index.d.cts +1 -0
  27. package/dist/design-classes/index.d.ts +1 -0
  28. package/dist/design-classes/index.js +29 -15
  29. package/dist/hooks/index.cjs +10 -1
  30. package/dist/hooks/index.js +3 -3
  31. package/dist/lib/drawable/three/index.cjs +442 -82
  32. package/dist/lib/drawable/three/index.d.ts +2 -2
  33. package/dist/lib/drawable/three/index.js +28 -24
  34. package/dist/lib/index.cjs +52 -3
  35. package/dist/lib/index.d.ts +1 -1
  36. package/dist/lib/index.js +4 -4
  37. package/dist/locales/index.cjs +30 -3
  38. package/dist/locales/index.js +21 -3
  39. package/dist/providers/index.cjs +5330 -3091
  40. package/dist/providers/index.d.ts +1 -1
  41. package/dist/providers/index.js +12 -9
  42. package/dist/runtime/index.cjs +5400 -3182
  43. package/dist/runtime/index.d.ts +7 -3
  44. package/dist/runtime/index.js +8 -8
  45. package/dist/ssr/index.cjs +24 -8
  46. package/dist/ssr/index.js +24 -8
  47. package/dist/{themeTokens-Ccu7EmGY.d.ts → themeTokens-D6MFwKrC.d.ts} +6 -2
  48. package/locales/ar.json +10 -1
  49. package/locales/en.json +10 -1
  50. package/locales/sl.json +10 -1
  51. package/package.json +6 -6
  52. package/scripts/theme-contrast-audit.mjs +20 -1
  53. package/tailwind-preset.cjs +3 -0
  54. package/themes/_base.css +61 -0
  55. package/themes/_contract.md +37 -0
  56. package/themes/almadar-heritage.css +82 -0
  57. package/themes/almadar-website.css +82 -0
  58. package/themes/almadar.css +82 -0
  59. package/themes/aqua.css +82 -0
  60. package/themes/arabesque.css +82 -0
  61. package/themes/art-deco.css +82 -0
  62. package/themes/art-nouveau.css +82 -0
  63. package/themes/atelier.css +82 -0
  64. package/themes/bauhaus.css +82 -0
  65. package/themes/bloomberg-dense.css +83 -1
  66. package/themes/blueprint.css +84 -0
  67. package/themes/clay.css +82 -0
  68. package/themes/comic.css +82 -0
  69. package/themes/constructivist.css +82 -0
  70. package/themes/corporate.css +82 -0
  71. package/themes/cyberpunk.css +82 -0
  72. package/themes/frutiger-aero.css +83 -0
  73. package/themes/game-adventure.css +15 -0
  74. package/themes/game-rpg.css +16 -0
  75. package/themes/game-sci-fi.css +15 -0
  76. package/themes/game-ui-pack.css +15 -0
  77. package/themes/gazette.css +82 -0
  78. package/themes/glass.css +82 -0
  79. package/themes/gothic.css +82 -0
  80. package/themes/ink-wash.css +82 -0
  81. package/themes/kawaii.css +82 -0
  82. package/themes/kiosk.css +82 -0
  83. package/themes/linear-clean.css +82 -0
  84. package/themes/luxury.css +82 -0
  85. package/themes/mac-classic.css +90 -8
  86. package/themes/material.css +82 -0
  87. package/themes/memphis.css +82 -0
  88. package/themes/mid-century.css +82 -0
  89. package/themes/minimalist.css +82 -0
  90. package/themes/mudcloth.css +82 -0
  91. package/themes/neon.css +83 -0
  92. package/themes/neumorphic.css +82 -0
  93. package/themes/newsprint.css +82 -0
  94. package/themes/notion-editorial.css +82 -0
  95. package/themes/orb.css +82 -0
  96. package/themes/pixel.css +82 -0
  97. package/themes/prism.css +82 -0
  98. package/themes/retro.css +83 -0
  99. package/themes/risograph.css +82 -0
  100. package/themes/scandi.css +82 -0
  101. package/themes/sketch.css +82 -0
  102. package/themes/solarpunk.css +82 -0
  103. package/themes/steampunk.css +82 -0
  104. package/themes/swiss.css +82 -0
  105. package/themes/terminal.css +84 -1
  106. package/themes/trait-wars.css +82 -0
  107. package/themes/ukiyo-e.css +82 -0
  108. package/themes/vaporwave.css +82 -0
  109. package/themes/victorian.css +82 -0
  110. package/themes/wabi-sabi.css +82 -0
  111. package/themes/win95.css +84 -0
  112. package/themes/wireframe.css +82 -0
  113. package/themes/zine.css +82 -0
  114. package/dist/Typography-CFI7JUVE.js +0 -5
@@ -1,23 +1,26 @@
1
1
  import { useTapReveal } from './chunk-XJTHMW4G.js';
2
2
  import { useEventBus } from './chunk-6EV3OEBQ.js';
3
- import { updateAssetStatus } from './chunk-O7OLVVBR.js';
3
+ import { updateAssetStatus } from './chunk-Q47ZN2DW.js';
4
4
  import { cn, domPassthrough } from './chunk-64WACXM7.js';
5
+ import { __publicField } from './chunk-QPXIAUJD.js';
5
6
  import { createLogger } from '@almadar/logger';
6
- import React, { useCallback, createContext, useContext } from 'react';
7
- import { useThemeScope } from '@almadar/ui/context';
7
+ import React, { useCallback, createContext, useContext, useState, useLayoutEffect } from 'react';
8
+ import { useThemeScope, useTheme } from '@almadar/ui/context';
8
9
  import { jsx } from 'react/jsx-runtime';
9
10
  import { renderUiEntriesOf } from '@almadar/core/patterns';
10
- import { isInlineTrait } from '@almadar/core';
11
+ import { DIAGRAM_TONES, isInlineTrait, DIAGRAM_TONE_COLOR_KEYS, DIAGRAM_TEXT_SIZES } from '@almadar/core';
11
12
  import { parseListenSource } from '@almadar/runtime';
12
13
 
13
14
  var log = createLogger("almadar:ui:theme-color");
14
15
  var THEME_SERIES = [
15
- "var(--color-primary)",
16
- "var(--color-success)",
17
- "var(--color-warning)",
18
- "var(--color-error)",
19
- "var(--color-info)",
20
- "var(--color-accent)"
16
+ "var(--color-series-1)",
17
+ "var(--color-series-2)",
18
+ "var(--color-series-3)",
19
+ "var(--color-series-4)",
20
+ "var(--color-series-5)",
21
+ "var(--color-series-6)",
22
+ "var(--color-series-7)",
23
+ "var(--color-series-8)"
21
24
  ];
22
25
  function channel(value, unitScale) {
23
26
  return Math.round(Math.min(255, Math.max(0, Number(value) * unitScale)));
@@ -25,8 +28,40 @@ function channel(value, unitScale) {
25
28
  function make(r, g, b, a) {
26
29
  return { r, g, b, a, css: `rgb(${r}, ${g}, ${b})` };
27
30
  }
31
+ function gamma(linear) {
32
+ const c = Math.min(1, Math.max(0, linear));
33
+ return Math.round(255 * (c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055));
34
+ }
35
+ function fromOklab(L, a, b, alpha) {
36
+ const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
37
+ const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
38
+ const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
39
+ return make(
40
+ gamma(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
41
+ gamma(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
42
+ gamma(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s),
43
+ alpha
44
+ );
45
+ }
46
+ function component(raw, percentOf) {
47
+ if (raw === "none") return 0;
48
+ return raw.endsWith("%") ? Number(raw.slice(0, -1)) / 100 * percentOf : Number(raw);
49
+ }
50
+ var NUM = "(none|-?[\\d.]+(?:e-?\\d+)?%?)";
51
+ var OKLCH = new RegExp(`^oklch\\(\\s*${NUM}\\s+${NUM}\\s+${NUM}(?:deg)?\\s*(?:\\/\\s*${NUM}\\s*)?\\)$`, "i");
52
+ var OKLAB = new RegExp(`^oklab\\(\\s*${NUM}\\s+${NUM}\\s+${NUM}\\s*(?:\\/\\s*${NUM}\\s*)?\\)$`, "i");
28
53
  function parseComputedColor(value) {
29
54
  const v = value.trim();
55
+ const lch = OKLCH.exec(v);
56
+ if (lch) {
57
+ const hue = component(lch[3], 360) * Math.PI / 180;
58
+ const chroma = component(lch[2], 0.4);
59
+ return fromOklab(component(lch[1], 1), chroma * Math.cos(hue), chroma * Math.sin(hue), lch[4] === void 0 ? 1 : component(lch[4], 1));
60
+ }
61
+ const lab = OKLAB.exec(v);
62
+ if (lab) {
63
+ return fromOklab(component(lab[1], 1), component(lab[2], 0.4), component(lab[3], 0.4), lab[4] === void 0 ? 1 : component(lab[4], 1));
64
+ }
30
65
  const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(v);
31
66
  if (hex) {
32
67
  const h = hex[1].length === 3 ? hex[1].split("").map((c) => c + c).join("") : hex[1];
@@ -678,30 +713,40 @@ function instanceOf(orbital, orbitalId, ref) {
678
713
  if (typeof ref === "object" && ref._resolved) return { ...at, name: ref.name ?? ref._resolved.name, trait: ref._resolved };
679
714
  return null;
680
715
  }
716
+ var wiresBySchema = /* @__PURE__ */ new WeakMap();
681
717
  function traitEventWires(schema) {
718
+ const cached = wiresBySchema.get(schema);
719
+ if (cached) return cached;
682
720
  const instances = (schema.orbitals ?? []).flatMap(
683
721
  (o) => (o.traits ?? []).map((ref) => instanceOf(o.name, o.id, ref)).filter((i) => i !== null)
684
722
  );
723
+ const listensByEvent = /* @__PURE__ */ new Map();
724
+ for (const listener of instances) {
725
+ for (const listen of listener.trait.listens ?? []) {
726
+ const { bareEvent, matcher } = parseListenSource(listen, listener.orbital);
727
+ const bucket = listensByEvent.get(bareEvent);
728
+ if (bucket) bucket.push({ listener, matcher });
729
+ else listensByEvent.set(bareEvent, [{ listener, matcher }]);
730
+ }
731
+ }
685
732
  const wires = [];
686
733
  for (const emitter of instances) {
734
+ const source = { orbital: emitter.orbital, trait: emitter.name, ...emitter.orbitalId ? { orbitalId: emitter.orbitalId } : {}, ...emitter.trait.id ? { traitId: emitter.trait.id } : {} };
687
735
  for (const emit of emitter.trait.emits ?? []) {
688
- for (const listener of instances) {
736
+ for (const { listener, matcher } of listensByEvent.get(emit.event) ?? []) {
689
737
  if (listener.orbital === emitter.orbital && listener.name === emitter.name) continue;
690
- for (const listen of listener.trait.listens ?? []) {
691
- const { bareEvent, matcher } = parseListenSource(listen, listener.orbital);
692
- const source = { orbital: emitter.orbital, trait: emitter.name, ...emitter.orbitalId ? { orbitalId: emitter.orbitalId } : {}, ...emitter.trait.id ? { traitId: emitter.trait.id } : {} };
693
- if (bareEvent !== emit.event || !matcher(source)) continue;
694
- wires.push({
695
- emitterOrbital: emitter.orbital,
696
- emitterTrait: emitter.name,
697
- listenerOrbital: listener.orbital,
698
- listenerTrait: listener.name,
699
- event: emit.event
700
- });
701
- }
738
+ if (!matcher(source)) continue;
739
+ wires.push({
740
+ emitterOrbital: emitter.orbital,
741
+ emitterTrait: emitter.name,
742
+ listenerOrbital: listener.orbital,
743
+ listenerTrait: listener.name,
744
+ event: emit.event
745
+ });
702
746
  }
703
747
  }
704
748
  }
749
+ wiresBySchema.set(schema, wires);
705
750
  return wires;
706
751
  }
707
752
 
@@ -1902,6 +1947,458 @@ function DrawFxLayer(_props) {
1902
1947
  return null;
1903
1948
  }
1904
1949
 
1950
+ // lib/drawable/themeDrawables.ts
1951
+ function opt(value, resolve) {
1952
+ return value === void 0 ? void 0 : resolve(value);
1953
+ }
1954
+ function themeShape(node, resolve) {
1955
+ return {
1956
+ ...node,
1957
+ ...node.fill !== void 0 ? { fill: resolve(node.fill) } : {},
1958
+ ...node.stroke !== void 0 ? { stroke: resolve(node.stroke) } : {},
1959
+ ...node.gradient ? { gradient: { ...node.gradient, stops: node.gradient.stops.map((s) => ({ ...s, color: resolve(s.color) })) } } : {},
1960
+ ...node.fillPattern?.color !== void 0 ? { fillPattern: { ...node.fillPattern, color: resolve(node.fillPattern.color) } } : {},
1961
+ ...node.shadow ? { shadow: { ...node.shadow, color: resolve(node.shadow.color) } } : {},
1962
+ ...node.animation ? {
1963
+ animation: {
1964
+ ...node.animation,
1965
+ keyframes: node.animation.keyframes.map((k) => ({
1966
+ ...k,
1967
+ ...k.fill !== void 0 ? { fill: resolve(k.fill) } : {},
1968
+ ...k.stroke !== void 0 ? { stroke: resolve(k.stroke) } : {}
1969
+ }))
1970
+ }
1971
+ } : {}
1972
+ };
1973
+ }
1974
+ function themeText(node, resolve) {
1975
+ return { ...node, color: resolve(node.color) };
1976
+ }
1977
+ function themeMaterial(material, resolve) {
1978
+ if (!material) return material;
1979
+ return {
1980
+ ...material,
1981
+ ...material.color !== void 0 ? { color: resolve(material.color) } : {},
1982
+ ...material.emissive !== void 0 ? { emissive: resolve(material.emissive) } : {}
1983
+ };
1984
+ }
1985
+ function resolveDrawableColors(nodes, resolve) {
1986
+ return nodes.map((node) => {
1987
+ switch (node.type) {
1988
+ case "draw-shape":
1989
+ return themeShape(node, resolve);
1990
+ case "draw-text":
1991
+ return themeText(node, resolve);
1992
+ case "draw-group":
1993
+ return { ...node, items: resolveDrawableColors(node.items, resolve) };
1994
+ case "draw-shape-layer":
1995
+ return { ...node, items: node.items.map((item) => themeShape(item, resolve)) };
1996
+ case "draw-text-layer":
1997
+ return { ...node, items: node.items.map((item) => themeText(item, resolve)) };
1998
+ case "draw-fx-layer":
1999
+ return {
2000
+ ...node,
2001
+ ...node.textColor !== void 0 ? { textColor: resolve(node.textColor) } : {},
2002
+ ...node.presets ? {
2003
+ presets: node.presets.map((p) => ({
2004
+ ...p,
2005
+ ...p.color !== void 0 ? { color: resolve(p.color) } : {},
2006
+ ...p.color2 !== void 0 ? { color2: resolve(p.color2) } : {}
2007
+ }))
2008
+ } : {}
2009
+ };
2010
+ case "draw-mesh":
2011
+ return {
2012
+ ...node,
2013
+ material: themeMaterial(node.material, resolve),
2014
+ ...node.outline ? { outline: { ...node.outline, ...node.outline.color !== void 0 ? { color: resolve(node.outline.color) } : {} } } : {},
2015
+ ...node.animation ? {
2016
+ animation: {
2017
+ ...node.animation,
2018
+ keyframes: node.animation.keyframes.map((k) => ({
2019
+ ...k,
2020
+ ...k.color !== void 0 ? { color: resolve(k.color) } : {},
2021
+ ...k.emissive !== void 0 ? { emissive: opt(k.emissive, resolve) } : {}
2022
+ }))
2023
+ }
2024
+ } : {}
2025
+ };
2026
+ default:
2027
+ return node;
2028
+ }
2029
+ });
2030
+ }
2031
+ function applySceneMaterials(nodes, theme) {
2032
+ return nodes.map((node) => {
2033
+ if (node.type === "draw-group") return { ...node, items: applySceneMaterials(node.items, theme) };
2034
+ if (node.type !== "draw-mesh") return node;
2035
+ const m = node.material ?? {};
2036
+ const pbr = (m.kind ?? "standard") === "standard" || m.kind === "physical";
2037
+ return {
2038
+ ...node,
2039
+ material: {
2040
+ ...m,
2041
+ ...pbr && m.roughness === void 0 ? { roughness: theme.scene.roughness } : {},
2042
+ ...pbr && m.metalness === void 0 ? { metalness: theme.scene.metalness } : {},
2043
+ ...m.flatShading === void 0 && theme.scene.flat ? { flatShading: true } : {}
2044
+ },
2045
+ ...node.outline === void 0 && theme.scene.outline > 0 ? { outline: { color: theme.tones.ink, width: theme.scene.outline * 0.02 } } : {}
2046
+ };
2047
+ });
2048
+ }
2049
+ var log2 = createLogger("almadar:ui:canvas-theme");
2050
+ var toneVar = (tone) => `--color-${DIAGRAM_TONE_COLOR_KEYS[tone]}`;
2051
+ var SERIES_TONES = ["series-1", "series-2", "series-3", "series-4", "series-5", "series-6", "series-7", "series-8"];
2052
+ var TONE_SET = new Set(DIAGRAM_TONES);
2053
+ function isDiagramTone(value) {
2054
+ return TONE_SET.has(value);
2055
+ }
2056
+ function colorCss(c) {
2057
+ return c.a >= 1 ? c.css : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;
2058
+ }
2059
+ function oneOf(value, allowed, fallback, name) {
2060
+ const hit = allowed.find((a) => a === value);
2061
+ if (hit !== void 0) return hit;
2062
+ if (value !== "") log2.warn("invalid-theme-keyword", { var: name, value, allowed });
2063
+ return fallback;
2064
+ }
2065
+ var PLAIN_LENGTH = /^(-?[\d.]+)(px)?$/;
2066
+ var ScopeReader = class {
2067
+ constructor(scope) {
2068
+ __publicField(this, "scope", scope);
2069
+ __publicField(this, "style");
2070
+ __publicField(this, "probe");
2071
+ this.style = getComputedStyle(scope);
2072
+ const doc = scope.ownerDocument;
2073
+ this.probe = doc.createElement("span");
2074
+ this.probe.style.position = "absolute";
2075
+ this.probe.style.visibility = "hidden";
2076
+ this.probe.style.pointerEvents = "none";
2077
+ scope.appendChild(this.probe);
2078
+ }
2079
+ dispose() {
2080
+ this.probe.remove();
2081
+ }
2082
+ raw(name) {
2083
+ return this.style.getPropertyValue(name).trim();
2084
+ }
2085
+ /** A length var in px: a plain value parses directly, a derived one (`calc`, `var`) through the probe. */
2086
+ length(name, fallback) {
2087
+ const raw = this.raw(name);
2088
+ const plain = PLAIN_LENGTH.exec(raw);
2089
+ if (plain) return Number(plain[1]);
2090
+ if (raw === "") return fallback;
2091
+ this.probe.style.width = `var(${name})`;
2092
+ const computed = PLAIN_LENGTH.exec(getComputedStyle(this.probe).width.trim());
2093
+ this.probe.style.width = "";
2094
+ if (computed) return Number(computed[1]);
2095
+ log2.warn("unresolvable-theme-length", { var: name, raw });
2096
+ return fallback;
2097
+ }
2098
+ number(name, fallback) {
2099
+ const raw = this.raw(name);
2100
+ if (raw === "") return fallback;
2101
+ const n = Number(raw);
2102
+ if (Number.isFinite(n)) return n;
2103
+ log2.warn("unresolvable-theme-number", { var: name, raw });
2104
+ return fallback;
2105
+ }
2106
+ /** A duration var in ms (`250ms`, `0.25s`, or a `var()` alias resolved through the probe). */
2107
+ durationMs(name, fallback) {
2108
+ const parse = (v) => {
2109
+ const m = /^([\d.]+)(ms|s)$/.exec(v.trim());
2110
+ return m ? Number(m[1]) * (m[2] === "s" ? 1e3 : 1) : null;
2111
+ };
2112
+ const raw = this.raw(name);
2113
+ const direct = parse(raw);
2114
+ if (direct !== null) return direct;
2115
+ if (raw === "") return fallback;
2116
+ this.probe.style.transitionDuration = `var(${name})`;
2117
+ const computed = parse(getComputedStyle(this.probe).transitionDuration.split(",")[0] ?? "");
2118
+ this.probe.style.transitionDuration = "";
2119
+ return computed ?? fallback;
2120
+ }
2121
+ color(name) {
2122
+ const raw = this.raw(name);
2123
+ return parseComputedColor(raw) ?? resolveThemeColor(`var(${name})`, this.scope);
2124
+ }
2125
+ shadow(name) {
2126
+ const raw = this.raw(name);
2127
+ if (raw === "" || raw === "none") return null;
2128
+ this.probe.style.boxShadow = `var(${name})`;
2129
+ const computed = getComputedStyle(this.probe).boxShadow.trim() || raw;
2130
+ this.probe.style.boxShadow = "";
2131
+ const colorMatch = /(rgba?\([^)]*\)|#[0-9a-f]{3,8}|oklch\([^)]*\))/i.exec(computed);
2132
+ const lengths = computed.replace(colorMatch?.[0] ?? "", "").trim().split(/\s+/).map((t) => PLAIN_LENGTH.exec(t)).filter((m) => m !== null).map((m) => Number(m[1]));
2133
+ if (lengths.length < 2) return null;
2134
+ const color = colorMatch ? parseComputedColor(colorMatch[0]) : null;
2135
+ return { offsetX: lengths[0], offsetY: lengths[1], blur: lengths[2] ?? 0, color: color ? colorCss(color) : "rgba(0, 0, 0, 0.25)" };
2136
+ }
2137
+ family(name, fallback) {
2138
+ return this.raw(name) || fallback;
2139
+ }
2140
+ };
2141
+ function dashList(raw, fallback) {
2142
+ if (raw === "") return fallback;
2143
+ const parts = raw.split(/[\s,]+/).map(Number);
2144
+ return parts.every((n) => Number.isFinite(n) && n >= 0) ? parts : fallback;
2145
+ }
2146
+ function prefersReducedMotion(scope) {
2147
+ const view = scope.ownerDocument.defaultView;
2148
+ return typeof view?.matchMedia === "function" && view.matchMedia("(prefers-reduced-motion: reduce)").matches;
2149
+ }
2150
+ function resolveCanvasTheme(scope) {
2151
+ const r = new ScopeReader(scope);
2152
+ try {
2153
+ const tones = {};
2154
+ for (const tone of DIAGRAM_TONES) {
2155
+ const c = r.color(toneVar(tone));
2156
+ tones[tone] = c ? colorCss(c) : "currentColor";
2157
+ }
2158
+ const ground = r.color("--surface-diagram");
2159
+ const text = {};
2160
+ const TEXT_FALLBACK = { xs: 12, sm: 14, base: 16, lg: 18, xl: 20, "2xl": 24 };
2161
+ for (const size of DIAGRAM_TEXT_SIZES) text[size] = r.length(`--text-${size}`, TEXT_FALLBACK[size]);
2162
+ const body = r.family("--font-family-body", r.family("--font-family", "system-ui, sans-serif"));
2163
+ return {
2164
+ ground: ground ? colorCss(ground) : "transparent",
2165
+ tones,
2166
+ series: SERIES_TONES.map((t) => tones[t]),
2167
+ strokes: {
2168
+ thin: r.length("--diagram-stroke-thin", 1),
2169
+ normal: r.length("--diagram-stroke-normal", 1.5),
2170
+ bold: r.length("--diagram-stroke-bold", 3)
2171
+ },
2172
+ lineCap: oneOf(r.raw("--diagram-line-cap"), ["butt", "round", "square"], "round", "--diagram-line-cap"),
2173
+ lineJoin: oneOf(r.raw("--diagram-line-join"), ["miter", "round", "bevel"], "round", "--diagram-line-join"),
2174
+ dash: dashList(r.raw("--diagram-dash"), [6, 4]),
2175
+ dot: dashList(r.raw("--diagram-dot"), [1, 4]),
2176
+ fillStyle: oneOf(r.raw("--diagram-fill-style"), ["solid", "tint", "outline", "hatch"], "tint", "--diagram-fill-style"),
2177
+ fillOpacity: r.number("--diagram-fill-opacity", 0.18),
2178
+ roughness: r.number("--diagram-roughness", 0),
2179
+ glow: r.number("--diagram-glow", 0),
2180
+ shadow: r.shadow("--diagram-shadow"),
2181
+ marker: oneOf(r.raw("--diagram-marker"), ["triangle", "open", "line", "dot"], "triangle", "--diagram-marker"),
2182
+ faces: {
2183
+ body,
2184
+ display: r.family("--font-family-display", body),
2185
+ mono: r.family("--font-family-mono", "ui-monospace, monospace")
2186
+ },
2187
+ label: {
2188
+ font: oneOf(r.raw("--diagram-label-font"), ["body", "display", "mono"], "body", "--diagram-label-font"),
2189
+ size: oneOf(r.raw("--diagram-label-size"), DIAGRAM_TEXT_SIZES, "sm", "--diagram-label-size"),
2190
+ weight: r.raw("--diagram-label-weight") || "500",
2191
+ uppercase: oneOf(r.raw("--diagram-label-case"), ["none", "uppercase"], "none", "--diagram-label-case") === "uppercase"
2192
+ },
2193
+ text,
2194
+ corner: r.length("--diagram-corner", 4),
2195
+ motion: {
2196
+ enabled: r.raw("--motion-enable") !== "off" && !prefersReducedMotion(scope),
2197
+ fastMs: r.durationMs("--duration-fast", 150),
2198
+ normalMs: r.durationMs("--duration-normal", 250),
2199
+ slowMs: r.durationMs("--duration-slow", 400),
2200
+ easing: r.raw("--easing-standard") || "ease"
2201
+ },
2202
+ scene: {
2203
+ roughness: r.number("--material-roughness", 0.6),
2204
+ metalness: r.number("--material-metalness", 0),
2205
+ flat: r.number("--material-flat", 0) >= 1,
2206
+ outline: r.number("--material-outline", 0),
2207
+ ambient: r.number("--light-ambient", 0.55),
2208
+ key: r.number("--light-key", 1),
2209
+ keyColor: (() => {
2210
+ const c = r.color("--light-key-color");
2211
+ return c ? colorCss(c) : "#ffffff";
2212
+ })(),
2213
+ fog: r.number("--scene-fog", 0)
2214
+ }
2215
+ };
2216
+ } finally {
2217
+ r.dispose();
2218
+ }
2219
+ }
2220
+ function withSeries(theme, series, scope) {
2221
+ if (!theme || !series || series.length === 0 || !scope) return theme;
2222
+ const colors = series.map((c, i) => markColor(theme, c, scope, SERIES_TONES[i % SERIES_TONES.length]));
2223
+ const tones = { ...theme.tones };
2224
+ SERIES_TONES.forEach((tone, i) => {
2225
+ tones[tone] = colors[i % colors.length];
2226
+ });
2227
+ return { ...theme, tones, series: SERIES_TONES.map((t) => tones[t]) };
2228
+ }
2229
+ function markColor(theme, value, scope, fallback) {
2230
+ if (value === void 0 || value === "") return theme.tones[fallback];
2231
+ if (isDiagramTone(value)) return theme.tones[value];
2232
+ if (value.startsWith("var(") || value.startsWith("color-mix(")) {
2233
+ const resolved = resolveThemeColor(value, scope);
2234
+ return resolved ? colorCss(resolved) : theme.tones[fallback];
2235
+ }
2236
+ return value;
2237
+ }
2238
+ function markStrokeWidth(theme, literal, role, fallback = "normal") {
2239
+ return literal ?? theme.strokes[role ?? fallback];
2240
+ }
2241
+ function labelFont(theme, roles, literalPx, literalFamily) {
2242
+ const px = literalPx ?? theme.text[roles.textSize ?? theme.label.size];
2243
+ const family = literalFamily ?? theme.faces[roles.font ?? theme.label.font];
2244
+ return { font: `${theme.label.weight} ${px}px ${family}`, px };
2245
+ }
2246
+ function labelText(theme, text, textCase = "theme") {
2247
+ return theme.label.uppercase && textCase === "theme" ? text.toUpperCase() : text;
2248
+ }
2249
+ function fillLabel(ctx, theme, text, x, y) {
2250
+ if (!text) return;
2251
+ ctx.save();
2252
+ ctx.strokeStyle = theme.ground;
2253
+ ctx.lineWidth = Math.max(2, theme.text.xs * 0.3);
2254
+ ctx.lineJoin = "round";
2255
+ ctx.strokeText(text, x, y);
2256
+ ctx.restore();
2257
+ ctx.fillText(text, x, y);
2258
+ }
2259
+ function outlineStroke(stroke, fill, style, fillTone) {
2260
+ return outlineCarriesFill(fill, style, fillTone) && fill !== void 0 ? fill : stroke;
2261
+ }
2262
+ function outlineCarriesFill(fill, style, fillTone) {
2263
+ return style === "outline" && fill !== void 0 && fillTone !== "fill";
2264
+ }
2265
+ function applyLineCharacter(ctx, theme, strokeColor) {
2266
+ ctx.lineCap = theme.lineCap;
2267
+ ctx.lineJoin = theme.lineJoin;
2268
+ if (theme.glow > 0) {
2269
+ ctx.shadowColor = strokeColor;
2270
+ ctx.shadowBlur = theme.glow;
2271
+ ctx.shadowOffsetX = 0;
2272
+ ctx.shadowOffsetY = 0;
2273
+ }
2274
+ }
2275
+ function markDash(theme, dash) {
2276
+ if (dash === "dashed") return theme.dash;
2277
+ if (dash === "dotted") return theme.dot;
2278
+ return [];
2279
+ }
2280
+ function wobble(x, y, k) {
2281
+ const s = Math.sin(x * 12.9898 + y * 78.233 + k * 37.719) * 43758.5453;
2282
+ return (s - Math.floor(s)) * 2 - 1;
2283
+ }
2284
+ function tracePolyline(ctx, theme, points, closed) {
2285
+ ctx.beginPath();
2286
+ if (points.length === 0) return;
2287
+ const amp = theme.roughness;
2288
+ const pts = closed ? [...points, points[0]] : [...points];
2289
+ ctx.moveTo(pts[0].x + amp * wobble(pts[0].x, pts[0].y, 0), pts[0].y + amp * wobble(pts[0].y, pts[0].x, 1));
2290
+ for (let i = 1; i < pts.length; i++) {
2291
+ const a = pts[i - 1];
2292
+ const b = pts[i];
2293
+ if (amp === 0) {
2294
+ ctx.lineTo(b.x, b.y);
2295
+ continue;
2296
+ }
2297
+ const steps = Math.max(1, Math.round(Math.hypot(b.x - a.x, b.y - a.y) / 24));
2298
+ for (let s = 1; s <= steps; s++) {
2299
+ const t = s / steps;
2300
+ const x = a.x + (b.x - a.x) * t;
2301
+ const y = a.y + (b.y - a.y) * t;
2302
+ ctx.lineTo(x + amp * wobble(x, y, 2), y + amp * wobble(y, x, 3));
2303
+ }
2304
+ }
2305
+ if (closed) ctx.closePath();
2306
+ }
2307
+ function arcPoints(cx, cy, rx, ry, startRad = 0, endRad = Math.PI * 2) {
2308
+ const steps = Math.max(12, Math.round(Math.abs(endRad - startRad) * Math.max(rx, ry) / 8));
2309
+ const out = [];
2310
+ for (let i = 0; i <= steps; i++) {
2311
+ const t = startRad + (endRad - startRad) * i / steps;
2312
+ out.push({ x: cx + rx * Math.cos(t), y: cy + ry * Math.sin(t) });
2313
+ }
2314
+ return out;
2315
+ }
2316
+ function fillMark(ctx, theme, color, style = theme.fillStyle) {
2317
+ if (style === "outline") return;
2318
+ ctx.save();
2319
+ ctx.shadowBlur = 0;
2320
+ ctx.shadowColor = "transparent";
2321
+ if (theme.shadow) {
2322
+ ctx.shadowColor = theme.shadow.color;
2323
+ ctx.shadowOffsetX = theme.shadow.offsetX;
2324
+ ctx.shadowOffsetY = theme.shadow.offsetY;
2325
+ ctx.shadowBlur = theme.shadow.blur;
2326
+ }
2327
+ if (style === "hatch") {
2328
+ ctx.clip();
2329
+ const span = ctx.canvas.width + ctx.canvas.height;
2330
+ ctx.strokeStyle = color;
2331
+ ctx.lineWidth = theme.strokes.thin;
2332
+ ctx.setLineDash([]);
2333
+ ctx.beginPath();
2334
+ for (let d = -span; d < span; d += 6) {
2335
+ ctx.moveTo(d, 0);
2336
+ ctx.lineTo(d + span, span);
2337
+ }
2338
+ ctx.stroke();
2339
+ } else {
2340
+ ctx.globalAlpha *= style === "tint" ? theme.fillOpacity : 1;
2341
+ ctx.fillStyle = color;
2342
+ ctx.fill();
2343
+ }
2344
+ ctx.restore();
2345
+ }
2346
+ function drawMarker(ctx, theme, x1, y1, x2, y2, color, width) {
2347
+ const size = Math.max(8, width * 4);
2348
+ const angle = Math.atan2(y2 - y1, x2 - x1);
2349
+ const left = { x: x2 - size * Math.cos(angle - Math.PI / 6), y: y2 - size * Math.sin(angle - Math.PI / 6) };
2350
+ const right = { x: x2 - size * Math.cos(angle + Math.PI / 6), y: y2 - size * Math.sin(angle + Math.PI / 6) };
2351
+ ctx.save();
2352
+ ctx.strokeStyle = color;
2353
+ ctx.fillStyle = color;
2354
+ ctx.lineWidth = width;
2355
+ ctx.setLineDash([]);
2356
+ switch (theme.marker) {
2357
+ case "triangle": {
2358
+ ctx.beginPath();
2359
+ ctx.moveTo(x2, y2);
2360
+ ctx.lineTo(left.x, left.y);
2361
+ ctx.lineTo(right.x, right.y);
2362
+ ctx.closePath();
2363
+ ctx.fill();
2364
+ break;
2365
+ }
2366
+ case "open":
2367
+ case "line": {
2368
+ ctx.beginPath();
2369
+ ctx.moveTo(left.x, left.y);
2370
+ ctx.lineTo(x2, y2);
2371
+ ctx.lineTo(right.x, right.y);
2372
+ if (theme.marker === "open") ctx.stroke();
2373
+ else {
2374
+ ctx.moveTo(x2 - size * 0.5 * Math.cos(angle + Math.PI / 2), y2 - size * 0.5 * Math.sin(angle + Math.PI / 2));
2375
+ ctx.lineTo(x2 + size * 0.5 * Math.cos(angle + Math.PI / 2), y2 + size * 0.5 * Math.sin(angle + Math.PI / 2));
2376
+ ctx.stroke();
2377
+ }
2378
+ break;
2379
+ }
2380
+ case "dot": {
2381
+ ctx.beginPath();
2382
+ ctx.arc(x2, y2, Math.max(2.5, width * 1.5), 0, Math.PI * 2);
2383
+ ctx.fill();
2384
+ break;
2385
+ }
2386
+ }
2387
+ ctx.restore();
2388
+ }
2389
+ function useCanvasTheme(scopeRef) {
2390
+ const { appliedTheme } = useTheme();
2391
+ const scopeKey = JSON.stringify(useThemeScope());
2392
+ const [state, setState] = useState({ theme: null, version: 0 });
2393
+ useLayoutEffect(() => {
2394
+ const scope = scopeRef.current;
2395
+ if (!scope) return;
2396
+ const theme = resolveCanvasTheme(scope);
2397
+ setState((prev) => ({ theme, version: prev.version + 1 }));
2398
+ }, [appliedTheme, scopeKey, scopeRef]);
2399
+ return state;
2400
+ }
2401
+
1905
2402
  // lib/drawable/hitTest.ts
1906
2403
  function shapeDrawnItem(n) {
1907
2404
  if (n.shape !== "rect") return { pos: n.position, id: n.id };
@@ -2000,4 +2497,4 @@ function rotatePoint(p, center, radians) {
2000
2497
  return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos };
2001
2498
  }
2002
2499
 
2003
- export { AVL_FONT, AVL_INK, AVL_STROKE, Box, CONFETTI_BURST_KEYFRAMES, CONNECTION_COLORS, DrawFxLayer, DrawGroup, DrawMesh, DrawShape, DrawSkinnedMesh, DrawableRegistryContext, EFFECT_CATEGORY_COLORS, HStack, OPERATOR_CATEGORY_COLORS, SPRITE_FALLBACK_FILL, SPRITE_FALLBACK_STROKE, STATE_COLORS, STRETCHED_ABOVE, STRETCHED_CONTROL, Stack, THEME_SERIES, VStack, applyMeshAnimation, applyShapeAnimation, asEffectOperator, atlasFailed, atlasUrlOf, avlBlend, avlSeries, avlTint, blit, buildHitIndex, clampSegments, collectDrawnItems, createConfettiParticles, effectCategoryOf, effectZoneOf, expandFxLayer, fxHash01, getAtlas, getImageStatus, getOrLoadImage, getStateRole, hitTestSprites, isAnimatedGroup, isAnimatedMesh, isAnimatedShape, isAtlasAsset, isSpriteSheetAtlas, isValidScenePos, paintShape, paintSkinnedMesh, parseApplicationLevel, parseOrbitalLevel, parseTraitLevel, parseTransitionLevel, polyhedronBounds, pressableProps, resolveAssetSource, resolveThemeColor, rowOpenControlProps, spriteAssetState, spriteRect, subRectFor, traitEventWires, traitLevelFromTrait, traitTransitionRenderUi, withPreviewPosition };
2500
+ export { AVL_FONT, AVL_INK, AVL_STROKE, Box, CONFETTI_BURST_KEYFRAMES, CONNECTION_COLORS, DrawFxLayer, DrawGroup, DrawMesh, DrawShape, DrawSkinnedMesh, DrawableRegistryContext, EFFECT_CATEGORY_COLORS, HStack, OPERATOR_CATEGORY_COLORS, SPRITE_FALLBACK_FILL, SPRITE_FALLBACK_STROKE, STATE_COLORS, STRETCHED_ABOVE, STRETCHED_CONTROL, Stack, THEME_SERIES, VStack, applyLineCharacter, applyMeshAnimation, applySceneMaterials, applyShapeAnimation, arcPoints, asEffectOperator, atlasFailed, atlasUrlOf, avlBlend, avlSeries, avlTint, blit, buildHitIndex, clampSegments, collectDrawnItems, createConfettiParticles, drawMarker, effectCategoryOf, effectZoneOf, expandFxLayer, fillLabel, fillMark, fxHash01, getAtlas, getImageStatus, getOrLoadImage, getStateRole, hitTestSprites, isAnimatedGroup, isAnimatedMesh, isAnimatedShape, isAtlasAsset, isSpriteSheetAtlas, isValidScenePos, labelFont, labelText, markColor, markDash, markStrokeWidth, outlineCarriesFill, outlineStroke, paintShape, paintSkinnedMesh, parseApplicationLevel, parseOrbitalLevel, parseTraitLevel, parseTransitionLevel, polyhedronBounds, pressableProps, resolveAssetSource, resolveDrawableColors, resolveThemeColor, rowOpenControlProps, spriteAssetState, spriteRect, subRectFor, tracePolyline, traitEventWires, traitLevelFromTrait, traitTransitionRenderUi, useCanvasTheme, withPreviewPosition, withSeries };