@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.
@@ -1164,6 +1164,7 @@ var init_Button = __esm({
1164
1164
  "button",
1165
1165
  {
1166
1166
  ref,
1167
+ type: "button",
1167
1168
  disabled: disabled || isLoading,
1168
1169
  className: cn(
1169
1170
  "relative inline-flex items-center justify-center gap-2",
@@ -1257,6 +1258,11 @@ var init_Typography = __esm({
1257
1258
  inherit: "text-inherit"
1258
1259
  };
1259
1260
  weightStyles = {
1261
+ // Neutral: an atom default of "none" must not override the variant's baked
1262
+ // weight (C-PATTERN-ENUM-BLOCKS-NEUTRAL-OVERRIDE — `variant={h2}` rendered
1263
+ // tiny because the ui-typography atom's `weight: light`/`size: xs` defaults
1264
+ // clobbered every variant).
1265
+ none: "",
1260
1266
  light: "font-light",
1261
1267
  normal: "font-normal",
1262
1268
  medium: "font-medium",
@@ -1282,6 +1288,7 @@ var init_Typography = __esm({
1282
1288
  label: "span"
1283
1289
  };
1284
1290
  typographySizeStyles = {
1291
+ none: "",
1285
1292
  xs: "text-xs",
1286
1293
  sm: "text-sm",
1287
1294
  md: "text-base",
@@ -23143,6 +23150,13 @@ function DataGrid({
23143
23150
  row: itemData
23144
23151
  };
23145
23152
  eventBus.emit(`UI:${action.event}`, payload);
23153
+ if (action.navigatesTo) {
23154
+ const url = action.navigatesTo.replace(
23155
+ /\{\{row\.(\w+(?:\.\w+)*)\}\}/g,
23156
+ (_, field) => String(itemData[field] ?? "")
23157
+ );
23158
+ eventBus.emit("UI:NAVIGATE", { url });
23159
+ }
23146
23160
  };
23147
23161
  const hasRenderProp = typeof children === "function";
23148
23162
  React84.useEffect(() => {
@@ -23210,13 +23224,49 @@ function DataGrid({
23210
23224
  const wrapDnd = (node) => dnd.isZone ? /* @__PURE__ */ jsxRuntime.jsx(dnd.SortableItem, { id: dndId, children: node }, dndId) : node;
23211
23225
  if (hasRenderProp) {
23212
23226
  return wrapDnd(
23213
- /* @__PURE__ */ jsxRuntime.jsx(
23227
+ /* @__PURE__ */ jsxRuntime.jsxs(
23214
23228
  Box,
23215
23229
  {
23216
23230
  "data-entity-row": true,
23217
23231
  "data-entity-id": id,
23218
23232
  className: cn(isSelected && "ring-2 ring-primary rounded-lg"),
23219
- children: children(itemData, index)
23233
+ children: [
23234
+ children(itemData, index),
23235
+ 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: [
23236
+ (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
23237
+ Button,
23238
+ {
23239
+ variant: action.variant === "primary" ? "primary" : "ghost",
23240
+ size: "sm",
23241
+ onClick: handleActionClick(action, itemData),
23242
+ "data-testid": `action-${action.event}`,
23243
+ "data-row-id": String(itemData.id),
23244
+ className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
23245
+ children: [
23246
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
23247
+ action.label
23248
+ ]
23249
+ },
23250
+ idx
23251
+ )),
23252
+ maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
23253
+ Menu,
23254
+ {
23255
+ position: "bottom-end",
23256
+ 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" }) }),
23257
+ items: actionDefs.slice(maxInlineActions).map((action) => ({
23258
+ label: action.label,
23259
+ icon: action.icon,
23260
+ event: action.event,
23261
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
23262
+ id: itemData.id,
23263
+ row: itemData
23264
+ })
23265
+ }))
23266
+ }
23267
+ )
23268
+ ] }) })
23269
+ ]
23220
23270
  },
23221
23271
  id
23222
23272
  )
@@ -24240,7 +24290,9 @@ var init_FilterGroup = __esm({
24240
24290
  showIcon = true,
24241
24291
  query,
24242
24292
  isLoading,
24243
- look = "toolbar"
24293
+ look = "toolbar",
24294
+ event,
24295
+ clearEvent
24244
24296
  }) => {
24245
24297
  const { t } = hooks.useTranslate();
24246
24298
  const eventBus = useEventBus();
@@ -24277,14 +24329,18 @@ var init_FilterGroup = __esm({
24277
24329
  queryState.setFilter(field, value === "all" ? null : value);
24278
24330
  }
24279
24331
  onFilterChange?.(field, value === "all" ? null : value);
24280
- eventBus.emit("UI:FILTER", {
24332
+ const payload = {
24281
24333
  entity,
24282
24334
  field,
24283
24335
  value: value === "all" || value === null ? "" : value,
24284
24336
  query
24285
- });
24337
+ };
24338
+ if (event && event !== "FILTER") {
24339
+ eventBus.emit(`UI:${event}`, payload);
24340
+ }
24341
+ eventBus.emit("UI:FILTER", payload);
24286
24342
  },
24287
- [onFilterChange, queryState, eventBus, entity, query]
24343
+ [onFilterChange, queryState, eventBus, entity, query, event]
24288
24344
  );
24289
24345
  const handleClearAll = React84.useCallback(() => {
24290
24346
  setSelectedValues({});
@@ -24292,8 +24348,11 @@ var init_FilterGroup = __esm({
24292
24348
  queryState.clearFilters();
24293
24349
  }
24294
24350
  onClearAll?.();
24351
+ if (clearEvent && clearEvent !== "CLEAR_FILTERS") {
24352
+ eventBus.emit(`UI:${clearEvent}`, { entity, query });
24353
+ }
24295
24354
  eventBus.emit("UI:CLEAR_FILTERS", { entity, query });
24296
- }, [onClearAll, queryState, eventBus, entity, query]);
24355
+ }, [onClearAll, queryState, eventBus, entity, query, clearEvent]);
24297
24356
  const activeFilterCount = Object.keys(selectedValues).length;
24298
24357
  if (variant === "pills") {
24299
24358
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -25421,7 +25480,7 @@ var init_SearchInput = __esm({
25421
25480
  queryState.setSearch(newValue);
25422
25481
  }
25423
25482
  onSearch?.(newValue);
25424
- if (event) {
25483
+ if (event && event !== "SEARCH") {
25425
25484
  eventBus.emit(`UI:${event}`, { searchTerm: newValue, entity });
25426
25485
  }
25427
25486
  if (event || entity || query) {
@@ -35,7 +35,7 @@ interface EventBusContextTypeExtended extends EventBusContextType {
35
35
  }
36
36
  declare const EventBusContext: React__default.Context<EventBusContextTypeExtended | null>;
37
37
  interface EventBusProviderProps {
38
- children: ReactNode;
38
+ children?: ReactNode;
39
39
  /**
40
40
  * @deprecated No-op. Logging is now gated by `@almadar/logger` —
41
41
  * use `setLogLevel('DEBUG')` or `setNamespaceLevel('almadar:eventbus', 'DEBUG')`
@@ -288,7 +288,7 @@ type TraitScopeChain = readonly TraitScope[];
288
288
  interface TraitScopeProviderProps {
289
289
  orbital: string;
290
290
  trait: string;
291
- children: React__default.ReactNode;
291
+ children?: React__default.ReactNode;
292
292
  }
293
293
  /**
294
294
  * Wrap a trait's rendered subtree to qualify bare `UI:*` emits with the
@@ -35,7 +35,7 @@ interface EventBusContextTypeExtended extends EventBusContextType {
35
35
  }
36
36
  declare const EventBusContext: React__default.Context<EventBusContextTypeExtended | null>;
37
37
  interface EventBusProviderProps {
38
- children: ReactNode;
38
+ children?: ReactNode;
39
39
  /**
40
40
  * @deprecated No-op. Logging is now gated by `@almadar/logger` —
41
41
  * use `setLogLevel('DEBUG')` or `setNamespaceLevel('almadar:eventbus', 'DEBUG')`
@@ -288,7 +288,7 @@ type TraitScopeChain = readonly TraitScope[];
288
288
  interface TraitScopeProviderProps {
289
289
  orbital: string;
290
290
  trait: string;
291
- children: React__default.ReactNode;
291
+ children?: React__default.ReactNode;
292
292
  }
293
293
  /**
294
294
  * Wrap a trait's rendered subtree to qualify bare `UI:*` emits with the
@@ -1089,6 +1089,7 @@ var init_Button = __esm({
1089
1089
  "button",
1090
1090
  {
1091
1091
  ref,
1092
+ type: "button",
1092
1093
  disabled: disabled || isLoading,
1093
1094
  className: cn(
1094
1095
  "relative inline-flex items-center justify-center gap-2",
@@ -1182,6 +1183,11 @@ var init_Typography = __esm({
1182
1183
  inherit: "text-inherit"
1183
1184
  };
1184
1185
  weightStyles = {
1186
+ // Neutral: an atom default of "none" must not override the variant's baked
1187
+ // weight (C-PATTERN-ENUM-BLOCKS-NEUTRAL-OVERRIDE — `variant={h2}` rendered
1188
+ // tiny because the ui-typography atom's `weight: light`/`size: xs` defaults
1189
+ // clobbered every variant).
1190
+ none: "",
1185
1191
  light: "font-light",
1186
1192
  normal: "font-normal",
1187
1193
  medium: "font-medium",
@@ -1207,6 +1213,7 @@ var init_Typography = __esm({
1207
1213
  label: "span"
1208
1214
  };
1209
1215
  typographySizeStyles = {
1216
+ none: "",
1210
1217
  xs: "text-xs",
1211
1218
  sm: "text-sm",
1212
1219
  md: "text-base",
@@ -23068,6 +23075,13 @@ function DataGrid({
23068
23075
  row: itemData
23069
23076
  };
23070
23077
  eventBus.emit(`UI:${action.event}`, payload);
23078
+ if (action.navigatesTo) {
23079
+ const url = action.navigatesTo.replace(
23080
+ /\{\{row\.(\w+(?:\.\w+)*)\}\}/g,
23081
+ (_, field) => String(itemData[field] ?? "")
23082
+ );
23083
+ eventBus.emit("UI:NAVIGATE", { url });
23084
+ }
23071
23085
  };
23072
23086
  const hasRenderProp = typeof children === "function";
23073
23087
  useEffect(() => {
@@ -23135,13 +23149,49 @@ function DataGrid({
23135
23149
  const wrapDnd = (node) => dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: dndId, children: node }, dndId) : node;
23136
23150
  if (hasRenderProp) {
23137
23151
  return wrapDnd(
23138
- /* @__PURE__ */ jsx(
23152
+ /* @__PURE__ */ jsxs(
23139
23153
  Box,
23140
23154
  {
23141
23155
  "data-entity-row": true,
23142
23156
  "data-entity-id": id,
23143
23157
  className: cn(isSelected && "ring-2 ring-primary rounded-lg"),
23144
- children: children(itemData, index)
23158
+ children: [
23159
+ children(itemData, index),
23160
+ 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: [
23161
+ (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxs(
23162
+ Button,
23163
+ {
23164
+ variant: action.variant === "primary" ? "primary" : "ghost",
23165
+ size: "sm",
23166
+ onClick: handleActionClick(action, itemData),
23167
+ "data-testid": `action-${action.event}`,
23168
+ "data-row-id": String(itemData.id),
23169
+ className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
23170
+ children: [
23171
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
23172
+ action.label
23173
+ ]
23174
+ },
23175
+ idx
23176
+ )),
23177
+ maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsx(
23178
+ Menu,
23179
+ {
23180
+ position: "bottom-end",
23181
+ 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" }) }),
23182
+ items: actionDefs.slice(maxInlineActions).map((action) => ({
23183
+ label: action.label,
23184
+ icon: action.icon,
23185
+ event: action.event,
23186
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
23187
+ id: itemData.id,
23188
+ row: itemData
23189
+ })
23190
+ }))
23191
+ }
23192
+ )
23193
+ ] }) })
23194
+ ]
23145
23195
  },
23146
23196
  id
23147
23197
  )
@@ -24165,7 +24215,9 @@ var init_FilterGroup = __esm({
24165
24215
  showIcon = true,
24166
24216
  query,
24167
24217
  isLoading,
24168
- look = "toolbar"
24218
+ look = "toolbar",
24219
+ event,
24220
+ clearEvent
24169
24221
  }) => {
24170
24222
  const { t } = useTranslate();
24171
24223
  const eventBus = useEventBus();
@@ -24202,14 +24254,18 @@ var init_FilterGroup = __esm({
24202
24254
  queryState.setFilter(field, value === "all" ? null : value);
24203
24255
  }
24204
24256
  onFilterChange?.(field, value === "all" ? null : value);
24205
- eventBus.emit("UI:FILTER", {
24257
+ const payload = {
24206
24258
  entity,
24207
24259
  field,
24208
24260
  value: value === "all" || value === null ? "" : value,
24209
24261
  query
24210
- });
24262
+ };
24263
+ if (event && event !== "FILTER") {
24264
+ eventBus.emit(`UI:${event}`, payload);
24265
+ }
24266
+ eventBus.emit("UI:FILTER", payload);
24211
24267
  },
24212
- [onFilterChange, queryState, eventBus, entity, query]
24268
+ [onFilterChange, queryState, eventBus, entity, query, event]
24213
24269
  );
24214
24270
  const handleClearAll = useCallback(() => {
24215
24271
  setSelectedValues({});
@@ -24217,8 +24273,11 @@ var init_FilterGroup = __esm({
24217
24273
  queryState.clearFilters();
24218
24274
  }
24219
24275
  onClearAll?.();
24276
+ if (clearEvent && clearEvent !== "CLEAR_FILTERS") {
24277
+ eventBus.emit(`UI:${clearEvent}`, { entity, query });
24278
+ }
24220
24279
  eventBus.emit("UI:CLEAR_FILTERS", { entity, query });
24221
- }, [onClearAll, queryState, eventBus, entity, query]);
24280
+ }, [onClearAll, queryState, eventBus, entity, query, clearEvent]);
24222
24281
  const activeFilterCount = Object.keys(selectedValues).length;
24223
24282
  if (variant === "pills") {
24224
24283
  return /* @__PURE__ */ jsxs(
@@ -25346,7 +25405,7 @@ var init_SearchInput = __esm({
25346
25405
  queryState.setSearch(newValue);
25347
25406
  }
25348
25407
  onSearch?.(newValue);
25349
- if (event) {
25408
+ if (event && event !== "SEARCH") {
25350
25409
  eventBus.emit(`UI:${event}`, { searchTerm: newValue, entity });
25351
25410
  }
25352
25411
  if (event || entity || query) {
@@ -1642,6 +1642,7 @@ var init_Button = __esm({
1642
1642
  "button",
1643
1643
  {
1644
1644
  ref,
1645
+ type: "button",
1645
1646
  disabled: disabled || isLoading,
1646
1647
  className: cn(
1647
1648
  "relative inline-flex items-center justify-center gap-2",
@@ -1735,6 +1736,11 @@ var init_Typography = __esm({
1735
1736
  inherit: "text-inherit"
1736
1737
  };
1737
1738
  weightStyles = {
1739
+ // Neutral: an atom default of "none" must not override the variant's baked
1740
+ // weight (C-PATTERN-ENUM-BLOCKS-NEUTRAL-OVERRIDE — `variant={h2}` rendered
1741
+ // tiny because the ui-typography atom's `weight: light`/`size: xs` defaults
1742
+ // clobbered every variant).
1743
+ none: "",
1738
1744
  light: "font-light",
1739
1745
  normal: "font-normal",
1740
1746
  medium: "font-medium",
@@ -1760,6 +1766,7 @@ var init_Typography = __esm({
1760
1766
  label: "span"
1761
1767
  };
1762
1768
  typographySizeStyles = {
1769
+ none: "",
1763
1770
  xs: "text-xs",
1764
1771
  sm: "text-sm",
1765
1772
  md: "text-base",
@@ -22697,6 +22704,13 @@ function DataGrid({
22697
22704
  row: itemData
22698
22705
  };
22699
22706
  eventBus.emit(`UI:${action.event}`, payload);
22707
+ if (action.navigatesTo) {
22708
+ const url = action.navigatesTo.replace(
22709
+ /\{\{row\.(\w+(?:\.\w+)*)\}\}/g,
22710
+ (_, field) => String(itemData[field] ?? "")
22711
+ );
22712
+ eventBus.emit("UI:NAVIGATE", { url });
22713
+ }
22700
22714
  };
22701
22715
  const hasRenderProp = typeof children === "function";
22702
22716
  React82.useEffect(() => {
@@ -22764,13 +22778,49 @@ function DataGrid({
22764
22778
  const wrapDnd = (node) => dnd.isZone ? /* @__PURE__ */ jsxRuntime.jsx(dnd.SortableItem, { id: dndId, children: node }, dndId) : node;
22765
22779
  if (hasRenderProp) {
22766
22780
  return wrapDnd(
22767
- /* @__PURE__ */ jsxRuntime.jsx(
22781
+ /* @__PURE__ */ jsxRuntime.jsxs(
22768
22782
  Box,
22769
22783
  {
22770
22784
  "data-entity-row": true,
22771
22785
  "data-entity-id": id,
22772
22786
  className: cn(isSelected && "ring-2 ring-primary rounded-lg"),
22773
- children: children(itemData, index)
22787
+ children: [
22788
+ children(itemData, index),
22789
+ 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: [
22790
+ (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
22791
+ Button,
22792
+ {
22793
+ variant: action.variant === "primary" ? "primary" : "ghost",
22794
+ size: "sm",
22795
+ onClick: handleActionClick(action, itemData),
22796
+ "data-testid": `action-${action.event}`,
22797
+ "data-row-id": String(itemData.id),
22798
+ className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
22799
+ children: [
22800
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
22801
+ action.label
22802
+ ]
22803
+ },
22804
+ idx
22805
+ )),
22806
+ maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
22807
+ Menu,
22808
+ {
22809
+ position: "bottom-end",
22810
+ 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" }) }),
22811
+ items: actionDefs.slice(maxInlineActions).map((action) => ({
22812
+ label: action.label,
22813
+ icon: action.icon,
22814
+ event: action.event,
22815
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
22816
+ id: itemData.id,
22817
+ row: itemData
22818
+ })
22819
+ }))
22820
+ }
22821
+ )
22822
+ ] }) })
22823
+ ]
22774
22824
  },
22775
22825
  id
22776
22826
  )
@@ -23730,7 +23780,9 @@ var init_FilterGroup = __esm({
23730
23780
  showIcon = true,
23731
23781
  query,
23732
23782
  isLoading,
23733
- look = "toolbar"
23783
+ look = "toolbar",
23784
+ event,
23785
+ clearEvent
23734
23786
  }) => {
23735
23787
  const { t } = hooks.useTranslate();
23736
23788
  const eventBus = useEventBus();
@@ -23767,14 +23819,18 @@ var init_FilterGroup = __esm({
23767
23819
  queryState.setFilter(field, value === "all" ? null : value);
23768
23820
  }
23769
23821
  onFilterChange?.(field, value === "all" ? null : value);
23770
- eventBus.emit("UI:FILTER", {
23822
+ const payload = {
23771
23823
  entity,
23772
23824
  field,
23773
23825
  value: value === "all" || value === null ? "" : value,
23774
23826
  query
23775
- });
23827
+ };
23828
+ if (event && event !== "FILTER") {
23829
+ eventBus.emit(`UI:${event}`, payload);
23830
+ }
23831
+ eventBus.emit("UI:FILTER", payload);
23776
23832
  },
23777
- [onFilterChange, queryState, eventBus, entity, query]
23833
+ [onFilterChange, queryState, eventBus, entity, query, event]
23778
23834
  );
23779
23835
  const handleClearAll = React82.useCallback(() => {
23780
23836
  setSelectedValues({});
@@ -23782,8 +23838,11 @@ var init_FilterGroup = __esm({
23782
23838
  queryState.clearFilters();
23783
23839
  }
23784
23840
  onClearAll?.();
23841
+ if (clearEvent && clearEvent !== "CLEAR_FILTERS") {
23842
+ eventBus.emit(`UI:${clearEvent}`, { entity, query });
23843
+ }
23785
23844
  eventBus.emit("UI:CLEAR_FILTERS", { entity, query });
23786
- }, [onClearAll, queryState, eventBus, entity, query]);
23845
+ }, [onClearAll, queryState, eventBus, entity, query, clearEvent]);
23787
23846
  const activeFilterCount = Object.keys(selectedValues).length;
23788
23847
  if (variant === "pills") {
23789
23848
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -24911,7 +24970,7 @@ var init_SearchInput = __esm({
24911
24970
  queryState.setSearch(newValue);
24912
24971
  }
24913
24972
  onSearch?.(newValue);
24914
- if (event) {
24973
+ if (event && event !== "SEARCH") {
24915
24974
  eventBus.emit(`UI:${event}`, { searchTerm: newValue, entity });
24916
24975
  }
24917
24976
  if (event || entity || query) {
@@ -44327,6 +44386,14 @@ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
44327
44386
  return writes;
44328
44387
  };
44329
44388
  }
44389
+ function containsConfigForward(value) {
44390
+ if (typeof value === "string") return value.startsWith("@config.");
44391
+ if (Array.isArray(value)) return value.some(containsConfigForward);
44392
+ if (value !== null && typeof value === "object") {
44393
+ return Object.values(value).some(containsConfigForward);
44394
+ }
44395
+ return false;
44396
+ }
44330
44397
  function getBindingConfig(binding) {
44331
44398
  const raw = binding.config;
44332
44399
  const normalized = runtime.normalizeCallSiteConfigToValues(raw);
@@ -44679,7 +44746,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44679
44746
  const sharedCallSite = sharedCallSiteRaw ? runtime.resolveCallSitePayloadCaptures(
44680
44747
  Object.fromEntries(
44681
44748
  Object.entries(sharedCallSiteRaw).filter(
44682
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
44749
+ ([, v]) => !containsConfigForward(v)
44683
44750
  )
44684
44751
  ),
44685
44752
  payload || {}
@@ -44744,7 +44811,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44744
44811
  const callSiteOverrides = callSiteConfig ? runtime.resolveCallSitePayloadCaptures(
44745
44812
  Object.fromEntries(
44746
44813
  Object.entries(callSiteConfig).filter(
44747
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
44814
+ ([, v]) => !containsConfigForward(v)
44748
44815
  )
44749
44816
  ),
44750
44817
  payload || {}
@@ -44986,7 +45053,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44986
45053
  }
44987
45054
  return () => scheduler.stopAll();
44988
45055
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
44989
- const processEventQueued = React82.useCallback(async (eventKey, payload) => {
45056
+ const processEventQueued = React82.useCallback(async (eventKey, payload, targetTrait) => {
44990
45057
  const normalizedEvent = normalizeEventKey(eventKey);
44991
45058
  const bindings = traitBindingsRef.current;
44992
45059
  const currentManager = managerRef.current;
@@ -45015,7 +45082,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45015
45082
  normalizedEvent,
45016
45083
  payload,
45017
45084
  void 0,
45018
- entityByTrait
45085
+ entityByTrait,
45086
+ void 0,
45087
+ targetTrait
45019
45088
  );
45020
45089
  crossTraitLog.debug("processEvent:results", {
45021
45090
  event: normalizedEvent,
@@ -45108,8 +45177,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45108
45177
  const effectTraces = result.effects.map(
45109
45178
  (e) => {
45110
45179
  if (Array.isArray(e)) {
45180
+ const head = String(e[0] ?? "unknown");
45181
+ const entityName = head === "fetch" && typeof e[1] === "string" ? e[1] : head === "persist" && typeof e[2] === "string" ? e[2] : void 0;
45111
45182
  return {
45112
- type: String(e[0] ?? "unknown"),
45183
+ type: head,
45184
+ ...entityName !== void 0 ? { entityName } : {},
45113
45185
  args: e.slice(1),
45114
45186
  status: "executed"
45115
45187
  };
@@ -45162,7 +45234,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45162
45234
  const orbital = orbitalsByTrait?.[traitName];
45163
45235
  if (orbital) dispatchedOrbitals.add(orbital);
45164
45236
  }
45165
- await onEventProcessed(normalizedEvent, payload, dispatchedOrbitals);
45237
+ const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
45238
+ await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
45166
45239
  }
45167
45240
  }, [entities, eventBus, sharedEntityStore]);
45168
45241
  const drainEventQueue = React82.useCallback(async () => {
@@ -45171,14 +45244,14 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45171
45244
  try {
45172
45245
  while (eventQueueRef.current.length > 0) {
45173
45246
  const entry = eventQueueRef.current.shift();
45174
- await processEventQueued(entry.eventKey, entry.payload);
45247
+ await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
45175
45248
  }
45176
45249
  } finally {
45177
45250
  processingRef.current = false;
45178
45251
  }
45179
45252
  }, [processEventQueued]);
45180
- const enqueueAndDrain = React82.useCallback((eventKey, payload) => {
45181
- eventQueueRef.current.push({ eventKey, payload });
45253
+ const enqueueAndDrain = React82.useCallback((eventKey, payload, targetTrait) => {
45254
+ eventQueueRef.current.push({ eventKey, payload, targetTrait });
45182
45255
  void drainEventQueue();
45183
45256
  }, [drainEventQueue]);
45184
45257
  React82.useCallback((eventKey, payload) => {
@@ -45275,7 +45348,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45275
45348
  crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
45276
45349
  const unsub = eventBus.on(busKey, (event) => {
45277
45350
  crossTraitLog.debug("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
45278
- enqueueAndDrain(listen.triggers, event.payload);
45351
+ enqueueAndDrain(listen.triggers, event.payload, binding.trait.name);
45279
45352
  });
45280
45353
  unsubscribes.push(() => {
45281
45354
  crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
@@ -45843,6 +45916,15 @@ function OrbPreview({
45843
45916
  }
45844
45917
  }
45845
45918
  }, [pages]);
45919
+ React82.useEffect(() => {
45920
+ const unsubscribe = eventBus.on("UI:NAVIGATE", (event) => {
45921
+ const url = event.payload?.url;
45922
+ if (typeof url === "string" && url.length > 0) {
45923
+ handleNavigate(url);
45924
+ }
45925
+ });
45926
+ return unsubscribe;
45927
+ }, [eventBus, handleNavigate]);
45846
45928
  if (!parseResult.ok) {
45847
45929
  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: [
45848
45930
  "Parse error: ",