@almadar/ui 6.15.0 → 6.17.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.
@@ -162,7 +162,7 @@ interface SendEventResult {
162
162
  }
163
163
  interface ServerBridgeContextValue {
164
164
  connected: boolean;
165
- 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>;
166
166
  }
167
167
  /**
168
168
  * Transport adapter for ServerBridgeProvider. Decouples the bridge's
@@ -162,7 +162,7 @@ interface SendEventResult {
162
162
  }
163
163
  interface ServerBridgeContextValue {
164
164
  connected: boolean;
165
- 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>;
166
166
  }
167
167
  /**
168
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;
@@ -59979,7 +60044,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59979
60044
  });
59980
60045
  const emittedByTrait = /* @__PURE__ */ new Map();
59981
60046
  const serverEffectResultsByTrait = /* @__PURE__ */ new Map();
59982
- bridgeEchoPendingRef.current.clear();
59983
60047
  for (const { traitName, result } of results) {
59984
60048
  const binding = bindingMap.get(traitName);
59985
60049
  const traitState = currentManager.getState(traitName);
@@ -60037,12 +60101,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60037
60101
  ui.perfEnd("processEvent:executeAll", _perfT2);
60038
60102
  emittedByTrait.set(traitName, emittedDuringExec);
60039
60103
  serverEffectResultsByTrait.set(traitName, transitionServerEffectResults);
60040
- for (const emittedKey of emittedDuringExec) {
60041
- bridgeEchoPendingRef.current.set(
60042
- emittedKey,
60043
- (bridgeEchoPendingRef.current.get(emittedKey) ?? 0) + 1
60044
- );
60045
- }
60046
60104
  await reRenderCallsiteCaptureChildren(traitName, payload ?? {}, entityByTrait, stateLog);
60047
60105
  } else if (!result.executed) {
60048
60106
  if (result.guardResult === false) {
@@ -60138,7 +60196,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60138
60196
  if (orbital) dispatchedOrbitals.add(orbital);
60139
60197
  }
60140
60198
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
60141
- void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
60199
+ const locallyEmitted = Array.from(emittedByTrait.values()).flat();
60200
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait, locallyEmitted);
60142
60201
  }
60143
60202
  ui.perfEnd("processEvent:total", _perfT0);
60144
60203
  ui.perfEnd(`event:${normalizedEvent}`, _perfT0);
@@ -60205,14 +60264,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60205
60264
  subscribedBusKeys.add(selfBusKey);
60206
60265
  crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
60207
60266
  const unsub = eventBus.on(selfBusKey, (event) => {
60208
- if (event.source && event.source.dispatched) {
60209
- const pendingEchoes = bridgeEchoPendingRef.current.get(eventKey) ?? 0;
60210
- if (pendingEchoes > 0) {
60211
- bridgeEchoPendingRef.current.set(eventKey, pendingEchoes - 1);
60212
- crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
60213
- return;
60214
- }
60215
- 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;
60216
60270
  }
60217
60271
  crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
60218
60272
  enqueueAndDrain(eventKey, event.payload, traitName, event.source?.tick, event.source?.trait);
@@ -60478,7 +60532,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60478
60532
  [serverActiveTraits]
60479
60533
  );
60480
60534
  const uiSlots = context.useUISlots();
60481
- const onEventProcessed = React96.useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
60535
+ const onEventProcessed = React96.useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait, locallyEmitted) => {
60482
60536
  if (!bridge.connected || !orbitalNames?.length) return;
60483
60537
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
60484
60538
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -60492,7 +60546,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60492
60546
  void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
60493
60547
  continue;
60494
60548
  }
60495
- 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 }) => {
60496
60550
  recordServerResponse(name, event, { ...meta, effectResults: effectResultsToTraces(meta.effectResults) });
60497
60551
  applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
60498
60552
  });
@@ -60563,6 +60617,30 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60563
60617
  }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits, routeParams]);
60564
60618
  return /* @__PURE__ */ jsxRuntime.jsx(providers.EntityBindingContext.Provider, { value: entityBindingSource, children });
60565
60619
  }
60620
+ function FitToBox({ children }) {
60621
+ const outerRef = React96.useRef(null);
60622
+ const innerRef = React96.useRef(null);
60623
+ const [scale, setScale] = React96.useState(1);
60624
+ React96.useEffect(() => {
60625
+ const outer = outerRef.current;
60626
+ const inner = innerRef.current;
60627
+ if (!outer || !inner) return;
60628
+ const update = () => {
60629
+ const sw = inner.scrollWidth;
60630
+ const sh = inner.scrollHeight;
60631
+ const cw = outer.clientWidth;
60632
+ const ch = outer.clientHeight;
60633
+ if (!sw || !sh || !cw || !ch) return;
60634
+ setScale(Math.min(1, cw / sw, ch / sh));
60635
+ };
60636
+ update();
60637
+ const ro = new ResizeObserver(update);
60638
+ ro.observe(outer);
60639
+ ro.observe(inner);
60640
+ return () => ro.disconnect();
60641
+ }, []);
60642
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: outerRef, className: "relative h-full w-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: innerRef, style: { transform: `scale(${scale})`, transformOrigin: "top left", width: "fit-content" }, children }) });
60643
+ }
60566
60644
  function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData, pageName, routeParams, onNavigate, onNavigateBack, onLocalFallback, persistence }) {
60567
60645
  const { traits: traits2, allEntities, allTraits, ir } = useResolvedSchema(schema, pageName);
60568
60646
  const allPageTraits = React96.useMemo(() => {
@@ -60748,6 +60826,7 @@ function OrbPreview({
60748
60826
  getAccessToken,
60749
60827
  initialPagePath,
60750
60828
  isolated = false,
60829
+ fit = false,
60751
60830
  user = null
60752
60831
  }) {
60753
60832
  if (serverUrl && transport) {
@@ -60904,7 +60983,7 @@ function OrbPreview({
60904
60983
  Box,
60905
60984
  {
60906
60985
  ref: containerRef,
60907
- className: `overflow-auto border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
60986
+ className: `${fit ? "overflow-hidden" : "overflow-auto"} border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
60908
60987
  style: { height },
60909
60988
  children: [
60910
60989
  localFallback && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-3 py-2 bg-[var(--color-warning)] bg-opacity-10 border-b border-[var(--color-warning)] flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-[var(--color-warning-foreground)] flex-1", children: "Preview server unreachable \u2014 running locally. Server-side state and persistence are disabled." }) }),
@@ -60917,7 +60996,22 @@ function OrbPreview({
60917
60996
  storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
60918
60997
  children: [
60919
60998
  /* @__PURE__ */ jsxRuntime.jsx(NavStackRefBridge, { apiRef: navStackRef }),
60920
- /* @__PURE__ */ jsxRuntime.jsx(providers.OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsxRuntime.jsx(context.UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
60999
+ /* @__PURE__ */ jsxRuntime.jsx(providers.OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsxRuntime.jsx(context.UISlotProvider, { children: fit ? /* @__PURE__ */ jsxRuntime.jsx(FitToBox, { children: /* @__PURE__ */ jsxRuntime.jsx(
61000
+ SchemaRunner,
61001
+ {
61002
+ schema: parseResult.schema,
61003
+ serverUrl,
61004
+ transport,
61005
+ getAccessToken,
61006
+ mockData: effectiveMockData,
61007
+ pageName: currentPage,
61008
+ routeParams,
61009
+ onNavigate: handleNavigateEffect,
61010
+ onNavigateBack: handleNavigateBack,
61011
+ onLocalFallback: handleLocalFallback,
61012
+ persistence
61013
+ }
61014
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
60921
61015
  SchemaRunner,
60922
61016
  {
60923
61017
  schema: parseResult.schema,
@@ -60948,7 +61042,8 @@ function BrowserPlayground({
60948
61042
  initialPagePath,
60949
61043
  height,
60950
61044
  className,
60951
- paused
61045
+ paused,
61046
+ fit
60952
61047
  }) {
60953
61048
  const [runtime] = React96.useState(
60954
61049
  () => new OrbitalServerRuntime.OrbitalServerRuntime({ mode, debug: false })
@@ -61010,6 +61105,7 @@ function BrowserPlayground({
61010
61105
  initialPagePath,
61011
61106
  height,
61012
61107
  className,
61108
+ fit,
61013
61109
  isolated: true
61014
61110
  }
61015
61111
  );
@@ -63431,6 +63527,7 @@ TraitCardNode.displayName = "TraitCardNode";
63431
63527
 
63432
63528
  // components/avl/organisms/FlowCanvas.tsx
63433
63529
  init_useEventBus();
63530
+ init_keyMapEvent();
63434
63531
  init_perf();
63435
63532
  var flowCanvasLog = logger.createLogger("almadar:ui:flow-canvas");
63436
63533
  var NODE_TYPES = {
@@ -63670,8 +63767,7 @@ function FlowCanvasInner({
63670
63767
  setExpandedOrbital(void 0);
63671
63768
  }
63672
63769
  } else if (e.key === "Delete" || e.key === "Backspace") {
63673
- const target = e.target;
63674
- if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) return;
63770
+ if (isEditableTarget(e.target)) return;
63675
63771
  if (selectedPattern && selectedPattern.nodeData) {
63676
63772
  onPatternDelete?.({ patternId: selectedPattern.patternId ?? "", nodeData: selectedPattern.nodeData });
63677
63773
  setSelectedPattern(null);