@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.
@@ -3987,7 +3987,7 @@ var init_Textarea = __esm({
3987
3987
  init_cn();
3988
3988
  init_useEventBus();
3989
3989
  Textarea = React87__default.forwardRef(
3990
- ({ className, error, onChange, ...props }, ref) => {
3990
+ ({ className, error, onChange, action, onKeyDown, ...props }, ref) => {
3991
3991
  const eventBus = useEventBus();
3992
3992
  const handleChange = (e) => {
3993
3993
  if (typeof onChange === "string") {
@@ -3996,11 +3996,18 @@ var init_Textarea = __esm({
3996
3996
  onChange?.(e);
3997
3997
  }
3998
3998
  };
3999
+ const handleKeyDown = (e) => {
4000
+ onKeyDown?.(e);
4001
+ if (!action || e.key !== "Enter" || !(e.metaKey || e.ctrlKey)) return;
4002
+ e.preventDefault();
4003
+ eventBus.emit(`UI:${action}`, { value: e.currentTarget.value });
4004
+ };
3999
4005
  return /* @__PURE__ */ jsx(
4000
4006
  "textarea",
4001
4007
  {
4002
4008
  ref,
4003
4009
  onChange: handleChange,
4010
+ onKeyDown: handleKeyDown,
4004
4011
  className: cn(
4005
4012
  "block w-full border-[length:var(--border-width)] shadow-sm",
4006
4013
  "px-3 py-2 text-sm text-foreground",
@@ -9393,6 +9400,29 @@ var init_useCamera = __esm({
9393
9400
  }
9394
9401
  });
9395
9402
 
9403
+ // lib/keyMapEvent.ts
9404
+ function isEditableTarget(target) {
9405
+ if (!(target instanceof HTMLElement)) return false;
9406
+ const tag = target.tagName;
9407
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
9408
+ return target.isContentEditable || target.contentEditable === "true";
9409
+ }
9410
+ function keyMapCode(e) {
9411
+ let code = e.code;
9412
+ if (e.altKey) code = `Alt+${code}`;
9413
+ if (e.shiftKey) code = `Shift+${code}`;
9414
+ if (e.metaKey || e.ctrlKey) code = `Mod+${code}`;
9415
+ return code;
9416
+ }
9417
+ function resolveKeyMapEvent(map, e) {
9418
+ if (!map || isEditableTarget(e.target)) return void 0;
9419
+ return map[keyMapCode(e)] ?? map[e.code];
9420
+ }
9421
+ var init_keyMapEvent = __esm({
9422
+ "lib/keyMapEvent.ts"() {
9423
+ }
9424
+ });
9425
+
9396
9426
  // lib/imageCache.ts
9397
9427
  function startLoad(url, onReady, existing) {
9398
9428
  const img = new Image();
@@ -11318,14 +11348,14 @@ function Canvas2D({
11318
11348
  useEffect(() => {
11319
11349
  if (!keyMap && !keyUpMap) return;
11320
11350
  const onDown = (e) => {
11321
- const ev = keyMap?.[e.code];
11351
+ const ev = resolveKeyMapEvent(keyMap, e);
11322
11352
  if (ev) {
11323
11353
  eventBus.emit(`UI:${ev}`, {});
11324
11354
  e.preventDefault();
11325
11355
  }
11326
11356
  };
11327
11357
  const onUp = (e) => {
11328
- const ev = keyUpMap?.[e.code];
11358
+ const ev = resolveKeyMapEvent(keyUpMap, e);
11329
11359
  if (ev) eventBus.emit(`UI:${ev}`, {});
11330
11360
  };
11331
11361
  window.addEventListener("keydown", onDown);
@@ -11449,6 +11479,7 @@ var init_Canvas2D = __esm({
11449
11479
  init_useCamera();
11450
11480
  init_useCanvasGestures();
11451
11481
  init_verificationRegistry();
11482
+ init_keyMapEvent();
11452
11483
  init_webPainter2d();
11453
11484
  init_projector();
11454
11485
  init_paintDispatch();
@@ -27378,6 +27409,40 @@ var init_DashboardLayout = __esm({
27378
27409
  NavLinkBottom.displayName = "NavLinkBottom";
27379
27410
  }
27380
27411
  });
27412
+
27413
+ // lib/relationLabel.ts
27414
+ function relationLabel(value) {
27415
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
27416
+ return null;
27417
+ for (const key of ["name", "title", "label"]) {
27418
+ const candidate = value[key];
27419
+ if (typeof candidate === "string" && candidate !== "") return candidate;
27420
+ }
27421
+ const id = value.id;
27422
+ return id !== void 0 && id !== null ? String(id) : null;
27423
+ }
27424
+ function relationDisplayLabels(value, options) {
27425
+ if (value === void 0 || value === null || value === "") return [];
27426
+ if (Array.isArray(value)) {
27427
+ return value.flatMap((item) => relationDisplayLabels(item, options));
27428
+ }
27429
+ const hydrated = relationLabel(value);
27430
+ if (hydrated !== null) return [hydrated];
27431
+ const raw = String(value);
27432
+ const match = options?.find((opt) => opt.value === raw);
27433
+ return [match ? match.label : raw];
27434
+ }
27435
+ function resolveRelationCellDisplay(value, options) {
27436
+ if (value === null || value === void 0 || value === "") return void 0;
27437
+ const isObjectShaped = typeof value === "object" && !(value instanceof Date) || Array.isArray(value) && value.some((v) => v !== null && typeof v === "object" && !(v instanceof Date));
27438
+ if (!isObjectShaped && !options) return void 0;
27439
+ const labels = relationDisplayLabels(value, options);
27440
+ return labels.length > 0 ? labels.join(", ") : void 0;
27441
+ }
27442
+ var init_relationLabel = __esm({
27443
+ "lib/relationLabel.ts"() {
27444
+ }
27445
+ });
27381
27446
  function downloadItemUrl(url, label) {
27382
27447
  const a = document.createElement("a");
27383
27448
  a.href = url;
@@ -28209,7 +28274,8 @@ function DataGrid({
28209
28274
  positionEvent,
28210
28275
  dndItemIdField,
28211
28276
  dndRoot,
28212
- look = "dense"
28277
+ look = "dense",
28278
+ relationsData
28213
28279
  }) {
28214
28280
  const eventBus = useEventBus();
28215
28281
  const { t } = useTranslate();
@@ -28399,6 +28465,10 @@ function DataGrid({
28399
28465
  );
28400
28466
  }
28401
28467
  const titleValue = getNestedValue(itemData, titleField?.name ?? "");
28468
+ const titleDisplay = resolveRelationCellDisplay(
28469
+ titleValue,
28470
+ titleField ? relationsData?.[titleField.name] : void 0
28471
+ ) ?? (titleValue !== void 0 && titleValue !== null ? String(titleValue) : void 0);
28402
28472
  return wrapDnd(
28403
28473
  /* @__PURE__ */ jsxs(
28404
28474
  Box,
@@ -28423,7 +28493,7 @@ function DataGrid({
28423
28493
  "img",
28424
28494
  {
28425
28495
  src: imgUrl,
28426
- alt: titleValue !== void 0 ? String(titleValue) : "",
28496
+ alt: titleDisplay ?? "",
28427
28497
  className: "w-full h-full object-cover",
28428
28498
  loading: "lazy"
28429
28499
  }
@@ -28438,18 +28508,18 @@ function DataGrid({
28438
28508
  onChange: () => toggleSelection(id),
28439
28509
  onClick: (e) => e.stopPropagation(),
28440
28510
  className: "w-4 h-4 mt-1 flex-shrink-0 accent-primary",
28441
- "aria-label": t("card.selectItem", { item: titleValue !== void 0 ? String(titleValue) : t("card.itemFallback") })
28511
+ "aria-label": t("card.selectItem", { item: titleDisplay ?? t("card.itemFallback") })
28442
28512
  }
28443
28513
  ),
28444
28514
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
28445
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
28515
+ titleDisplay !== void 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
28446
28516
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
28447
28517
  /* @__PURE__ */ jsx(
28448
28518
  Typography,
28449
28519
  {
28450
28520
  variant: titleField?.variant === "h3" ? "h3" : "h4",
28451
28521
  className: "font-semibold truncate min-w-0",
28452
- children: String(titleValue)
28522
+ children: titleDisplay
28453
28523
  }
28454
28524
  )
28455
28525
  ] }),
@@ -28458,7 +28528,7 @@ function DataGrid({
28458
28528
  if (val === void 0 || val === null || val === "") return null;
28459
28529
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
28460
28530
  field.icon && renderIconInput(field.icon, { size: "xs" }),
28461
- /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
28531
+ /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: resolveRelationCellDisplay(val, relationsData?.[field.name]) ?? humanizeEnumValue(formatValue(val, field.format)) })
28462
28532
  ] }, field.name);
28463
28533
  }) })
28464
28534
  ] }),
@@ -28497,7 +28567,7 @@ function DataGrid({
28497
28567
  bodyFields.filter((f3) => f3.variant === "caption" && f3.format !== "boolean").map((field) => {
28498
28568
  const value = getNestedValue(itemData, field.name);
28499
28569
  if (value === void 0 || value === null || value === "") return null;
28500
- return /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", className: "line-clamp-2", children: formatValue(value, field.format) }, field.name);
28570
+ return /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", className: "line-clamp-2", children: resolveRelationCellDisplay(value, relationsData?.[field.name]) ?? formatValue(value, field.format) }, field.name);
28501
28571
  }),
28502
28572
  /* @__PURE__ */ jsx(HStack, { gap: "md", className: "flex-wrap gap-y-1", children: bodyFields.filter((f3) => f3.variant !== "caption" || f3.format === "boolean").map((field) => {
28503
28573
  const value = getNestedValue(itemData, field.name);
@@ -28512,7 +28582,7 @@ function DataGrid({
28512
28582
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
28513
28583
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
28514
28584
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
28515
- /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
28585
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: resolveRelationCellDisplay(value, relationsData?.[field.name]) ?? formatValue(value, field.format) })
28516
28586
  ] }, field.name);
28517
28587
  }) })
28518
28588
  ] }) })
@@ -28556,6 +28626,7 @@ var init_DataGrid = __esm({
28556
28626
  "use client";
28557
28627
  init_cn();
28558
28628
  init_format();
28629
+ init_relationLabel();
28559
28630
  init_getNestedValue();
28560
28631
  init_useEventBus();
28561
28632
  init_Box();
@@ -28608,7 +28679,9 @@ function statusVariant3(value) {
28608
28679
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
28609
28680
  return "default";
28610
28681
  }
28611
- function formatValue2(value, format, boolLabels) {
28682
+ function formatValue2(value, format, boolLabels, relationOptions) {
28683
+ const relationDisplay = resolveRelationCellDisplay(value, relationOptions);
28684
+ if (relationDisplay !== void 0) return relationDisplay;
28612
28685
  if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
28613
28686
  const isNo = value === false || value === 0 || String(value) === "false";
28614
28687
  return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
@@ -28668,7 +28741,8 @@ function DataList({
28668
28741
  positionEvent,
28669
28742
  dndItemIdField,
28670
28743
  dndRoot,
28671
- look = "dense"
28744
+ look = "dense",
28745
+ relationsData
28672
28746
  }) {
28673
28747
  const eventBus = useEventBus();
28674
28748
  const { t } = useTranslate();
@@ -28856,13 +28930,13 @@ function DataList({
28856
28930
  return f3.variant === "badge" ? (
28857
28931
  // `format` applies here too — a boolean field badged
28858
28932
  // without it renders the raw "false" instead of "No".
28859
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(v)), children: formatValue2(v, f3.format) }, f3.name)
28933
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(v)), children: formatValue2(v, f3.format, void 0, relationsData?.[f3.name]) }, f3.name)
28860
28934
  ) : /* @__PURE__ */ jsx(
28861
28935
  Typography,
28862
28936
  {
28863
28937
  variant: "caption",
28864
28938
  className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
28865
- children: formatValue2(v, f3.format)
28939
+ children: formatValue2(v, f3.format, void 0, relationsData?.[f3.name])
28866
28940
  },
28867
28941
  f3.name
28868
28942
  );
@@ -28940,6 +29014,10 @@ function DataList({
28940
29014
  }
28941
29015
  const id = itemData.id || String(index);
28942
29016
  const titleValue = getNestedValue(itemData, titleField?.name ?? "");
29017
+ const titleDisplay = resolveRelationCellDisplay(
29018
+ titleValue,
29019
+ titleField ? relationsData?.[titleField.name] : void 0
29020
+ ) ?? (titleValue !== void 0 && titleValue !== null ? String(titleValue) : void 0);
28943
29021
  return wrapDnd(
28944
29022
  /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
28945
29023
  /* @__PURE__ */ jsxs(
@@ -28964,7 +29042,7 @@ function DataList({
28964
29042
  {
28965
29043
  variant: titleField?.variant === "h3" ? "h3" : "h4",
28966
29044
  className: cn("font-semibold truncate flex-1", isCompact && "text-sm"),
28967
- children: String(titleValue)
29045
+ children: titleDisplay
28968
29046
  }
28969
29047
  ),
28970
29048
  badgeFields.map((field) => {
@@ -28972,7 +29050,7 @@ function DataList({
28972
29050
  if (val === void 0 || val === null) return null;
28973
29051
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center flex-shrink-0", children: [
28974
29052
  field.icon && renderIconInput2(field.icon, { size: "xs" }),
28975
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(val)), children: formatValue2(val, field.format) })
29053
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(val)), children: formatValue2(val, field.format, void 0, relationsData?.[field.name]) })
28976
29054
  ] }, field.name);
28977
29055
  })
28978
29056
  ] }),
@@ -28993,7 +29071,7 @@ function DataList({
28993
29071
  ]
28994
29072
  }
28995
29073
  ),
28996
- /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
29074
+ /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }, relationsData?.[field.name]) })
28997
29075
  ] }, field.name);
28998
29076
  }) }),
28999
29077
  progressFields.map((field) => {
@@ -29073,6 +29151,7 @@ var init_DataList = __esm({
29073
29151
  "use client";
29074
29152
  init_cn();
29075
29153
  init_format();
29154
+ init_relationLabel();
29076
29155
  init_getNestedValue();
29077
29156
  init_useEventBus();
29078
29157
  init_Box();
@@ -32747,6 +32826,7 @@ var init_MathCanvas = __esm({
32747
32826
  "use client";
32748
32827
  init_useEventBus();
32749
32828
  init_perf();
32829
+ init_keyMapEvent();
32750
32830
  init_atoms();
32751
32831
  init_Stack();
32752
32832
  init_gameFonts();
@@ -32800,14 +32880,14 @@ var init_MathCanvas = __esm({
32800
32880
  useEffect(() => {
32801
32881
  if (!stableKeyMap && !stableKeyUpMap) return;
32802
32882
  const onDown = (e) => {
32803
- const ev = stableKeyMap?.[e.code];
32883
+ const ev = resolveKeyMapEvent(stableKeyMap, e);
32804
32884
  if (ev) {
32805
32885
  eventBus.emit(`UI:${ev}`, {});
32806
32886
  e.preventDefault();
32807
32887
  }
32808
32888
  };
32809
32889
  const onUp = (e) => {
32810
- const ev = stableKeyUpMap?.[e.code];
32890
+ const ev = resolveKeyMapEvent(stableKeyUpMap, e);
32811
32891
  if (ev) eventBus.emit(`UI:${ev}`, {});
32812
32892
  };
32813
32893
  window.addEventListener("keydown", onDown);
@@ -35223,33 +35303,6 @@ var init_Lightbox = __esm({
35223
35303
  Lightbox.displayName = "Lightbox";
35224
35304
  }
35225
35305
  });
35226
-
35227
- // lib/relationLabel.ts
35228
- function relationLabel(value) {
35229
- if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
35230
- return null;
35231
- for (const key of ["name", "title", "label"]) {
35232
- const candidate = value[key];
35233
- if (typeof candidate === "string" && candidate !== "") return candidate;
35234
- }
35235
- const id = value.id;
35236
- return id !== void 0 && id !== null ? String(id) : null;
35237
- }
35238
- function relationDisplayLabels(value, options) {
35239
- if (value === void 0 || value === null || value === "") return [];
35240
- if (Array.isArray(value)) {
35241
- return value.flatMap((item) => relationDisplayLabels(item, options));
35242
- }
35243
- const hydrated = relationLabel(value);
35244
- if (hydrated !== null) return [hydrated];
35245
- const raw = String(value);
35246
- const match = options?.find((opt) => opt.value === raw);
35247
- return [match ? match.label : raw];
35248
- }
35249
- var init_relationLabel = __esm({
35250
- "lib/relationLabel.ts"() {
35251
- }
35252
- });
35253
35306
  function renderIconInput3(icon, props) {
35254
35307
  return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
35255
35308
  }
@@ -35313,7 +35366,8 @@ function TableView({
35313
35366
  reorderEvent,
35314
35367
  positionEvent,
35315
35368
  dndItemIdField,
35316
- dndRoot
35369
+ dndRoot,
35370
+ relationsData
35317
35371
  }) {
35318
35372
  const eventBus = useEventBus();
35319
35373
  const { t } = useTranslate();
@@ -35398,7 +35452,7 @@ function TableView({
35398
35452
  const colFloors = React87__default.useMemo(
35399
35453
  () => colDefs.map((col) => {
35400
35454
  const longest = data.reduce((widest, row) => {
35401
- const cell = formatCell(asFieldValue(getNestedValue(row, col.field ?? col.key)), col.format);
35455
+ const cell = formatCell(asFieldValue(getNestedValue(row, col.field ?? col.key)), col.format, relationsData?.[col.field ?? col.key]);
35402
35456
  return Math.max(widest, cell.length);
35403
35457
  }, columnLabel(col).length);
35404
35458
  const chrome = col.format === "badge" ? BADGE_CHROME_CH : 0;
@@ -35498,9 +35552,11 @@ function TableView({
35498
35552
  col.className
35499
35553
  );
35500
35554
  if (col.format === "badge" && raw != null && raw !== "") {
35501
- 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);
35555
+ const relationDisplay = resolveRelationCellDisplay(raw, relationsData?.[col.field ?? col.key]);
35556
+ const label = relationDisplay ?? humanizeEnumValue(String(raw));
35557
+ 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);
35502
35558
  }
35503
- return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
35559
+ 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);
35504
35560
  }),
35505
35561
  hasActions && /* @__PURE__ */ jsxs(
35506
35562
  HStack,
@@ -35602,11 +35658,9 @@ var init_TableView = __esm({
35602
35658
  init_Menu();
35603
35659
  init_useDataDnd();
35604
35660
  tableViewLog = createLogger("almadar:ui:table-view");
35605
- formatCell = (value, format) => {
35606
- if (value !== null && value !== void 0 && typeof value === "object" && !(value instanceof Date)) {
35607
- const labels = relationDisplayLabels(value);
35608
- if (labels.length > 0) return labels.join(", ");
35609
- }
35661
+ formatCell = (value, format, relationOptions) => {
35662
+ const relationDisplay = resolveRelationCellDisplay(value, relationOptions);
35663
+ if (relationDisplay !== void 0) return relationDisplay;
35610
35664
  return formatValue(value, format);
35611
35665
  };
35612
35666
  MAX_MEASURED_COL_CH = 32;
@@ -44178,7 +44232,8 @@ function DataTable({
44178
44232
  headerActions,
44179
44233
  showTotal = true,
44180
44234
  className,
44181
- look = "dense"
44235
+ look = "dense",
44236
+ relationsData
44182
44237
  }) {
44183
44238
  const [openActionMenu, setOpenActionMenu] = useState(
44184
44239
  null
@@ -44473,6 +44528,11 @@ function DataTable({
44473
44528
  "data-column": String(col.key),
44474
44529
  className: "px-4 py-3 text-sm text-foreground whitespace-nowrap sm:whitespace-normal",
44475
44530
  children: col.render ? col.render(cellValue, row, rowIndex) : (() => {
44531
+ const relationDisplay = resolveRelationCellDisplay(
44532
+ cellValue,
44533
+ relationsData?.[String(col.key)]
44534
+ );
44535
+ if (relationDisplay !== void 0) return relationDisplay;
44476
44536
  const boolVal = asBooleanValue2(cellValue);
44477
44537
  if (boolVal !== null) {
44478
44538
  return boolVal ? /* @__PURE__ */ jsx(Badge, { variant: "success", children: t("common.yes") }) : /* @__PURE__ */ jsx(Badge, { variant: "neutral", children: t("common.no") });
@@ -44561,6 +44621,7 @@ var init_DataTable = __esm({
44561
44621
  init_cn();
44562
44622
  init_format();
44563
44623
  init_getNestedValue();
44624
+ init_relationLabel();
44564
44625
  init_atoms();
44565
44626
  init_Box();
44566
44627
  init_Stack();
@@ -52921,7 +52982,7 @@ function runTickFrame(entityId, orderedWriters, store) {
52921
52982
  }
52922
52983
  var log3 = createLogger("almadar:ui:effects:client-handlers");
52923
52984
  function createClientEffectHandlers(options) {
52924
- const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated } = options;
52985
+ const { eventBus, slotSetter, navigate, navigateBack, callService, liveEntity, persistDelegated, callServiceDelegated } = options;
52925
52986
  return {
52926
52987
  emit: (event, payload, source) => {
52927
52988
  const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
@@ -52934,6 +52995,7 @@ function createClientEffectHandlers(options) {
52934
52995
  // carries its outcome — tell the executor so the placeholder above is
52935
52996
  // never read as a denial (see `EffectHandlers.persistDelegated`).
52936
52997
  ...persistDelegated === true ? { persistDelegated: true } : {},
52998
+ ...callServiceDelegated === true ? { callServiceDelegated: true } : {},
52937
52999
  // @almadar/runtime EffectHandlers.set types value:unknown — should be FieldValue (upstream fix queued)
52938
53000
  set: ((_entityId, field, value) => {
52939
53001
  if (!liveEntity) {
@@ -53509,7 +53571,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53509
53571
  }, [traitStates]);
53510
53572
  const traitSnapshotDataRef = useRef(/* @__PURE__ */ new Map());
53511
53573
  const traitFieldStatesRef = useRef(/* @__PURE__ */ new Map());
53512
- const bridgeEchoPendingRef = useRef(/* @__PURE__ */ new Map());
53513
53574
  const bindingSnapshotsRef = useRef(/* @__PURE__ */ new Map());
53514
53575
  const bindingListenersRef = useRef(/* @__PURE__ */ new Map());
53515
53576
  const publishBindingSnapshot = useCallback((traitName, row) => {
@@ -53670,7 +53731,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
53670
53731
  // No local persistence adapter (bridge mode, or a syncOnly tick):
53671
53732
  // nothing persists client-side, the server owns the write and
53672
53733
  // reports it — never read the placeholder as a denial.
53673
- persistDelegated: (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0
53734
+ persistDelegated: (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0,
53735
+ // Bridge mode with no consumer-supplied callService: the server
53736
+ // runs every call-service and its cascade carries the result —
53737
+ // the client's mock must not also run one.
53738
+ callServiceDelegated: optionsRef.current?.callService === void 0 && (syncOnly ? void 0 : optionsRef.current?.persistence) === void 0
53674
53739
  });
53675
53740
  const persistence = syncOnly ? void 0 : optionsRef.current?.persistence;
53676
53741
  let handlers = clientHandlers;
@@ -54029,7 +54094,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54029
54094
  });
54030
54095
  const emittedByTrait = /* @__PURE__ */ new Map();
54031
54096
  const serverEffectResultsByTrait = /* @__PURE__ */ new Map();
54032
- bridgeEchoPendingRef.current.clear();
54033
54097
  for (const { traitName, result } of results) {
54034
54098
  const binding = bindingMap.get(traitName);
54035
54099
  const traitState = currentManager.getState(traitName);
@@ -54087,12 +54151,6 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54087
54151
  perfEnd("processEvent:executeAll", _perfT2);
54088
54152
  emittedByTrait.set(traitName, emittedDuringExec);
54089
54153
  serverEffectResultsByTrait.set(traitName, transitionServerEffectResults);
54090
- for (const emittedKey of emittedDuringExec) {
54091
- bridgeEchoPendingRef.current.set(
54092
- emittedKey,
54093
- (bridgeEchoPendingRef.current.get(emittedKey) ?? 0) + 1
54094
- );
54095
- }
54096
54154
  await reRenderCallsiteCaptureChildren(traitName, payload ?? {}, entityByTrait, stateLog);
54097
54155
  } else if (!result.executed) {
54098
54156
  if (result.guardResult === false) {
@@ -54188,7 +54246,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54188
54246
  if (orbital) dispatchedOrbitals.add(orbital);
54189
54247
  }
54190
54248
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
54191
- void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
54249
+ const locallyEmitted = Array.from(emittedByTrait.values()).flat();
54250
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait, locallyEmitted);
54192
54251
  }
54193
54252
  perfEnd("processEvent:total", _perfT0);
54194
54253
  perfEnd(`event:${normalizedEvent}`, _perfT0);
@@ -54255,14 +54314,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
54255
54314
  subscribedBusKeys.add(selfBusKey);
54256
54315
  crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
54257
54316
  const unsub = eventBus.on(selfBusKey, (event) => {
54258
- if (event.source && event.source.dispatched) {
54259
- const pendingEchoes = bridgeEchoPendingRef.current.get(eventKey) ?? 0;
54260
- if (pendingEchoes > 0) {
54261
- bridgeEchoPendingRef.current.set(eventKey, pendingEchoes - 1);
54262
- crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
54263
- return;
54264
- }
54265
- crossTraitLog.debug("self:fire-server-cascade", { traitName, busKey: selfBusKey, eventKey });
54317
+ if (event.source?.dispatched) {
54318
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
54319
+ return;
54266
54320
  }
54267
54321
  crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
54268
54322
  enqueueAndDrain(eventKey, event.payload, traitName, event.source?.tick, event.source?.trait);
@@ -54631,7 +54685,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
54631
54685
  [serverActiveTraits]
54632
54686
  );
54633
54687
  const uiSlots = useUISlots();
54634
- const onEventProcessed = useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
54688
+ const onEventProcessed = useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait, locallyEmitted) => {
54635
54689
  if (!bridge.connected || !orbitalNames?.length) return;
54636
54690
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
54637
54691
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -54645,7 +54699,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
54645
54699
  void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
54646
54700
  continue;
54647
54701
  }
54648
- void bridge.sendEvent(name, event, withActiveTraits(payload)).then(({ effects, meta }) => {
54702
+ void bridge.sendEvent(name, event, withActiveTraits(payload), void 0, void 0, locallyEmitted).then(({ effects, meta }) => {
54649
54703
  recordServerResponse(name, event, { ...meta, effectResults: effectResultsToTraces(meta.effectResults) });
54650
54704
  applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
54651
54705
  });
@@ -54716,6 +54770,30 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
54716
54770
  }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits, routeParams]);
54717
54771
  return /* @__PURE__ */ jsx(EntityBindingContext.Provider, { value: entityBindingSource, children });
54718
54772
  }
54773
+ function FitToBox({ children }) {
54774
+ const outerRef = useRef(null);
54775
+ const innerRef = useRef(null);
54776
+ const [scale, setScale] = useState(1);
54777
+ useEffect(() => {
54778
+ const outer = outerRef.current;
54779
+ const inner = innerRef.current;
54780
+ if (!outer || !inner) return;
54781
+ const update = () => {
54782
+ const sw = inner.scrollWidth;
54783
+ const sh = inner.scrollHeight;
54784
+ const cw = outer.clientWidth;
54785
+ const ch = outer.clientHeight;
54786
+ if (!sw || !sh || !cw || !ch) return;
54787
+ setScale(Math.min(1, cw / sw, ch / sh));
54788
+ };
54789
+ update();
54790
+ const ro = new ResizeObserver(update);
54791
+ ro.observe(outer);
54792
+ ro.observe(inner);
54793
+ return () => ro.disconnect();
54794
+ }, []);
54795
+ 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 }) });
54796
+ }
54719
54797
  function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData, pageName, routeParams, onNavigate, onNavigateBack, onLocalFallback, persistence }) {
54720
54798
  const { traits: traits2, allEntities, allTraits, ir } = useResolvedSchema(schema, pageName);
54721
54799
  const allPageTraits = useMemo(() => {
@@ -54901,6 +54979,7 @@ function OrbPreview({
54901
54979
  getAccessToken,
54902
54980
  initialPagePath,
54903
54981
  isolated = false,
54982
+ fit = false,
54904
54983
  user = null
54905
54984
  }) {
54906
54985
  if (serverUrl && transport) {
@@ -55057,7 +55136,7 @@ function OrbPreview({
55057
55136
  Box,
55058
55137
  {
55059
55138
  ref: containerRef,
55060
- className: `overflow-auto border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
55139
+ className: `${fit ? "overflow-hidden" : "overflow-auto"} border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
55061
55140
  style: { height },
55062
55141
  children: [
55063
55142
  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." }) }),
@@ -55070,7 +55149,22 @@ function OrbPreview({
55070
55149
  storageKey: `almadar:navstack:${parseResult.schema.name ?? "preview"}`,
55071
55150
  children: [
55072
55151
  /* @__PURE__ */ jsx(NavStackRefBridge, { apiRef: navStackRef }),
55073
- /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
55152
+ /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, isolated, user, children: /* @__PURE__ */ jsx(UISlotProvider, { children: fit ? /* @__PURE__ */ jsx(FitToBox, { children: /* @__PURE__ */ jsx(
55153
+ SchemaRunner,
55154
+ {
55155
+ schema: parseResult.schema,
55156
+ serverUrl,
55157
+ transport,
55158
+ getAccessToken,
55159
+ mockData: effectiveMockData,
55160
+ pageName: currentPage,
55161
+ routeParams,
55162
+ onNavigate: handleNavigateEffect,
55163
+ onNavigateBack: handleNavigateBack,
55164
+ onLocalFallback: handleLocalFallback,
55165
+ persistence
55166
+ }
55167
+ ) }) : /* @__PURE__ */ jsx(
55074
55168
  SchemaRunner,
55075
55169
  {
55076
55170
  schema: parseResult.schema,
@@ -55101,7 +55195,8 @@ function BrowserPlayground({
55101
55195
  initialPagePath,
55102
55196
  height,
55103
55197
  className,
55104
- paused
55198
+ paused,
55199
+ fit
55105
55200
  }) {
55106
55201
  const [runtime] = useState(
55107
55202
  () => new OrbitalServerRuntime({ mode, debug: false })
@@ -55163,6 +55258,7 @@ function BrowserPlayground({
55163
55258
  initialPagePath,
55164
55259
  height,
55165
55260
  className,
55261
+ fit,
55166
55262
  isolated: true
55167
55263
  }
55168
55264
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "6.15.0",
3
+ "version": "6.17.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -118,11 +118,11 @@
118
118
  "access": "public"
119
119
  },
120
120
  "dependencies": {
121
- "@almadar/core": "^10.91.0",
121
+ "@almadar/core": "^10.92.0",
122
122
  "@almadar/evaluator": "^2.45.0",
123
123
  "@almadar/logger": "^1.12.0",
124
- "@almadar/runtime": "^6.76.0",
125
- "@almadar/std": "^16.212.0",
124
+ "@almadar/runtime": "^6.78.0",
125
+ "@almadar/std": "^16.213.0",
126
126
  "@almadar/syntax": "^1.17.0",
127
127
  "@dnd-kit/core": "^6.3.1",
128
128
  "@dnd-kit/sortable": "^10.0.0",