@almadar/ui 6.14.0 → 6.16.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.
@@ -140,6 +140,13 @@ interface ServerClientEffect {
140
140
  /** Metadata about what the server returned, for debugger logging */
141
141
  interface ServerResponseMeta {
142
142
  success: boolean;
143
+ /** `OrbitalEventResponse.transitioned` — whether any arm accepted the event. */
144
+ transitioned: boolean;
145
+ /** Server-side emits with their evaluated payloads (`ServerResponseTrace.emitted`). */
146
+ emitted?: ReadonlyArray<{
147
+ event: string;
148
+ payload?: EventPayload;
149
+ }>;
143
150
  clientEffects: number;
144
151
  dataEntities: Record<string, number>;
145
152
  /** Raw entity data from server response (for EntityStore advancement) */
@@ -155,7 +162,7 @@ interface SendEventResult {
155
162
  }
156
163
  interface ServerBridgeContextValue {
157
164
  connected: boolean;
158
- sendEvent: (orbitalName: string, event: string, payload?: EventPayload, tick?: string, sourceTrait?: string) => Promise<SendEventResult>;
165
+ sendEvent: (orbitalName: string, event: string, payload?: EventPayload, tick?: string, sourceTrait?: string, locallyEmitted?: readonly string[]) => Promise<SendEventResult>;
159
166
  }
160
167
  /**
161
168
  * Transport adapter for ServerBridgeProvider. Decouples the bridge's
@@ -140,6 +140,13 @@ interface ServerClientEffect {
140
140
  /** Metadata about what the server returned, for debugger logging */
141
141
  interface ServerResponseMeta {
142
142
  success: boolean;
143
+ /** `OrbitalEventResponse.transitioned` — whether any arm accepted the event. */
144
+ transitioned: boolean;
145
+ /** Server-side emits with their evaluated payloads (`ServerResponseTrace.emitted`). */
146
+ emitted?: ReadonlyArray<{
147
+ event: string;
148
+ payload?: EventPayload;
149
+ }>;
143
150
  clientEffects: number;
144
151
  dataEntities: Record<string, number>;
145
152
  /** Raw entity data from server response (for EntityStore advancement) */
@@ -155,7 +162,7 @@ interface SendEventResult {
155
162
  }
156
163
  interface ServerBridgeContextValue {
157
164
  connected: boolean;
158
- sendEvent: (orbitalName: string, event: string, payload?: EventPayload, tick?: string, sourceTrait?: string) => Promise<SendEventResult>;
165
+ sendEvent: (orbitalName: string, event: string, payload?: EventPayload, tick?: string, sourceTrait?: string, locallyEmitted?: readonly string[]) => Promise<SendEventResult>;
159
166
  }
160
167
  /**
161
168
  * Transport adapter for ServerBridgeProvider. Decouples the bridge's
@@ -7283,7 +7283,7 @@ var init_Textarea = __esm({
7283
7283
  init_cn();
7284
7284
  init_useEventBus();
7285
7285
  Textarea = React96__namespace.default.forwardRef(
7286
- ({ className, error, onChange, ...props }, ref) => {
7286
+ ({ className, error, onChange, action, onKeyDown, ...props }, ref) => {
7287
7287
  const eventBus = useEventBus();
7288
7288
  const handleChange = (e) => {
7289
7289
  if (typeof onChange === "string") {
@@ -7292,11 +7292,18 @@ var init_Textarea = __esm({
7292
7292
  onChange?.(e);
7293
7293
  }
7294
7294
  };
7295
+ const handleKeyDown = (e) => {
7296
+ onKeyDown?.(e);
7297
+ if (!action || e.key !== "Enter" || !(e.metaKey || e.ctrlKey)) return;
7298
+ e.preventDefault();
7299
+ eventBus.emit(`UI:${action}`, { value: e.currentTarget.value });
7300
+ };
7295
7301
  return /* @__PURE__ */ jsxRuntime.jsx(
7296
7302
  "textarea",
7297
7303
  {
7298
7304
  ref,
7299
7305
  onChange: handleChange,
7306
+ onKeyDown: handleKeyDown,
7300
7307
  className: cn(
7301
7308
  "block w-full border-[length:var(--border-width)] shadow-sm",
7302
7309
  "px-3 py-2 text-sm text-foreground",
@@ -12746,6 +12753,29 @@ var init_useCanvasGestures = __esm({
12746
12753
  }
12747
12754
  });
12748
12755
 
12756
+ // lib/keyMapEvent.ts
12757
+ function isEditableTarget(target) {
12758
+ if (!(target instanceof HTMLElement)) return false;
12759
+ const tag = target.tagName;
12760
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
12761
+ return target.isContentEditable || target.contentEditable === "true";
12762
+ }
12763
+ function keyMapCode(e) {
12764
+ let code = e.code;
12765
+ if (e.altKey) code = `Alt+${code}`;
12766
+ if (e.shiftKey) code = `Shift+${code}`;
12767
+ if (e.metaKey || e.ctrlKey) code = `Mod+${code}`;
12768
+ return code;
12769
+ }
12770
+ function resolveKeyMapEvent(map, e) {
12771
+ if (!map || isEditableTarget(e.target)) return void 0;
12772
+ return map[keyMapCode(e)] ?? map[e.code];
12773
+ }
12774
+ var init_keyMapEvent = __esm({
12775
+ "lib/keyMapEvent.ts"() {
12776
+ }
12777
+ });
12778
+
12749
12779
  // lib/imageCache.ts
12750
12780
  function startLoad(url, onReady, existing) {
12751
12781
  const img = new Image();
@@ -14671,14 +14701,14 @@ function Canvas2D({
14671
14701
  React96.useEffect(() => {
14672
14702
  if (!keyMap && !keyUpMap) return;
14673
14703
  const onDown = (e) => {
14674
- const ev = keyMap?.[e.code];
14704
+ const ev = resolveKeyMapEvent(keyMap, e);
14675
14705
  if (ev) {
14676
14706
  eventBus.emit(`UI:${ev}`, {});
14677
14707
  e.preventDefault();
14678
14708
  }
14679
14709
  };
14680
14710
  const onUp = (e) => {
14681
- const ev = keyUpMap?.[e.code];
14711
+ const ev = resolveKeyMapEvent(keyUpMap, e);
14682
14712
  if (ev) eventBus.emit(`UI:${ev}`, {});
14683
14713
  };
14684
14714
  window.addEventListener("keydown", onDown);
@@ -14802,6 +14832,7 @@ var init_Canvas2D = __esm({
14802
14832
  init_useCamera();
14803
14833
  init_useCanvasGestures();
14804
14834
  init_verificationRegistry();
14835
+ init_keyMapEvent();
14805
14836
  init_webPainter2d();
14806
14837
  init_projector();
14807
14838
  init_paintDispatch();
@@ -30055,6 +30086,40 @@ var init_DashboardLayout = __esm({
30055
30086
  NavLinkBottom.displayName = "NavLinkBottom";
30056
30087
  }
30057
30088
  });
30089
+
30090
+ // lib/relationLabel.ts
30091
+ function relationLabel(value) {
30092
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
30093
+ return null;
30094
+ for (const key of ["name", "title", "label"]) {
30095
+ const candidate = value[key];
30096
+ if (typeof candidate === "string" && candidate !== "") return candidate;
30097
+ }
30098
+ const id = value.id;
30099
+ return id !== void 0 && id !== null ? String(id) : null;
30100
+ }
30101
+ function relationDisplayLabels(value, options) {
30102
+ if (value === void 0 || value === null || value === "") return [];
30103
+ if (Array.isArray(value)) {
30104
+ return value.flatMap((item) => relationDisplayLabels(item, options));
30105
+ }
30106
+ const hydrated = relationLabel(value);
30107
+ if (hydrated !== null) return [hydrated];
30108
+ const raw = String(value);
30109
+ const match = options?.find((opt) => opt.value === raw);
30110
+ return [match ? match.label : raw];
30111
+ }
30112
+ function resolveRelationCellDisplay(value, options) {
30113
+ if (value === null || value === void 0 || value === "") return void 0;
30114
+ const isObjectShaped = typeof value === "object" && !(value instanceof Date) || Array.isArray(value) && value.some((v) => v !== null && typeof v === "object" && !(v instanceof Date));
30115
+ if (!isObjectShaped && !options) return void 0;
30116
+ const labels = relationDisplayLabels(value, options);
30117
+ return labels.length > 0 ? labels.join(", ") : void 0;
30118
+ }
30119
+ var init_relationLabel = __esm({
30120
+ "lib/relationLabel.ts"() {
30121
+ }
30122
+ });
30058
30123
  function downloadItemUrl(url, label) {
30059
30124
  const a = document.createElement("a");
30060
30125
  a.href = url;
@@ -30886,7 +30951,8 @@ function DataGrid({
30886
30951
  positionEvent,
30887
30952
  dndItemIdField,
30888
30953
  dndRoot,
30889
- look = "dense"
30954
+ look = "dense",
30955
+ relationsData
30890
30956
  }) {
30891
30957
  const eventBus = useEventBus();
30892
30958
  const { t } = hooks.useTranslate();
@@ -31076,6 +31142,10 @@ function DataGrid({
31076
31142
  );
31077
31143
  }
31078
31144
  const titleValue = core.getNestedValue(itemData, titleField?.name ?? "");
31145
+ const titleDisplay = resolveRelationCellDisplay(
31146
+ titleValue,
31147
+ titleField ? relationsData?.[titleField.name] : void 0
31148
+ ) ?? (titleValue !== void 0 && titleValue !== null ? String(titleValue) : void 0);
31079
31149
  return wrapDnd(
31080
31150
  /* @__PURE__ */ jsxRuntime.jsxs(
31081
31151
  Box,
@@ -31100,7 +31170,7 @@ function DataGrid({
31100
31170
  "img",
31101
31171
  {
31102
31172
  src: imgUrl,
31103
- alt: titleValue !== void 0 ? String(titleValue) : "",
31173
+ alt: titleDisplay ?? "",
31104
31174
  className: "w-full h-full object-cover",
31105
31175
  loading: "lazy"
31106
31176
  }
@@ -31115,18 +31185,18 @@ function DataGrid({
31115
31185
  onChange: () => toggleSelection(id),
31116
31186
  onClick: (e) => e.stopPropagation(),
31117
31187
  className: "w-4 h-4 mt-1 flex-shrink-0 accent-primary",
31118
- "aria-label": t("card.selectItem", { item: titleValue !== void 0 ? String(titleValue) : t("card.itemFallback") })
31188
+ "aria-label": t("card.selectItem", { item: titleDisplay ?? t("card.itemFallback") })
31119
31189
  }
31120
31190
  ),
31121
31191
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
31122
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
31192
+ titleDisplay !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
31123
31193
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
31124
31194
  /* @__PURE__ */ jsxRuntime.jsx(
31125
31195
  Typography,
31126
31196
  {
31127
31197
  variant: titleField?.variant === "h3" ? "h3" : "h4",
31128
31198
  className: "font-semibold truncate min-w-0",
31129
- children: String(titleValue)
31199
+ children: titleDisplay
31130
31200
  }
31131
31201
  )
31132
31202
  ] }),
@@ -31135,7 +31205,7 @@ function DataGrid({
31135
31205
  if (val === void 0 || val === null || val === "") return null;
31136
31206
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
31137
31207
  field.icon && renderIconInput(field.icon, { size: "xs" }),
31138
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
31208
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: resolveRelationCellDisplay(val, relationsData?.[field.name]) ?? humanizeEnumValue(formatValue(val, field.format)) })
31139
31209
  ] }, field.name);
31140
31210
  }) })
31141
31211
  ] }),
@@ -31174,7 +31244,7 @@ function DataGrid({
31174
31244
  bodyFields.filter((f3) => f3.variant === "caption" && f3.format !== "boolean").map((field) => {
31175
31245
  const value = core.getNestedValue(itemData, field.name);
31176
31246
  if (value === void 0 || value === null || value === "") return null;
31177
- return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", className: "line-clamp-2", children: formatValue(value, field.format) }, field.name);
31247
+ return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", className: "line-clamp-2", children: resolveRelationCellDisplay(value, relationsData?.[field.name]) ?? formatValue(value, field.format) }, field.name);
31178
31248
  }),
31179
31249
  /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "md", className: "flex-wrap gap-y-1", children: bodyFields.filter((f3) => f3.variant !== "caption" || f3.format === "boolean").map((field) => {
31180
31250
  const value = core.getNestedValue(itemData, field.name);
@@ -31189,7 +31259,7 @@ function DataGrid({
31189
31259
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
31190
31260
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
31191
31261
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
31192
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
31262
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: resolveRelationCellDisplay(value, relationsData?.[field.name]) ?? formatValue(value, field.format) })
31193
31263
  ] }, field.name);
31194
31264
  }) })
31195
31265
  ] }) })
@@ -31233,6 +31303,7 @@ var init_DataGrid = __esm({
31233
31303
  "use client";
31234
31304
  init_cn();
31235
31305
  init_format();
31306
+ init_relationLabel();
31236
31307
  init_getNestedValue();
31237
31308
  init_useEventBus();
31238
31309
  init_Box();
@@ -31285,7 +31356,9 @@ function statusVariant3(value) {
31285
31356
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
31286
31357
  return "default";
31287
31358
  }
31288
- function formatValue2(value, format, boolLabels) {
31359
+ function formatValue2(value, format, boolLabels, relationOptions) {
31360
+ const relationDisplay = resolveRelationCellDisplay(value, relationOptions);
31361
+ if (relationDisplay !== void 0) return relationDisplay;
31289
31362
  if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
31290
31363
  const isNo = value === false || value === 0 || String(value) === "false";
31291
31364
  return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
@@ -31345,7 +31418,8 @@ function DataList({
31345
31418
  positionEvent,
31346
31419
  dndItemIdField,
31347
31420
  dndRoot,
31348
- look = "dense"
31421
+ look = "dense",
31422
+ relationsData
31349
31423
  }) {
31350
31424
  const eventBus = useEventBus();
31351
31425
  const { t } = hooks.useTranslate();
@@ -31533,13 +31607,13 @@ function DataList({
31533
31607
  return f3.variant === "badge" ? (
31534
31608
  // `format` applies here too — a boolean field badged
31535
31609
  // without it renders the raw "false" instead of "No".
31536
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant3(String(v)), children: formatValue2(v, f3.format) }, f3.name)
31610
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant3(String(v)), children: formatValue2(v, f3.format, void 0, relationsData?.[f3.name]) }, f3.name)
31537
31611
  ) : /* @__PURE__ */ jsxRuntime.jsx(
31538
31612
  Typography,
31539
31613
  {
31540
31614
  variant: "caption",
31541
31615
  className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
31542
- children: formatValue2(v, f3.format)
31616
+ children: formatValue2(v, f3.format, void 0, relationsData?.[f3.name])
31543
31617
  },
31544
31618
  f3.name
31545
31619
  );
@@ -31617,6 +31691,10 @@ function DataList({
31617
31691
  }
31618
31692
  const id = itemData.id || String(index);
31619
31693
  const titleValue = core.getNestedValue(itemData, titleField?.name ?? "");
31694
+ const titleDisplay = resolveRelationCellDisplay(
31695
+ titleValue,
31696
+ titleField ? relationsData?.[titleField.name] : void 0
31697
+ ) ?? (titleValue !== void 0 && titleValue !== null ? String(titleValue) : void 0);
31620
31698
  return wrapDnd(
31621
31699
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
31622
31700
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -31641,7 +31719,7 @@ function DataList({
31641
31719
  {
31642
31720
  variant: titleField?.variant === "h3" ? "h3" : "h4",
31643
31721
  className: cn("font-semibold truncate flex-1", isCompact && "text-sm"),
31644
- children: String(titleValue)
31722
+ children: titleDisplay
31645
31723
  }
31646
31724
  ),
31647
31725
  badgeFields.map((field) => {
@@ -31649,7 +31727,7 @@ function DataList({
31649
31727
  if (val === void 0 || val === null) return null;
31650
31728
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center flex-shrink-0", children: [
31651
31729
  field.icon && renderIconInput2(field.icon, { size: "xs" }),
31652
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant3(String(val)), children: formatValue2(val, field.format) })
31730
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant3(String(val)), children: formatValue2(val, field.format, void 0, relationsData?.[field.name]) })
31653
31731
  ] }, field.name);
31654
31732
  })
31655
31733
  ] }),
@@ -31670,7 +31748,7 @@ function DataList({
31670
31748
  ]
31671
31749
  }
31672
31750
  ),
31673
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
31751
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }, relationsData?.[field.name]) })
31674
31752
  ] }, field.name);
31675
31753
  }) }),
31676
31754
  progressFields.map((field) => {
@@ -31750,6 +31828,7 @@ var init_DataList = __esm({
31750
31828
  "use client";
31751
31829
  init_cn();
31752
31830
  init_format();
31831
+ init_relationLabel();
31753
31832
  init_getNestedValue();
31754
31833
  init_useEventBus();
31755
31834
  init_Box();
@@ -35594,6 +35673,7 @@ var init_MathCanvas = __esm({
35594
35673
  "use client";
35595
35674
  init_useEventBus();
35596
35675
  init_perf();
35676
+ init_keyMapEvent();
35597
35677
  init_atoms();
35598
35678
  init_Stack();
35599
35679
  init_gameFonts();
@@ -35647,14 +35727,14 @@ var init_MathCanvas = __esm({
35647
35727
  React96.useEffect(() => {
35648
35728
  if (!stableKeyMap && !stableKeyUpMap) return;
35649
35729
  const onDown = (e) => {
35650
- const ev = stableKeyMap?.[e.code];
35730
+ const ev = resolveKeyMapEvent(stableKeyMap, e);
35651
35731
  if (ev) {
35652
35732
  eventBus.emit(`UI:${ev}`, {});
35653
35733
  e.preventDefault();
35654
35734
  }
35655
35735
  };
35656
35736
  const onUp = (e) => {
35657
- const ev = stableKeyUpMap?.[e.code];
35737
+ const ev = resolveKeyMapEvent(stableKeyUpMap, e);
35658
35738
  if (ev) eventBus.emit(`UI:${ev}`, {});
35659
35739
  };
35660
35740
  window.addEventListener("keydown", onDown);
@@ -38070,33 +38150,6 @@ var init_Lightbox = __esm({
38070
38150
  Lightbox.displayName = "Lightbox";
38071
38151
  }
38072
38152
  });
38073
-
38074
- // lib/relationLabel.ts
38075
- function relationLabel(value) {
38076
- if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
38077
- return null;
38078
- for (const key of ["name", "title", "label"]) {
38079
- const candidate = value[key];
38080
- if (typeof candidate === "string" && candidate !== "") return candidate;
38081
- }
38082
- const id = value.id;
38083
- return id !== void 0 && id !== null ? String(id) : null;
38084
- }
38085
- function relationDisplayLabels(value, options) {
38086
- if (value === void 0 || value === null || value === "") return [];
38087
- if (Array.isArray(value)) {
38088
- return value.flatMap((item) => relationDisplayLabels(item, options));
38089
- }
38090
- const hydrated = relationLabel(value);
38091
- if (hydrated !== null) return [hydrated];
38092
- const raw = String(value);
38093
- const match = options?.find((opt) => opt.value === raw);
38094
- return [match ? match.label : raw];
38095
- }
38096
- var init_relationLabel = __esm({
38097
- "lib/relationLabel.ts"() {
38098
- }
38099
- });
38100
38153
  function renderIconInput3(icon, props) {
38101
38154
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
38102
38155
  }
@@ -38160,7 +38213,8 @@ function TableView({
38160
38213
  reorderEvent,
38161
38214
  positionEvent,
38162
38215
  dndItemIdField,
38163
- dndRoot
38216
+ dndRoot,
38217
+ relationsData
38164
38218
  }) {
38165
38219
  const eventBus = useEventBus();
38166
38220
  const { t } = hooks.useTranslate();
@@ -38245,7 +38299,7 @@ function TableView({
38245
38299
  const colFloors = React96__namespace.default.useMemo(
38246
38300
  () => colDefs.map((col) => {
38247
38301
  const longest = data.reduce((widest, row) => {
38248
- const cell = formatCell(asFieldValue(core.getNestedValue(row, col.field ?? col.key)), col.format);
38302
+ const cell = formatCell(asFieldValue(core.getNestedValue(row, col.field ?? col.key)), col.format, relationsData?.[col.field ?? col.key]);
38249
38303
  return Math.max(widest, cell.length);
38250
38304
  }, columnLabel(col).length);
38251
38305
  const chrome = col.format === "badge" ? BADGE_CHROME_CH : 0;
@@ -38345,9 +38399,11 @@ function TableView({
38345
38399
  col.className
38346
38400
  );
38347
38401
  if (col.format === "badge" && raw != null && raw !== "") {
38348
- return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: humanizeEnumValue(String(raw)) }) }, col.key);
38402
+ const relationDisplay = resolveRelationCellDisplay(raw, relationsData?.[col.field ?? col.key]);
38403
+ const label = relationDisplay ?? humanizeEnumValue(String(raw));
38404
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: label }) }, col.key);
38349
38405
  }
38350
- return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
38406
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format, relationsData?.[col.field ?? col.key]) }) }, col.key);
38351
38407
  }),
38352
38408
  hasActions && /* @__PURE__ */ jsxRuntime.jsxs(
38353
38409
  HStack,
@@ -38449,11 +38505,9 @@ var init_TableView = __esm({
38449
38505
  init_Menu();
38450
38506
  init_useDataDnd();
38451
38507
  tableViewLog = logger.createLogger("almadar:ui:table-view");
38452
- formatCell = (value, format) => {
38453
- if (value !== null && value !== void 0 && typeof value === "object" && !(value instanceof Date)) {
38454
- const labels = relationDisplayLabels(value);
38455
- if (labels.length > 0) return labels.join(", ");
38456
- }
38508
+ formatCell = (value, format, relationOptions) => {
38509
+ const relationDisplay = resolveRelationCellDisplay(value, relationOptions);
38510
+ if (relationDisplay !== void 0) return relationDisplay;
38457
38511
  return formatValue(value, format);
38458
38512
  };
38459
38513
  MAX_MEASURED_COL_CH = 32;
@@ -46754,7 +46808,8 @@ function DataTable({
46754
46808
  headerActions,
46755
46809
  showTotal = true,
46756
46810
  className,
46757
- look = "dense"
46811
+ look = "dense",
46812
+ relationsData
46758
46813
  }) {
46759
46814
  const [openActionMenu, setOpenActionMenu] = React96.useState(
46760
46815
  null
@@ -47049,6 +47104,11 @@ function DataTable({
47049
47104
  "data-column": String(col.key),
47050
47105
  className: "px-4 py-3 text-sm text-foreground whitespace-nowrap sm:whitespace-normal",
47051
47106
  children: col.render ? col.render(cellValue, row, rowIndex) : (() => {
47107
+ const relationDisplay = resolveRelationCellDisplay(
47108
+ cellValue,
47109
+ relationsData?.[String(col.key)]
47110
+ );
47111
+ if (relationDisplay !== void 0) return relationDisplay;
47052
47112
  const boolVal = asBooleanValue2(cellValue);
47053
47113
  if (boolVal !== null) {
47054
47114
  return boolVal ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "success", children: t("common.yes") }) : /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "neutral", children: t("common.no") });
@@ -47137,6 +47197,7 @@ var init_DataTable = __esm({
47137
47197
  init_cn();
47138
47198
  init_format();
47139
47199
  init_getNestedValue();
47200
+ init_relationLabel();
47140
47201
  init_atoms();
47141
47202
  init_Box();
47142
47203
  init_Stack();
@@ -59043,7 +59104,7 @@ function runTickFrame(entityId, orderedWriters, store) {
59043
59104
  }
59044
59105
  var log9 = logger.createLogger("almadar:ui:effects:client-handlers");
59045
59106
  function createClientEffectHandlers(options) {
59046
- const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated } = options;
59107
+ const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated, callServiceDelegated } = options;
59047
59108
  return {
59048
59109
  emit: (event, payload, source) => {
59049
59110
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -59056,6 +59117,7 @@ function createClientEffectHandlers(options) {
59056
59117
  // carries its outcome — tell the executor so the placeholder above is
59057
59118
  // never read as a denial (see `EffectHandlers.persistDelegated`).
59058
59119
  ...persistDelegated === true ? { persistDelegated: true } : {},
59120
+ ...callServiceDelegated === true ? { callServiceDelegated: true } : {},
59059
59121
  // @almadar/runtime EffectHandlers.set types value:unknown — should be FieldValue (upstream fix queued)
59060
59122
  set: ((_entityId, field, value) => {
59061
59123
  if (!liveEntity) {
@@ -59459,7 +59521,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59459
59521
  }, [traitStates]);
59460
59522
  const traitSnapshotDataRef = React96.useRef(/* @__PURE__ */ new Map());
59461
59523
  const traitFieldStatesRef = React96.useRef(/* @__PURE__ */ new Map());
59462
- const bridgeEchoPendingRef = React96.useRef(/* @__PURE__ */ new Map());
59463
59524
  const bindingSnapshotsRef = React96.useRef(/* @__PURE__ */ new Map());
59464
59525
  const bindingListenersRef = React96.useRef(/* @__PURE__ */ new Map());
59465
59526
  const publishBindingSnapshot = React96.useCallback((traitName, row) => {
@@ -59620,7 +59681,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59620
59681
  // No local persistence adapter (bridge mode, or a syncOnly tick):
59621
59682
  // nothing persists client-side, the server owns the write and
59622
59683
  // reports it — never read the placeholder as a denial.
59623
- persistDelegated: (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0
59684
+ persistDelegated: (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0,
59685
+ // Bridge mode with no consumer-supplied callService: the server
59686
+ // runs every call-service and its cascade carries the result —
59687
+ // the client's mock must not also run one.
59688
+ callServiceDelegated: optionsRef.current?.callService === void 0 && (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0
59624
59689
  });
59625
59690
  const persistence = syncOnly ? void 0 : optionsRef.current?.persistence;
59626
59691
  let handlers = clientHandlers;
@@ -59936,6 +60001,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59936
60001
  const _perfT0 = ui.perfStart("processEvent:total");
59937
60002
  const bindings = traitBindingsRef.current;
59938
60003
  const currentManager = managerRef.current;
60004
+ if (sourceTrait === void 0 && tick === void 0) {
60005
+ for (const snap of traitSnapshotDataRef.current.values()) {
60006
+ snap.cascadeReceived = [];
60007
+ }
60008
+ }
59939
60009
  crossTraitLog.debug("processEvent:enter", () => ({
59940
60010
  event: normalizedEvent,
59941
60011
  payload: JSON.stringify(payload ?? null),
@@ -59974,7 +60044,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59974
60044
  });
59975
60045
  const emittedByTrait = /* @__PURE__ */ new Map();
59976
60046
  const serverEffectResultsByTrait = /* @__PURE__ */ new Map();
59977
- bridgeEchoPendingRef.current.clear();
59978
60047
  for (const { traitName, result } of results) {
59979
60048
  const binding = bindingMap.get(traitName);
59980
60049
  const traitState = currentManager.getState(traitName);
@@ -60032,12 +60101,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60032
60101
  ui.perfEnd("processEvent:executeAll", _perfT2);
60033
60102
  emittedByTrait.set(traitName, emittedDuringExec);
60034
60103
  serverEffectResultsByTrait.set(traitName, transitionServerEffectResults);
60035
- for (const emittedKey of emittedDuringExec) {
60036
- bridgeEchoPendingRef.current.set(
60037
- emittedKey,
60038
- (bridgeEchoPendingRef.current.get(emittedKey) ?? 0) + 1
60039
- );
60040
- }
60041
60104
  await reRenderCallsiteCaptureChildren(traitName, payload ?? {}, entityByTrait, stateLog);
60042
60105
  } else if (!result.executed) {
60043
60106
  if (result.guardResult === false) {
@@ -60111,6 +60174,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60111
60174
  // the verifier reads emittedEvents only.
60112
60175
  orbitalName: "",
60113
60176
  success: true,
60177
+ transitioned: true,
60114
60178
  clientEffects: effectTraces.length,
60115
60179
  dataEntities: {},
60116
60180
  emittedEvents,
@@ -60132,7 +60196,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60132
60196
  if (orbital) dispatchedOrbitals.add(orbital);
60133
60197
  }
60134
60198
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
60135
- void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
60199
+ const locallyEmitted = Array.from(emittedByTrait.values()).flat();
60200
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait, locallyEmitted);
60136
60201
  }
60137
60202
  ui.perfEnd("processEvent:total", _perfT0);
60138
60203
  ui.perfEnd(`event:${normalizedEvent}`, _perfT0);
@@ -60199,14 +60264,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60199
60264
  subscribedBusKeys.add(selfBusKey);
60200
60265
  crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
60201
60266
  const unsub = eventBus.on(selfBusKey, (event) => {
60202
- if (event.source && event.source.dispatched) {
60203
- const pendingEchoes = bridgeEchoPendingRef.current.get(eventKey) ?? 0;
60204
- if (pendingEchoes > 0) {
60205
- bridgeEchoPendingRef.current.set(eventKey, pendingEchoes - 1);
60206
- crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
60207
- return;
60208
- }
60209
- crossTraitLog.debug("self:fire-server-cascade", { traitName, busKey: selfBusKey, eventKey });
60267
+ if (event.source?.dispatched) {
60268
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
60269
+ return;
60210
60270
  }
60211
60271
  crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
60212
60272
  enqueueAndDrain(eventKey, event.payload, traitName, event.source?.tick, event.source?.trait);
@@ -60472,7 +60532,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60472
60532
  [serverActiveTraits]
60473
60533
  );
60474
60534
  const uiSlots = context.useUISlots();
60475
- const onEventProcessed = React96.useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
60535
+ const onEventProcessed = React96.useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait, locallyEmitted) => {
60476
60536
  if (!bridge.connected || !orbitalNames?.length) return;
60477
60537
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
60478
60538
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -60486,7 +60546,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60486
60546
  void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
60487
60547
  continue;
60488
60548
  }
60489
- void bridge.sendEvent(name, event, withActiveTraits(payload)).then(({ effects, meta }) => {
60549
+ void bridge.sendEvent(name, event, withActiveTraits(payload), void 0, void 0, locallyEmitted).then(({ effects, meta }) => {
60490
60550
  recordServerResponse(name, event, { ...meta, effectResults: effectResultsToTraces(meta.effectResults) });
60491
60551
  applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
60492
60552
  });
@@ -63425,6 +63485,7 @@ TraitCardNode.displayName = "TraitCardNode";
63425
63485
 
63426
63486
  // components/avl/organisms/FlowCanvas.tsx
63427
63487
  init_useEventBus();
63488
+ init_keyMapEvent();
63428
63489
  init_perf();
63429
63490
  var flowCanvasLog = logger.createLogger("almadar:ui:flow-canvas");
63430
63491
  var NODE_TYPES = {
@@ -63664,8 +63725,7 @@ function FlowCanvasInner({
63664
63725
  setExpandedOrbital(void 0);
63665
63726
  }
63666
63727
  } else if (e.key === "Delete" || e.key === "Backspace") {
63667
- const target = e.target;
63668
- if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) return;
63728
+ if (isEditableTarget(e.target)) return;
63669
63729
  if (selectedPattern && selectedPattern.nodeData) {
63670
63730
  onPatternDelete?.({ patternId: selectedPattern.patternId ?? "", nodeData: selectedPattern.nodeData });
63671
63731
  setSelectedPattern(null);