@almadar/ui 5.101.0 → 5.104.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 (71) hide show
  1. package/dist/avl/index.cjs +253 -49
  2. package/dist/avl/index.css +3 -3
  3. package/dist/avl/index.js +255 -51
  4. package/dist/components/core/atoms/AtlasImage.d.ts +2 -2
  5. package/dist/components/core/atoms/FlipContainer.d.ts +1 -1
  6. package/dist/components/core/molecules/Card.d.ts +1 -1
  7. package/dist/components/core/molecules/Chart.d.ts +3 -3
  8. package/dist/components/core/molecules/DataGrid.d.ts +1 -1
  9. package/dist/components/core/molecules/DataList.d.ts +1 -1
  10. package/dist/components/core/molecules/DocPagination.d.ts +8 -1
  11. package/dist/components/core/molecules/DocSearch.d.ts +8 -1
  12. package/dist/components/core/molecules/FlipCard.d.ts +1 -1
  13. package/dist/components/core/molecules/LineChart.d.ts +3 -1
  14. package/dist/components/core/molecules/MapView.d.ts +18 -1
  15. package/dist/components/core/molecules/SortableList.d.ts +1 -1
  16. package/dist/components/core/molecules/TableView.d.ts +1 -1
  17. package/dist/components/core/organisms/DataTable.d.ts +7 -3
  18. package/dist/components/core/organisms/debug/RuntimeDebugger.d.ts +10 -1
  19. package/dist/components/core/organisms/debug/tabs/EntitiesTab.d.ts +2 -1
  20. package/dist/components/core/organisms/debug/tabs/EventDispatcherTab.d.ts +2 -1
  21. package/dist/components/core/organisms/debug/tabs/EventFlowTab.d.ts +6 -1
  22. package/dist/components/core/organisms/debug/tabs/GuardsPanel.d.ts +6 -1
  23. package/dist/components/core/organisms/debug/tabs/ServerBridgeTab.d.ts +12 -1
  24. package/dist/components/core/organisms/debug/tabs/TicksTab.d.ts +2 -1
  25. package/dist/components/core/organisms/debug/tabs/TraitsTab.d.ts +2 -1
  26. package/dist/components/core/organisms/debug/tabs/TransitionTimeline.d.ts +11 -1
  27. package/dist/components/core/organisms/debug/tabs/VerificationTab.d.ts +8 -1
  28. package/dist/components/game/2d/atoms/ActionButton.d.ts +2 -1
  29. package/dist/components/game/2d/atoms/ChoiceButton.d.ts +2 -1
  30. package/dist/components/game/2d/atoms/ComboCounter.d.ts +2 -1
  31. package/dist/components/game/2d/atoms/ControlButton.d.ts +2 -1
  32. package/dist/components/game/2d/atoms/DamageNumber.d.ts +2 -1
  33. package/dist/components/game/2d/atoms/DialogueBubble.d.ts +2 -1
  34. package/dist/components/game/2d/atoms/GameIcon.d.ts +2 -1
  35. package/dist/components/game/2d/atoms/HealthBar.d.ts +2 -1
  36. package/dist/components/game/2d/atoms/ItemSlot.d.ts +2 -1
  37. package/dist/components/game/2d/atoms/MiniMap.d.ts +2 -1
  38. package/dist/components/game/2d/atoms/ResourceCounter.d.ts +2 -1
  39. package/dist/components/game/2d/atoms/ScoreDisplay.d.ts +2 -1
  40. package/dist/components/game/2d/atoms/StatusEffect.d.ts +2 -1
  41. package/dist/components/game/2d/atoms/TimerDisplay.d.ts +2 -1
  42. package/dist/components/game/2d/atoms/TurnIndicator.d.ts +2 -1
  43. package/dist/components/game/2d/atoms/WaypointMarker.d.ts +2 -1
  44. package/dist/components/game/2d/molecules/Canvas2D.d.ts +5 -4
  45. package/dist/components/game/2d/molecules/GameHud.d.ts +2 -1
  46. package/dist/components/game/2d/molecules/GameMenu.d.ts +2 -1
  47. package/dist/components/game/2d/molecules/InventoryGrid.d.ts +2 -1
  48. package/dist/components/game/2d/molecules/ResourceBar.d.ts +2 -1
  49. package/dist/components/game/2d/molecules/StatBadge.d.ts +2 -1
  50. package/dist/components/game/shared/lib/editorUtils.d.ts +8 -8
  51. package/dist/components/index.cjs +100 -24
  52. package/dist/components/index.css +3 -3
  53. package/dist/components/index.js +85 -13
  54. package/dist/hooks/index.cjs +71 -0
  55. package/dist/hooks/index.d.ts +1 -0
  56. package/dist/hooks/index.js +69 -2
  57. package/dist/hooks/useGitHub.d.ts +7 -7
  58. package/dist/hooks/useSharedEntityStore.d.ts +53 -0
  59. package/dist/hooks/useTraitStateMachine.d.ts +32 -1
  60. package/dist/marketing/index.cjs +2 -2
  61. package/dist/marketing/index.js +2 -2
  62. package/dist/providers/EventBusProvider.d.ts +1 -1
  63. package/dist/providers/SelectionProvider.d.ts +1 -1
  64. package/dist/providers/ServerBridge.d.ts +1 -1
  65. package/dist/providers/index.cjs +11 -5
  66. package/dist/providers/index.css +3 -3
  67. package/dist/providers/index.js +11 -5
  68. package/dist/runtime/index.cjs +253 -49
  69. package/dist/runtime/index.css +3 -3
  70. package/dist/runtime/index.js +255 -51
  71. package/package.json +11 -16
@@ -630,7 +630,7 @@ var init_AvlFieldType = __esm({
630
630
  }
631
631
  });
632
632
 
633
- // node_modules/clsx/dist/clsx.mjs
633
+ // node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
634
634
  function r(e) {
635
635
  var t, f3, n = "";
636
636
  if ("string" == typeof e || "number" == typeof e) n += e;
@@ -645,11 +645,11 @@ function clsx() {
645
645
  return n;
646
646
  }
647
647
  var init_clsx = __esm({
648
- "node_modules/clsx/dist/clsx.mjs"() {
648
+ "node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs"() {
649
649
  }
650
650
  });
651
651
 
652
- // node_modules/tailwind-merge/dist/bundle-mjs.mjs
652
+ // node_modules/.pnpm/tailwind-merge@2.6.1/node_modules/tailwind-merge/dist/bundle-mjs.mjs
653
653
  function twJoin() {
654
654
  let index = 0;
655
655
  let argument;
@@ -693,7 +693,7 @@ function createTailwindMerge(createConfigFirst, ...createConfigRest) {
693
693
  }
694
694
  var CLASS_PART_SEPARATOR, createClassGroupUtils, getGroupRecursive, arbitraryPropertyRegex, getGroupIdForArbitraryProperty, createClassMap, processClassesRecursively, getPart, isThemeGetter, getPrefixedClassGroupEntries, createLruCache, IMPORTANT_MODIFIER, createParseClassName, sortModifiers, createConfigUtils, SPLIT_CLASSES_REGEX, mergeClassList, toValue, fromTheme, arbitraryValueRegex, fractionRegex, stringLengths, tshirtUnitRegex, lengthUnitRegex, colorFunctionRegex, shadowRegex, imageRegex, isLength, isArbitraryLength, isNumber, isArbitraryNumber, isInteger, isPercent, isArbitraryValue, isTshirtSize, sizeLabels, isArbitrarySize, isArbitraryPosition, imageLabels, isArbitraryImage, isArbitraryShadow, isAny, getIsArbitraryValue, isLengthOnly, isNever, isShadow, isImage, getDefaultConfig, twMerge;
695
695
  var init_bundle_mjs = __esm({
696
- "node_modules/tailwind-merge/dist/bundle-mjs.mjs"() {
696
+ "node_modules/.pnpm/tailwind-merge@2.6.1/node_modules/tailwind-merge/dist/bundle-mjs.mjs"() {
697
697
  CLASS_PART_SEPARATOR = "-";
698
698
  createClassGroupUtils = (config) => {
699
699
  const classMap = createClassMap(config);
@@ -14003,6 +14003,9 @@ var init_useUnitSpriteAtlas = __esm({
14003
14003
  init_spriteAnimation();
14004
14004
  }
14005
14005
  });
14006
+ function normalizeBackdrop(bg) {
14007
+ return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
14008
+ }
14006
14009
  function SideView({
14007
14010
  player,
14008
14011
  platforms,
@@ -14012,7 +14015,7 @@ function SideView({
14012
14015
  canvasHeight,
14013
14016
  follow,
14014
14017
  bgColor,
14015
- backgroundImage,
14018
+ backgroundImage: backgroundImageRaw,
14016
14019
  playerSprite,
14017
14020
  tileSprites,
14018
14021
  effects,
@@ -14020,6 +14023,7 @@ function SideView({
14020
14023
  keyUpMap,
14021
14024
  className
14022
14025
  }) {
14026
+ const backgroundImage = normalizeBackdrop(backgroundImageRaw);
14023
14027
  const canvasRef = React105.useRef(null);
14024
14028
  const eventBus = useEventBus();
14025
14029
  const keysRef = React105.useRef(/* @__PURE__ */ new Set());
@@ -14311,7 +14315,7 @@ function Canvas2D({
14311
14315
  effects: _effectsPropRaw = [],
14312
14316
  platforms,
14313
14317
  player,
14314
- backgroundImage,
14318
+ backgroundImage: backgroundImageRaw,
14315
14319
  // Interaction state
14316
14320
  selectedUnitId = null,
14317
14321
  validMoves = [],
@@ -14352,6 +14356,7 @@ function Canvas2D({
14352
14356
  // Remote asset loading
14353
14357
  assetManifest
14354
14358
  }) {
14359
+ const backgroundImage = normalizeBackdrop(backgroundImageRaw);
14355
14360
  const isSide = projection === "side";
14356
14361
  const isFree = projection === "free";
14357
14362
  const flatLike = projection === "hex" || projection === "flat" || isFree;
@@ -20335,9 +20340,9 @@ var init_BiologyCanvas = __esm({
20335
20340
  }
20336
20341
  });
20337
20342
 
20338
- // node_modules/katex/dist/katex.min.css
20343
+ // node_modules/.pnpm/katex@0.16.47/node_modules/katex/dist/katex.min.css
20339
20344
  var init_katex_min = __esm({
20340
- "node_modules/katex/dist/katex.min.css"() {
20345
+ "node_modules/.pnpm/katex@0.16.47/node_modules/katex/dist/katex.min.css"() {
20341
20346
  }
20342
20347
  });
20343
20348
  var Tabs;
@@ -32543,8 +32548,9 @@ var init_LineChart = __esm({
32543
32548
  const safeData = data ?? [];
32544
32549
  const sortedData = React105.useMemo(() => {
32545
32550
  if (safeData.length === 0) return [];
32551
+ if (!safeData.some((d) => d.date != null)) return [...safeData];
32546
32552
  return [...safeData].sort(
32547
- (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
32553
+ (a, b) => new Date(a.date ?? 0).getTime() - new Date(b.date ?? 0).getTime()
32548
32554
  );
32549
32555
  }, [safeData]);
32550
32556
  const points = React105.useMemo(() => {
@@ -33295,12 +33301,12 @@ var init_MapView = __esm({
33295
33301
  shadowSize: [41, 41]
33296
33302
  });
33297
33303
  L.Marker.prototype.options.icon = defaultIcon;
33298
- const { useEffect: useEffect71, useRef: useRef67, useCallback: useCallback104, useState: useState105 } = React105__namespace.default;
33304
+ const { useEffect: useEffect71, useRef: useRef68, useCallback: useCallback104, useState: useState105 } = React105__namespace.default;
33299
33305
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33300
33306
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33301
33307
  function MapUpdater({ centerLat, centerLng, zoom }) {
33302
33308
  const map = useMap();
33303
- const prevRef = useRef67({ centerLat, centerLng, zoom });
33309
+ const prevRef = useRef68({ centerLat, centerLng, zoom });
33304
33310
  useEffect71(() => {
33305
33311
  const prev = prevRef.current;
33306
33312
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
@@ -46291,7 +46297,7 @@ function getAllEvents(traits2) {
46291
46297
  function EventDispatcherTab({ traits: traits2, schema }) {
46292
46298
  const eventBus = useEventBus();
46293
46299
  const { t } = hooks.useTranslate();
46294
- const [log11, setLog] = React105__namespace.useState([]);
46300
+ const [log12, setLog] = React105__namespace.useState([]);
46295
46301
  const prevStatesRef = React105__namespace.useRef(/* @__PURE__ */ new Map());
46296
46302
  React105__namespace.useEffect(() => {
46297
46303
  for (const trait of traits2) {
@@ -46355,9 +46361,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
46355
46361
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
46356
46362
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
46357
46363
  ] }),
46358
- log11.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
46364
+ log12.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
46359
46365
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
46360
- /* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log11.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
46366
+ /* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log12.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
46361
46367
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
46362
46368
  " ",
46363
46369
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
@@ -53642,7 +53648,67 @@ function convertFnFormLambdasInProps(props) {
53642
53648
 
53643
53649
  // hooks/index.ts
53644
53650
  init_useEventBus();
53645
- var log7 = logger.createLogger("almadar:ui:effects:client-handlers");
53651
+ var log7 = logger.createLogger("almadar:ui:shared-entity-store");
53652
+ var EMPTY_ENTITY_STATE = {};
53653
+ function createSharedEntityStore() {
53654
+ const states = /* @__PURE__ */ new Map();
53655
+ const subscribers2 = /* @__PURE__ */ new Map();
53656
+ const getSnapshot2 = (entityId) => states.get(entityId) ?? EMPTY_ENTITY_STATE;
53657
+ const subscribe = (entityId, callback) => {
53658
+ let set = subscribers2.get(entityId);
53659
+ if (!set) {
53660
+ set = /* @__PURE__ */ new Set();
53661
+ subscribers2.set(entityId, set);
53662
+ }
53663
+ set.add(callback);
53664
+ return () => {
53665
+ const current = subscribers2.get(entityId);
53666
+ if (!current) return;
53667
+ current.delete(callback);
53668
+ if (current.size === 0) {
53669
+ subscribers2.delete(entityId);
53670
+ }
53671
+ };
53672
+ };
53673
+ const commit = (entityId, nextState) => {
53674
+ states.set(entityId, nextState);
53675
+ const set = subscribers2.get(entityId);
53676
+ if (!set) return;
53677
+ set.forEach((callback) => {
53678
+ try {
53679
+ callback();
53680
+ } catch (error) {
53681
+ log7.error("Shared entity subscriber error", {
53682
+ entityId,
53683
+ error: error instanceof Error ? error : String(error)
53684
+ });
53685
+ }
53686
+ });
53687
+ };
53688
+ const seed = (entityId, initialState) => {
53689
+ if (!states.has(entityId)) {
53690
+ states.set(entityId, initialState);
53691
+ }
53692
+ };
53693
+ return { getSnapshot: getSnapshot2, subscribe, commit, seed };
53694
+ }
53695
+ function useSharedEntityStore() {
53696
+ const storeRef = React105.useRef(null);
53697
+ if (storeRef.current === null) {
53698
+ storeRef.current = createSharedEntityStore();
53699
+ }
53700
+ return storeRef.current;
53701
+ }
53702
+ function runTickFrame(entityId, orderedWriters, store) {
53703
+ let scratch = store.getSnapshot(entityId);
53704
+ for (const writer of orderedWriters) {
53705
+ const writes = writer(scratch);
53706
+ scratch = core.mergeEntityFrame(scratch, writes);
53707
+ }
53708
+ store.commit(entityId, scratch);
53709
+ return scratch;
53710
+ }
53711
+ var log8 = logger.createLogger("almadar:ui:effects:client-handlers");
53646
53712
  function createClientEffectHandlers(options) {
53647
53713
  const { eventBus, slotSetter, navigate, notify, callService, liveEntity } = options;
53648
53714
  return {
@@ -53651,12 +53717,12 @@ function createClientEffectHandlers(options) {
53651
53717
  eventBus.emit(prefixedEvent, payload);
53652
53718
  },
53653
53719
  persist: async () => {
53654
- log7.warn("persist is server-side only, ignored on client");
53720
+ log8.warn("persist is server-side only, ignored on client");
53655
53721
  },
53656
53722
  // @almadar/runtime EffectHandlers.set types value:unknown — should be FieldValue (upstream fix queued)
53657
53723
  set: ((_entityId, field, value) => {
53658
53724
  if (!liveEntity) {
53659
- log7.warn("set is server-side only, ignored on client (no live entity)");
53725
+ log8.warn("set is server-side only, ignored on client (no live entity)");
53660
53726
  return;
53661
53727
  }
53662
53728
  liveEntity[field] = value;
@@ -53688,10 +53754,10 @@ function createClientEffectHandlers(options) {
53688
53754
  slotSetter.addPattern(slot, pattern, props);
53689
53755
  },
53690
53756
  navigate: navigate ?? ((path) => {
53691
- log7.warn("No navigate handler, ignoring", { path });
53757
+ log8.warn("No navigate handler, ignoring", { path });
53692
53758
  }),
53693
53759
  notify: notify ?? ((msg, type) => {
53694
- log7.debug("notify", { type, message: msg });
53760
+ log8.debug("notify", { type, message: msg });
53695
53761
  })
53696
53762
  };
53697
53763
  }
@@ -53736,11 +53802,101 @@ function toTraitDefinition(binding) {
53736
53802
  function normalizeEventKey(eventKey) {
53737
53803
  return eventKey.startsWith("UI:") ? eventKey.slice(3) : eventKey;
53738
53804
  }
53805
+ var SHARED_ENTITY_WRITE_OPS = /* @__PURE__ */ new Set(["set"]);
53806
+ var SHARED_ENTITY_RENDER_OPS = /* @__PURE__ */ new Set(["render-ui", "render"]);
53807
+ function collectAllTraitEffects(trait) {
53808
+ return [
53809
+ ...trait.ticks.flatMap((t) => t.effects),
53810
+ ...trait.transitions.flatMap((t) => t.effects)
53811
+ ];
53812
+ }
53813
+ function effectsCallOp(effects, ops) {
53814
+ for (const effect of effects) {
53815
+ let found = false;
53816
+ core.walkSExpr(effect, (node) => {
53817
+ if (!found && Array.isArray(node) && typeof node[0] === "string" && ops.has(node[0])) {
53818
+ found = true;
53819
+ }
53820
+ });
53821
+ if (found) return true;
53822
+ }
53823
+ return false;
53824
+ }
53825
+ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
53826
+ return (scratch) => {
53827
+ const traitName = binding.trait.name;
53828
+ const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
53829
+ if (tick.appliesTo.length > 0 && !tick.appliesTo.includes(currentState)) return [];
53830
+ const scratchEntity = { ...scratch };
53831
+ const writes = [];
53832
+ const ctx = evaluator.createMinimalContext(scratchEntity, {}, currentState);
53833
+ const declaredDefaults = runtime.collectDeclaredConfigDefaults(binding.trait);
53834
+ const callSiteConfig = binding.config;
53835
+ if (declaredDefaults || callSiteConfig) {
53836
+ ctx.config = { ...declaredDefaults ?? {}, ...callSiteConfig ?? {} };
53837
+ }
53838
+ ctx.mutateEntity = (changes) => {
53839
+ for (const [field, value] of Object.entries(changes)) {
53840
+ const fieldValue = value;
53841
+ scratchEntity[field] = fieldValue;
53842
+ writes.push({ field, value: fieldValue });
53843
+ }
53844
+ };
53845
+ ctx.emit = (event, payload) => {
53846
+ emit(event, payload);
53847
+ };
53848
+ if (tick.guard !== void 0 && !evaluator.evaluateGuard(tick.guard, ctx)) {
53849
+ tickLog.debug("guard-blocked", { traitName, tick: tick.name, state: currentState });
53850
+ return [];
53851
+ }
53852
+ evaluator.executeEffects(tick.effects, ctx);
53853
+ return writes;
53854
+ };
53855
+ }
53739
53856
  function useTraitStateMachine(traitBindings, uiSlots, options) {
53740
53857
  const eventBus = useEventBus();
53741
53858
  const { entities } = providers.useEntitySchema();
53742
53859
  const traitConfigsByName = options?.traitConfigsByName;
53743
53860
  const orbitalsByTrait = options?.orbitalsByTrait;
53861
+ const sharedEntityStore = useSharedEntityStore();
53862
+ const sharedGroups = React105.useMemo(() => {
53863
+ const groups = /* @__PURE__ */ new Map();
53864
+ for (const binding of traitBindings) {
53865
+ const linkedEntityName = binding.linkedEntity ?? binding.trait.linkedEntity;
53866
+ if (!linkedEntityName) continue;
53867
+ const entityDef = entities.get(linkedEntityName);
53868
+ if (!entityDef?.shared) continue;
53869
+ const orbitalName = orbitalsByTrait?.[binding.trait.name] ?? "";
53870
+ const storeKey = `${orbitalName}::${linkedEntityName}`;
53871
+ let group = groups.get(storeKey);
53872
+ if (!group) {
53873
+ let defaults;
53874
+ for (const field of entityDef.fields) {
53875
+ if (field.default !== void 0) {
53876
+ (defaults ?? (defaults = {}))[field.name] = field.default;
53877
+ }
53878
+ }
53879
+ group = { storeKey, entityName: linkedEntityName, writerBindings: [], renderBindings: [], defaults };
53880
+ groups.set(storeKey, group);
53881
+ }
53882
+ const allEffects = collectAllTraitEffects(binding.trait);
53883
+ if (effectsCallOp(allEffects, SHARED_ENTITY_WRITE_OPS)) group.writerBindings.push(binding);
53884
+ if (effectsCallOp(allEffects, SHARED_ENTITY_RENDER_OPS)) group.renderBindings.push(binding);
53885
+ }
53886
+ return groups;
53887
+ }, [traitBindings, entities, orbitalsByTrait]);
53888
+ for (const group of sharedGroups.values()) {
53889
+ if (group.defaults) sharedEntityStore.seed(group.storeKey, group.defaults);
53890
+ }
53891
+ const sharedKeyByTraitNameRef = React105.useRef(/* @__PURE__ */ new Map());
53892
+ React105.useEffect(() => {
53893
+ const map = /* @__PURE__ */ new Map();
53894
+ for (const group of sharedGroups.values()) {
53895
+ for (const binding of group.writerBindings) map.set(binding.trait.name, group.storeKey);
53896
+ for (const binding of group.renderBindings) map.set(binding.trait.name, group.storeKey);
53897
+ }
53898
+ sharedKeyByTraitNameRef.current = map;
53899
+ }, [sharedGroups]);
53744
53900
  const manager = React105.useMemo(() => {
53745
53901
  const traitDefs = traitBindings.map(toTraitDefinition);
53746
53902
  const m = new runtime.StateMachineManager(traitDefs);
@@ -53923,14 +54079,21 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53923
54079
  };
53924
54080
  }, [traitBindings]);
53925
54081
  const executeTransitionEffects = React105.useCallback(async (params) => {
53926
- const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log11 } = params;
54082
+ const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log12 } = params;
53927
54083
  const traitName = binding.trait.name;
53928
54084
  const linkedEntity = binding.linkedEntity || "";
53929
54085
  const entityId = payload?.entityId;
53930
- let liveEntity = traitFieldStatesRef.current.get(traitName);
53931
- if (!liveEntity) {
53932
- liveEntity = {};
53933
- traitFieldStatesRef.current.set(traitName, liveEntity);
54086
+ const sharedKey = sharedKeyByTraitNameRef.current.get(traitName);
54087
+ let liveEntity;
54088
+ if (sharedKey !== void 0) {
54089
+ liveEntity = { ...sharedEntityStore.getSnapshot(sharedKey) };
54090
+ } else {
54091
+ let existing = traitFieldStatesRef.current.get(traitName);
54092
+ if (!existing) {
54093
+ existing = {};
54094
+ traitFieldStatesRef.current.set(traitName, existing);
54095
+ }
54096
+ liveEntity = existing;
53934
54097
  }
53935
54098
  const effects = syncOnly ? params.effects.filter(
53936
54099
  (e) => Array.isArray(e) && SYNC_TICK_OPERATORS.has(String(e[0]))
@@ -54012,7 +54175,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54012
54175
  ...handlers,
54013
54176
  set: async (targetId, field, value) => {
54014
54177
  if (baseSet) await baseSet(targetId, field, value);
54015
- log11.debug("set:write", {
54178
+ log12.debug("set:write", {
54016
54179
  traitName,
54017
54180
  field,
54018
54181
  value: JSON.stringify(value),
@@ -54052,17 +54215,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54052
54215
  const executor = new runtime.EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
54053
54216
  try {
54054
54217
  await executor.executeAll(effects);
54055
- log11.debug("effects:executed", () => ({
54218
+ if (sharedKey !== void 0) {
54219
+ sharedEntityStore.commit(sharedKey, liveEntity);
54220
+ }
54221
+ log12.debug("effects:executed", () => ({
54056
54222
  traitName,
54057
54223
  transition: `${previousState}->${newState}`,
54058
54224
  event: flushEvent,
54059
54225
  effectCount: effects.length,
54060
54226
  emitted: emittedDuringExec.join(","),
54061
- entityAfter: JSON.stringify(traitFieldStatesRef.current.get(traitName) ?? {}),
54227
+ entityAfter: JSON.stringify(liveEntity ?? {}),
54062
54228
  slotsTouched: Array.from(pendingSlots.keys()).join(",")
54063
54229
  }));
54064
54230
  for (const [slot, patterns] of pendingSlots) {
54065
- log11.debug("flush:slot", {
54231
+ log12.debug("flush:slot", {
54066
54232
  traitName,
54067
54233
  slot,
54068
54234
  patternCount: patterns.length,
@@ -54077,7 +54243,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54077
54243
  });
54078
54244
  }
54079
54245
  } catch (error) {
54080
- log11.error("effects:error", {
54246
+ log12.error("effects:error", {
54081
54247
  traitName,
54082
54248
  transition: `${previousState}->${newState}`,
54083
54249
  event: flushEvent,
@@ -54086,14 +54252,16 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54086
54252
  });
54087
54253
  }
54088
54254
  return emittedDuringExec;
54089
- }, [eventBus, flushSlot]);
54255
+ }, [eventBus, flushSlot, sharedEntityStore]);
54090
54256
  const runTickEffects = React105.useCallback((tick, binding) => {
54091
54257
  const traitName = binding.trait.name;
54092
54258
  const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
54093
54259
  if (tick.appliesTo.length > 0 && !tick.appliesTo.includes(currentState)) return;
54094
54260
  if (tick.guard !== void 0) {
54261
+ const sharedKey = sharedKeyByTraitNameRef.current.get(traitName);
54262
+ const entity = sharedKey !== void 0 ? sharedEntityStore.getSnapshot(sharedKey) : traitFieldStatesRef.current.get(traitName) ?? {};
54095
54263
  const guardCtx = {
54096
- entity: traitFieldStatesRef.current.get(traitName) ?? {},
54264
+ entity,
54097
54265
  payload: {},
54098
54266
  state: currentState
54099
54267
  };
@@ -54118,10 +54286,46 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54118
54286
  syncOnly: true,
54119
54287
  log: tickLog
54120
54288
  });
54121
- }, [executeTransitionEffects]);
54289
+ }, [executeTransitionEffects, sharedEntityStore]);
54290
+ const emitFromSharedWriter = React105.useCallback((event, payload) => {
54291
+ const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
54292
+ eventBus.emit(prefixedEvent, payload);
54293
+ }, [eventBus]);
54122
54294
  React105.useEffect(() => {
54123
54295
  const scheduler = runtime.createTickScheduler();
54296
+ const writerTraitNames = /* @__PURE__ */ new Set();
54297
+ for (const group of sharedGroups.values()) {
54298
+ const ticksByInterval = /* @__PURE__ */ new Map();
54299
+ for (const binding of group.writerBindings) {
54300
+ writerTraitNames.add(binding.trait.name);
54301
+ for (const tick of binding.trait.ticks ?? []) {
54302
+ const intervalKey = String(tick.interval);
54303
+ const entries = ticksByInterval.get(intervalKey) ?? [];
54304
+ entries.push({ binding, tick });
54305
+ ticksByInterval.set(intervalKey, entries);
54306
+ }
54307
+ }
54308
+ for (const entries of ticksByInterval.values()) {
54309
+ const interval = entries[0].tick.interval;
54310
+ const onDue = () => {
54311
+ const writers = entries.map(
54312
+ ({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
54313
+ );
54314
+ runTickFrame(group.storeKey, writers, sharedEntityStore);
54315
+ };
54316
+ if (interval === "frame") {
54317
+ scheduler.add(0, onDue);
54318
+ } else if (typeof interval === "number") {
54319
+ scheduler.add(interval, onDue);
54320
+ } else if (runtime.isValidCronExpression(interval)) {
54321
+ scheduler.addCron(interval, onDue);
54322
+ } else {
54323
+ scheduler.add(runtime.parseDurationString(interval), onDue);
54324
+ }
54325
+ }
54326
+ }
54124
54327
  for (const binding of traitBindings) {
54328
+ if (writerTraitNames.has(binding.trait.name)) continue;
54125
54329
  for (const tick of binding.trait.ticks ?? []) {
54126
54330
  if (tick.interval === "frame") {
54127
54331
  scheduler.add(0, () => runTickEffects(tick, binding));
@@ -54135,7 +54339,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54135
54339
  }
54136
54340
  }
54137
54341
  return () => scheduler.stopAll();
54138
- }, [traitBindings, runTickEffects]);
54342
+ }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
54139
54343
  const processEventQueued = React105.useCallback(async (eventKey, payload) => {
54140
54344
  const normalizedEvent = normalizeEventKey(eventKey);
54141
54345
  const bindings = traitBindingsRef.current;
@@ -54479,7 +54683,7 @@ function buildOrbitalsByTrait(schema, resolvedPages = []) {
54479
54683
  // runtime/OrbPreview.tsx
54480
54684
  init_verificationRegistry();
54481
54685
  var PERF_NAMESPACE = "almadar:perf:canvas";
54482
- var log8 = logger.createLogger(PERF_NAMESPACE);
54686
+ var log9 = logger.createLogger(PERF_NAMESPACE);
54483
54687
  var RING_SIZE = 50;
54484
54688
  var ring = [];
54485
54689
  var writeIdx = 0;
@@ -54530,7 +54734,7 @@ function perfEnd(name, startToken, detail) {
54530
54734
  }
54531
54735
  }
54532
54736
  push({ name, durationMs, ts: endTs, detail });
54533
- log8.debug(name, () => ({ durationMs, ...detail ?? {} }));
54737
+ log9.debug(name, () => ({ durationMs, ...detail ?? {} }));
54534
54738
  }
54535
54739
  var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
54536
54740
  if (!isEnabled()) return;
@@ -54540,7 +54744,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
54540
54744
  ts: commitTime,
54541
54745
  detail: { baseDuration }
54542
54746
  });
54543
- log8.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
54747
+ log9.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
54544
54748
  };
54545
54749
 
54546
54750
  // lib/prepareSchemaForPreview.ts
@@ -55182,7 +55386,7 @@ init_useEventBus();
55182
55386
  // components/avl/hooks/useCanvasDnd.tsx
55183
55387
  init_useEventBus();
55184
55388
  init_useAlmadarDndCollision();
55185
- var log9 = logger.createLogger("almadar:ui:canvas-dnd");
55389
+ var log10 = logger.createLogger("almadar:ui:canvas-dnd");
55186
55390
  function useCanvasDraggable({
55187
55391
  id,
55188
55392
  payload,
@@ -55221,7 +55425,7 @@ function defaultEmit(eventBus, drop) {
55221
55425
  if (payload.kind === "pattern") {
55222
55426
  const patternType = payload.data["type"];
55223
55427
  if (typeof patternType !== "string") {
55224
- log9.warn("default-emit:pattern:missing-type");
55428
+ log10.warn("default-emit:pattern:missing-type");
55225
55429
  return;
55226
55430
  }
55227
55431
  const out = { patternType, containerNode: target.containerNode };
@@ -55230,23 +55434,23 @@ function defaultEmit(eventBus, drop) {
55230
55434
  out.index = resolved.index;
55231
55435
  }
55232
55436
  eventBus.emit("UI:PATTERN_DROP", out);
55233
- log9.info("default-emit:pattern", { patternType, level: target.level });
55437
+ log10.info("default-emit:pattern", { patternType, level: target.level });
55234
55438
  return;
55235
55439
  }
55236
55440
  if (payload.kind === "behavior") {
55237
55441
  const behaviorName = payload.data["name"];
55238
55442
  if (typeof behaviorName !== "string") {
55239
- log9.warn("default-emit:behavior:missing-name");
55443
+ log10.warn("default-emit:behavior:missing-name");
55240
55444
  return;
55241
55445
  }
55242
55446
  eventBus.emit("UI:BEHAVIOR_DROP", {
55243
55447
  behaviorName,
55244
55448
  containerNode: target.containerNode
55245
55449
  });
55246
- log9.info("default-emit:behavior", { behaviorName, level: target.level });
55450
+ log10.info("default-emit:behavior", { behaviorName, level: target.level });
55247
55451
  return;
55248
55452
  }
55249
- log9.debug("default-emit:unhandled-kind", { kind: payload.kind });
55453
+ log10.debug("default-emit:unhandled-kind", { kind: payload.kind });
55250
55454
  }
55251
55455
  function CanvasDndProvider({
55252
55456
  children,
@@ -55262,9 +55466,9 @@ function CanvasDndProvider({
55262
55466
  if (payload) {
55263
55467
  setActivePayload(payload);
55264
55468
  eventBus.emit("UI:DRAG_START", { kind: payload.kind, data: payload.data });
55265
- log9.info("dragStart", { id: e.active.id, kind: payload.kind });
55469
+ log10.info("dragStart", { id: e.active.id, kind: payload.kind });
55266
55470
  } else {
55267
- log9.warn("dragStart:missing-payload", { id: e.active.id });
55471
+ log10.warn("dragStart:missing-payload", { id: e.active.id });
55268
55472
  }
55269
55473
  }, [eventBus]);
55270
55474
  const handleDragEnd = React105__namespace.default.useCallback((e) => {
@@ -55274,7 +55478,7 @@ function CanvasDndProvider({
55274
55478
  const overData = e.over?.data.current;
55275
55479
  const target = overData?.target;
55276
55480
  const accepts = overData?.accepts;
55277
- log9.info("dragEnd", {
55481
+ log10.info("dragEnd", {
55278
55482
  activeId: e.active.id,
55279
55483
  overId: e.over?.id,
55280
55484
  hasPayload: !!payload,
@@ -55286,7 +55490,7 @@ function CanvasDndProvider({
55286
55490
  }
55287
55491
  if (!payload || !target) return;
55288
55492
  if (accepts && !accepts.includes(payload.kind)) {
55289
- log9.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
55493
+ log10.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
55290
55494
  return;
55291
55495
  }
55292
55496
  const activator = e.activatorEvent;
@@ -55298,7 +55502,7 @@ function CanvasDndProvider({
55298
55502
  }, [eventBus, onDrop]);
55299
55503
  const handleDragCancel = React105__namespace.default.useCallback(() => {
55300
55504
  setActivePayload(null);
55301
- log9.info("dragCancel");
55505
+ log10.info("dragCancel");
55302
55506
  }, []);
55303
55507
  return /* @__PURE__ */ jsxRuntime.jsxs(
55304
55508
  core$1.DndContext,
@@ -57131,7 +57335,7 @@ init_AvlTransitionLane();
57131
57335
  init_AvlSwimLane();
57132
57336
  init_avl_atom_types();
57133
57337
  init_avl_elk_layout();
57134
- var log10 = logger.createLogger("almadar:ui:avl:trait-scene");
57338
+ var log11 = logger.createLogger("almadar:ui:avl:trait-scene");
57135
57339
  var SWIM_GUTTER2 = 120;
57136
57340
  var CENTER_W2 = 360;
57137
57341
  var AvlTraitScene = ({
@@ -57144,7 +57348,7 @@ var AvlTraitScene = ({
57144
57348
  const dataKey = React105.useMemo(() => JSON.stringify(data), [data]);
57145
57349
  React105.useEffect(() => {
57146
57350
  computeTraitLayout(data).then(setLayout).catch((error) => {
57147
- log10.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
57351
+ log11.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
57148
57352
  });
57149
57353
  }, [dataKey]);
57150
57354
  if (!layout) {
@@ -1,4 +1,4 @@
1
- /* node_modules/katex/dist/katex.min.css */
1
+ /* node_modules/.pnpm/katex@0.16.47/node_modules/katex/dist/katex.min.css */
2
2
  @font-face {
3
3
  font-display: block;
4
4
  font-family: KaTeX_AMS;
@@ -214,11 +214,11 @@
214
214
  border-color: currentColor;
215
215
  }
216
216
  .katex .katex-version:after {
217
- content: "0.16.45";
217
+ content: "0.16.47";
218
218
  }
219
219
  .katex .katex-mathml {
220
- clip: rect(1px, 1px, 1px, 1px);
221
220
  border: 0;
221
+ clip-path: inset(50%);
222
222
  height: 1px;
223
223
  overflow: hidden;
224
224
  padding: 0;