@almadar/ui 5.158.0 → 5.160.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.
@@ -360,7 +360,16 @@ function isPlainObject(value) {
360
360
  if (typeof value === "function") return false;
361
361
  return true;
362
362
  }
363
+ function isEvaluatorResolvedData(value) {
364
+ return evaluatorResolvedData.has(value);
365
+ }
366
+ function brandResolved(value) {
367
+ if (value !== null && typeof value === "object" && !React87__namespace.default.isValidElement(value) && !(value instanceof Date)) {
368
+ resolvedMarkerFree.add(value);
369
+ }
370
+ }
363
371
  function subtreeHasMarker(value) {
372
+ if (resolvedMarkerFree.has(value)) return false;
364
373
  const cached = markerPresenceCache.get(value);
365
374
  if (cached !== void 0) return cached;
366
375
  let found = false;
@@ -382,10 +391,23 @@ function subtreeHasMarker(value) {
382
391
  }
383
392
  function walkValue(value, scopeTrait, entity, config, state) {
384
393
  if (core.isRenderBindingMarker(value)) {
385
- return { resolved: resolveMarkerExpression(value.expression, entity, config, state), changed: true };
394
+ const cached = markerResolutionCache.get(value);
395
+ if (cached !== void 0 && cached.entity === entity && cached.config === config && cached.state === state) {
396
+ return { resolved: cached.resolved, changed: false };
397
+ }
398
+ const resolved = resolveMarkerExpression(value.expression, entity, config, state);
399
+ markerResolutionCache.set(value, { entity, config, state, resolved });
400
+ if (resolved !== null && typeof resolved === "object" && !React87__namespace.default.isValidElement(resolved) && !(resolved instanceof Date)) {
401
+ resolvedMarkerFree.add(resolved);
402
+ evaluatorResolvedData.add(resolved);
403
+ }
404
+ return { resolved, changed: true };
386
405
  }
387
406
  if (Array.isArray(value)) {
388
- if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
407
+ if (!subtreeHasMarker(value)) {
408
+ brandResolved(value);
409
+ return { resolved: value, changed: false };
410
+ }
389
411
  const out = [];
390
412
  let changed = false;
391
413
  for (const item of value) {
@@ -400,10 +422,14 @@ function walkValue(value, scopeTrait, entity, config, state) {
400
422
  out.push(resolved);
401
423
  if (itemChanged) changed = true;
402
424
  }
425
+ brandResolved(out);
403
426
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
404
427
  }
405
428
  if (isPlainObject(value)) {
406
- if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
429
+ if (!subtreeHasMarker(value)) {
430
+ brandResolved(value);
431
+ return { resolved: value, changed: false };
432
+ }
407
433
  const sourceTrait = value._sourceTrait;
408
434
  if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
409
435
  return { resolved: value, changed: false };
@@ -415,11 +441,13 @@ function walkValue(value, scopeTrait, entity, config, state) {
415
441
  out[key] = resolved;
416
442
  if (itemChanged) changed = true;
417
443
  }
444
+ brandResolved(out);
418
445
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
419
446
  }
420
447
  return { resolved: value, changed: false };
421
448
  }
422
449
  function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
450
+ if (resolvedMarkerFree.has(props)) return props;
423
451
  const out = {};
424
452
  let changed = false;
425
453
  for (const [key, value] of Object.entries(props)) {
@@ -427,13 +455,17 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
427
455
  out[key] = resolved;
428
456
  if (propChanged) changed = true;
429
457
  }
458
+ brandResolved(out);
430
459
  return changed ? out : props;
431
460
  }
432
- var markerPresenceCache;
461
+ var markerPresenceCache, markerResolutionCache, resolvedMarkerFree, evaluatorResolvedData;
433
462
  var init_resolve_render_bindings = __esm({
434
463
  "lib/resolve-render-bindings.ts"() {
435
464
  "use client";
436
465
  markerPresenceCache = /* @__PURE__ */ new WeakMap();
466
+ markerResolutionCache = /* @__PURE__ */ new WeakMap();
467
+ resolvedMarkerFree = /* @__PURE__ */ new WeakSet();
468
+ evaluatorResolvedData = /* @__PURE__ */ new WeakSet();
437
469
  }
438
470
  });
439
471
  function cn(...inputs) {
@@ -11867,6 +11899,10 @@ var init_molecules = __esm({
11867
11899
  init_puzzleObject();
11868
11900
  }
11869
11901
  });
11902
+ var init_perf = __esm({
11903
+ "lib/perf.ts"() {
11904
+ }
11905
+ });
11870
11906
  function themeBodyFont(el) {
11871
11907
  if (typeof getComputedStyle !== "function") return "system-ui, sans-serif";
11872
11908
  const v = getComputedStyle(el).getPropertyValue("--font-family-body").trim();
@@ -12215,6 +12251,7 @@ var init_LearningCanvas = __esm({
12215
12251
  "components/learning/atoms/LearningCanvas.tsx"() {
12216
12252
  "use client";
12217
12253
  init_cn();
12254
+ init_perf();
12218
12255
  init_useEventBus();
12219
12256
  DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
12220
12257
  TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
@@ -12258,6 +12295,7 @@ var init_LearningCanvas = __esm({
12258
12295
  return [...shapes, ...traceOut, ...readoutOut];
12259
12296
  }, [shapes, traces, readouts, width, height]);
12260
12297
  const draw = React87.useCallback(() => {
12298
+ const _perfT = ui.perfStart("learningcanvas:paint");
12261
12299
  const canvas = canvasRef.current;
12262
12300
  if (!canvas) return;
12263
12301
  const ctx = canvas.getContext("2d");
@@ -12279,6 +12317,7 @@ var init_LearningCanvas = __esm({
12279
12317
  for (const shape of derivedShapes) {
12280
12318
  if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
12281
12319
  }
12320
+ ui.perfEnd("learningcanvas:paint", _perfT);
12282
12321
  }, [width, height, backgroundColor, derivedShapes]);
12283
12322
  React87.useEffect(() => {
12284
12323
  draw();
@@ -26575,8 +26614,8 @@ function DataGrid({
26575
26614
  }
26576
26615
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
26577
26616
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
26578
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
26579
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: formatValue(value, field.format) })
26617
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
26618
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
26580
26619
  ] }, field.name);
26581
26620
  }) })
26582
26621
  ] }) })
@@ -27004,7 +27043,11 @@ function DataList({
27004
27043
  Box,
27005
27044
  {
27006
27045
  className: cn(
27007
- "group flex items-center gap-4 transition-all duration-fast",
27046
+ // items-start, not items-center: a multi-line row (title + meta +
27047
+ // progress) centred its action cluster in the vertical middle, so
27048
+ // the buttons floated between the meta fields instead of anchoring
27049
+ // to the title they act on (U-DATALIST-ACTIONS-FLOAT-MID-ROW).
27050
+ "group flex items-start gap-4 transition-all duration-fast",
27008
27051
  isCompact ? "px-4 py-2" : "px-6 py-4",
27009
27052
  "hover:bg-muted/80",
27010
27053
  !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
@@ -27035,11 +27078,19 @@ function DataList({
27035
27078
  if (value === void 0 || value === null || value === "") return null;
27036
27079
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
27037
27080
  field.icon && renderIconInput2(field.icon, { size: "xs", className: "text-muted-foreground" }),
27038
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "secondary", children: [
27039
- field.label ?? fieldLabel3(field.name),
27040
- ":"
27041
- ] }),
27042
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
27081
+ /* @__PURE__ */ jsxRuntime.jsxs(
27082
+ Typography,
27083
+ {
27084
+ variant: "caption",
27085
+ color: "secondary",
27086
+ className: cn(field.format !== "boolean" && "sr-only"),
27087
+ children: [
27088
+ field.label ?? fieldLabel3(field.name),
27089
+ ":"
27090
+ ]
27091
+ }
27092
+ ),
27093
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
27043
27094
  ] }, field.name);
27044
27095
  }) }),
27045
27096
  progressFields.map((field) => {
@@ -27058,7 +27109,7 @@ function DataList({
27058
27109
  ]
27059
27110
  }
27060
27111
  ),
27061
- isCard && !isLast && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mx-6 border-b border-border/40" })
27112
+ (isCard || isCompact) && !isLast && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("border-b border-border/40", isCompact ? "mx-4" : "mx-6") })
27062
27113
  ] }, id)
27063
27114
  );
27064
27115
  };
@@ -27068,7 +27119,13 @@ function DataList({
27068
27119
  {
27069
27120
  className: cn(
27070
27121
  isCard && "bg-card rounded-xl border border-border shadow-elevation-dialog overflow-hidden",
27071
- !isCard && gapClass,
27122
+ // `gap-*` is inert on a block container, and Box only emits a display
27123
+ // class when its `display` prop is set — so every non-card list had
27124
+ // been asking for a gap that CSS silently dropped. flex-col makes it
27125
+ // real. `compact` keeps gap-0 on purpose: it separates with the row
27126
+ // divider above instead, never both (Almadar_UI_Beauty.md 6).
27127
+ !isCard && "flex flex-col",
27128
+ !isCard && !isCompact && gapClass,
27072
27129
  listLookStyles[look],
27073
27130
  className
27074
27131
  ),
@@ -30683,6 +30740,7 @@ var init_MathCanvas = __esm({
30683
30740
  "components/learning/molecules/MathCanvas.tsx"() {
30684
30741
  "use client";
30685
30742
  init_useEventBus();
30743
+ init_perf();
30686
30744
  init_atoms();
30687
30745
  init_Stack();
30688
30746
  init_LearningCanvas();
@@ -30748,6 +30806,7 @@ var init_MathCanvas = __esm({
30748
30806
  };
30749
30807
  }, [stableKeyMap, stableKeyUpMap, eventBus]);
30750
30808
  const derivedShapes = React87.useMemo(() => {
30809
+ const _perfT = ui.perfStart("mathcanvas:derive");
30751
30810
  const out = [];
30752
30811
  const margin = 24;
30753
30812
  const plotW = width - margin * 2;
@@ -30991,6 +31050,7 @@ var init_MathCanvas = __esm({
30991
31050
  }
30992
31051
  }
30993
31052
  out.push(...shapes);
31053
+ ui.perfEnd("mathcanvas:derive", _perfT);
30994
31054
  return out;
30995
31055
  }, [
30996
31056
  width,
@@ -32254,12 +32314,12 @@ var init_MapView = __esm({
32254
32314
  shadowSize: [41, 41]
32255
32315
  });
32256
32316
  L.Marker.prototype.options.icon = defaultIcon;
32257
- const { useEffect: useEffect67, useRef: useRef64, useCallback: useCallback97, useState: useState95 } = React87__namespace.default;
32317
+ const { useEffect: useEffect67, useRef: useRef65, useCallback: useCallback97, useState: useState95 } = React87__namespace.default;
32258
32318
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32259
32319
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32260
32320
  function MapUpdater({ centerLat, centerLng, zoom }) {
32261
32321
  const map = useMap();
32262
- const prevRef = useRef64({ centerLat, centerLng, zoom });
32322
+ const prevRef = useRef65({ centerLat, centerLng, zoom });
32263
32323
  useEffect67(() => {
32264
32324
  const prev = prevRef.current;
32265
32325
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
@@ -37344,7 +37404,7 @@ var init_RichBlockEditor = __esm({
37344
37404
  {
37345
37405
  variant: "bordered",
37346
37406
  padding: "none",
37347
- className: cn("flex flex-col", className),
37407
+ className: cn("flex flex-col text-card-foreground", className),
37348
37408
  children: [
37349
37409
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
37350
37410
  Box,
@@ -42572,6 +42632,7 @@ var init_DetailPanel = __esm({
42572
42632
  "use client";
42573
42633
  init_atoms();
42574
42634
  init_Box();
42635
+ init_Input();
42575
42636
  init_Stack();
42576
42637
  init_SimpleGrid();
42577
42638
  init_Menu();
@@ -42587,6 +42648,7 @@ var init_DetailPanel = __esm({
42587
42648
  ReactMarkdown2 = React87.lazy(() => import('react-markdown'));
42588
42649
  DetailPanel = ({
42589
42650
  title: propTitle,
42651
+ onTitleCommit,
42590
42652
  subtitle,
42591
42653
  status,
42592
42654
  avatar,
@@ -42608,6 +42670,7 @@ var init_DetailPanel = __esm({
42608
42670
  }) => {
42609
42671
  const eventBus = useEventBus();
42610
42672
  const { t } = hooks.useTranslate();
42673
+ const [titleDraft, setTitleDraft] = React87__namespace.default.useState(null);
42611
42674
  const isFieldDefArray = (arr) => {
42612
42675
  if (!arr || arr.length === 0) return false;
42613
42676
  const first = arr[0];
@@ -42828,6 +42891,50 @@ var init_DetailPanel = __esm({
42828
42891
  }),
42829
42892
  status && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42830
42893
  ] });
42894
+ const commitTitle = () => {
42895
+ if (!onTitleCommit) return;
42896
+ const next = (titleDraft ?? "").trim();
42897
+ setTitleDraft(null);
42898
+ if (!next || next === title) return;
42899
+ onTitleCommit(next, normalizedData?.id !== void 0 ? String(normalizedData.id) : "");
42900
+ };
42901
+ const titleNode = onTitleCommit && titleDraft !== null ? /* @__PURE__ */ jsxRuntime.jsx(
42902
+ Input,
42903
+ {
42904
+ value: titleDraft,
42905
+ autoFocus: true,
42906
+ "aria-label": t("common.title"),
42907
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
42908
+ onChange: (e) => setTitleDraft(e.target.value),
42909
+ onBlur: commitTitle,
42910
+ onKeyDown: (e) => {
42911
+ if (e.key === "Enter") {
42912
+ e.preventDefault();
42913
+ commitTitle();
42914
+ } else if (e.key === "Escape") {
42915
+ e.preventDefault();
42916
+ setTitleDraft(null);
42917
+ }
42918
+ },
42919
+ "data-testid": "detail-title-input"
42920
+ }
42921
+ ) : onTitleCommit ? /* @__PURE__ */ jsxRuntime.jsx(
42922
+ Box,
42923
+ {
42924
+ role: "button",
42925
+ tabIndex: 0,
42926
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
42927
+ onClick: () => setTitleDraft(title ?? ""),
42928
+ onKeyDown: (e) => {
42929
+ if (e.key === "Enter" || e.key === " ") {
42930
+ e.preventDefault();
42931
+ setTitleDraft(title ?? "");
42932
+ }
42933
+ },
42934
+ "data-testid": "detail-title-editable",
42935
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" })
42936
+ }
42937
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" });
42831
42938
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
42832
42939
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
42833
42940
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -42847,7 +42954,7 @@ var init_DetailPanel = __esm({
42847
42954
  avatar,
42848
42955
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42849
42956
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42850
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42957
+ titleNode,
42851
42958
  statusBadges
42852
42959
  ] }),
42853
42960
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -42947,16 +43054,11 @@ var init_DetailPanel = __esm({
42947
43054
  footer
42948
43055
  ] })
42949
43056
  ] }) });
42950
- return /* @__PURE__ */ jsxRuntime.jsx(
42951
- Box,
42952
- {
42953
- className: cn(
42954
- slideOver && "fixed inset-y-0 right-0 w-full max-w-2xl bg-card shadow-lg z-50 overflow-y-auto p-6",
42955
- className
42956
- ),
42957
- children: content
42958
- }
42959
- );
43057
+ if (!slideOver) {
43058
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, children: content });
43059
+ }
43060
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("fixed inset-y-0 right-0 w-full max-w-2xl bg-card shadow-lg z-50 overflow-y-auto p-6", className), children: content });
43061
+ return typeof document === "undefined" ? panel : reactDom.createPortal(panel, document.body);
42960
43062
  };
42961
43063
  DetailPanel.displayName = "DetailPanel";
42962
43064
  }
@@ -43408,7 +43510,7 @@ var init_Form = __esm({
43408
43510
  });
43409
43511
  debug(
43410
43512
  "forms",
43411
- `Calculation triggered: ${calc.variableName} = ${value}`
43513
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
43412
43514
  );
43413
43515
  }
43414
43516
  });
@@ -49888,6 +49990,7 @@ function isPlainConfigObject(value) {
49888
49990
  return proto === Object.prototype || proto === null;
49889
49991
  }
49890
49992
  function subtreeHasTraitRef(value) {
49993
+ if (isEvaluatorResolvedData(value)) return false;
49891
49994
  const cached = traitRefPresenceCache.get(value);
49892
49995
  if (cached !== void 0) return cached;
49893
49996
  let found = false;
@@ -49960,6 +50063,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
49960
50063
  };
49961
50064
  rendered[key] = /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content: childContent, onDismiss });
49962
50065
  } else if (Array.isArray(value)) {
50066
+ if (!isEvaluatorResolvedData(value) && value.some((el) => isPatternConfig(el))) ; else if (!subtreeHasTraitRef(value)) {
50067
+ rendered[key] = value;
50068
+ continue;
50069
+ }
49963
50070
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
49964
50071
  rendered[key] = value.map((item, i) => {
49965
50072
  const el = item;
@@ -51401,6 +51508,53 @@ function useNavigationId2() {
51401
51508
 
51402
51509
  // providers/ServerBridge.tsx
51403
51510
  init_useEventBus();
51511
+
51512
+ // lib/tick-send-relay.ts
51513
+ function createTickSendRelay(send) {
51514
+ const lanes = /* @__PURE__ */ new Map();
51515
+ const flush = (key, lane) => {
51516
+ const next = lane.pending;
51517
+ lane.pending = void 0;
51518
+ if (next === void 0) {
51519
+ lane.inFlight = false;
51520
+ return;
51521
+ }
51522
+ lane.inFlight = true;
51523
+ void send(key, next).catch(() => void 0).then(() => flush(key, lane));
51524
+ };
51525
+ return {
51526
+ send(key, value) {
51527
+ const lane = lanes.get(key) ?? { inFlight: false };
51528
+ lanes.set(key, lane);
51529
+ if (lane.inFlight) {
51530
+ lane.pending = value;
51531
+ return;
51532
+ }
51533
+ lane.pending = value;
51534
+ flush(key, lane);
51535
+ },
51536
+ clear() {
51537
+ for (const lane of lanes.values()) {
51538
+ lane.pending = void 0;
51539
+ }
51540
+ }
51541
+ };
51542
+ }
51543
+
51544
+ // lib/command-send-pump.ts
51545
+ function createCommandSendPump() {
51546
+ let tail = Promise.resolve();
51547
+ return {
51548
+ enqueue(job) {
51549
+ const result = tail.then(job);
51550
+ tail = result.then(
51551
+ () => void 0,
51552
+ () => void 0
51553
+ );
51554
+ return result;
51555
+ }
51556
+ };
51557
+ }
51404
51558
  var xOrbitalLog = logger.createLogger("almadar:runtime:cross-orbital");
51405
51559
  var serverBridgeLog = logger.createLogger("almadar:ui:server-bridge");
51406
51560
  function reEmitServerEvent(eventBus, emitted, origin) {
@@ -51535,99 +51689,118 @@ function ServerBridgeProvider({
51535
51689
  async () => transport.unregister(),
51536
51690
  [transport]
51537
51691
  );
51692
+ const tickRelay = React87.useMemo(
51693
+ () => createTickSendRelay(async (_key, snap) => {
51694
+ await transport.sendEvent(snap.orbitalName, snap.event, snap.payload, getTabClientId(), snap.tick, snap.sourceTrait);
51695
+ }),
51696
+ [transport]
51697
+ );
51698
+ React87.useEffect(() => () => tickRelay.clear(), [tickRelay]);
51699
+ const commandPump = React87.useMemo(createCommandSendPump, []);
51700
+ const disposedRef = React87.useRef(false);
51701
+ React87.useEffect(() => {
51702
+ disposedRef.current = false;
51703
+ return () => {
51704
+ disposedRef.current = true;
51705
+ };
51706
+ }, []);
51538
51707
  const sendEvent = React87.useCallback(async (orbitalName, event, payload, tick, sourceTrait) => {
51539
51708
  const emptyMeta = { success: false, clientEffects: 0, dataEntities: {}, emittedEvents: [] };
51540
51709
  if (!connected) return { effects: [], meta: emptyMeta };
51541
- try {
51542
- const result = await transport.sendEvent(orbitalName, event, payload, getTabClientId(), tick, sourceTrait);
51543
- const effects = [];
51544
- if (tick !== void 0) {
51545
- return { effects, meta: { ...emptyMeta, success: !!result.success, error: result.error } };
51546
- }
51547
- const responseData = result.data || {};
51548
- const dataEntities = {};
51549
- for (const [entityName, records] of Object.entries(responseData)) {
51550
- dataEntities[entityName] = Array.isArray(records) ? records.length : 0;
51551
- }
51552
- const meta = {
51553
- success: !!result.success,
51554
- clientEffects: result.clientEffects?.length ?? 0,
51555
- dataEntities,
51556
- data: responseData,
51557
- emittedEvents: result.emittedEvents?.map((e) => e.event) ?? [],
51558
- error: result.error
51559
- };
51560
- if (result.success) {
51561
- const tagged = result.clientEffectsByTrait;
51562
- const tuples = tagged ? tagged.map((entry) => ({ effect: entry.effect, traitName: entry.traitName })) : (result.clientEffects ?? []).map((eff) => ({ effect: eff }));
51563
- for (const { effect, traitName } of tuples) {
51564
- const effectType = effect[0];
51565
- if (effectType === "render-ui") {
51566
- const slot = effect[1];
51567
- const pattern = effect[2];
51568
- effects.push({
51569
- type: "render-ui",
51570
- slot,
51571
- pattern: pattern !== null && typeof pattern === "object" ? pattern : void 0,
51572
- traitName
51573
- });
51574
- } else if (effectType === "navigate") {
51575
- const route = effect[1];
51576
- const rawParams = effect[2];
51577
- const rawOptions = effect[3];
51578
- const optionsObj = rawOptions !== null && rawOptions !== void 0 && typeof rawOptions === "object" && !Array.isArray(rawOptions) ? rawOptions : void 0;
51579
- const crumb = typeof optionsObj?.crumb === "string" ? optionsObj.crumb : void 0;
51580
- effects.push({
51581
- type: "navigate",
51582
- route,
51583
- params: rawParams !== null && rawParams !== void 0 && typeof rawParams === "object" && !Array.isArray(rawParams) ? rawParams : void 0,
51584
- crumb,
51585
- traitName
51586
- });
51587
- } else if (effectType === "navigate-back") {
51588
- effects.push({ type: "navigate-back", traitName });
51589
- } else if (effectType === "notify") {
51590
- const message = effect[1];
51591
- effects.push({ type: "notify", message: typeof message === "string" ? message : void 0, traitName });
51592
- }
51593
- }
51594
- if (result.emittedEvents) {
51595
- for (const emitted of result.emittedEvents) {
51596
- if (emitted.event === event) continue;
51597
- reEmitServerEvent(
51598
- eventBus,
51599
- { ...emitted, source: { ...emitted.source, dispatched: true } },
51600
- orbitalName
51601
- );
51710
+ if (tick !== void 0) {
51711
+ tickRelay.send(`${orbitalName}:${event}`, { orbitalName, event, payload, tick, sourceTrait });
51712
+ return { effects: [], meta: { ...emptyMeta, success: true } };
51713
+ }
51714
+ return commandPump.enqueue(async () => {
51715
+ if (disposedRef.current) return { effects: [], meta: emptyMeta };
51716
+ try {
51717
+ const result = await transport.sendEvent(orbitalName, event, payload, getTabClientId(), tick, sourceTrait);
51718
+ const effects = [];
51719
+ const responseData = result.data || {};
51720
+ const dataEntities = {};
51721
+ for (const [entityName, records] of Object.entries(responseData)) {
51722
+ dataEntities[entityName] = Array.isArray(records) ? records.length : 0;
51723
+ }
51724
+ const meta = {
51725
+ success: !!result.success,
51726
+ clientEffects: result.clientEffects?.length ?? 0,
51727
+ dataEntities,
51728
+ data: responseData,
51729
+ emittedEvents: result.emittedEvents?.map((e) => e.event) ?? [],
51730
+ error: result.error
51731
+ };
51732
+ if (result.success) {
51733
+ const tagged = result.clientEffectsByTrait;
51734
+ const tuples = tagged ? tagged.map((entry) => ({ effect: entry.effect, traitName: entry.traitName })) : (result.clientEffects ?? []).map((eff) => ({ effect: eff }));
51735
+ for (const { effect, traitName } of tuples) {
51736
+ const effectType = effect[0];
51737
+ if (effectType === "render-ui") {
51738
+ const slot = effect[1];
51739
+ const pattern = effect[2];
51740
+ effects.push({
51741
+ type: "render-ui",
51742
+ slot,
51743
+ pattern: pattern !== null && typeof pattern === "object" ? pattern : void 0,
51744
+ traitName
51745
+ });
51746
+ } else if (effectType === "navigate") {
51747
+ const route = effect[1];
51748
+ const rawParams = effect[2];
51749
+ const rawOptions = effect[3];
51750
+ const optionsObj = rawOptions !== null && rawOptions !== void 0 && typeof rawOptions === "object" && !Array.isArray(rawOptions) ? rawOptions : void 0;
51751
+ const crumb = typeof optionsObj?.crumb === "string" ? optionsObj.crumb : void 0;
51752
+ effects.push({
51753
+ type: "navigate",
51754
+ route,
51755
+ params: rawParams !== null && rawParams !== void 0 && typeof rawParams === "object" && !Array.isArray(rawParams) ? rawParams : void 0,
51756
+ crumb,
51757
+ traitName
51758
+ });
51759
+ } else if (effectType === "navigate-back") {
51760
+ effects.push({ type: "navigate-back", traitName });
51761
+ } else if (effectType === "notify") {
51762
+ const message = effect[1];
51763
+ effects.push({ type: "notify", message: typeof message === "string" ? message : void 0, traitName });
51764
+ }
51765
+ }
51766
+ if (result.emittedEvents) {
51767
+ for (const emitted of result.emittedEvents) {
51768
+ if (emitted.event === event) continue;
51769
+ reEmitServerEvent(
51770
+ eventBus,
51771
+ { ...emitted, source: { ...emitted.source, dispatched: true } },
51772
+ orbitalName
51773
+ );
51774
+ }
51602
51775
  }
51776
+ } else if (result.error) {
51777
+ xOrbitalLog.warn("response:fail", {
51778
+ orbital: orbitalName,
51779
+ event,
51780
+ error: result.error
51781
+ });
51603
51782
  }
51604
- } else if (result.error) {
51605
- xOrbitalLog.warn("response:fail", {
51606
- orbital: orbitalName,
51607
- event,
51608
- error: result.error
51609
- });
51610
- }
51611
- return { effects, meta };
51612
- } catch (err) {
51613
- const msg = err instanceof Error ? err.message : String(err);
51614
- if (err instanceof TypeError) {
51615
- xOrbitalLog.warn("response:network", {
51616
- orbital: orbitalName,
51617
- event,
51618
- error: msg,
51619
- reason: "peer endpoint unreachable (expected in standalone single-orbital mode)"
51620
- });
51621
- } else {
51622
- xOrbitalLog.error("response:network", {
51623
- orbital: orbitalName,
51624
- event,
51625
- error: msg
51626
- });
51783
+ return { effects, meta };
51784
+ } catch (err) {
51785
+ const msg = err instanceof Error ? err.message : String(err);
51786
+ if (err instanceof TypeError) {
51787
+ xOrbitalLog.warn("response:network", {
51788
+ orbital: orbitalName,
51789
+ event,
51790
+ error: msg,
51791
+ reason: "peer endpoint unreachable (expected in standalone single-orbital mode)"
51792
+ });
51793
+ } else {
51794
+ xOrbitalLog.error("response:network", {
51795
+ orbital: orbitalName,
51796
+ event,
51797
+ error: msg
51798
+ });
51799
+ }
51800
+ return { effects: [], meta: { ...emptyMeta, error: msg } };
51627
51801
  }
51628
- return { effects: [], meta: { ...emptyMeta, error: msg } };
51629
- }
51630
- }, [connected, transport, eventBus]);
51802
+ });
51803
+ }, [connected, transport, eventBus, tickRelay, commandPump]);
51631
51804
  React87.useEffect(() => {
51632
51805
  if (!schema) return;
51633
51806
  let cancelled = false;
@@ -3,7 +3,7 @@ import { EntityRow, SExpr } from '@almadar/core';
3
3
  export { ANONYMOUS_USER } from '@almadar/core';
4
4
  import { U as UIThemeDefinition, j as UserData } from '../UserContext-g_LcDiGN.cjs';
5
5
  export { a as CurrentPagePathContext, b as CurrentPagePathProvider, c as CurrentPagePathProviderProps, d as DesignThemeProvider, O as OrbitalThemeProvider, e as OrbitalThemeProviderProps, h as UserContext, i as UserContextValue, k as UserProvider, l as UserProviderProps, u as useCurrentPagePath, m as useDesignTheme, n as useHasPermission, o as useHasRole, q as useUser, r as useUserForEvaluation } from '../UserContext-g_LcDiGN.cjs';
6
- export { E as EntityBindingContext, a as EntityBindingSource, b as EntitySchemaContextValue, c as EntitySchemaProvider, d as EntitySchemaProviderProps, S as SendEventResult, e as ServerBridgeContextValue, f as ServerBridgeProvider, g as ServerBridgeProviderProps, h as ServerBridgeTransport, i as ServerClientEffect, j as ServerResponseMeta, T as TraitContext, k as TraitContextValue, l as TraitInstance, m as TraitProvider, n as TraitProviderProps, u as useEntityBindingSnapshot, o as useEntitySchema, p as useEntitySchemaOptional, q as useServerBridge, r as useTrait, s as useTraitContext } from '../EntityBindingContext-Bn3ePJQC.cjs';
6
+ export { o as EntityBindingContext, E as EntityBindingSource, a as EntitySchemaContextValue, b as EntitySchemaProvider, c as EntitySchemaProviderProps, p as SendEventResult, d as ServerBridgeContextValue, e as ServerBridgeProvider, q as ServerBridgeProviderProps, S as ServerBridgeTransport, f as ServerClientEffect, r as ServerResponseMeta, T as TraitContext, g as TraitContextValue, h as TraitInstance, i as TraitProvider, j as TraitProviderProps, s as useEntityBindingSnapshot, u as useEntitySchema, k as useEntitySchemaOptional, l as useServerBridge, m as useTrait, n as useTraitContext } from '../EntityBindingContext-0Evn_LcT.cjs';
7
7
  import { j as UseOfflineExecutorResult, U as UseOfflineExecutorOptions } from '../offline-executor-QUdKOj7f.cjs';
8
8
  export { c as NavigationContextValue, d as NavigationProvider, e as NavigationProviderProps, f as NavigationState, k as comparePathSpecificity, m as extractRouteParams, n as findPageByName, o as findPageByPath, p as getAllPages, q as getDefaultPage, r as matchPath, s as matchPathAmong, t as pathMatches, u as useActivePage, v as useInitPayload, w as useNavigateTo, x as useNavigation, y as useNavigationId, z as useNavigationState } from '../offline-executor-QUdKOj7f.cjs';
9
9
  import { E as EventBusContextType } from '../event-bus-types-Bl78kokd.cjs';