@almadar/ui 5.152.0 → 5.154.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 (35) hide show
  1. package/dist/{EntityBindingContext-CD9ZoXb4.d.cts → EntityBindingContext-BfZGeDfX.d.cts} +4 -2
  2. package/dist/{EntityBindingContext-CD9ZoXb4.d.ts → EntityBindingContext-BfZGeDfX.d.ts} +4 -2
  3. package/dist/NavStackContext-BoVV9nWb.d.cts +83 -0
  4. package/dist/NavStackContext-BoVV9nWb.d.ts +83 -0
  5. package/dist/avl/index.cjs +571 -76
  6. package/dist/avl/index.js +572 -77
  7. package/dist/{avl-schema-parser-Cx_4SVg9.d.ts → avl-schema-parser-CLIm28Wa.d.ts} +1 -1
  8. package/dist/{avl-schema-parser-CVzkNzg7.d.cts → avl-schema-parser-Do_LPV1o.d.cts} +1 -1
  9. package/dist/{cn-DJTUjk1M.d.ts → cn-CFurBb2q.d.ts} +1 -1
  10. package/dist/{cn-BnAJZcNb.d.cts → cn-TH-RHihd.d.cts} +1 -1
  11. package/dist/components/index.cjs +502 -52
  12. package/dist/components/index.d.cts +58 -9
  13. package/dist/components/index.d.ts +58 -9
  14. package/dist/components/index.js +501 -55
  15. package/dist/hooks/index.cjs +4 -0
  16. package/dist/hooks/index.d.cts +1 -0
  17. package/dist/hooks/index.d.ts +1 -0
  18. package/dist/hooks/index.js +1 -0
  19. package/dist/lib/drawable/three/index.cjs +212 -0
  20. package/dist/lib/drawable/three/index.d.cts +3 -3
  21. package/dist/lib/drawable/three/index.d.ts +3 -3
  22. package/dist/lib/drawable/three/index.js +212 -0
  23. package/dist/lib/index.d.cts +2 -2
  24. package/dist/lib/index.d.ts +2 -2
  25. package/dist/{paintDispatch-CxdLjHQq.d.ts → paintDispatch-B5n2DTB-.d.ts} +178 -2
  26. package/dist/{paintDispatch-BjZjUbcb.d.cts → paintDispatch-DgBctxIq.d.cts} +178 -2
  27. package/dist/providers/index.cjs +677 -59
  28. package/dist/providers/index.d.cts +2 -1
  29. package/dist/providers/index.d.ts +2 -1
  30. package/dist/providers/index.js +677 -62
  31. package/dist/runtime/index.cjs +570 -75
  32. package/dist/runtime/index.d.cts +12 -5
  33. package/dist/runtime/index.d.ts +12 -5
  34. package/dist/runtime/index.js +571 -76
  35. package/package.json +4 -4
@@ -2847,6 +2847,10 @@ function useGitHubBranches(owner, repo, enabled = true) {
2847
2847
  });
2848
2848
  }
2849
2849
 
2850
+ Object.defineProperty(exports, "useNavStack", {
2851
+ enumerable: true,
2852
+ get: function () { return providers.useNavStack; }
2853
+ });
2850
2854
  exports.ALMADAR_DND_MIME = ALMADAR_DND_MIME;
2851
2855
  exports.DEFAULT_SLOTS = DEFAULT_SLOTS;
2852
2856
  exports.I18nProvider = I18nProvider;
@@ -4,6 +4,7 @@ import { a as useEventBus } from '../useEventBus-Ckr4wqW3.cjs';
4
4
  export { u as useEmitEvent, b as useEventListener } from '../useEventBus-Ckr4wqW3.cjs';
5
5
  export { E as EventBusContextType } from '../event-bus-types-Bl78kokd.cjs';
6
6
  export { D as DEFAULT_SLOTS, S as SlotAnimation, a as SlotChangeCallback, b as SlotContent, c as SlotRenderConfig, U as UISlotManager, u as useUISlotManager } from '../useUISlots-GNwGLlW2.cjs';
7
+ export { a as NavStackApi, u as useNavStack } from '../NavStackContext-BoVV9nWb.cjs';
7
8
  import * as React$1 from 'react';
8
9
  import React__default from 'react';
9
10
  import * as _tanstack_react_query from '@tanstack/react-query';
@@ -4,6 +4,7 @@ import { a as useEventBus } from '../useEventBus-CQWyAWpK.js';
4
4
  export { u as useEmitEvent, b as useEventListener } from '../useEventBus-CQWyAWpK.js';
5
5
  export { E as EventBusContextType } from '../event-bus-types-Bl78kokd.js';
6
6
  export { D as DEFAULT_SLOTS, S as SlotAnimation, a as SlotChangeCallback, b as SlotContent, c as SlotRenderConfig, U as UISlotManager, u as useUISlotManager } from '../useUISlots-GNwGLlW2.js';
7
+ export { a as NavStackApi, u as useNavStack } from '../NavStackContext-BoVV9nWb.js';
7
8
  import * as React$1 from 'react';
8
9
  import React__default from 'react';
9
10
  import * as _tanstack_react_query from '@tanstack/react-query';
@@ -1,6 +1,7 @@
1
1
  import { createContext, useCallback, useState, useEffect, useMemo, useContext, useRef, useSyncExternalStore } from 'react';
2
2
  import { createLogger } from '@almadar/logger';
3
3
  import { EventBusContext, useTraitScopeChain } from '@almadar/ui/providers';
4
+ export { useNavStack } from '@almadar/ui/providers';
4
5
  import { mergeEntityFrame } from '@almadar/core';
5
6
  import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
6
7
 
@@ -712,6 +712,201 @@ function applyMeshAnimation(node, timeMs) {
712
712
  emissive: trackValue("emissive")
713
713
  };
714
714
  }
715
+
716
+ // components/core/atoms/fx.ts
717
+ function resolveFxView(item, presets) {
718
+ const row = presets?.find((p) => p.type === item.type);
719
+ if (!row) return item;
720
+ return {
721
+ ...item,
722
+ space: item.space ?? row.space,
723
+ effect: item.effect ?? row.effect,
724
+ color: item.color ?? row.color,
725
+ size: item.size ?? row.size,
726
+ vx: item.vx ?? row.vx,
727
+ vy: item.vy ?? row.vy,
728
+ vz: item.vz ?? row.vz,
729
+ particleCount: item.particleCount ?? row.particleCount,
730
+ shape: row.shape,
731
+ count: row.count,
732
+ glow: row.glow,
733
+ gravity: row.gravity,
734
+ color2: row.color2
735
+ };
736
+ }
737
+ function fxLifecycle(item, epochNowMs, tickMs) {
738
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
739
+ const lifeMs = maxTtl * tickMs;
740
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
741
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
742
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
743
+ }
744
+ function fxHash01(seed, salt) {
745
+ let h = 2166136261 ^ salt;
746
+ for (let i = 0; i < seed.length; i++) {
747
+ h ^= seed.charCodeAt(i);
748
+ h = Math.imul(h, 16777619);
749
+ }
750
+ h ^= h >>> 13;
751
+ h = Math.imul(h, 1274126177);
752
+ h ^= h >>> 16;
753
+ return (h >>> 0) / 4294967296;
754
+ }
755
+
756
+ // components/game/molecules/DrawFxLayer.tsx
757
+ var DEFAULT_TICK_MS = 500;
758
+ var DEFAULT_TEXT_COLOR = "#ffe066";
759
+ var DEFAULT_SPARK_COLOR = "#ffffff";
760
+ var easeOut = (t) => 1 - (1 - t) * (1 - t);
761
+ function fxPosition(view, dim, ageSec) {
762
+ const g = view.gravity ?? 0;
763
+ const fall = 0.5 * g * ageSec * ageSec;
764
+ const base = view.position ?? { x: view.x, y: view.z ?? 0, z: view.y };
765
+ if (!Number.isFinite(base.x) || !Number.isFinite(base.y)) return void 0;
766
+ return {
767
+ x: base.x + (view.vx ?? 0) * ageSec,
768
+ y: base.y + (view.vz ?? 0) * ageSec,
769
+ ...base.z !== void 0 || view.vy !== void 0 || g !== 0 ? { z: (base.z ?? 0) + (view.vy ?? 0) * ageSec - fall } : {}
770
+ };
771
+ }
772
+ function sparkShape(view, pos, i, fade, progress) {
773
+ const size = view.size ?? 0.5;
774
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
775
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut(progress);
776
+ const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
777
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
778
+ return {
779
+ type: "draw-shape",
780
+ shape: "ellipse",
781
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
782
+ anchor: "center",
783
+ radiusX: r,
784
+ fill: color,
785
+ blendMode: "lighter",
786
+ opacity: fade,
787
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
788
+ };
789
+ }
790
+ function proceduralShapes(view, pos, fade, progress) {
791
+ const size = view.size ?? 0.5;
792
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
793
+ switch (view.shape ?? "spark") {
794
+ case "ring": {
795
+ return [
796
+ {
797
+ type: "draw-shape",
798
+ shape: "ellipse",
799
+ position: pos,
800
+ anchor: "center",
801
+ radiusX: size * easeOut(progress),
802
+ stroke: view.color2 ?? color,
803
+ strokeWidth: 2,
804
+ blendMode: "lighter",
805
+ opacity: fade,
806
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
807
+ }
808
+ ];
809
+ }
810
+ case "puff": {
811
+ const count = view.count ?? 3;
812
+ return Array.from({ length: count }, (_, i) => {
813
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
814
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut(progress);
815
+ return {
816
+ type: "draw-shape",
817
+ shape: "ellipse",
818
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
819
+ anchor: "center",
820
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
821
+ fill: i === 0 && view.color2 ? view.color2 : color,
822
+ opacity: fade * 0.6,
823
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
824
+ };
825
+ });
826
+ }
827
+ case "streak": {
828
+ const count = view.count ?? 5;
829
+ return Array.from({ length: count }, (_, i) => {
830
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
831
+ const inner = size * (0.2 + 0.8 * easeOut(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
832
+ const len = size * 0.35;
833
+ return {
834
+ type: "draw-shape",
835
+ shape: "ellipse",
836
+ position: pos,
837
+ anchor: "center",
838
+ offsetX: inner + len / 2,
839
+ offsetY: 0,
840
+ radiusX: len / 2,
841
+ radiusY: size * 0.04,
842
+ rotate: angle,
843
+ fill: color,
844
+ blendMode: "lighter",
845
+ opacity: fade,
846
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
847
+ };
848
+ });
849
+ }
850
+ default: {
851
+ const count = view.count ?? 6;
852
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
853
+ }
854
+ }
855
+ }
856
+ function expandFxItem(view, node, epochNowMs, dim) {
857
+ if (view.space === "screen") return [];
858
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
859
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
860
+ if (progress >= 1) return [];
861
+ const pos = fxPosition(view, dim, ageMs / 1e3);
862
+ if (!pos) return [];
863
+ const out = [];
864
+ const artItems = node.art?.[view.type]?.["idle"];
865
+ const sprite = node.sprites?.[view.type];
866
+ if (Array.isArray(artItems)) {
867
+ out.push({
868
+ type: "draw-group",
869
+ position: pos,
870
+ opacity: fade,
871
+ ...view.size !== void 0 ? { scale: view.size } : {},
872
+ items: artItems
873
+ });
874
+ } else if (sprite?.url) {
875
+ const spriteSize = view.size ?? 0.6;
876
+ out.push({
877
+ type: "draw-sprite",
878
+ position: pos,
879
+ asset: sprite,
880
+ anchor: "center",
881
+ width: spriteSize,
882
+ height: spriteSize,
883
+ opacity: fade
884
+ });
885
+ } else {
886
+ out.push(...proceduralShapes(view, pos, fade, progress));
887
+ }
888
+ if (view.message) {
889
+ const text = {
890
+ type: "draw-text",
891
+ text: view.message,
892
+ position: pos,
893
+ offsetY: -(0.15 + 0.55 * progress),
894
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
895
+ opacity: fade
896
+ };
897
+ out.push(text);
898
+ }
899
+ return out;
900
+ }
901
+ function expandFxLayer(node, epochNowMs, dim) {
902
+ if (!Array.isArray(node.items)) return [];
903
+ const out = [];
904
+ for (const item of node.items) {
905
+ if (!item || typeof item.id !== "string") continue;
906
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
907
+ }
908
+ return out;
909
+ }
715
910
  var NUMERIC_TRACKS2 = [
716
911
  "offsetX",
717
912
  "offsetY",
@@ -1577,6 +1772,8 @@ function Drawable3D({ node, projector, groupOpacity = 1 }) {
1577
1772
  if (node.clip) warnUnsupported3d("draw-group:clip");
1578
1773
  return /* @__PURE__ */ jsxRuntime.jsx(Group3D, { node, projector, groupOpacity });
1579
1774
  }
1775
+ case "draw-fx-layer":
1776
+ return /* @__PURE__ */ jsxRuntime.jsx(FxLayer3D, { node, projector, groupOpacity });
1580
1777
  }
1581
1778
  })();
1582
1779
  return node.id ? /* @__PURE__ */ jsxRuntime.jsx("group", { userData: { drawableId: node.id }, children: rendered }) : rendered;
@@ -1624,6 +1821,21 @@ function Group3D({
1624
1821
  ] });
1625
1822
  return scopedStore ? /* @__PURE__ */ jsxRuntime.jsx(BoneRegistryContext.Provider, { value: scopedStore, children }) : children;
1626
1823
  }
1824
+ function FxLayer3D({
1825
+ node,
1826
+ projector,
1827
+ groupOpacity
1828
+ }) {
1829
+ const [, setFrame] = React3.useState(0);
1830
+ const live = Array.isArray(node.items) && node.items.length > 0;
1831
+ fiber.useFrame(() => {
1832
+ if (live) setFrame((f) => (f + 1) % 1e6);
1833
+ });
1834
+ if (!live) return null;
1835
+ const epochNow = typeof performance !== "undefined" ? performance.timeOrigin + performance.now() : 0;
1836
+ const children = expandFxLayer(node, epochNow, "3d");
1837
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children.map((c, i) => /* @__PURE__ */ jsxRuntime.jsx(Drawable3D, { node: c, projector, groupOpacity }, i)) });
1838
+ }
1627
1839
 
1628
1840
  // lib/drawable/three/game3dTheme.ts
1629
1841
  var GRID_COLORS_3D = {
@@ -1,12 +1,12 @@
1
- import { C as CanvasPointLightConfig, I as IsometricTile, a as IsometricUnit, b as IsometricFeature, A as ApplicationLevelData, O as OrbitalLevelData, T as TraitLevelData, c as TransitionLevelData } from '../../../avl-schema-parser-CVzkNzg7.cjs';
2
- export { d as Canvas3DHost, e as Canvas3DHostHandle, f as Canvas3DHostProps, d as GameCanvas3D, U as UnitAnimationState } from '../../../avl-schema-parser-CVzkNzg7.cjs';
1
+ import { C as CanvasPointLightConfig, I as IsometricTile, a as IsometricUnit, b as IsometricFeature, A as ApplicationLevelData, O as OrbitalLevelData, T as TraitLevelData, c as TransitionLevelData } from '../../../avl-schema-parser-Do_LPV1o.cjs';
2
+ export { d as Canvas3DHost, e as Canvas3DHostHandle, f as Canvas3DHostProps, d as GameCanvas3D, U as UnitAnimationState } from '../../../avl-schema-parser-Do_LPV1o.cjs';
3
3
  import * as React$1 from 'react';
4
4
  import React__default, { Component, ReactNode, ErrorInfo } from 'react';
5
5
  import * as THREE from 'three';
6
6
  import { QuadraticBezierCurve3 } from 'three';
7
7
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
8
8
  import { JsonObject, EventEmit, OrbitalSchema } from '@almadar/core';
9
- import '../../../paintDispatch-BjZjUbcb.cjs';
9
+ import '../../../paintDispatch-DgBctxIq.cjs';
10
10
  import '../../../types-B3nHgnMG.cjs';
11
11
 
12
12
  /**
@@ -1,12 +1,12 @@
1
- import { C as CanvasPointLightConfig, I as IsometricTile, a as IsometricUnit, b as IsometricFeature, A as ApplicationLevelData, O as OrbitalLevelData, T as TraitLevelData, c as TransitionLevelData } from '../../../avl-schema-parser-Cx_4SVg9.js';
2
- export { d as Canvas3DHost, e as Canvas3DHostHandle, f as Canvas3DHostProps, d as GameCanvas3D, U as UnitAnimationState } from '../../../avl-schema-parser-Cx_4SVg9.js';
1
+ import { C as CanvasPointLightConfig, I as IsometricTile, a as IsometricUnit, b as IsometricFeature, A as ApplicationLevelData, O as OrbitalLevelData, T as TraitLevelData, c as TransitionLevelData } from '../../../avl-schema-parser-CLIm28Wa.js';
2
+ export { d as Canvas3DHost, e as Canvas3DHostHandle, f as Canvas3DHostProps, d as GameCanvas3D, U as UnitAnimationState } from '../../../avl-schema-parser-CLIm28Wa.js';
3
3
  import * as React$1 from 'react';
4
4
  import React__default, { Component, ReactNode, ErrorInfo } from 'react';
5
5
  import * as THREE from 'three';
6
6
  import { QuadraticBezierCurve3 } from 'three';
7
7
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
8
8
  import { JsonObject, EventEmit, OrbitalSchema } from '@almadar/core';
9
- import '../../../paintDispatch-CxdLjHQq.js';
9
+ import '../../../paintDispatch-B5n2DTB-.js';
10
10
  import '../../../types-B3nHgnMG.js';
11
11
 
12
12
  /**
@@ -688,6 +688,201 @@ function applyMeshAnimation(node, timeMs) {
688
688
  emissive: trackValue("emissive")
689
689
  };
690
690
  }
691
+
692
+ // components/core/atoms/fx.ts
693
+ function resolveFxView(item, presets) {
694
+ const row = presets?.find((p) => p.type === item.type);
695
+ if (!row) return item;
696
+ return {
697
+ ...item,
698
+ space: item.space ?? row.space,
699
+ effect: item.effect ?? row.effect,
700
+ color: item.color ?? row.color,
701
+ size: item.size ?? row.size,
702
+ vx: item.vx ?? row.vx,
703
+ vy: item.vy ?? row.vy,
704
+ vz: item.vz ?? row.vz,
705
+ particleCount: item.particleCount ?? row.particleCount,
706
+ shape: row.shape,
707
+ count: row.count,
708
+ glow: row.glow,
709
+ gravity: row.gravity,
710
+ color2: row.color2
711
+ };
712
+ }
713
+ function fxLifecycle(item, epochNowMs, tickMs) {
714
+ const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
715
+ const lifeMs = maxTtl * tickMs;
716
+ const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
717
+ const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
718
+ return { lifeMs, ageMs, progress, fade: 1 - progress };
719
+ }
720
+ function fxHash01(seed, salt) {
721
+ let h = 2166136261 ^ salt;
722
+ for (let i = 0; i < seed.length; i++) {
723
+ h ^= seed.charCodeAt(i);
724
+ h = Math.imul(h, 16777619);
725
+ }
726
+ h ^= h >>> 13;
727
+ h = Math.imul(h, 1274126177);
728
+ h ^= h >>> 16;
729
+ return (h >>> 0) / 4294967296;
730
+ }
731
+
732
+ // components/game/molecules/DrawFxLayer.tsx
733
+ var DEFAULT_TICK_MS = 500;
734
+ var DEFAULT_TEXT_COLOR = "#ffe066";
735
+ var DEFAULT_SPARK_COLOR = "#ffffff";
736
+ var easeOut = (t) => 1 - (1 - t) * (1 - t);
737
+ function fxPosition(view, dim, ageSec) {
738
+ const g = view.gravity ?? 0;
739
+ const fall = 0.5 * g * ageSec * ageSec;
740
+ const base = view.position ?? { x: view.x, y: view.z ?? 0, z: view.y };
741
+ if (!Number.isFinite(base.x) || !Number.isFinite(base.y)) return void 0;
742
+ return {
743
+ x: base.x + (view.vx ?? 0) * ageSec,
744
+ y: base.y + (view.vz ?? 0) * ageSec,
745
+ ...base.z !== void 0 || view.vy !== void 0 || g !== 0 ? { z: (base.z ?? 0) + (view.vy ?? 0) * ageSec - fall } : {}
746
+ };
747
+ }
748
+ function sparkShape(view, pos, i, fade, progress) {
749
+ const size = view.size ?? 0.5;
750
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
751
+ const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut(progress);
752
+ const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
753
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
754
+ return {
755
+ type: "draw-shape",
756
+ shape: "ellipse",
757
+ position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
758
+ anchor: "center",
759
+ radiusX: r,
760
+ fill: color,
761
+ blendMode: "lighter",
762
+ opacity: fade,
763
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
764
+ };
765
+ }
766
+ function proceduralShapes(view, pos, fade, progress) {
767
+ const size = view.size ?? 0.5;
768
+ const color = view.color ?? DEFAULT_SPARK_COLOR;
769
+ switch (view.shape ?? "spark") {
770
+ case "ring": {
771
+ return [
772
+ {
773
+ type: "draw-shape",
774
+ shape: "ellipse",
775
+ position: pos,
776
+ anchor: "center",
777
+ radiusX: size * easeOut(progress),
778
+ stroke: view.color2 ?? color,
779
+ strokeWidth: 2,
780
+ blendMode: "lighter",
781
+ opacity: fade,
782
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
783
+ }
784
+ ];
785
+ }
786
+ case "puff": {
787
+ const count = view.count ?? 3;
788
+ return Array.from({ length: count }, (_, i) => {
789
+ const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
790
+ const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut(progress);
791
+ return {
792
+ type: "draw-shape",
793
+ shape: "ellipse",
794
+ position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
795
+ anchor: "center",
796
+ radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
797
+ fill: i === 0 && view.color2 ? view.color2 : color,
798
+ opacity: fade * 0.6,
799
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
800
+ };
801
+ });
802
+ }
803
+ case "streak": {
804
+ const count = view.count ?? 5;
805
+ return Array.from({ length: count }, (_, i) => {
806
+ const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
807
+ const inner = size * (0.2 + 0.8 * easeOut(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
808
+ const len = size * 0.35;
809
+ return {
810
+ type: "draw-shape",
811
+ shape: "ellipse",
812
+ position: pos,
813
+ anchor: "center",
814
+ offsetX: inner + len / 2,
815
+ offsetY: 0,
816
+ radiusX: len / 2,
817
+ radiusY: size * 0.04,
818
+ rotate: angle,
819
+ fill: color,
820
+ blendMode: "lighter",
821
+ opacity: fade,
822
+ ...view.glow ? { shadow: { color, blur: view.glow } } : {}
823
+ };
824
+ });
825
+ }
826
+ default: {
827
+ const count = view.count ?? 6;
828
+ return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
829
+ }
830
+ }
831
+ }
832
+ function expandFxItem(view, node, epochNowMs, dim) {
833
+ if (view.space === "screen") return [];
834
+ const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
835
+ const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
836
+ if (progress >= 1) return [];
837
+ const pos = fxPosition(view, dim, ageMs / 1e3);
838
+ if (!pos) return [];
839
+ const out = [];
840
+ const artItems = node.art?.[view.type]?.["idle"];
841
+ const sprite = node.sprites?.[view.type];
842
+ if (Array.isArray(artItems)) {
843
+ out.push({
844
+ type: "draw-group",
845
+ position: pos,
846
+ opacity: fade,
847
+ ...view.size !== void 0 ? { scale: view.size } : {},
848
+ items: artItems
849
+ });
850
+ } else if (sprite?.url) {
851
+ const spriteSize = view.size ?? 0.6;
852
+ out.push({
853
+ type: "draw-sprite",
854
+ position: pos,
855
+ asset: sprite,
856
+ anchor: "center",
857
+ width: spriteSize,
858
+ height: spriteSize,
859
+ opacity: fade
860
+ });
861
+ } else {
862
+ out.push(...proceduralShapes(view, pos, fade, progress));
863
+ }
864
+ if (view.message) {
865
+ const text = {
866
+ type: "draw-text",
867
+ text: view.message,
868
+ position: pos,
869
+ offsetY: -(0.15 + 0.55 * progress),
870
+ color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
871
+ opacity: fade
872
+ };
873
+ out.push(text);
874
+ }
875
+ return out;
876
+ }
877
+ function expandFxLayer(node, epochNowMs, dim) {
878
+ if (!Array.isArray(node.items)) return [];
879
+ const out = [];
880
+ for (const item of node.items) {
881
+ if (!item || typeof item.id !== "string") continue;
882
+ out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim));
883
+ }
884
+ return out;
885
+ }
691
886
  var NUMERIC_TRACKS2 = [
692
887
  "offsetX",
693
888
  "offsetY",
@@ -1553,6 +1748,8 @@ function Drawable3D({ node, projector, groupOpacity = 1 }) {
1553
1748
  if (node.clip) warnUnsupported3d("draw-group:clip");
1554
1749
  return /* @__PURE__ */ jsx(Group3D, { node, projector, groupOpacity });
1555
1750
  }
1751
+ case "draw-fx-layer":
1752
+ return /* @__PURE__ */ jsx(FxLayer3D, { node, projector, groupOpacity });
1556
1753
  }
1557
1754
  })();
1558
1755
  return node.id ? /* @__PURE__ */ jsx("group", { userData: { drawableId: node.id }, children: rendered }) : rendered;
@@ -1600,6 +1797,21 @@ function Group3D({
1600
1797
  ] });
1601
1798
  return scopedStore ? /* @__PURE__ */ jsx(BoneRegistryContext.Provider, { value: scopedStore, children }) : children;
1602
1799
  }
1800
+ function FxLayer3D({
1801
+ node,
1802
+ projector,
1803
+ groupOpacity
1804
+ }) {
1805
+ const [, setFrame] = useState(0);
1806
+ const live = Array.isArray(node.items) && node.items.length > 0;
1807
+ useFrame(() => {
1808
+ if (live) setFrame((f) => (f + 1) % 1e6);
1809
+ });
1810
+ if (!live) return null;
1811
+ const epochNow = typeof performance !== "undefined" ? performance.timeOrigin + performance.now() : 0;
1812
+ const children = expandFxLayer(node, epochNow, "3d");
1813
+ return /* @__PURE__ */ jsx(Fragment, { children: children.map((c, i) => /* @__PURE__ */ jsx(Drawable3D, { node: c, projector, groupOpacity }, i)) });
1814
+ }
1603
1815
 
1604
1816
  // lib/drawable/three/game3dTheme.ts
1605
1817
  var GRID_COLORS_3D = {
@@ -1,8 +1,8 @@
1
- export { C as ContentSegment, D as DEFAULT_CONFIG, a as DomEntityBox, b as DomLayoutData, c as DomOutputsBox, d as DomStateNode, e as DomTransitionLabel, f as DomTransitionPath, E as EntityDefinition, L as LessonSegment, R as RenderOptions, S as StateDefinition, g as StateMachineDefinition, T as TraitSnapshotGetter, h as TransitionDefinition, V as VisualizerConfig, i as bindCanvasCapture, j as bindEventBus, k as bindLastDrawables, l as bindTraitStateGetter, m as clearVerification, n as cn, o as extractOutputsFromTransitions, p as extractStateMachine, q as formatGuard, r as getAllChecks, s as getBridgeHealth, t as getEffectSummary, u as getSnapshot, v as getSummary, w as getTraitSnapshots, x as getTransitions, y as getTransitionsForTrait, z as parseContentSegments, A as parseLessonSegments, B as parseMarkdownWithCodeBlocks, F as recordServerResponse, G as recordTransition, H as registerCheck, I as registerTraitSnapshot, J as renderStateMachineToDomData, K as renderStateMachineToSvg, M as subscribeToVerification, N as updateAssetStatus, O as updateBridgeHealth, P as updateCheck, Q as waitForTransition } from '../cn-BnAJZcNb.cjs';
1
+ export { C as ContentSegment, D as DEFAULT_CONFIG, a as DomEntityBox, b as DomLayoutData, c as DomOutputsBox, d as DomStateNode, e as DomTransitionLabel, f as DomTransitionPath, E as EntityDefinition, L as LessonSegment, R as RenderOptions, S as StateDefinition, g as StateMachineDefinition, T as TraitSnapshotGetter, h as TransitionDefinition, V as VisualizerConfig, i as bindCanvasCapture, j as bindEventBus, k as bindLastDrawables, l as bindTraitStateGetter, m as clearVerification, n as cn, o as extractOutputsFromTransitions, p as extractStateMachine, q as formatGuard, r as getAllChecks, s as getBridgeHealth, t as getEffectSummary, u as getSnapshot, v as getSummary, w as getTraitSnapshots, x as getTransitions, y as getTransitionsForTrait, z as parseContentSegments, A as parseLessonSegments, B as parseMarkdownWithCodeBlocks, F as recordServerResponse, G as recordTransition, H as registerCheck, I as registerTraitSnapshot, J as renderStateMachineToDomData, K as renderStateMachineToSvg, M as subscribeToVerification, N as updateAssetStatus, O as updateBridgeHealth, P as updateCheck, Q as waitForTransition } from '../cn-TH-RHihd.cjs';
2
2
  import { FieldValue, EntityRow, EventPayload } from '@almadar/core';
3
3
  export { AssetLoadStatus, BridgeHealth, CheckStatus, EffectTrace, EventLogEntry, OrbitalVerificationAPI, ServerResponseTrace, TraitStateSnapshot, TransitionTrace, VerificationCheck, VerificationSnapshot, VerificationSummary } from '@almadar/core';
4
4
  import 'react';
5
- import '../paintDispatch-BjZjUbcb.cjs';
5
+ import '../paintDispatch-DgBctxIq.cjs';
6
6
  import '../types-B3nHgnMG.cjs';
7
7
  import 'clsx';
8
8
 
@@ -1,8 +1,8 @@
1
- export { C as ContentSegment, D as DEFAULT_CONFIG, a as DomEntityBox, b as DomLayoutData, c as DomOutputsBox, d as DomStateNode, e as DomTransitionLabel, f as DomTransitionPath, E as EntityDefinition, L as LessonSegment, R as RenderOptions, S as StateDefinition, g as StateMachineDefinition, T as TraitSnapshotGetter, h as TransitionDefinition, V as VisualizerConfig, i as bindCanvasCapture, j as bindEventBus, k as bindLastDrawables, l as bindTraitStateGetter, m as clearVerification, n as cn, o as extractOutputsFromTransitions, p as extractStateMachine, q as formatGuard, r as getAllChecks, s as getBridgeHealth, t as getEffectSummary, u as getSnapshot, v as getSummary, w as getTraitSnapshots, x as getTransitions, y as getTransitionsForTrait, z as parseContentSegments, A as parseLessonSegments, B as parseMarkdownWithCodeBlocks, F as recordServerResponse, G as recordTransition, H as registerCheck, I as registerTraitSnapshot, J as renderStateMachineToDomData, K as renderStateMachineToSvg, M as subscribeToVerification, N as updateAssetStatus, O as updateBridgeHealth, P as updateCheck, Q as waitForTransition } from '../cn-DJTUjk1M.js';
1
+ export { C as ContentSegment, D as DEFAULT_CONFIG, a as DomEntityBox, b as DomLayoutData, c as DomOutputsBox, d as DomStateNode, e as DomTransitionLabel, f as DomTransitionPath, E as EntityDefinition, L as LessonSegment, R as RenderOptions, S as StateDefinition, g as StateMachineDefinition, T as TraitSnapshotGetter, h as TransitionDefinition, V as VisualizerConfig, i as bindCanvasCapture, j as bindEventBus, k as bindLastDrawables, l as bindTraitStateGetter, m as clearVerification, n as cn, o as extractOutputsFromTransitions, p as extractStateMachine, q as formatGuard, r as getAllChecks, s as getBridgeHealth, t as getEffectSummary, u as getSnapshot, v as getSummary, w as getTraitSnapshots, x as getTransitions, y as getTransitionsForTrait, z as parseContentSegments, A as parseLessonSegments, B as parseMarkdownWithCodeBlocks, F as recordServerResponse, G as recordTransition, H as registerCheck, I as registerTraitSnapshot, J as renderStateMachineToDomData, K as renderStateMachineToSvg, M as subscribeToVerification, N as updateAssetStatus, O as updateBridgeHealth, P as updateCheck, Q as waitForTransition } from '../cn-CFurBb2q.js';
2
2
  import { FieldValue, EntityRow, EventPayload } from '@almadar/core';
3
3
  export { AssetLoadStatus, BridgeHealth, CheckStatus, EffectTrace, EventLogEntry, OrbitalVerificationAPI, ServerResponseTrace, TraitStateSnapshot, TransitionTrace, VerificationCheck, VerificationSnapshot, VerificationSummary } from '@almadar/core';
4
4
  import 'react';
5
- import '../paintDispatch-CxdLjHQq.js';
5
+ import '../paintDispatch-B5n2DTB-.js';
6
6
  import '../types-B3nHgnMG.js';
7
7
  import 'clsx';
8
8