@almadar/ui 5.122.14 → 5.123.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.
@@ -3225,6 +3225,11 @@ var init_Typography = __esm({
3225
3225
  inherit: "text-inherit"
3226
3226
  };
3227
3227
  weightStyles = {
3228
+ // Neutral: an atom default of "none" must not override the variant's baked
3229
+ // weight (C-PATTERN-ENUM-BLOCKS-NEUTRAL-OVERRIDE — `variant={h2}` rendered
3230
+ // tiny because the ui-typography atom's `weight: light`/`size: xs` defaults
3231
+ // clobbered every variant).
3232
+ none: "",
3228
3233
  light: "font-light",
3229
3234
  normal: "font-normal",
3230
3235
  medium: "font-medium",
@@ -3250,6 +3255,7 @@ var init_Typography = __esm({
3250
3255
  label: "span"
3251
3256
  };
3252
3257
  typographySizeStyles = {
3258
+ none: "",
3253
3259
  xs: "text-xs",
3254
3260
  sm: "text-sm",
3255
3261
  md: "text-base",
@@ -5111,6 +5117,7 @@ var init_Button = __esm({
5111
5117
  "button",
5112
5118
  {
5113
5119
  ref,
5120
+ type: "button",
5114
5121
  disabled: disabled || isLoading,
5115
5122
  className: cn(
5116
5123
  "relative inline-flex items-center justify-center gap-2",
@@ -25409,6 +25416,13 @@ function DataGrid({
25409
25416
  row: itemData
25410
25417
  };
25411
25418
  eventBus.emit(`UI:${action.event}`, payload);
25419
+ if (action.navigatesTo) {
25420
+ const url = action.navigatesTo.replace(
25421
+ /\{\{row\.(\w+(?:\.\w+)*)\}\}/g,
25422
+ (_, field) => String(itemData[field] ?? "")
25423
+ );
25424
+ eventBus.emit("UI:NAVIGATE", { url });
25425
+ }
25412
25426
  };
25413
25427
  const hasRenderProp = typeof children === "function";
25414
25428
  React91.useEffect(() => {
@@ -25476,13 +25490,49 @@ function DataGrid({
25476
25490
  const wrapDnd = (node) => dnd.isZone ? /* @__PURE__ */ jsxRuntime.jsx(dnd.SortableItem, { id: dndId, children: node }, dndId) : node;
25477
25491
  if (hasRenderProp) {
25478
25492
  return wrapDnd(
25479
- /* @__PURE__ */ jsxRuntime.jsx(
25493
+ /* @__PURE__ */ jsxRuntime.jsxs(
25480
25494
  Box,
25481
25495
  {
25482
25496
  "data-entity-row": true,
25483
25497
  "data-entity-id": id,
25484
25498
  className: cn(isSelected && "ring-2 ring-primary rounded-lg"),
25485
- children: children(itemData, index)
25499
+ children: [
25500
+ children(itemData, index),
25501
+ actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-3 border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-end", children: [
25502
+ (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
25503
+ Button,
25504
+ {
25505
+ variant: action.variant === "primary" ? "primary" : "ghost",
25506
+ size: "sm",
25507
+ onClick: handleActionClick(action, itemData),
25508
+ "data-testid": `action-${action.event}`,
25509
+ "data-row-id": String(itemData.id),
25510
+ className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
25511
+ children: [
25512
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
25513
+ action.label
25514
+ ]
25515
+ },
25516
+ idx
25517
+ )),
25518
+ maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
25519
+ Menu,
25520
+ {
25521
+ position: "bottom-end",
25522
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
25523
+ items: actionDefs.slice(maxInlineActions).map((action) => ({
25524
+ label: action.label,
25525
+ icon: action.icon,
25526
+ event: action.event,
25527
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
25528
+ id: itemData.id,
25529
+ row: itemData
25530
+ })
25531
+ }))
25532
+ }
25533
+ )
25534
+ ] }) })
25535
+ ]
25486
25536
  },
25487
25537
  id
25488
25538
  )
@@ -26506,7 +26556,9 @@ var init_FilterGroup = __esm({
26506
26556
  showIcon = true,
26507
26557
  query,
26508
26558
  isLoading,
26509
- look = "toolbar"
26559
+ look = "toolbar",
26560
+ event,
26561
+ clearEvent
26510
26562
  }) => {
26511
26563
  const { t } = hooks.useTranslate();
26512
26564
  const eventBus = useEventBus();
@@ -26543,14 +26595,18 @@ var init_FilterGroup = __esm({
26543
26595
  queryState.setFilter(field, value === "all" ? null : value);
26544
26596
  }
26545
26597
  onFilterChange?.(field, value === "all" ? null : value);
26546
- eventBus.emit("UI:FILTER", {
26598
+ const payload = {
26547
26599
  entity,
26548
26600
  field,
26549
26601
  value: value === "all" || value === null ? "" : value,
26550
26602
  query
26551
- });
26603
+ };
26604
+ if (event && event !== "FILTER") {
26605
+ eventBus.emit(`UI:${event}`, payload);
26606
+ }
26607
+ eventBus.emit("UI:FILTER", payload);
26552
26608
  },
26553
- [onFilterChange, queryState, eventBus, entity, query]
26609
+ [onFilterChange, queryState, eventBus, entity, query, event]
26554
26610
  );
26555
26611
  const handleClearAll = React91.useCallback(() => {
26556
26612
  setSelectedValues({});
@@ -26558,8 +26614,11 @@ var init_FilterGroup = __esm({
26558
26614
  queryState.clearFilters();
26559
26615
  }
26560
26616
  onClearAll?.();
26617
+ if (clearEvent && clearEvent !== "CLEAR_FILTERS") {
26618
+ eventBus.emit(`UI:${clearEvent}`, { entity, query });
26619
+ }
26561
26620
  eventBus.emit("UI:CLEAR_FILTERS", { entity, query });
26562
- }, [onClearAll, queryState, eventBus, entity, query]);
26621
+ }, [onClearAll, queryState, eventBus, entity, query, clearEvent]);
26563
26622
  const activeFilterCount = Object.keys(selectedValues).length;
26564
26623
  if (variant === "pills") {
26565
26624
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -27687,7 +27746,7 @@ var init_SearchInput = __esm({
27687
27746
  queryState.setSearch(newValue);
27688
27747
  }
27689
27748
  onSearch?.(newValue);
27690
- if (event) {
27749
+ if (event && event !== "SEARCH") {
27691
27750
  eventBus.emit(`UI:${event}`, { searchTerm: newValue, entity });
27692
27751
  }
27693
27752
  if (event || entity || query) {
@@ -50206,6 +50265,14 @@ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
50206
50265
  return writes;
50207
50266
  };
50208
50267
  }
50268
+ function containsConfigForward(value) {
50269
+ if (typeof value === "string") return value.startsWith("@config.");
50270
+ if (Array.isArray(value)) return value.some(containsConfigForward);
50271
+ if (value !== null && typeof value === "object") {
50272
+ return Object.values(value).some(containsConfigForward);
50273
+ }
50274
+ return false;
50275
+ }
50209
50276
  function getBindingConfig(binding) {
50210
50277
  const raw = binding.config;
50211
50278
  const normalized = runtime.normalizeCallSiteConfigToValues(raw);
@@ -50558,7 +50625,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50558
50625
  const sharedCallSite = sharedCallSiteRaw ? runtime.resolveCallSitePayloadCaptures(
50559
50626
  Object.fromEntries(
50560
50627
  Object.entries(sharedCallSiteRaw).filter(
50561
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
50628
+ ([, v]) => !containsConfigForward(v)
50562
50629
  )
50563
50630
  ),
50564
50631
  payload || {}
@@ -50623,7 +50690,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50623
50690
  const callSiteOverrides = callSiteConfig ? runtime.resolveCallSitePayloadCaptures(
50624
50691
  Object.fromEntries(
50625
50692
  Object.entries(callSiteConfig).filter(
50626
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
50693
+ ([, v]) => !containsConfigForward(v)
50627
50694
  )
50628
50695
  ),
50629
50696
  payload || {}
@@ -50865,7 +50932,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50865
50932
  }
50866
50933
  return () => scheduler.stopAll();
50867
50934
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
50868
- const processEventQueued = React91.useCallback(async (eventKey, payload) => {
50935
+ const processEventQueued = React91.useCallback(async (eventKey, payload, targetTrait) => {
50869
50936
  const normalizedEvent = normalizeEventKey(eventKey);
50870
50937
  const bindings = traitBindingsRef.current;
50871
50938
  const currentManager = managerRef.current;
@@ -50894,7 +50961,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50894
50961
  normalizedEvent,
50895
50962
  payload,
50896
50963
  void 0,
50897
- entityByTrait
50964
+ entityByTrait,
50965
+ void 0,
50966
+ targetTrait
50898
50967
  );
50899
50968
  crossTraitLog.debug("processEvent:results", {
50900
50969
  event: normalizedEvent,
@@ -50987,8 +51056,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50987
51056
  const effectTraces = result.effects.map(
50988
51057
  (e) => {
50989
51058
  if (Array.isArray(e)) {
51059
+ const head = String(e[0] ?? "unknown");
51060
+ const entityName = head === "fetch" && typeof e[1] === "string" ? e[1] : head === "persist" && typeof e[2] === "string" ? e[2] : void 0;
50990
51061
  return {
50991
- type: String(e[0] ?? "unknown"),
51062
+ type: head,
51063
+ ...entityName !== void 0 ? { entityName } : {},
50992
51064
  args: e.slice(1),
50993
51065
  status: "executed"
50994
51066
  };
@@ -51041,7 +51113,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51041
51113
  const orbital = orbitalsByTrait?.[traitName];
51042
51114
  if (orbital) dispatchedOrbitals.add(orbital);
51043
51115
  }
51044
- await onEventProcessed(normalizedEvent, payload, dispatchedOrbitals);
51116
+ const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
51117
+ await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
51045
51118
  }
51046
51119
  }, [entities, eventBus, sharedEntityStore]);
51047
51120
  const drainEventQueue = React91.useCallback(async () => {
@@ -51050,14 +51123,14 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51050
51123
  try {
51051
51124
  while (eventQueueRef.current.length > 0) {
51052
51125
  const entry = eventQueueRef.current.shift();
51053
- await processEventQueued(entry.eventKey, entry.payload);
51126
+ await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
51054
51127
  }
51055
51128
  } finally {
51056
51129
  processingRef.current = false;
51057
51130
  }
51058
51131
  }, [processEventQueued]);
51059
- const enqueueAndDrain = React91.useCallback((eventKey, payload) => {
51060
- eventQueueRef.current.push({ eventKey, payload });
51132
+ const enqueueAndDrain = React91.useCallback((eventKey, payload, targetTrait) => {
51133
+ eventQueueRef.current.push({ eventKey, payload, targetTrait });
51061
51134
  void drainEventQueue();
51062
51135
  }, [drainEventQueue]);
51063
51136
  React91.useCallback((eventKey, payload) => {
@@ -51154,7 +51227,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51154
51227
  crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
51155
51228
  const unsub = eventBus.on(busKey, (event) => {
51156
51229
  crossTraitLog.debug("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
51157
- enqueueAndDrain(listen.triggers, event.payload);
51230
+ enqueueAndDrain(listen.triggers, event.payload, binding.trait.name);
51158
51231
  });
51159
51232
  unsubscribes.push(() => {
51160
51233
  crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
@@ -51621,6 +51694,15 @@ function OrbPreview({
51621
51694
  }
51622
51695
  }
51623
51696
  }, [pages]);
51697
+ React91.useEffect(() => {
51698
+ const unsubscribe = eventBus.on("UI:NAVIGATE", (event) => {
51699
+ const url = event.payload?.url;
51700
+ if (typeof url === "string" && url.length > 0) {
51701
+ handleNavigate(url);
51702
+ }
51703
+ });
51704
+ return unsubscribe;
51705
+ }, [eventBus, handleNavigate]);
51624
51706
  if (!parseResult.ok) {
51625
51707
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, style: { height }, children: /* @__PURE__ */ jsxRuntime.jsxs(Typography, { as: "pre", color: "error", variant: "small", className: "font-mono whitespace-pre-wrap break-all m-0 p-4", children: [
51626
51708
  "Parse error: ",
package/dist/avl/index.js CHANGED
@@ -3149,6 +3149,11 @@ var init_Typography = __esm({
3149
3149
  inherit: "text-inherit"
3150
3150
  };
3151
3151
  weightStyles = {
3152
+ // Neutral: an atom default of "none" must not override the variant's baked
3153
+ // weight (C-PATTERN-ENUM-BLOCKS-NEUTRAL-OVERRIDE — `variant={h2}` rendered
3154
+ // tiny because the ui-typography atom's `weight: light`/`size: xs` defaults
3155
+ // clobbered every variant).
3156
+ none: "",
3152
3157
  light: "font-light",
3153
3158
  normal: "font-normal",
3154
3159
  medium: "font-medium",
@@ -3174,6 +3179,7 @@ var init_Typography = __esm({
3174
3179
  label: "span"
3175
3180
  };
3176
3181
  typographySizeStyles = {
3182
+ none: "",
3177
3183
  xs: "text-xs",
3178
3184
  sm: "text-sm",
3179
3185
  md: "text-base",
@@ -5035,6 +5041,7 @@ var init_Button = __esm({
5035
5041
  "button",
5036
5042
  {
5037
5043
  ref,
5044
+ type: "button",
5038
5045
  disabled: disabled || isLoading,
5039
5046
  className: cn(
5040
5047
  "relative inline-flex items-center justify-center gap-2",
@@ -25333,6 +25340,13 @@ function DataGrid({
25333
25340
  row: itemData
25334
25341
  };
25335
25342
  eventBus.emit(`UI:${action.event}`, payload);
25343
+ if (action.navigatesTo) {
25344
+ const url = action.navigatesTo.replace(
25345
+ /\{\{row\.(\w+(?:\.\w+)*)\}\}/g,
25346
+ (_, field) => String(itemData[field] ?? "")
25347
+ );
25348
+ eventBus.emit("UI:NAVIGATE", { url });
25349
+ }
25336
25350
  };
25337
25351
  const hasRenderProp = typeof children === "function";
25338
25352
  useEffect(() => {
@@ -25400,13 +25414,49 @@ function DataGrid({
25400
25414
  const wrapDnd = (node) => dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: dndId, children: node }, dndId) : node;
25401
25415
  if (hasRenderProp) {
25402
25416
  return wrapDnd(
25403
- /* @__PURE__ */ jsx(
25417
+ /* @__PURE__ */ jsxs(
25404
25418
  Box,
25405
25419
  {
25406
25420
  "data-entity-row": true,
25407
25421
  "data-entity-id": id,
25408
25422
  className: cn(isSelected && "ring-2 ring-primary rounded-lg"),
25409
- children: children(itemData, index)
25423
+ children: [
25424
+ children(itemData, index),
25425
+ actionDefs.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 border-t border-border", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-end", children: [
25426
+ (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxs(
25427
+ Button,
25428
+ {
25429
+ variant: action.variant === "primary" ? "primary" : "ghost",
25430
+ size: "sm",
25431
+ onClick: handleActionClick(action, itemData),
25432
+ "data-testid": `action-${action.event}`,
25433
+ "data-row-id": String(itemData.id),
25434
+ className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
25435
+ children: [
25436
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
25437
+ action.label
25438
+ ]
25439
+ },
25440
+ idx
25441
+ )),
25442
+ maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsx(
25443
+ Menu,
25444
+ {
25445
+ position: "bottom-end",
25446
+ trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
25447
+ items: actionDefs.slice(maxInlineActions).map((action) => ({
25448
+ label: action.label,
25449
+ icon: action.icon,
25450
+ event: action.event,
25451
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
25452
+ id: itemData.id,
25453
+ row: itemData
25454
+ })
25455
+ }))
25456
+ }
25457
+ )
25458
+ ] }) })
25459
+ ]
25410
25460
  },
25411
25461
  id
25412
25462
  )
@@ -26430,7 +26480,9 @@ var init_FilterGroup = __esm({
26430
26480
  showIcon = true,
26431
26481
  query,
26432
26482
  isLoading,
26433
- look = "toolbar"
26483
+ look = "toolbar",
26484
+ event,
26485
+ clearEvent
26434
26486
  }) => {
26435
26487
  const { t } = useTranslate();
26436
26488
  const eventBus = useEventBus();
@@ -26467,14 +26519,18 @@ var init_FilterGroup = __esm({
26467
26519
  queryState.setFilter(field, value === "all" ? null : value);
26468
26520
  }
26469
26521
  onFilterChange?.(field, value === "all" ? null : value);
26470
- eventBus.emit("UI:FILTER", {
26522
+ const payload = {
26471
26523
  entity,
26472
26524
  field,
26473
26525
  value: value === "all" || value === null ? "" : value,
26474
26526
  query
26475
- });
26527
+ };
26528
+ if (event && event !== "FILTER") {
26529
+ eventBus.emit(`UI:${event}`, payload);
26530
+ }
26531
+ eventBus.emit("UI:FILTER", payload);
26476
26532
  },
26477
- [onFilterChange, queryState, eventBus, entity, query]
26533
+ [onFilterChange, queryState, eventBus, entity, query, event]
26478
26534
  );
26479
26535
  const handleClearAll = useCallback(() => {
26480
26536
  setSelectedValues({});
@@ -26482,8 +26538,11 @@ var init_FilterGroup = __esm({
26482
26538
  queryState.clearFilters();
26483
26539
  }
26484
26540
  onClearAll?.();
26541
+ if (clearEvent && clearEvent !== "CLEAR_FILTERS") {
26542
+ eventBus.emit(`UI:${clearEvent}`, { entity, query });
26543
+ }
26485
26544
  eventBus.emit("UI:CLEAR_FILTERS", { entity, query });
26486
- }, [onClearAll, queryState, eventBus, entity, query]);
26545
+ }, [onClearAll, queryState, eventBus, entity, query, clearEvent]);
26487
26546
  const activeFilterCount = Object.keys(selectedValues).length;
26488
26547
  if (variant === "pills") {
26489
26548
  return /* @__PURE__ */ jsxs(
@@ -27611,7 +27670,7 @@ var init_SearchInput = __esm({
27611
27670
  queryState.setSearch(newValue);
27612
27671
  }
27613
27672
  onSearch?.(newValue);
27614
- if (event) {
27673
+ if (event && event !== "SEARCH") {
27615
27674
  eventBus.emit(`UI:${event}`, { searchTerm: newValue, entity });
27616
27675
  }
27617
27676
  if (event || entity || query) {
@@ -50130,6 +50189,14 @@ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
50130
50189
  return writes;
50131
50190
  };
50132
50191
  }
50192
+ function containsConfigForward(value) {
50193
+ if (typeof value === "string") return value.startsWith("@config.");
50194
+ if (Array.isArray(value)) return value.some(containsConfigForward);
50195
+ if (value !== null && typeof value === "object") {
50196
+ return Object.values(value).some(containsConfigForward);
50197
+ }
50198
+ return false;
50199
+ }
50133
50200
  function getBindingConfig(binding) {
50134
50201
  const raw = binding.config;
50135
50202
  const normalized = normalizeCallSiteConfigToValues(raw);
@@ -50482,7 +50549,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50482
50549
  const sharedCallSite = sharedCallSiteRaw ? resolveCallSitePayloadCaptures(
50483
50550
  Object.fromEntries(
50484
50551
  Object.entries(sharedCallSiteRaw).filter(
50485
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
50552
+ ([, v]) => !containsConfigForward(v)
50486
50553
  )
50487
50554
  ),
50488
50555
  payload || {}
@@ -50547,7 +50614,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50547
50614
  const callSiteOverrides = callSiteConfig ? resolveCallSitePayloadCaptures(
50548
50615
  Object.fromEntries(
50549
50616
  Object.entries(callSiteConfig).filter(
50550
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
50617
+ ([, v]) => !containsConfigForward(v)
50551
50618
  )
50552
50619
  ),
50553
50620
  payload || {}
@@ -50789,7 +50856,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50789
50856
  }
50790
50857
  return () => scheduler.stopAll();
50791
50858
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
50792
- const processEventQueued = useCallback(async (eventKey, payload) => {
50859
+ const processEventQueued = useCallback(async (eventKey, payload, targetTrait) => {
50793
50860
  const normalizedEvent = normalizeEventKey(eventKey);
50794
50861
  const bindings = traitBindingsRef.current;
50795
50862
  const currentManager = managerRef.current;
@@ -50818,7 +50885,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50818
50885
  normalizedEvent,
50819
50886
  payload,
50820
50887
  void 0,
50821
- entityByTrait
50888
+ entityByTrait,
50889
+ void 0,
50890
+ targetTrait
50822
50891
  );
50823
50892
  crossTraitLog.debug("processEvent:results", {
50824
50893
  event: normalizedEvent,
@@ -50911,8 +50980,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50911
50980
  const effectTraces = result.effects.map(
50912
50981
  (e) => {
50913
50982
  if (Array.isArray(e)) {
50983
+ const head = String(e[0] ?? "unknown");
50984
+ const entityName = head === "fetch" && typeof e[1] === "string" ? e[1] : head === "persist" && typeof e[2] === "string" ? e[2] : void 0;
50914
50985
  return {
50915
- type: String(e[0] ?? "unknown"),
50986
+ type: head,
50987
+ ...entityName !== void 0 ? { entityName } : {},
50916
50988
  args: e.slice(1),
50917
50989
  status: "executed"
50918
50990
  };
@@ -50965,7 +51037,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50965
51037
  const orbital = orbitalsByTrait?.[traitName];
50966
51038
  if (orbital) dispatchedOrbitals.add(orbital);
50967
51039
  }
50968
- await onEventProcessed(normalizedEvent, payload, dispatchedOrbitals);
51040
+ const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
51041
+ await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
50969
51042
  }
50970
51043
  }, [entities, eventBus, sharedEntityStore]);
50971
51044
  const drainEventQueue = useCallback(async () => {
@@ -50974,14 +51047,14 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50974
51047
  try {
50975
51048
  while (eventQueueRef.current.length > 0) {
50976
51049
  const entry = eventQueueRef.current.shift();
50977
- await processEventQueued(entry.eventKey, entry.payload);
51050
+ await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
50978
51051
  }
50979
51052
  } finally {
50980
51053
  processingRef.current = false;
50981
51054
  }
50982
51055
  }, [processEventQueued]);
50983
- const enqueueAndDrain = useCallback((eventKey, payload) => {
50984
- eventQueueRef.current.push({ eventKey, payload });
51056
+ const enqueueAndDrain = useCallback((eventKey, payload, targetTrait) => {
51057
+ eventQueueRef.current.push({ eventKey, payload, targetTrait });
50985
51058
  void drainEventQueue();
50986
51059
  }, [drainEventQueue]);
50987
51060
  useCallback((eventKey, payload) => {
@@ -51078,7 +51151,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51078
51151
  crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
51079
51152
  const unsub = eventBus.on(busKey, (event) => {
51080
51153
  crossTraitLog.debug("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
51081
- enqueueAndDrain(listen.triggers, event.payload);
51154
+ enqueueAndDrain(listen.triggers, event.payload, binding.trait.name);
51082
51155
  });
51083
51156
  unsubscribes.push(() => {
51084
51157
  crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
@@ -51545,6 +51618,15 @@ function OrbPreview({
51545
51618
  }
51546
51619
  }
51547
51620
  }, [pages]);
51621
+ useEffect(() => {
51622
+ const unsubscribe = eventBus.on("UI:NAVIGATE", (event) => {
51623
+ const url = event.payload?.url;
51624
+ if (typeof url === "string" && url.length > 0) {
51625
+ handleNavigate(url);
51626
+ }
51627
+ });
51628
+ return unsubscribe;
51629
+ }, [eventBus, handleNavigate]);
51548
51630
  if (!parseResult.ok) {
51549
51631
  return /* @__PURE__ */ jsx(Box, { className, style: { height }, children: /* @__PURE__ */ jsxs(Typography, { as: "pre", color: "error", variant: "small", className: "font-mono whitespace-pre-wrap break-all m-0 p-4", children: [
51550
51632
  "Parse error: ",