@almadar/ui 5.151.0 → 5.153.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/{UISlotContext-D8_SoDsD.d.cts → UISlotContext-BlRDbHDy.d.cts} +1 -1
  2. package/dist/{UISlotContext-C1FsU9GB.d.ts → UISlotContext-CB89mv7N.d.ts} +1 -1
  3. package/dist/avl/index.cjs +749 -116
  4. package/dist/avl/index.js +752 -119
  5. package/dist/{avl-schema-parser-Cx_4SVg9.d.ts → avl-schema-parser-CLIm28Wa.d.ts} +1 -1
  6. package/dist/{avl-schema-parser-CVzkNzg7.d.cts → avl-schema-parser-Do_LPV1o.d.cts} +1 -1
  7. package/dist/{cn-DJTUjk1M.d.ts → cn-CFurBb2q.d.ts} +1 -1
  8. package/dist/{cn-BnAJZcNb.d.cts → cn-TH-RHihd.d.cts} +1 -1
  9. package/dist/components/index.cjs +751 -116
  10. package/dist/components/index.d.cts +114 -16
  11. package/dist/components/index.d.ts +114 -16
  12. package/dist/components/index.js +754 -120
  13. package/dist/context/index.d.cts +2 -2
  14. package/dist/context/index.d.ts +2 -2
  15. package/dist/hooks/index.d.cts +1 -1
  16. package/dist/hooks/index.d.ts +1 -1
  17. package/dist/lib/drawable/three/index.cjs +212 -0
  18. package/dist/lib/drawable/three/index.d.cts +3 -3
  19. package/dist/lib/drawable/three/index.d.ts +3 -3
  20. package/dist/lib/drawable/three/index.js +212 -0
  21. package/dist/lib/index.cjs +11 -2
  22. package/dist/lib/index.d.cts +10 -3
  23. package/dist/lib/index.d.ts +10 -3
  24. package/dist/lib/index.js +11 -3
  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 +749 -116
  28. package/dist/providers/index.js +752 -119
  29. package/dist/runtime/index.cjs +749 -116
  30. package/dist/runtime/index.d.cts +2 -2
  31. package/dist/runtime/index.d.ts +2 -2
  32. package/dist/runtime/index.js +752 -119
  33. package/dist/{useUISlots-BesZYMks.d.cts → useUISlots-GNwGLlW2.d.cts} +1 -1
  34. package/dist/{useUISlots-BesZYMks.d.ts → useUISlots-GNwGLlW2.d.ts} +1 -1
  35. package/package.json +4 -4
package/dist/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
4
+ import { isFileValue } from '@almadar/core';
4
5
 
5
6
  var __defProp = Object.defineProperty;
6
7
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -864,8 +865,6 @@ function sortRows(rows, field, direction = "asc") {
864
865
  const dir = direction === "desc" ? -1 : 1;
865
866
  return [...rows].sort((a, b) => dir * compareCellValues(a?.[field], b?.[field]));
866
867
  }
867
-
868
- // lib/getNestedValue.ts
869
868
  function getNestedValue(obj, path) {
870
869
  if (obj === null || obj === void 0 || !path) {
871
870
  return void 0;
@@ -886,6 +885,15 @@ function getNestedValue(obj, path) {
886
885
  }
887
886
  return value;
888
887
  }
888
+ function resolveImageUrl(value) {
889
+ if (typeof value === "string") {
890
+ return value;
891
+ }
892
+ if (isFileValue(value)) {
893
+ return value.url;
894
+ }
895
+ return void 0;
896
+ }
889
897
  function formatNestedFieldLabel(path) {
890
898
  const lastPart = path.includes(".") ? path.split(".").pop() : path;
891
899
  return lastPart.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).replace(/Id$/, "").trim();
@@ -2024,4 +2032,4 @@ var JAZARI_COLORS = {
2024
2032
  darkBg: "#1a1a2e"
2025
2033
  };
2026
2034
 
2027
- export { ApiError, DEFAULT_CONFIG, JAZARI_COLORS, apiClient, arrowheadPath, bindCanvasCapture, bindEventBus, bindLastDrawables, bindTraitStateGetter, brainIconPath, clearDebugEvents, clearEntityProvider, clearGuardHistory, clearTicks, clearTraits, clearVerification, cn, compareCellValues, computeJazariLayout, debug, debugCollision, debugError, debugGameState, debugGroup, debugGroupEnd, debugInput, debugPhysics, debugTable, debugTime, debugTimeEnd, debugWarn, eightPointedStarPath, extractOutputsFromTransitions, extractStateMachine, formatDate, formatDateTime, formatGuard, formatNestedFieldLabel, formatTime, formatValue, gearTeethPath, getAllChecks, getAllTicks, getAllTraits, getBridgeHealth, getDebugEvents, getEffectSummary, getEntitiesByType, getEntityById, getEntitySnapshot, getEventsBySource, getEventsByType, getGuardEvaluationsForTrait, getGuardHistory, getNestedValue, getRecentEvents, getRecentGuardEvaluations, getSnapshot, getSummary, getTick, getTrait, getTraitSnapshots, getTransitions, getTransitionsForTrait, humanizeEnumValue, humanizeFieldName, initDebugShortcut, isDebugEnabled, lockIconPath, logDebugEvent, logEffectExecuted, logError, logEventFired, logInfo, logStateChange, logWarning, onDebugToggle, parseContentSegments, parseLessonSegments, parseMarkdownWithCodeBlocks, pipeIconPath, recordGuardEvaluation, recordServerResponse, recordTransition, registerCheck, registerTick, registerTrait, registerTraitSnapshot, renderStateMachineToDomData, renderStateMachineToSvg, setDebugEnabled, setEntityProvider, setTickActive, sortRows, subscribeToDebugEvents, subscribeToGuardChanges, subscribeToTickChanges, subscribeToTraitChanges, subscribeToVerification, toggleDebug, unregisterTick, unregisterTrait, updateAssetStatus, updateBridgeHealth, updateCheck, updateGuardResult, updateTickExecution, updateTraitState, waitForTransition };
2035
+ export { ApiError, DEFAULT_CONFIG, JAZARI_COLORS, apiClient, arrowheadPath, bindCanvasCapture, bindEventBus, bindLastDrawables, bindTraitStateGetter, brainIconPath, clearDebugEvents, clearEntityProvider, clearGuardHistory, clearTicks, clearTraits, clearVerification, cn, compareCellValues, computeJazariLayout, debug, debugCollision, debugError, debugGameState, debugGroup, debugGroupEnd, debugInput, debugPhysics, debugTable, debugTime, debugTimeEnd, debugWarn, eightPointedStarPath, extractOutputsFromTransitions, extractStateMachine, formatDate, formatDateTime, formatGuard, formatNestedFieldLabel, formatTime, formatValue, gearTeethPath, getAllChecks, getAllTicks, getAllTraits, getBridgeHealth, getDebugEvents, getEffectSummary, getEntitiesByType, getEntityById, getEntitySnapshot, getEventsBySource, getEventsByType, getGuardEvaluationsForTrait, getGuardHistory, getNestedValue, getRecentEvents, getRecentGuardEvaluations, getSnapshot, getSummary, getTick, getTrait, getTraitSnapshots, getTransitions, getTransitionsForTrait, humanizeEnumValue, humanizeFieldName, initDebugShortcut, isDebugEnabled, lockIconPath, logDebugEvent, logEffectExecuted, logError, logEventFired, logInfo, logStateChange, logWarning, onDebugToggle, parseContentSegments, parseLessonSegments, parseMarkdownWithCodeBlocks, pipeIconPath, recordGuardEvaluation, recordServerResponse, recordTransition, registerCheck, registerTick, registerTrait, registerTraitSnapshot, renderStateMachineToDomData, renderStateMachineToSvg, resolveImageUrl, setDebugEnabled, setEntityProvider, setTickActive, sortRows, subscribeToDebugEvents, subscribeToGuardChanges, subscribeToTickChanges, subscribeToTraitChanges, subscribeToVerification, toggleDebug, unregisterTick, unregisterTrait, updateAssetStatus, updateBridgeHealth, updateCheck, updateGuardResult, updateTickExecution, updateTraitState, waitForTransition };
@@ -505,6 +505,182 @@ interface DrawTextLayerProps extends DrawableBase {
505
505
  items: DrawTextProps[];
506
506
  }
507
507
 
508
+ /**
509
+ * fx — the shared transient-effect contract (type-only module + pure helpers).
510
+ *
511
+ * One `FxItem` descriptor feeds BOTH render substrates: `draw-fx-layer` (game
512
+ * molecules — world-space, painted on the canvas) and `fx-overlay` (core
513
+ * molecules — screen-space, DOM/CSS). The substrate is NAME-BLIND: an item's
514
+ * look comes entirely from its resolved typed fields (color/size/shape/…),
515
+ * never from a kind→look table inside a component. The effect-kind vocabulary
516
+ * (`hit`, `explosion`, `correct-burst`, …) is declared by the CONSUMER — a
517
+ * board, lab, or themed organism — as an `FxPreset[]` table in its own `.lolo`
518
+ * config; `resolveFxView` merges the matching row under the item's own fields.
519
+ *
520
+ * Lifetime is owned by the fx mechanic (`std-fx-particles`): `ttl` decays every
521
+ * `tickMs` and removal is authoritative. Renderers derive smooth per-frame
522
+ * progress from `bornAt` (epoch ms, stamped at spawn) against the paint clock,
523
+ * falling back to the stepped `1 − ttl/maxTtl` fraction when `bornAt` is absent
524
+ * or no clock is running. Sub-particle scatter is seeded from the item `id` —
525
+ * deterministic, no `Math.random` at paint time.
526
+ */
527
+
528
+ /** Which render substrate an fx entry belongs to. */
529
+ type FxSpace = 'world' | 'screen';
530
+ /** The screen-space render kinds `fx-overlay` can play — component capability, not domain vocabulary. */
531
+ type FxOverlayKind = 'confetti' | 'flash' | 'shake' | 'sparkle' | 'float-text' | 'streak-glow';
532
+ /** The procedural world-space recipes `draw-fx-layer` can paint — component capability, not domain vocabulary. */
533
+ type FxProceduralShape = 'spark' | 'ring' | 'puff' | 'streak';
534
+ /**
535
+ * One live transient effect — a row of the fx mechanic's `fx` entity list.
536
+ * Only `id`/`type`/`x`/`ttl` are guaranteed; everything else is optional so the
537
+ * 13 pre-existing producer emit sites keep compiling unchanged.
538
+ */
539
+ interface FxItem {
540
+ /** Stable identity — the render key and the deterministic scatter seed. */
541
+ id: string;
542
+ /** Effect-kind key into the consumer-declared `FxPreset[]` table. */
543
+ type: string;
544
+ /** Canonical scene position; when absent the renderer derives it from `x`/`z`/`y`. */
545
+ position?: ScenePos;
546
+ /** Scene x in world units. */
547
+ x: number;
548
+ /** Ground-plane row in world units (2D boards paint it as screen y). */
549
+ z?: number;
550
+ /** Vertical axis in world units (3D height; 2D fallback row). */
551
+ y?: number;
552
+ /** Floating text painted with the effect (damage numbers, "+1", …). */
553
+ message?: string;
554
+ /** Remaining lifetime in mechanic ticks — the authoritative removal clock. */
555
+ ttl: number;
556
+ /** Spawn-time ttl (stamped by the mechanic when absent); grounds the fade fraction. */
557
+ maxTtl?: number;
558
+ /** Spawn timestamp in epoch ms (stamped by the mechanic) — enables smooth paint-clock progress. */
559
+ bornAt?: number;
560
+ /** Render substrate routing; default `world`. */
561
+ space?: FxSpace;
562
+ /** Screen-space render kind (`fx-overlay` vocabulary); ignored by the world layer. */
563
+ effect?: FxOverlayKind;
564
+ /** Primary color; renderer defaults apply when absent. */
565
+ color?: string;
566
+ /** Size in world units (canvas) / relative scale (overlay). */
567
+ size?: number;
568
+ /** Drift velocity along scene x, world-units/sec (render hint — the mechanic never integrates). */
569
+ vx?: number;
570
+ /** Drift velocity along the vertical axis, world-units/sec. */
571
+ vy?: number;
572
+ /** Drift velocity along the ground-plane row, world-units/sec. */
573
+ vz?: number;
574
+ /** Sub-particle count override (procedural burst / confetti). */
575
+ particleCount?: number;
576
+ }
577
+ /**
578
+ * One row of a consumer-declared effect vocabulary: the look + recipe for one
579
+ * fx `type`. Item fields always win over the preset row (`resolveFxView`).
580
+ */
581
+ interface FxPreset {
582
+ /** The effect-kind key this row styles. */
583
+ type: string;
584
+ /** Primary color. */
585
+ color?: string;
586
+ /** Secondary color (ring stroke / puff core accents). */
587
+ color2?: string;
588
+ /** Size in world units (canvas) / relative scale (overlay). */
589
+ size?: number;
590
+ /** Procedural recipe when no vector art or sprite matches. Default `spark`. */
591
+ shape?: FxProceduralShape;
592
+ /** Sub-particle count for the procedural recipe. */
593
+ count?: number;
594
+ /** Glow strength — soft shadow blur in world units around procedural particles. */
595
+ glow?: number;
596
+ /** Downward acceleration in world-units/sec² applied to the drift. */
597
+ gravity?: number;
598
+ /** Default drift velocities, world-units/sec. */
599
+ vx?: number;
600
+ vy?: number;
601
+ vz?: number;
602
+ /** Render substrate routing for this kind. */
603
+ space?: FxSpace;
604
+ /** Screen-space render kind for this kind. */
605
+ effect?: FxOverlayKind;
606
+ /** Sub-particle count for screen-space kinds (confetti/sparkle). */
607
+ particleCount?: number;
608
+ }
609
+ /**
610
+ * A screen-space fx entry as `fx-overlay` consumes it — the same mechanic rows,
611
+ * filtered to `space == "screen"`. Deliberately carries NO `ScenePos`: overlay
612
+ * coordinates are viewport fractions, and the absence of a core `ScenePos` is
613
+ * what keeps `fx-overlay` out of the canvas-drawable capability.
614
+ */
615
+ interface FxOverlayItem {
616
+ /** Stable identity — the mount key that starts the CSS animation, and the scatter seed. */
617
+ id: string;
618
+ /** Effect-kind key (consumer vocabulary); display comes from `effect` + style fields. */
619
+ type: string;
620
+ /** Overlay render kind; default `sparkle`. */
621
+ effect?: FxOverlayKind;
622
+ /** Floating text (`float-text`). */
623
+ message?: string;
624
+ /** Primary color. */
625
+ color?: string;
626
+ /** Relative scale multiplier. */
627
+ size?: number;
628
+ /** Horizontal viewport fraction 0..1; default 0.5. */
629
+ x?: number;
630
+ /** Vertical viewport fraction 0..1 (`z` wins, then `y`); default 0.4. */
631
+ z?: number;
632
+ y?: number;
633
+ /** Remaining lifetime in mechanic ticks — removal unmounts the node. */
634
+ ttl: number;
635
+ /** Spawn-time ttl; animation duration = maxTtl × tickMs. */
636
+ maxTtl?: number;
637
+ /** Spawn timestamp in epoch ms. */
638
+ bornAt?: number;
639
+ /** Render substrate routing; `world` entries are skipped defensively. */
640
+ space?: FxSpace;
641
+ /** Particle count for confetti/sparkle. */
642
+ particleCount?: number;
643
+ /** Drift velocities (unused by the DOM overlay; present so mechanic rows pass through untouched). */
644
+ vx?: number;
645
+ vy?: number;
646
+ vz?: number;
647
+ }
648
+
649
+ /**
650
+ * `draw-fx-layer` — the batched transient-effects pass in one descriptor.
651
+ *
652
+ * Collapses the fx triple every board used to hand-roll (message text layer +
653
+ * sprite fallback layer + vector-art group per fx entry) into ONE node:
654
+ * `{ type: draw-fx-layer, items: @entity.fx, presets: @config.fxPresets, … }`.
655
+ * NAME-BLIND: each item's look comes from its resolved typed fields via the
656
+ * consumer-declared `FxPreset[]` table — never a kind→look table in here.
657
+ * Render priority per item: vector `art[type].idle` (draw-group) → sprite
658
+ * `sprites[type]` (draw-sprite) → a procedural seeded recipe
659
+ * (spark/ring/puff/streak draw-shapes); a `message` always adds a rising,
660
+ * fading draw-text. Expansion is PURE (descriptors → descriptors, routed
661
+ * through the normal paint dispatch); per-frame smoothness comes from the host
662
+ * paint clock against each item's `bornAt`, while the fx mechanic's ttl decay
663
+ * stays the authoritative removal clock. `space: "screen"` entries are skipped
664
+ * — the `fx-overlay` core molecule owns those. The React component renders
665
+ * `null` (a drawable is painted by the host, not the DOM).
666
+ */
667
+
668
+ interface DrawFxLayerProps extends DrawableBase {
669
+ type: 'draw-fx-layer';
670
+ /** The live fx entries — the mechanic's `@entity.fx` list passed whole; `space: "screen"` entries are skipped (the overlay owns those). */
671
+ items: FxItem[];
672
+ /** Consumer-declared effect vocabulary — one row per fx `type` (look + recipe). Unknown types fall back to a neutral spark burst. */
673
+ presets?: FxPreset[];
674
+ /** Vector art per fx type (an asset-art map: type → anim-state → drawables); a type's `idle` state wins over sprites/procedural. */
675
+ art?: Record<string, Record<string, DrawableNode[]>>;
676
+ /** Sprite asset per fx type (an asset-manifest effects map) — used when no vector art matches. */
677
+ sprites?: Record<string, Asset>;
678
+ /** The fx mechanic's decay tick period in ms; lifetime = maxTtl × tickMs. Default 500. */
679
+ tickMs?: number;
680
+ /** Floating-message text color when an item declares none. Default `#ffe066`. */
681
+ textColor?: string;
682
+ }
683
+
508
684
  /**
509
685
  * Drawable paint dispatch (2D) + the `DrawableNode` union.
510
686
  *
@@ -517,6 +693,6 @@ interface DrawTextLayerProps extends DrawableBase {
517
693
  */
518
694
 
519
695
  /** Every drawable descriptor. The host's `children` are a `DrawableNode[]`. */
520
- type DrawableNode = DrawSpriteProps | DrawShapeProps | DrawTextProps | DrawGroupProps | DrawMeshProps | DrawSpriteLayerProps | DrawShapeLayerProps | DrawTextLayerProps;
696
+ type DrawableNode = DrawSpriteProps | DrawShapeProps | DrawTextProps | DrawGroupProps | DrawMeshProps | DrawSpriteLayerProps | DrawShapeLayerProps | DrawTextLayerProps | DrawFxLayerProps;
521
697
 
522
- export type { DrawableNode as D, MeshShapeKind as M, MeshMaterial as a, DrawGroupProps as b, DrawTextProps as c, DrawMeshProps as d };
698
+ export type { DrawableNode as D, FxOverlayItem as F, MeshShapeKind as M, MeshMaterial as a, DrawGroupProps as b, DrawTextProps as c, DrawMeshProps as d, FxOverlayKind as e };
@@ -505,6 +505,182 @@ interface DrawTextLayerProps extends DrawableBase {
505
505
  items: DrawTextProps[];
506
506
  }
507
507
 
508
+ /**
509
+ * fx — the shared transient-effect contract (type-only module + pure helpers).
510
+ *
511
+ * One `FxItem` descriptor feeds BOTH render substrates: `draw-fx-layer` (game
512
+ * molecules — world-space, painted on the canvas) and `fx-overlay` (core
513
+ * molecules — screen-space, DOM/CSS). The substrate is NAME-BLIND: an item's
514
+ * look comes entirely from its resolved typed fields (color/size/shape/…),
515
+ * never from a kind→look table inside a component. The effect-kind vocabulary
516
+ * (`hit`, `explosion`, `correct-burst`, …) is declared by the CONSUMER — a
517
+ * board, lab, or themed organism — as an `FxPreset[]` table in its own `.lolo`
518
+ * config; `resolveFxView` merges the matching row under the item's own fields.
519
+ *
520
+ * Lifetime is owned by the fx mechanic (`std-fx-particles`): `ttl` decays every
521
+ * `tickMs` and removal is authoritative. Renderers derive smooth per-frame
522
+ * progress from `bornAt` (epoch ms, stamped at spawn) against the paint clock,
523
+ * falling back to the stepped `1 − ttl/maxTtl` fraction when `bornAt` is absent
524
+ * or no clock is running. Sub-particle scatter is seeded from the item `id` —
525
+ * deterministic, no `Math.random` at paint time.
526
+ */
527
+
528
+ /** Which render substrate an fx entry belongs to. */
529
+ type FxSpace = 'world' | 'screen';
530
+ /** The screen-space render kinds `fx-overlay` can play — component capability, not domain vocabulary. */
531
+ type FxOverlayKind = 'confetti' | 'flash' | 'shake' | 'sparkle' | 'float-text' | 'streak-glow';
532
+ /** The procedural world-space recipes `draw-fx-layer` can paint — component capability, not domain vocabulary. */
533
+ type FxProceduralShape = 'spark' | 'ring' | 'puff' | 'streak';
534
+ /**
535
+ * One live transient effect — a row of the fx mechanic's `fx` entity list.
536
+ * Only `id`/`type`/`x`/`ttl` are guaranteed; everything else is optional so the
537
+ * 13 pre-existing producer emit sites keep compiling unchanged.
538
+ */
539
+ interface FxItem {
540
+ /** Stable identity — the render key and the deterministic scatter seed. */
541
+ id: string;
542
+ /** Effect-kind key into the consumer-declared `FxPreset[]` table. */
543
+ type: string;
544
+ /** Canonical scene position; when absent the renderer derives it from `x`/`z`/`y`. */
545
+ position?: ScenePos;
546
+ /** Scene x in world units. */
547
+ x: number;
548
+ /** Ground-plane row in world units (2D boards paint it as screen y). */
549
+ z?: number;
550
+ /** Vertical axis in world units (3D height; 2D fallback row). */
551
+ y?: number;
552
+ /** Floating text painted with the effect (damage numbers, "+1", …). */
553
+ message?: string;
554
+ /** Remaining lifetime in mechanic ticks — the authoritative removal clock. */
555
+ ttl: number;
556
+ /** Spawn-time ttl (stamped by the mechanic when absent); grounds the fade fraction. */
557
+ maxTtl?: number;
558
+ /** Spawn timestamp in epoch ms (stamped by the mechanic) — enables smooth paint-clock progress. */
559
+ bornAt?: number;
560
+ /** Render substrate routing; default `world`. */
561
+ space?: FxSpace;
562
+ /** Screen-space render kind (`fx-overlay` vocabulary); ignored by the world layer. */
563
+ effect?: FxOverlayKind;
564
+ /** Primary color; renderer defaults apply when absent. */
565
+ color?: string;
566
+ /** Size in world units (canvas) / relative scale (overlay). */
567
+ size?: number;
568
+ /** Drift velocity along scene x, world-units/sec (render hint — the mechanic never integrates). */
569
+ vx?: number;
570
+ /** Drift velocity along the vertical axis, world-units/sec. */
571
+ vy?: number;
572
+ /** Drift velocity along the ground-plane row, world-units/sec. */
573
+ vz?: number;
574
+ /** Sub-particle count override (procedural burst / confetti). */
575
+ particleCount?: number;
576
+ }
577
+ /**
578
+ * One row of a consumer-declared effect vocabulary: the look + recipe for one
579
+ * fx `type`. Item fields always win over the preset row (`resolveFxView`).
580
+ */
581
+ interface FxPreset {
582
+ /** The effect-kind key this row styles. */
583
+ type: string;
584
+ /** Primary color. */
585
+ color?: string;
586
+ /** Secondary color (ring stroke / puff core accents). */
587
+ color2?: string;
588
+ /** Size in world units (canvas) / relative scale (overlay). */
589
+ size?: number;
590
+ /** Procedural recipe when no vector art or sprite matches. Default `spark`. */
591
+ shape?: FxProceduralShape;
592
+ /** Sub-particle count for the procedural recipe. */
593
+ count?: number;
594
+ /** Glow strength — soft shadow blur in world units around procedural particles. */
595
+ glow?: number;
596
+ /** Downward acceleration in world-units/sec² applied to the drift. */
597
+ gravity?: number;
598
+ /** Default drift velocities, world-units/sec. */
599
+ vx?: number;
600
+ vy?: number;
601
+ vz?: number;
602
+ /** Render substrate routing for this kind. */
603
+ space?: FxSpace;
604
+ /** Screen-space render kind for this kind. */
605
+ effect?: FxOverlayKind;
606
+ /** Sub-particle count for screen-space kinds (confetti/sparkle). */
607
+ particleCount?: number;
608
+ }
609
+ /**
610
+ * A screen-space fx entry as `fx-overlay` consumes it — the same mechanic rows,
611
+ * filtered to `space == "screen"`. Deliberately carries NO `ScenePos`: overlay
612
+ * coordinates are viewport fractions, and the absence of a core `ScenePos` is
613
+ * what keeps `fx-overlay` out of the canvas-drawable capability.
614
+ */
615
+ interface FxOverlayItem {
616
+ /** Stable identity — the mount key that starts the CSS animation, and the scatter seed. */
617
+ id: string;
618
+ /** Effect-kind key (consumer vocabulary); display comes from `effect` + style fields. */
619
+ type: string;
620
+ /** Overlay render kind; default `sparkle`. */
621
+ effect?: FxOverlayKind;
622
+ /** Floating text (`float-text`). */
623
+ message?: string;
624
+ /** Primary color. */
625
+ color?: string;
626
+ /** Relative scale multiplier. */
627
+ size?: number;
628
+ /** Horizontal viewport fraction 0..1; default 0.5. */
629
+ x?: number;
630
+ /** Vertical viewport fraction 0..1 (`z` wins, then `y`); default 0.4. */
631
+ z?: number;
632
+ y?: number;
633
+ /** Remaining lifetime in mechanic ticks — removal unmounts the node. */
634
+ ttl: number;
635
+ /** Spawn-time ttl; animation duration = maxTtl × tickMs. */
636
+ maxTtl?: number;
637
+ /** Spawn timestamp in epoch ms. */
638
+ bornAt?: number;
639
+ /** Render substrate routing; `world` entries are skipped defensively. */
640
+ space?: FxSpace;
641
+ /** Particle count for confetti/sparkle. */
642
+ particleCount?: number;
643
+ /** Drift velocities (unused by the DOM overlay; present so mechanic rows pass through untouched). */
644
+ vx?: number;
645
+ vy?: number;
646
+ vz?: number;
647
+ }
648
+
649
+ /**
650
+ * `draw-fx-layer` — the batched transient-effects pass in one descriptor.
651
+ *
652
+ * Collapses the fx triple every board used to hand-roll (message text layer +
653
+ * sprite fallback layer + vector-art group per fx entry) into ONE node:
654
+ * `{ type: draw-fx-layer, items: @entity.fx, presets: @config.fxPresets, … }`.
655
+ * NAME-BLIND: each item's look comes from its resolved typed fields via the
656
+ * consumer-declared `FxPreset[]` table — never a kind→look table in here.
657
+ * Render priority per item: vector `art[type].idle` (draw-group) → sprite
658
+ * `sprites[type]` (draw-sprite) → a procedural seeded recipe
659
+ * (spark/ring/puff/streak draw-shapes); a `message` always adds a rising,
660
+ * fading draw-text. Expansion is PURE (descriptors → descriptors, routed
661
+ * through the normal paint dispatch); per-frame smoothness comes from the host
662
+ * paint clock against each item's `bornAt`, while the fx mechanic's ttl decay
663
+ * stays the authoritative removal clock. `space: "screen"` entries are skipped
664
+ * — the `fx-overlay` core molecule owns those. The React component renders
665
+ * `null` (a drawable is painted by the host, not the DOM).
666
+ */
667
+
668
+ interface DrawFxLayerProps extends DrawableBase {
669
+ type: 'draw-fx-layer';
670
+ /** The live fx entries — the mechanic's `@entity.fx` list passed whole; `space: "screen"` entries are skipped (the overlay owns those). */
671
+ items: FxItem[];
672
+ /** Consumer-declared effect vocabulary — one row per fx `type` (look + recipe). Unknown types fall back to a neutral spark burst. */
673
+ presets?: FxPreset[];
674
+ /** Vector art per fx type (an asset-art map: type → anim-state → drawables); a type's `idle` state wins over sprites/procedural. */
675
+ art?: Record<string, Record<string, DrawableNode[]>>;
676
+ /** Sprite asset per fx type (an asset-manifest effects map) — used when no vector art matches. */
677
+ sprites?: Record<string, Asset>;
678
+ /** The fx mechanic's decay tick period in ms; lifetime = maxTtl × tickMs. Default 500. */
679
+ tickMs?: number;
680
+ /** Floating-message text color when an item declares none. Default `#ffe066`. */
681
+ textColor?: string;
682
+ }
683
+
508
684
  /**
509
685
  * Drawable paint dispatch (2D) + the `DrawableNode` union.
510
686
  *
@@ -517,6 +693,6 @@ interface DrawTextLayerProps extends DrawableBase {
517
693
  */
518
694
 
519
695
  /** Every drawable descriptor. The host's `children` are a `DrawableNode[]`. */
520
- type DrawableNode = DrawSpriteProps | DrawShapeProps | DrawTextProps | DrawGroupProps | DrawMeshProps | DrawSpriteLayerProps | DrawShapeLayerProps | DrawTextLayerProps;
696
+ type DrawableNode = DrawSpriteProps | DrawShapeProps | DrawTextProps | DrawGroupProps | DrawMeshProps | DrawSpriteLayerProps | DrawShapeLayerProps | DrawTextLayerProps | DrawFxLayerProps;
521
697
 
522
- export type { DrawableNode as D, MeshShapeKind as M, MeshMaterial as a, DrawGroupProps as b, DrawTextProps as c, DrawMeshProps as d };
698
+ export type { DrawableNode as D, FxOverlayItem as F, MeshShapeKind as M, MeshMaterial as a, DrawGroupProps as b, DrawTextProps as c, DrawMeshProps as d, FxOverlayKind as e };