@almadar/ui 6.23.0 → 6.25.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.
@@ -37340,13 +37340,13 @@ var init_MapView = __esm({
37340
37340
  shadowSize: [41, 41]
37341
37341
  });
37342
37342
  L.Marker.prototype.options.icon = defaultIcon;
37343
- const { useEffect: useEffect71, useRef: useRef70, useCallback: useCallback101, useState: useState109 } = React96__namespace.default;
37343
+ const { useEffect: useEffect72, useRef: useRef71, useCallback: useCallback102, useState: useState110 } = React96__namespace.default;
37344
37344
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
37345
37345
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
37346
37346
  function MapUpdater({ centerLat, centerLng, zoom }) {
37347
37347
  const map = useMap();
37348
- const prevRef = useRef70({ centerLat, centerLng, zoom });
37349
- useEffect71(() => {
37348
+ const prevRef = useRef71({ centerLat, centerLng, zoom });
37349
+ useEffect72(() => {
37350
37350
  const prev = prevRef.current;
37351
37351
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
37352
37352
  map.setView([centerLat, centerLng], zoom);
@@ -37357,7 +37357,7 @@ var init_MapView = __esm({
37357
37357
  }
37358
37358
  function MapClickHandler({ onMapClick }) {
37359
37359
  const map = useMap();
37360
- useEffect71(() => {
37360
+ useEffect72(() => {
37361
37361
  if (!onMapClick) return;
37362
37362
  const handler = (e) => {
37363
37363
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -37385,8 +37385,8 @@ var init_MapView = __esm({
37385
37385
  showAttribution = true
37386
37386
  }) {
37387
37387
  const eventBus = useEventBus2();
37388
- const [clickedPosition, setClickedPosition] = useState109(null);
37389
- const handleMapClick = useCallback101((lat, lng) => {
37388
+ const [clickedPosition, setClickedPosition] = useState110(null);
37389
+ const handleMapClick = useCallback102((lat, lng) => {
37390
37390
  if (showClickedPin) {
37391
37391
  setClickedPosition({ lat, lng });
37392
37392
  }
@@ -37395,7 +37395,7 @@ var init_MapView = __esm({
37395
37395
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
37396
37396
  }
37397
37397
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
37398
- const handleMarkerClick = useCallback101((marker) => {
37398
+ const handleMarkerClick = useCallback102((marker) => {
37399
37399
  onMarkerClick?.(marker);
37400
37400
  if (markerClickEvent) {
37401
37401
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -51711,7 +51711,7 @@ function getAllEvents(traits2) {
51711
51711
  function EventDispatcherTab({ traits: traits2, schema }) {
51712
51712
  const eventBus = useEventBus();
51713
51713
  const { t } = hooks.useTranslate();
51714
- const [log12, setLog] = React96__namespace.useState([]);
51714
+ const [log13, setLog] = React96__namespace.useState([]);
51715
51715
  const prevStatesRef = React96__namespace.useRef(/* @__PURE__ */ new Map());
51716
51716
  React96__namespace.useEffect(() => {
51717
51717
  for (const trait of traits2) {
@@ -51775,9 +51775,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
51775
51775
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.otherEvents") }),
51776
51776
  /* @__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)) })
51777
51777
  ] }),
51778
- log12.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
51778
+ log13.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
51779
51779
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-muted-foreground mb-1", children: t("debug.recentTransitions") }),
51780
- /* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log12.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
51780
+ /* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log13.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
51781
51781
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary", children: entry.traitName }),
51782
51782
  " ",
51783
51783
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: entry.from }),
@@ -59072,7 +59072,25 @@ function convertFnFormLambdasInProps(props) {
59072
59072
 
59073
59073
  // hooks/index.ts
59074
59074
  init_useEventBus();
59075
- var log8 = logger.createLogger("almadar:ui:shared-entity-store");
59075
+ logger.createLogger("almadar:ui:ui-slots");
59076
+ var ALL_SLOTS2 = core.UI_SLOTS.filter(
59077
+ (slot) => !slot.includes(".") && slot !== "hud" && slot !== "screen"
59078
+ );
59079
+ ALL_SLOTS2.reduce(
59080
+ (acc, slot) => {
59081
+ acc[slot] = null;
59082
+ return acc;
59083
+ },
59084
+ {}
59085
+ );
59086
+ ALL_SLOTS2.reduce(
59087
+ (acc, slot) => {
59088
+ acc[slot] = {};
59089
+ return acc;
59090
+ },
59091
+ {}
59092
+ );
59093
+ var log9 = logger.createLogger("almadar:ui:shared-entity-store");
59076
59094
  var EMPTY_ENTITY_STATE = {};
59077
59095
  function createSharedEntityStore() {
59078
59096
  const states = /* @__PURE__ */ new Map();
@@ -59102,7 +59120,7 @@ function createSharedEntityStore() {
59102
59120
  try {
59103
59121
  callback();
59104
59122
  } catch (error) {
59105
- log8.error("Shared entity subscriber error", {
59123
+ log9.error("Shared entity subscriber error", {
59106
59124
  entityId,
59107
59125
  error: error instanceof Error ? error : String(error)
59108
59126
  });
@@ -59133,7 +59151,7 @@ function runTickFrame(entityId, orderedWriters, store) {
59133
59151
  store.commit(entityId, scratch);
59134
59152
  return scratch;
59135
59153
  }
59136
- var log9 = logger.createLogger("almadar:ui:effects:client-handlers");
59154
+ var log10 = logger.createLogger("almadar:ui:effects:client-handlers");
59137
59155
  function createClientEffectHandlers(options) {
59138
59156
  const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated, callServiceDelegated } = options;
59139
59157
  return {
@@ -59142,7 +59160,7 @@ function createClientEffectHandlers(options) {
59142
59160
  eventBus.emit(prefixedEvent, payload, source);
59143
59161
  },
59144
59162
  persist: async () => {
59145
- log9.debug("persist is server-side only, ignored on client");
59163
+ log10.debug("persist is server-side only, ignored on client");
59146
59164
  },
59147
59165
  // Bridge mode: the server runs every persist and the response
59148
59166
  // carries its outcome — tell the executor so the placeholder above is
@@ -59152,7 +59170,7 @@ function createClientEffectHandlers(options) {
59152
59170
  // @almadar/runtime EffectHandlers.set types value:unknown — should be FieldValue (upstream fix queued)
59153
59171
  set: ((_entityId, field, value) => {
59154
59172
  if (!liveEntity) {
59155
- log9.warn("set is server-side only, ignored on client (no live entity)");
59173
+ log10.warn("set is server-side only, ignored on client (no live entity)");
59156
59174
  return;
59157
59175
  }
59158
59176
  liveEntity[field] = value;
@@ -59188,10 +59206,10 @@ function createClientEffectHandlers(options) {
59188
59206
  window.location.href = path;
59189
59207
  return;
59190
59208
  }
59191
- log9.warn("No navigate handler, ignoring", { path });
59209
+ log10.warn("No navigate handler, ignoring", { path });
59192
59210
  }),
59193
59211
  navigateBack: navigateBack ?? (() => {
59194
- log9.warn("No navigate-back handler, ignoring");
59212
+ log10.warn("No navigate-back handler, ignoring");
59195
59213
  })
59196
59214
  };
59197
59215
  }
@@ -59273,6 +59291,13 @@ function effectsCallOp(effects, ops) {
59273
59291
  }
59274
59292
  return false;
59275
59293
  }
59294
+ function diffDroppedTraitNames(prev, next) {
59295
+ const dropped = [];
59296
+ for (const name of prev) {
59297
+ if (!next.has(name)) dropped.push(name);
59298
+ }
59299
+ return dropped;
59300
+ }
59276
59301
  function classifySharedTick(tick) {
59277
59302
  const writes = effectsCallOp(tick.effects, SHARED_ENTITY_WRITE_OPS);
59278
59303
  const renders = effectsCallOp(tick.effects, SHARED_ENTITY_RENDER_OPS);
@@ -59475,6 +59500,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59475
59500
  const uiSlotsRef = React96.useRef(uiSlots);
59476
59501
  const embeddedTraitsRef = React96.useRef(options?.embeddedTraits);
59477
59502
  const optionsRef = React96.useRef(options);
59503
+ const prevActiveTraitNamesRef = React96.useRef(/* @__PURE__ */ new Set());
59478
59504
  React96.useEffect(() => {
59479
59505
  traitBindingsRef.current = traitBindings;
59480
59506
  }, [traitBindings]);
@@ -59603,6 +59629,18 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59603
59629
  };
59604
59630
  }, [traitBindings]);
59605
59631
  React96.useEffect(() => {
59632
+ const nextActiveTraitNames = new Set(
59633
+ traitBindings.map((b) => b.trait.name).filter((n) => !!n)
59634
+ );
59635
+ const dropped = diffDroppedTraitNames(prevActiveTraitNamesRef.current, nextActiveTraitNames);
59636
+ if (dropped.length > 0) {
59637
+ for (const traitName of dropped) {
59638
+ for (const slot of ALL_SLOTS2) {
59639
+ uiSlotsRef.current.clearBySource(slot, traitName);
59640
+ }
59641
+ }
59642
+ }
59643
+ prevActiveTraitNamesRef.current = nextActiveTraitNames;
59606
59644
  const newManager = managerRef.current;
59607
59645
  newManager.resetAll();
59608
59646
  setTraitStates(newManager.getAllStates());
@@ -59650,7 +59688,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59650
59688
  };
59651
59689
  }, [traitBindings]);
59652
59690
  const executeTransitionEffects = React96.useCallback(async (params) => {
59653
- const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log12, callsitePayload } = params;
59691
+ const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log13, callsitePayload } = params;
59654
59692
  const traitName = binding.trait.name;
59655
59693
  const linkedEntity = binding.linkedEntity || "";
59656
59694
  const entityId = payload?.entityId;
@@ -59787,7 +59825,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59787
59825
  if (sharedKey !== void 0) {
59788
59826
  sharedWrites.push({ field, value });
59789
59827
  }
59790
- log12.debug("set:write", {
59828
+ log13.debug("set:write", {
59791
59829
  traitName,
59792
59830
  field,
59793
59831
  value: JSON.stringify(value),
@@ -59859,7 +59897,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59859
59897
  core.mergeEntityFrame(sharedEntityStore.getSnapshot(sharedKey), sharedWrites)
59860
59898
  );
59861
59899
  }
59862
- log12.debug("effects:executed", () => ({
59900
+ log13.debug("effects:executed", () => ({
59863
59901
  traitName,
59864
59902
  transition: `${previousState}->${newState}`,
59865
59903
  event: flushEvent,
@@ -59868,26 +59906,31 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59868
59906
  entityAfter: JSON.stringify(liveEntity ?? {}),
59869
59907
  slotsTouched: Array.from(pendingSlots.keys()).join(",")
59870
59908
  }));
59871
- for (const [slot, patterns] of pendingSlots) {
59872
- log12.debug("flush:slot", {
59873
- traitName,
59874
- slot,
59875
- patternCount: patterns.length,
59876
- event: flushEvent,
59877
- transition: `${previousState}->${newState}`,
59878
- cleared: patterns.length === 0
59879
- });
59880
- flushSlot(traitName, slot, patterns, {
59881
- event: flushEvent,
59882
- state: previousState,
59883
- entity: binding.linkedEntity
59884
- });
59909
+ const stillActive = traitBindingsRef.current.some((b) => b.trait.name === traitName);
59910
+ if (!stillActive) {
59911
+ log13.debug("flush:skip-stale-navigation", { traitName, event: flushEvent });
59912
+ } else {
59913
+ for (const [slot, patterns] of pendingSlots) {
59914
+ log13.debug("flush:slot", {
59915
+ traitName,
59916
+ slot,
59917
+ patternCount: patterns.length,
59918
+ event: flushEvent,
59919
+ transition: `${previousState}->${newState}`,
59920
+ cleared: patterns.length === 0
59921
+ });
59922
+ flushSlot(traitName, slot, patterns, {
59923
+ event: flushEvent,
59924
+ state: previousState,
59925
+ entity: binding.linkedEntity
59926
+ });
59927
+ }
59885
59928
  }
59886
59929
  if (didWrite && sharedKey === void 0) {
59887
59930
  publishBindingSnapshot(traitName, liveEntity);
59888
59931
  }
59889
59932
  } catch (error) {
59890
- log12.error("effects:error", {
59933
+ log13.error("effects:error", {
59891
59934
  traitName,
59892
59935
  transition: `${previousState}->${newState}`,
59893
59936
  event: flushEvent,
@@ -59897,7 +59940,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59897
59940
  }
59898
59941
  return { emitted: emittedDuringExec, serverEffectResults };
59899
59942
  }, [eventBus, flushSlot, sharedEntityStore, publishBindingSnapshot, orbitalsByTrait, viewer]);
59900
- const reRenderCallsiteCaptureChildren = React96.useCallback(async (traitName, callsitePayload, entityByTrait, log12, visited = /* @__PURE__ */ new Set()) => {
59943
+ const reRenderCallsiteCaptureChildren = React96.useCallback(async (traitName, callsitePayload, entityByTrait, log13, visited = /* @__PURE__ */ new Set()) => {
59901
59944
  const children = callsiteCaptureChildrenByTrait?.get(traitName);
59902
59945
  if (!children || children.size === 0) return;
59903
59946
  const mgr = managerRef.current;
@@ -59911,7 +59954,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59911
59954
  if (lifecycleEvent === void 0) continue;
59912
59955
  const [entry] = mgr.sendEvent(lifecycleEvent, {}, void 0, entityByTrait, void 0, childName);
59913
59956
  if (!entry || !entry.result.executed) continue;
59914
- log12.debug("callsite-capture-child:rerender", {
59957
+ log13.debug("callsite-capture-child:rerender", {
59915
59958
  referrer: traitName,
59916
59959
  child: childName,
59917
59960
  lifecycleEvent,
@@ -59925,10 +59968,10 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59925
59968
  payload: {},
59926
59969
  flushEvent: lifecycleEvent,
59927
59970
  syncOnly: false,
59928
- log: log12,
59971
+ log: log13,
59929
59972
  callsitePayload
59930
59973
  });
59931
- await reRenderCallsiteCaptureChildren(childName, callsitePayload, entityByTrait, log12, visited);
59974
+ await reRenderCallsiteCaptureChildren(childName, callsitePayload, entityByTrait, log13, visited);
59932
59975
  }
59933
59976
  }, [callsiteCaptureChildrenByTrait, executeTransitionEffects]);
59934
59977
  const runTickEffects = React96.useCallback((tick, binding) => {
@@ -61152,7 +61195,7 @@ init_useEventBus();
61152
61195
  // components/avl/hooks/useCanvasDnd.tsx
61153
61196
  init_useEventBus();
61154
61197
  init_useAlmadarDndCollision();
61155
- var log10 = logger.createLogger("almadar:ui:canvas-dnd");
61198
+ var log11 = logger.createLogger("almadar:ui:canvas-dnd");
61156
61199
  function useCanvasDraggable({
61157
61200
  id,
61158
61201
  payload,
@@ -61190,7 +61233,7 @@ function defaultEmit(eventBus, drop) {
61190
61233
  if (payload.kind === "pattern") {
61191
61234
  const patternType = payload.data["type"];
61192
61235
  if (typeof patternType !== "string") {
61193
- log10.warn("default-emit:pattern:missing-type");
61236
+ log11.warn("default-emit:pattern:missing-type");
61194
61237
  return;
61195
61238
  }
61196
61239
  const out = { patternType, containerNode: target.containerNode };
@@ -61199,26 +61242,26 @@ function defaultEmit(eventBus, drop) {
61199
61242
  out.index = resolved.index;
61200
61243
  }
61201
61244
  eventBus.emit("UI:PATTERN_DROP", out);
61202
- log10.info("default-emit:pattern", { patternType, level: target.level });
61245
+ log11.info("default-emit:pattern", { patternType, level: target.level });
61203
61246
  return;
61204
61247
  }
61205
61248
  if (payload.kind === "behavior") {
61206
61249
  const behaviorName = payload.data["name"];
61207
61250
  if (typeof behaviorName !== "string") {
61208
- log10.warn("default-emit:behavior:missing-name");
61251
+ log11.warn("default-emit:behavior:missing-name");
61209
61252
  return;
61210
61253
  }
61211
61254
  eventBus.emit("UI:BEHAVIOR_DROP", {
61212
61255
  behaviorName,
61213
61256
  containerNode: target.containerNode
61214
61257
  });
61215
- log10.info("default-emit:behavior", { behaviorName, level: target.level });
61258
+ log11.info("default-emit:behavior", { behaviorName, level: target.level });
61216
61259
  return;
61217
61260
  }
61218
61261
  if (payload.kind === "pattern-instance") {
61219
61262
  const fromPath = payload.data["fromPath"];
61220
61263
  if (typeof fromPath !== "string") {
61221
- log10.warn("default-emit:pattern-instance:missing-fromPath");
61264
+ log11.warn("default-emit:pattern-instance:missing-fromPath");
61222
61265
  return;
61223
61266
  }
61224
61267
  const out = { fromPath, loc: payload.data["loc"] };
@@ -61227,10 +61270,10 @@ function defaultEmit(eventBus, drop) {
61227
61270
  out.toIndex = resolved.index;
61228
61271
  }
61229
61272
  eventBus.emit("UI:PATTERN_MOVE", out);
61230
- log10.info("default-emit:pattern-instance", { fromPath, level: target.level });
61273
+ log11.info("default-emit:pattern-instance", { fromPath, level: target.level });
61231
61274
  return;
61232
61275
  }
61233
- log10.debug("default-emit:unhandled-kind", { kind: payload.kind });
61276
+ log11.debug("default-emit:unhandled-kind", { kind: payload.kind });
61234
61277
  }
61235
61278
  function CanvasDndProvider({
61236
61279
  children,
@@ -61253,9 +61296,9 @@ function CanvasDndProvider({
61253
61296
  if (payload) {
61254
61297
  setActivePayload(payload);
61255
61298
  eventBus.emit("UI:DRAG_START", { kind: payload.kind, data: payload.data });
61256
- log10.info("dragStart", { id: e.active.id, kind: payload.kind });
61299
+ log11.info("dragStart", { id: e.active.id, kind: payload.kind });
61257
61300
  } else {
61258
- log10.warn("dragStart:missing-payload", { id: e.active.id });
61301
+ log11.warn("dragStart:missing-payload", { id: e.active.id });
61259
61302
  }
61260
61303
  }, [eventBus, trackPointer]);
61261
61304
  const handleDragEnd = React96__namespace.default.useCallback((e) => {
@@ -61266,7 +61309,7 @@ function CanvasDndProvider({
61266
61309
  const overData = e.over?.data.current;
61267
61310
  const target = overData?.target;
61268
61311
  const accepts = overData?.accepts;
61269
- log10.info("dragEnd", {
61312
+ log11.info("dragEnd", {
61270
61313
  activeId: e.active.id,
61271
61314
  overId: e.over?.id,
61272
61315
  hasPayload: !!payload,
@@ -61278,12 +61321,12 @@ function CanvasDndProvider({
61278
61321
  }
61279
61322
  if (!payload || !target) return;
61280
61323
  if (accepts && !accepts.includes(payload.kind)) {
61281
- log10.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
61324
+ log11.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
61282
61325
  return;
61283
61326
  }
61284
61327
  const cursor = lastPointerRef.current;
61285
61328
  const resolved = target.resolvePath && cursor ? target.resolvePath(cursor) : null;
61286
- log10.debug("dragEnd:resolve", {
61329
+ log11.debug("dragEnd:resolve", {
61287
61330
  hasResolver: !!target.resolvePath,
61288
61331
  cursorX: cursor?.x,
61289
61332
  cursorY: cursor?.y,
@@ -61297,7 +61340,7 @@ function CanvasDndProvider({
61297
61340
  const handleDragCancel = React96__namespace.default.useCallback(() => {
61298
61341
  setActivePayload(null);
61299
61342
  document.removeEventListener("pointermove", trackPointer);
61300
- log10.info("dragCancel");
61343
+ log11.info("dragCancel");
61301
61344
  }, [trackPointer]);
61302
61345
  return /* @__PURE__ */ jsxRuntime.jsxs(
61303
61346
  core$1.DndContext,
@@ -63356,7 +63399,7 @@ init_AvlTransitionLane();
63356
63399
  init_AvlSwimLane();
63357
63400
  init_avl_atom_types();
63358
63401
  init_avl_elk_layout();
63359
- var log11 = logger.createLogger("almadar:ui:avl:trait-scene");
63402
+ var log12 = logger.createLogger("almadar:ui:avl:trait-scene");
63360
63403
  var SWIM_GUTTER2 = 120;
63361
63404
  var CENTER_W2 = 360;
63362
63405
  var AvlTraitScene = ({
@@ -63369,7 +63412,7 @@ var AvlTraitScene = ({
63369
63412
  const dataKey = React96.useMemo(() => JSON.stringify(data), [data]);
63370
63413
  React96.useEffect(() => {
63371
63414
  computeTraitLayout(data).then(setLayout).catch((error) => {
63372
- log11.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
63415
+ log12.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
63373
63416
  });
63374
63417
  }, [dataKey]);
63375
63418
  if (!layout) {