@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.
package/dist/avl/index.js CHANGED
@@ -7207,7 +7207,7 @@ var init_Textarea = __esm({
7207
7207
  init_cn();
7208
7208
  init_useEventBus();
7209
7209
  Textarea = React96__default.forwardRef(
7210
- ({ className, error, onChange, ...props }, ref) => {
7210
+ ({ className, error, onChange, action, onKeyDown, ...props }, ref) => {
7211
7211
  const eventBus = useEventBus();
7212
7212
  const handleChange = (e) => {
7213
7213
  if (typeof onChange === "string") {
@@ -7216,11 +7216,18 @@ var init_Textarea = __esm({
7216
7216
  onChange?.(e);
7217
7217
  }
7218
7218
  };
7219
+ const handleKeyDown = (e) => {
7220
+ onKeyDown?.(e);
7221
+ if (!action || e.key !== "Enter" || !(e.metaKey || e.ctrlKey)) return;
7222
+ e.preventDefault();
7223
+ eventBus.emit(`UI:${action}`, { value: e.currentTarget.value });
7224
+ };
7219
7225
  return /* @__PURE__ */ jsx(
7220
7226
  "textarea",
7221
7227
  {
7222
7228
  ref,
7223
7229
  onChange: handleChange,
7230
+ onKeyDown: handleKeyDown,
7224
7231
  className: cn(
7225
7232
  "block w-full border-[length:var(--border-width)] shadow-sm",
7226
7233
  "px-3 py-2 text-sm text-foreground",
@@ -12670,6 +12677,29 @@ var init_useCanvasGestures = __esm({
12670
12677
  }
12671
12678
  });
12672
12679
 
12680
+ // lib/keyMapEvent.ts
12681
+ function isEditableTarget(target) {
12682
+ if (!(target instanceof HTMLElement)) return false;
12683
+ const tag = target.tagName;
12684
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
12685
+ return target.isContentEditable || target.contentEditable === "true";
12686
+ }
12687
+ function keyMapCode(e) {
12688
+ let code = e.code;
12689
+ if (e.altKey) code = `Alt+${code}`;
12690
+ if (e.shiftKey) code = `Shift+${code}`;
12691
+ if (e.metaKey || e.ctrlKey) code = `Mod+${code}`;
12692
+ return code;
12693
+ }
12694
+ function resolveKeyMapEvent(map, e) {
12695
+ if (!map || isEditableTarget(e.target)) return void 0;
12696
+ return map[keyMapCode(e)] ?? map[e.code];
12697
+ }
12698
+ var init_keyMapEvent = __esm({
12699
+ "lib/keyMapEvent.ts"() {
12700
+ }
12701
+ });
12702
+
12673
12703
  // lib/imageCache.ts
12674
12704
  function startLoad(url, onReady, existing) {
12675
12705
  const img = new Image();
@@ -14595,14 +14625,14 @@ function Canvas2D({
14595
14625
  useEffect(() => {
14596
14626
  if (!keyMap && !keyUpMap) return;
14597
14627
  const onDown = (e) => {
14598
- const ev = keyMap?.[e.code];
14628
+ const ev = resolveKeyMapEvent(keyMap, e);
14599
14629
  if (ev) {
14600
14630
  eventBus.emit(`UI:${ev}`, {});
14601
14631
  e.preventDefault();
14602
14632
  }
14603
14633
  };
14604
14634
  const onUp = (e) => {
14605
- const ev = keyUpMap?.[e.code];
14635
+ const ev = resolveKeyMapEvent(keyUpMap, e);
14606
14636
  if (ev) eventBus.emit(`UI:${ev}`, {});
14607
14637
  };
14608
14638
  window.addEventListener("keydown", onDown);
@@ -14726,6 +14756,7 @@ var init_Canvas2D = __esm({
14726
14756
  init_useCamera();
14727
14757
  init_useCanvasGestures();
14728
14758
  init_verificationRegistry();
14759
+ init_keyMapEvent();
14729
14760
  init_webPainter2d();
14730
14761
  init_projector();
14731
14762
  init_paintDispatch();
@@ -29979,6 +30010,40 @@ var init_DashboardLayout = __esm({
29979
30010
  NavLinkBottom.displayName = "NavLinkBottom";
29980
30011
  }
29981
30012
  });
30013
+
30014
+ // lib/relationLabel.ts
30015
+ function relationLabel(value) {
30016
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
30017
+ return null;
30018
+ for (const key of ["name", "title", "label"]) {
30019
+ const candidate = value[key];
30020
+ if (typeof candidate === "string" && candidate !== "") return candidate;
30021
+ }
30022
+ const id = value.id;
30023
+ return id !== void 0 && id !== null ? String(id) : null;
30024
+ }
30025
+ function relationDisplayLabels(value, options) {
30026
+ if (value === void 0 || value === null || value === "") return [];
30027
+ if (Array.isArray(value)) {
30028
+ return value.flatMap((item) => relationDisplayLabels(item, options));
30029
+ }
30030
+ const hydrated = relationLabel(value);
30031
+ if (hydrated !== null) return [hydrated];
30032
+ const raw = String(value);
30033
+ const match = options?.find((opt) => opt.value === raw);
30034
+ return [match ? match.label : raw];
30035
+ }
30036
+ function resolveRelationCellDisplay(value, options) {
30037
+ if (value === null || value === void 0 || value === "") return void 0;
30038
+ const isObjectShaped = typeof value === "object" && !(value instanceof Date) || Array.isArray(value) && value.some((v) => v !== null && typeof v === "object" && !(v instanceof Date));
30039
+ if (!isObjectShaped && !options) return void 0;
30040
+ const labels = relationDisplayLabels(value, options);
30041
+ return labels.length > 0 ? labels.join(", ") : void 0;
30042
+ }
30043
+ var init_relationLabel = __esm({
30044
+ "lib/relationLabel.ts"() {
30045
+ }
30046
+ });
29982
30047
  function downloadItemUrl(url, label) {
29983
30048
  const a = document.createElement("a");
29984
30049
  a.href = url;
@@ -30810,7 +30875,8 @@ function DataGrid({
30810
30875
  positionEvent,
30811
30876
  dndItemIdField,
30812
30877
  dndRoot,
30813
- look = "dense"
30878
+ look = "dense",
30879
+ relationsData
30814
30880
  }) {
30815
30881
  const eventBus = useEventBus();
30816
30882
  const { t } = useTranslate();
@@ -31000,6 +31066,10 @@ function DataGrid({
31000
31066
  );
31001
31067
  }
31002
31068
  const titleValue = getNestedValue(itemData, titleField?.name ?? "");
31069
+ const titleDisplay = resolveRelationCellDisplay(
31070
+ titleValue,
31071
+ titleField ? relationsData?.[titleField.name] : void 0
31072
+ ) ?? (titleValue !== void 0 && titleValue !== null ? String(titleValue) : void 0);
31003
31073
  return wrapDnd(
31004
31074
  /* @__PURE__ */ jsxs(
31005
31075
  Box,
@@ -31024,7 +31094,7 @@ function DataGrid({
31024
31094
  "img",
31025
31095
  {
31026
31096
  src: imgUrl,
31027
- alt: titleValue !== void 0 ? String(titleValue) : "",
31097
+ alt: titleDisplay ?? "",
31028
31098
  className: "w-full h-full object-cover",
31029
31099
  loading: "lazy"
31030
31100
  }
@@ -31039,18 +31109,18 @@ function DataGrid({
31039
31109
  onChange: () => toggleSelection(id),
31040
31110
  onClick: (e) => e.stopPropagation(),
31041
31111
  className: "w-4 h-4 mt-1 flex-shrink-0 accent-primary",
31042
- "aria-label": t("card.selectItem", { item: titleValue !== void 0 ? String(titleValue) : t("card.itemFallback") })
31112
+ "aria-label": t("card.selectItem", { item: titleDisplay ?? t("card.itemFallback") })
31043
31113
  }
31044
31114
  ),
31045
31115
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
31046
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
31116
+ titleDisplay !== void 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
31047
31117
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
31048
31118
  /* @__PURE__ */ jsx(
31049
31119
  Typography,
31050
31120
  {
31051
31121
  variant: titleField?.variant === "h3" ? "h3" : "h4",
31052
31122
  className: "font-semibold truncate min-w-0",
31053
- children: String(titleValue)
31123
+ children: titleDisplay
31054
31124
  }
31055
31125
  )
31056
31126
  ] }),
@@ -31059,7 +31129,7 @@ function DataGrid({
31059
31129
  if (val === void 0 || val === null || val === "") return null;
31060
31130
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
31061
31131
  field.icon && renderIconInput(field.icon, { size: "xs" }),
31062
- /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
31132
+ /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: resolveRelationCellDisplay(val, relationsData?.[field.name]) ?? humanizeEnumValue(formatValue(val, field.format)) })
31063
31133
  ] }, field.name);
31064
31134
  }) })
31065
31135
  ] }),
@@ -31098,7 +31168,7 @@ function DataGrid({
31098
31168
  bodyFields.filter((f3) => f3.variant === "caption" && f3.format !== "boolean").map((field) => {
31099
31169
  const value = getNestedValue(itemData, field.name);
31100
31170
  if (value === void 0 || value === null || value === "") return null;
31101
- return /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", className: "line-clamp-2", children: formatValue(value, field.format) }, field.name);
31171
+ return /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", className: "line-clamp-2", children: resolveRelationCellDisplay(value, relationsData?.[field.name]) ?? formatValue(value, field.format) }, field.name);
31102
31172
  }),
31103
31173
  /* @__PURE__ */ jsx(HStack, { gap: "md", className: "flex-wrap gap-y-1", children: bodyFields.filter((f3) => f3.variant !== "caption" || f3.format === "boolean").map((field) => {
31104
31174
  const value = getNestedValue(itemData, field.name);
@@ -31113,7 +31183,7 @@ function DataGrid({
31113
31183
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
31114
31184
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
31115
31185
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
31116
- /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
31186
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: resolveRelationCellDisplay(value, relationsData?.[field.name]) ?? formatValue(value, field.format) })
31117
31187
  ] }, field.name);
31118
31188
  }) })
31119
31189
  ] }) })
@@ -31157,6 +31227,7 @@ var init_DataGrid = __esm({
31157
31227
  "use client";
31158
31228
  init_cn();
31159
31229
  init_format();
31230
+ init_relationLabel();
31160
31231
  init_getNestedValue();
31161
31232
  init_useEventBus();
31162
31233
  init_Box();
@@ -31209,7 +31280,9 @@ function statusVariant3(value) {
31209
31280
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
31210
31281
  return "default";
31211
31282
  }
31212
- function formatValue2(value, format, boolLabels) {
31283
+ function formatValue2(value, format, boolLabels, relationOptions) {
31284
+ const relationDisplay = resolveRelationCellDisplay(value, relationOptions);
31285
+ if (relationDisplay !== void 0) return relationDisplay;
31213
31286
  if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
31214
31287
  const isNo = value === false || value === 0 || String(value) === "false";
31215
31288
  return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
@@ -31269,7 +31342,8 @@ function DataList({
31269
31342
  positionEvent,
31270
31343
  dndItemIdField,
31271
31344
  dndRoot,
31272
- look = "dense"
31345
+ look = "dense",
31346
+ relationsData
31273
31347
  }) {
31274
31348
  const eventBus = useEventBus();
31275
31349
  const { t } = useTranslate();
@@ -31457,13 +31531,13 @@ function DataList({
31457
31531
  return f3.variant === "badge" ? (
31458
31532
  // `format` applies here too — a boolean field badged
31459
31533
  // without it renders the raw "false" instead of "No".
31460
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(v)), children: formatValue2(v, f3.format) }, f3.name)
31534
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(v)), children: formatValue2(v, f3.format, void 0, relationsData?.[f3.name]) }, f3.name)
31461
31535
  ) : /* @__PURE__ */ jsx(
31462
31536
  Typography,
31463
31537
  {
31464
31538
  variant: "caption",
31465
31539
  className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
31466
- children: formatValue2(v, f3.format)
31540
+ children: formatValue2(v, f3.format, void 0, relationsData?.[f3.name])
31467
31541
  },
31468
31542
  f3.name
31469
31543
  );
@@ -31541,6 +31615,10 @@ function DataList({
31541
31615
  }
31542
31616
  const id = itemData.id || String(index);
31543
31617
  const titleValue = getNestedValue(itemData, titleField?.name ?? "");
31618
+ const titleDisplay = resolveRelationCellDisplay(
31619
+ titleValue,
31620
+ titleField ? relationsData?.[titleField.name] : void 0
31621
+ ) ?? (titleValue !== void 0 && titleValue !== null ? String(titleValue) : void 0);
31544
31622
  return wrapDnd(
31545
31623
  /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
31546
31624
  /* @__PURE__ */ jsxs(
@@ -31565,7 +31643,7 @@ function DataList({
31565
31643
  {
31566
31644
  variant: titleField?.variant === "h3" ? "h3" : "h4",
31567
31645
  className: cn("font-semibold truncate flex-1", isCompact && "text-sm"),
31568
- children: String(titleValue)
31646
+ children: titleDisplay
31569
31647
  }
31570
31648
  ),
31571
31649
  badgeFields.map((field) => {
@@ -31573,7 +31651,7 @@ function DataList({
31573
31651
  if (val === void 0 || val === null) return null;
31574
31652
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center flex-shrink-0", children: [
31575
31653
  field.icon && renderIconInput2(field.icon, { size: "xs" }),
31576
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(val)), children: formatValue2(val, field.format) })
31654
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(val)), children: formatValue2(val, field.format, void 0, relationsData?.[field.name]) })
31577
31655
  ] }, field.name);
31578
31656
  })
31579
31657
  ] }),
@@ -31594,7 +31672,7 @@ function DataList({
31594
31672
  ]
31595
31673
  }
31596
31674
  ),
31597
- /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
31675
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }, relationsData?.[field.name]) })
31598
31676
  ] }, field.name);
31599
31677
  }) }),
31600
31678
  progressFields.map((field) => {
@@ -31674,6 +31752,7 @@ var init_DataList = __esm({
31674
31752
  "use client";
31675
31753
  init_cn();
31676
31754
  init_format();
31755
+ init_relationLabel();
31677
31756
  init_getNestedValue();
31678
31757
  init_useEventBus();
31679
31758
  init_Box();
@@ -35518,6 +35597,7 @@ var init_MathCanvas = __esm({
35518
35597
  "use client";
35519
35598
  init_useEventBus();
35520
35599
  init_perf();
35600
+ init_keyMapEvent();
35521
35601
  init_atoms();
35522
35602
  init_Stack();
35523
35603
  init_gameFonts();
@@ -35571,14 +35651,14 @@ var init_MathCanvas = __esm({
35571
35651
  useEffect(() => {
35572
35652
  if (!stableKeyMap && !stableKeyUpMap) return;
35573
35653
  const onDown = (e) => {
35574
- const ev = stableKeyMap?.[e.code];
35654
+ const ev = resolveKeyMapEvent(stableKeyMap, e);
35575
35655
  if (ev) {
35576
35656
  eventBus.emit(`UI:${ev}`, {});
35577
35657
  e.preventDefault();
35578
35658
  }
35579
35659
  };
35580
35660
  const onUp = (e) => {
35581
- const ev = stableKeyUpMap?.[e.code];
35661
+ const ev = resolveKeyMapEvent(stableKeyUpMap, e);
35582
35662
  if (ev) eventBus.emit(`UI:${ev}`, {});
35583
35663
  };
35584
35664
  window.addEventListener("keydown", onDown);
@@ -37994,33 +38074,6 @@ var init_Lightbox = __esm({
37994
38074
  Lightbox.displayName = "Lightbox";
37995
38075
  }
37996
38076
  });
37997
-
37998
- // lib/relationLabel.ts
37999
- function relationLabel(value) {
38000
- if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
38001
- return null;
38002
- for (const key of ["name", "title", "label"]) {
38003
- const candidate = value[key];
38004
- if (typeof candidate === "string" && candidate !== "") return candidate;
38005
- }
38006
- const id = value.id;
38007
- return id !== void 0 && id !== null ? String(id) : null;
38008
- }
38009
- function relationDisplayLabels(value, options) {
38010
- if (value === void 0 || value === null || value === "") return [];
38011
- if (Array.isArray(value)) {
38012
- return value.flatMap((item) => relationDisplayLabels(item, options));
38013
- }
38014
- const hydrated = relationLabel(value);
38015
- if (hydrated !== null) return [hydrated];
38016
- const raw = String(value);
38017
- const match = options?.find((opt) => opt.value === raw);
38018
- return [match ? match.label : raw];
38019
- }
38020
- var init_relationLabel = __esm({
38021
- "lib/relationLabel.ts"() {
38022
- }
38023
- });
38024
38077
  function renderIconInput3(icon, props) {
38025
38078
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
38026
38079
  }
@@ -38084,7 +38137,8 @@ function TableView({
38084
38137
  reorderEvent,
38085
38138
  positionEvent,
38086
38139
  dndItemIdField,
38087
- dndRoot
38140
+ dndRoot,
38141
+ relationsData
38088
38142
  }) {
38089
38143
  const eventBus = useEventBus();
38090
38144
  const { t } = useTranslate();
@@ -38169,7 +38223,7 @@ function TableView({
38169
38223
  const colFloors = React96__default.useMemo(
38170
38224
  () => colDefs.map((col) => {
38171
38225
  const longest = data.reduce((widest, row) => {
38172
- const cell = formatCell(asFieldValue(getNestedValue(row, col.field ?? col.key)), col.format);
38226
+ const cell = formatCell(asFieldValue(getNestedValue(row, col.field ?? col.key)), col.format, relationsData?.[col.field ?? col.key]);
38173
38227
  return Math.max(widest, cell.length);
38174
38228
  }, columnLabel(col).length);
38175
38229
  const chrome = col.format === "badge" ? BADGE_CHROME_CH : 0;
@@ -38269,9 +38323,11 @@ function TableView({
38269
38323
  col.className
38270
38324
  );
38271
38325
  if (col.format === "badge" && raw != null && raw !== "") {
38272
- return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: humanizeEnumValue(String(raw)) }) }, col.key);
38326
+ const relationDisplay = resolveRelationCellDisplay(raw, relationsData?.[col.field ?? col.key]);
38327
+ const label = relationDisplay ?? humanizeEnumValue(String(raw));
38328
+ return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: label }) }, col.key);
38273
38329
  }
38274
- return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
38330
+ return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format, relationsData?.[col.field ?? col.key]) }) }, col.key);
38275
38331
  }),
38276
38332
  hasActions && /* @__PURE__ */ jsxs(
38277
38333
  HStack,
@@ -38373,11 +38429,9 @@ var init_TableView = __esm({
38373
38429
  init_Menu();
38374
38430
  init_useDataDnd();
38375
38431
  tableViewLog = createLogger("almadar:ui:table-view");
38376
- formatCell = (value, format) => {
38377
- if (value !== null && value !== void 0 && typeof value === "object" && !(value instanceof Date)) {
38378
- const labels = relationDisplayLabels(value);
38379
- if (labels.length > 0) return labels.join(", ");
38380
- }
38432
+ formatCell = (value, format, relationOptions) => {
38433
+ const relationDisplay = resolveRelationCellDisplay(value, relationOptions);
38434
+ if (relationDisplay !== void 0) return relationDisplay;
38381
38435
  return formatValue(value, format);
38382
38436
  };
38383
38437
  MAX_MEASURED_COL_CH = 32;
@@ -46678,7 +46732,8 @@ function DataTable({
46678
46732
  headerActions,
46679
46733
  showTotal = true,
46680
46734
  className,
46681
- look = "dense"
46735
+ look = "dense",
46736
+ relationsData
46682
46737
  }) {
46683
46738
  const [openActionMenu, setOpenActionMenu] = useState(
46684
46739
  null
@@ -46973,6 +47028,11 @@ function DataTable({
46973
47028
  "data-column": String(col.key),
46974
47029
  className: "px-4 py-3 text-sm text-foreground whitespace-nowrap sm:whitespace-normal",
46975
47030
  children: col.render ? col.render(cellValue, row, rowIndex) : (() => {
47031
+ const relationDisplay = resolveRelationCellDisplay(
47032
+ cellValue,
47033
+ relationsData?.[String(col.key)]
47034
+ );
47035
+ if (relationDisplay !== void 0) return relationDisplay;
46976
47036
  const boolVal = asBooleanValue2(cellValue);
46977
47037
  if (boolVal !== null) {
46978
47038
  return boolVal ? /* @__PURE__ */ jsx(Badge, { variant: "success", children: t("common.yes") }) : /* @__PURE__ */ jsx(Badge, { variant: "neutral", children: t("common.no") });
@@ -47061,6 +47121,7 @@ var init_DataTable = __esm({
47061
47121
  init_cn();
47062
47122
  init_format();
47063
47123
  init_getNestedValue();
47124
+ init_relationLabel();
47064
47125
  init_atoms();
47065
47126
  init_Box();
47066
47127
  init_Stack();
@@ -58967,7 +59028,7 @@ function runTickFrame(entityId, orderedWriters, store) {
58967
59028
  }
58968
59029
  var log9 = createLogger("almadar:ui:effects:client-handlers");
58969
59030
  function createClientEffectHandlers(options) {
58970
- const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated } = options;
59031
+ const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated, callServiceDelegated } = options;
58971
59032
  return {
58972
59033
  emit: (event, payload, source) => {
58973
59034
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -58980,6 +59041,7 @@ function createClientEffectHandlers(options) {
58980
59041
  // carries its outcome — tell the executor so the placeholder above is
58981
59042
  // never read as a denial (see `EffectHandlers.persistDelegated`).
58982
59043
  ...persistDelegated === true ? { persistDelegated: true } : {},
59044
+ ...callServiceDelegated === true ? { callServiceDelegated: true } : {},
58983
59045
  // @almadar/runtime EffectHandlers.set types value:unknown — should be FieldValue (upstream fix queued)
58984
59046
  set: ((_entityId, field, value) => {
58985
59047
  if (!liveEntity) {
@@ -59383,7 +59445,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59383
59445
  }, [traitStates]);
59384
59446
  const traitSnapshotDataRef = useRef(/* @__PURE__ */ new Map());
59385
59447
  const traitFieldStatesRef = useRef(/* @__PURE__ */ new Map());
59386
- const bridgeEchoPendingRef = useRef(/* @__PURE__ */ new Map());
59387
59448
  const bindingSnapshotsRef = useRef(/* @__PURE__ */ new Map());
59388
59449
  const bindingListenersRef = useRef(/* @__PURE__ */ new Map());
59389
59450
  const publishBindingSnapshot = useCallback((traitName, row) => {
@@ -59544,7 +59605,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59544
59605
  // No local persistence adapter (bridge mode, or a syncOnly tick):
59545
59606
  // nothing persists client-side, the server owns the write and
59546
59607
  // reports it — never read the placeholder as a denial.
59547
- persistDelegated: (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0
59608
+ persistDelegated: (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0,
59609
+ // Bridge mode with no consumer-supplied callService: the server
59610
+ // runs every call-service and its cascade carries the result —
59611
+ // the client's mock must not also run one.
59612
+ callServiceDelegated: optionsRef.current?.callService === void 0 && (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0
59548
59613
  });
59549
59614
  const persistence = syncOnly ? void 0 : optionsRef.current?.persistence;
59550
59615
  let handlers = clientHandlers;
@@ -59903,7 +59968,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59903
59968
  });
59904
59969
  const emittedByTrait = /* @__PURE__ */ new Map();
59905
59970
  const serverEffectResultsByTrait = /* @__PURE__ */ new Map();
59906
- bridgeEchoPendingRef.current.clear();
59907
59971
  for (const { traitName, result } of results) {
59908
59972
  const binding = bindingMap.get(traitName);
59909
59973
  const traitState = currentManager.getState(traitName);
@@ -59961,12 +60025,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
59961
60025
  perfEnd("processEvent:executeAll", _perfT2);
59962
60026
  emittedByTrait.set(traitName, emittedDuringExec);
59963
60027
  serverEffectResultsByTrait.set(traitName, transitionServerEffectResults);
59964
- for (const emittedKey of emittedDuringExec) {
59965
- bridgeEchoPendingRef.current.set(
59966
- emittedKey,
59967
- (bridgeEchoPendingRef.current.get(emittedKey) ?? 0) + 1
59968
- );
59969
- }
59970
60028
  await reRenderCallsiteCaptureChildren(traitName, payload ?? {}, entityByTrait, stateLog);
59971
60029
  } else if (!result.executed) {
59972
60030
  if (result.guardResult === false) {
@@ -60062,7 +60120,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60062
60120
  if (orbital) dispatchedOrbitals.add(orbital);
60063
60121
  }
60064
60122
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
60065
- void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
60123
+ const locallyEmitted = Array.from(emittedByTrait.values()).flat();
60124
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait, locallyEmitted);
60066
60125
  }
60067
60126
  perfEnd("processEvent:total", _perfT0);
60068
60127
  perfEnd(`event:${normalizedEvent}`, _perfT0);
@@ -60129,14 +60188,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
60129
60188
  subscribedBusKeys.add(selfBusKey);
60130
60189
  crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
60131
60190
  const unsub = eventBus.on(selfBusKey, (event) => {
60132
- if (event.source && event.source.dispatched) {
60133
- const pendingEchoes = bridgeEchoPendingRef.current.get(eventKey) ?? 0;
60134
- if (pendingEchoes > 0) {
60135
- bridgeEchoPendingRef.current.set(eventKey, pendingEchoes - 1);
60136
- crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
60137
- return;
60138
- }
60139
- crossTraitLog.debug("self:fire-server-cascade", { traitName, busKey: selfBusKey, eventKey });
60191
+ if (event.source?.dispatched) {
60192
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
60193
+ return;
60140
60194
  }
60141
60195
  crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
60142
60196
  enqueueAndDrain(eventKey, event.payload, traitName, event.source?.tick, event.source?.trait);
@@ -60402,7 +60456,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60402
60456
  [serverActiveTraits]
60403
60457
  );
60404
60458
  const uiSlots = useUISlots();
60405
- const onEventProcessed = useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
60459
+ const onEventProcessed = useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait, locallyEmitted) => {
60406
60460
  if (!bridge.connected || !orbitalNames?.length) return;
60407
60461
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
60408
60462
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -60416,7 +60470,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60416
60470
  void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
60417
60471
  continue;
60418
60472
  }
60419
- void bridge.sendEvent(name, event, withActiveTraits(payload)).then(({ effects, meta }) => {
60473
+ void bridge.sendEvent(name, event, withActiveTraits(payload), void 0, void 0, locallyEmitted).then(({ effects, meta }) => {
60420
60474
  recordServerResponse(name, event, { ...meta, effectResults: effectResultsToTraces(meta.effectResults) });
60421
60475
  applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
60422
60476
  });
@@ -60487,6 +60541,30 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60487
60541
  }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits, routeParams]);
60488
60542
  return /* @__PURE__ */ jsx(EntityBindingContext.Provider, { value: entityBindingSource, children });
60489
60543
  }
60544
+ function FitToBox({ children }) {
60545
+ const outerRef = useRef(null);
60546
+ const innerRef = useRef(null);
60547
+ const [scale, setScale] = useState(1);
60548
+ useEffect(() => {
60549
+ const outer = outerRef.current;
60550
+ const inner = innerRef.current;
60551
+ if (!outer || !inner) return;
60552
+ const update = () => {
60553
+ const sw = inner.scrollWidth;
60554
+ const sh = inner.scrollHeight;
60555
+ const cw = outer.clientWidth;
60556
+ const ch = outer.clientHeight;
60557
+ if (!sw || !sh || !cw || !ch) return;
60558
+ setScale(Math.min(1, cw / sw, ch / sh));
60559
+ };
60560
+ update();
60561
+ const ro = new ResizeObserver(update);
60562
+ ro.observe(outer);
60563
+ ro.observe(inner);
60564
+ return () => ro.disconnect();
60565
+ }, []);
60566
+ return /* @__PURE__ */ jsx("div", { ref: outerRef, className: "relative h-full w-full overflow-hidden", children: /* @__PURE__ */ jsx("div", { ref: innerRef, style: { transform: `scale(${scale})`, transformOrigin: "top left", width: "fit-content" }, children }) });
60567
+ }
60490
60568
  function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData, pageName, routeParams, onNavigate, onNavigateBack, onLocalFallback, persistence }) {
60491
60569
  const { traits: traits2, allEntities, allTraits, ir } = useResolvedSchema(schema, pageName);
60492
60570
  const allPageTraits = useMemo(() => {
@@ -60672,6 +60750,7 @@ function OrbPreview({
60672
60750
  getAccessToken,
60673
60751
  initialPagePath,
60674
60752
  isolated = false,
60753
+ fit = false,
60675
60754
  user = null
60676
60755
  }) {
60677
60756
  if (serverUrl && transport) {
@@ -60828,7 +60907,7 @@ function OrbPreview({
60828
60907
  Box,
60829
60908
  {
60830
60909
  ref: containerRef,
60831
- className: `overflow-auto border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
60910
+ className: `${fit ? "overflow-hidden" : "overflow-auto"} border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
60832
60911
  style: { height },
60833
60912
  children: [
60834
60913
  localFallback && /* @__PURE__ */ 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__ */ 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." }) }),
@@ -60841,7 +60920,22 @@ function OrbPreview({
60841
60920
  storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
60842
60921
  children: [
60843
60922
  /* @__PURE__ */ jsx(NavStackRefBridge, { apiRef: navStackRef }),
60844
- /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
60923
+ /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsx(UISlotProvider, { children: fit ? /* @__PURE__ */ jsx(FitToBox, { children: /* @__PURE__ */ jsx(
60924
+ SchemaRunner,
60925
+ {
60926
+ schema: parseResult.schema,
60927
+ serverUrl,
60928
+ transport,
60929
+ getAccessToken,
60930
+ mockData: effectiveMockData,
60931
+ pageName: currentPage,
60932
+ routeParams,
60933
+ onNavigate: handleNavigateEffect,
60934
+ onNavigateBack: handleNavigateBack,
60935
+ onLocalFallback: handleLocalFallback,
60936
+ persistence
60937
+ }
60938
+ ) }) : /* @__PURE__ */ jsx(
60845
60939
  SchemaRunner,
60846
60940
  {
60847
60941
  schema: parseResult.schema,
@@ -60872,7 +60966,8 @@ function BrowserPlayground({
60872
60966
  initialPagePath,
60873
60967
  height,
60874
60968
  className,
60875
- paused
60969
+ paused,
60970
+ fit
60876
60971
  }) {
60877
60972
  const [runtime] = useState(
60878
60973
  () => new OrbitalServerRuntime({ mode, debug: false })
@@ -60934,6 +61029,7 @@ function BrowserPlayground({
60934
61029
  initialPagePath,
60935
61030
  height,
60936
61031
  className,
61032
+ fit,
60937
61033
  isolated: true
60938
61034
  }
60939
61035
  );
@@ -63355,6 +63451,7 @@ TraitCardNode.displayName = "TraitCardNode";
63355
63451
 
63356
63452
  // components/avl/organisms/FlowCanvas.tsx
63357
63453
  init_useEventBus();
63454
+ init_keyMapEvent();
63358
63455
  init_perf();
63359
63456
  var flowCanvasLog = createLogger("almadar:ui:flow-canvas");
63360
63457
  var NODE_TYPES = {
@@ -63594,8 +63691,7 @@ function FlowCanvasInner({
63594
63691
  setExpandedOrbital(void 0);
63595
63692
  }
63596
63693
  } else if (e.key === "Delete" || e.key === "Backspace") {
63597
- const target = e.target;
63598
- if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) return;
63694
+ if (isEditableTarget(e.target)) return;
63599
63695
  if (selectedPattern && selectedPattern.nodeData) {
63600
63696
  onPatternDelete?.({ patternId: selectedPattern.patternId ?? "", nodeData: selectedPattern.nodeData });
63601
63697
  setSelectedPattern(null);
@@ -1,4 +1,4 @@
1
- import { AnimationName, Asset, ScenePos, EventEmit, JsonObject, SExpr } from '@almadar/core';
1
+ import { AnimationName, Asset, ScenePos, EventEmit, EventKey, JsonObject, SExpr } from '@almadar/core';
2
2
  import React__default from 'react';
3
3
  import * as THREE from 'three';
4
4
  import { D as DrawableNode } from './paintDispatch-Dh1pgljl.cjs';
@@ -485,10 +485,10 @@ interface Canvas3DHostProps {
485
485
  /** Enable the orbit camera controls. Default true; `follow`/`chase` modes always
486
486
  * disable them (the follow camera is authoritative). */
487
487
  controlsEnabled?: boolean;
488
- /** Maps a keydown `e.code` the board's SEMANTIC event (device-agnostic input). */
489
- keyMap?: Record<string, string>;
490
- /** Maps a keyup `e.code` the board's SEMANTIC event. */
491
- keyUpMap?: Record<string, string>;
488
+ /** Maps a keydown `e.code` optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}`; keystrokes inside inputs/textareas never route. */
489
+ keyMap?: Record<string, EventKey>;
490
+ /** Maps a keyup `e.code` optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event, emitted as `UI:{event}`; keystrokes inside inputs/textareas never route. */
491
+ keyUpMap?: Record<string, EventKey>;
492
492
  /** Side-view world size in pixels (accepted for API parity). */
493
493
  worldWidth?: number;
494
494
  /** Side-view world size in pixels (accepted for API parity). */