@almadar/ui 5.157.0 → 5.159.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.
@@ -3,6 +3,7 @@
3
3
  var React85 = require('react');
4
4
  var providers = require('@almadar/ui/providers');
5
5
  var logger = require('@almadar/logger');
6
+ var ui = require('@almadar/runtime/ui');
6
7
  var runtime = require('@almadar/runtime');
7
8
  var core = require('@almadar/core');
8
9
  var clsx = require('clsx');
@@ -13,7 +14,6 @@ var reactDom = require('react-dom');
13
14
  var hooks = require('@almadar/ui/hooks');
14
15
  var context = require('@almadar/ui/context');
15
16
  var evaluator = require('@almadar/evaluator');
16
- var ui = require('@almadar/runtime/ui');
17
17
  var reactRouterDom = require('react-router-dom');
18
18
  var ELK = require('elkjs/lib/elk.bundled.js');
19
19
  var SyntaxHighlighter = require('react-syntax-highlighter/dist/esm/prism-light.js');
@@ -867,6 +867,22 @@ var init_useTapReveal = __esm({
867
867
  "hooks/useTapReveal.ts"() {
868
868
  }
869
869
  });
870
+ function usePerfBuffer() {
871
+ return React85.useSyncExternalStore(ui.perfStore.subscribe, ui.perfStore.getSnapshot, ui.perfStore.getSnapshot);
872
+ }
873
+ exports.profilerOnRender = void 0;
874
+ var init_perf = __esm({
875
+ "lib/perf.ts"() {
876
+ exports.profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
877
+ ui.pushPerfEntry({
878
+ name: `profiler:${id}:${phase}`,
879
+ durationMs: actualDuration,
880
+ ts: commitTime,
881
+ detail: { baseDuration }
882
+ });
883
+ };
884
+ }
885
+ });
870
886
  function resolveMarkerExpression(expression, entity, config, state) {
871
887
  const ctx = runtime.createContextFromBindings({
872
888
  entity,
@@ -884,11 +900,54 @@ function isPlainObject(value) {
884
900
  if (typeof value === "function") return false;
885
901
  return true;
886
902
  }
903
+ function isEvaluatorResolvedData(value) {
904
+ return evaluatorResolvedData.has(value);
905
+ }
906
+ function brandResolved(value) {
907
+ if (value !== null && typeof value === "object" && !React85__namespace.default.isValidElement(value) && !(value instanceof Date)) {
908
+ resolvedMarkerFree.add(value);
909
+ }
910
+ }
911
+ function subtreeHasMarker(value) {
912
+ if (resolvedMarkerFree.has(value)) return false;
913
+ const cached = markerPresenceCache.get(value);
914
+ if (cached !== void 0) return cached;
915
+ let found = false;
916
+ const children = Array.isArray(value) ? value : Object.values(value);
917
+ for (const child of children) {
918
+ if (core.isRenderBindingMarker(child)) {
919
+ found = true;
920
+ break;
921
+ }
922
+ if (Array.isArray(child) || isPlainObject(child)) {
923
+ if (subtreeHasMarker(child)) {
924
+ found = true;
925
+ break;
926
+ }
927
+ }
928
+ }
929
+ markerPresenceCache.set(value, found);
930
+ return found;
931
+ }
887
932
  function walkValue(value, scopeTrait, entity, config, state) {
888
933
  if (core.isRenderBindingMarker(value)) {
889
- return { resolved: resolveMarkerExpression(value.expression, entity, config, state), changed: true };
934
+ const cached = markerResolutionCache.get(value);
935
+ if (cached !== void 0 && cached.entity === entity && cached.config === config && cached.state === state) {
936
+ return { resolved: cached.resolved, changed: false };
937
+ }
938
+ const resolved = resolveMarkerExpression(value.expression, entity, config, state);
939
+ markerResolutionCache.set(value, { entity, config, state, resolved });
940
+ if (resolved !== null && typeof resolved === "object" && !React85__namespace.default.isValidElement(resolved) && !(resolved instanceof Date)) {
941
+ resolvedMarkerFree.add(resolved);
942
+ evaluatorResolvedData.add(resolved);
943
+ }
944
+ return { resolved, changed: true };
890
945
  }
891
946
  if (Array.isArray(value)) {
947
+ if (!subtreeHasMarker(value)) {
948
+ brandResolved(value);
949
+ return { resolved: value, changed: false };
950
+ }
892
951
  const out = [];
893
952
  let changed = false;
894
953
  for (const item of value) {
@@ -903,9 +962,14 @@ function walkValue(value, scopeTrait, entity, config, state) {
903
962
  out.push(resolved);
904
963
  if (itemChanged) changed = true;
905
964
  }
965
+ brandResolved(out);
906
966
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
907
967
  }
908
968
  if (isPlainObject(value)) {
969
+ if (!subtreeHasMarker(value)) {
970
+ brandResolved(value);
971
+ return { resolved: value, changed: false };
972
+ }
909
973
  const sourceTrait = value._sourceTrait;
910
974
  if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
911
975
  return { resolved: value, changed: false };
@@ -917,11 +981,13 @@ function walkValue(value, scopeTrait, entity, config, state) {
917
981
  out[key] = resolved;
918
982
  if (itemChanged) changed = true;
919
983
  }
984
+ brandResolved(out);
920
985
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
921
986
  }
922
987
  return { resolved: value, changed: false };
923
988
  }
924
989
  function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
990
+ if (resolvedMarkerFree.has(props)) return props;
925
991
  const out = {};
926
992
  let changed = false;
927
993
  for (const [key, value] of Object.entries(props)) {
@@ -929,11 +995,17 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
929
995
  out[key] = resolved;
930
996
  if (propChanged) changed = true;
931
997
  }
998
+ brandResolved(out);
932
999
  return changed ? out : props;
933
1000
  }
1001
+ var markerPresenceCache, markerResolutionCache, resolvedMarkerFree, evaluatorResolvedData;
934
1002
  var init_resolve_render_bindings = __esm({
935
1003
  "lib/resolve-render-bindings.ts"() {
936
1004
  "use client";
1005
+ markerPresenceCache = /* @__PURE__ */ new WeakMap();
1006
+ markerResolutionCache = /* @__PURE__ */ new WeakMap();
1007
+ resolvedMarkerFree = /* @__PURE__ */ new WeakSet();
1008
+ evaluatorResolvedData = /* @__PURE__ */ new WeakSet();
937
1009
  }
938
1010
  });
939
1011
  function cn(...inputs) {
@@ -12258,6 +12330,7 @@ var init_LearningCanvas = __esm({
12258
12330
  "components/learning/atoms/LearningCanvas.tsx"() {
12259
12331
  "use client";
12260
12332
  init_cn();
12333
+ init_perf();
12261
12334
  init_useEventBus();
12262
12335
  DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
12263
12336
  TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
@@ -12301,6 +12374,7 @@ var init_LearningCanvas = __esm({
12301
12374
  return [...shapes, ...traceOut, ...readoutOut];
12302
12375
  }, [shapes, traces, readouts, width, height]);
12303
12376
  const draw = React85.useCallback(() => {
12377
+ const _perfT = ui.perfStart("learningcanvas:paint");
12304
12378
  const canvas = canvasRef.current;
12305
12379
  if (!canvas) return;
12306
12380
  const ctx = canvas.getContext("2d");
@@ -12322,6 +12396,7 @@ var init_LearningCanvas = __esm({
12322
12396
  for (const shape of derivedShapes) {
12323
12397
  if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
12324
12398
  }
12399
+ ui.perfEnd("learningcanvas:paint", _perfT);
12325
12400
  }, [width, height, backgroundColor, derivedShapes]);
12326
12401
  React85.useEffect(() => {
12327
12402
  draw();
@@ -26995,7 +27070,7 @@ function fileIcon(name) {
26995
27070
  return "file";
26996
27071
  }
26997
27072
  }
26998
- var TreeNodeItem, FileTree;
27073
+ var TreeNodeItem, FlatTreeNodeItem, FileTree;
26999
27074
  var init_FileTree = __esm({
27000
27075
  "components/core/molecules/FileTree.tsx"() {
27001
27076
  "use client";
@@ -27080,14 +27155,101 @@ var init_FileTree = __esm({
27080
27155
  )) })
27081
27156
  ] });
27082
27157
  };
27158
+ FlatTreeNodeItem = ({
27159
+ item,
27160
+ depth,
27161
+ indent,
27162
+ childrenByParent,
27163
+ onNodeSelect,
27164
+ defaultExpanded = false
27165
+ }) => {
27166
+ const [expanded, setExpanded] = React85.useState(defaultExpanded || depth < 1);
27167
+ const children = childrenByParent.get(item.id);
27168
+ const hasChildren = !!children && children.length > 0;
27169
+ const handleClick = React85.useCallback(() => {
27170
+ if (hasChildren) setExpanded((prev) => !prev);
27171
+ onNodeSelect?.(item.id);
27172
+ }, [hasChildren, item.id, onNodeSelect]);
27173
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
27174
+ /* @__PURE__ */ jsxRuntime.jsxs(
27175
+ Box,
27176
+ {
27177
+ className: "flex items-center gap-1.5 py-0.5 px-2 cursor-pointer rounded-sm transition-colors hover:bg-muted",
27178
+ style: { paddingLeft: depth * indent + 8 },
27179
+ onClick: handleClick,
27180
+ role: "treeitem",
27181
+ "aria-expanded": hasChildren ? expanded : void 0,
27182
+ children: [
27183
+ hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
27184
+ Icon,
27185
+ {
27186
+ name: expanded ? "chevron-down" : "chevron-right",
27187
+ size: "xs",
27188
+ className: "text-[var(--color-muted-foreground)] flex-shrink-0"
27189
+ }
27190
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Box, { style: { width: 12, flexShrink: 0 } }),
27191
+ /* @__PURE__ */ jsxRuntime.jsx(
27192
+ Icon,
27193
+ {
27194
+ name: item.icon ?? (hasChildren ? expanded ? "folder-open" : "folder" : "file"),
27195
+ size: "xs",
27196
+ className: hasChildren ? "text-[var(--color-warning)]" : "text-[var(--color-muted-foreground)]"
27197
+ }
27198
+ ),
27199
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "truncate font-mono text-xs", children: item.label })
27200
+ ]
27201
+ }
27202
+ ),
27203
+ hasChildren && expanded && /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "group", children: children.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
27204
+ FlatTreeNodeItem,
27205
+ {
27206
+ item: child,
27207
+ depth: depth + 1,
27208
+ indent,
27209
+ childrenByParent,
27210
+ onNodeSelect
27211
+ },
27212
+ child.id
27213
+ )) })
27214
+ ] });
27215
+ };
27083
27216
  FileTree = ({
27084
27217
  tree,
27218
+ items,
27085
27219
  selectedPath,
27086
27220
  onFileSelect,
27221
+ onNodeSelect,
27087
27222
  className,
27088
27223
  indent = 16
27089
27224
  }) => {
27090
- if (tree.length === 0) return null;
27225
+ if (items) {
27226
+ if (items.length === 0) return null;
27227
+ const ids = new Set(items.map((node) => node.id));
27228
+ const childrenByParent = /* @__PURE__ */ new Map();
27229
+ const roots = [];
27230
+ for (const item of items) {
27231
+ if (item.parentId && ids.has(item.parentId)) {
27232
+ const siblings = childrenByParent.get(item.parentId);
27233
+ if (siblings) siblings.push(item);
27234
+ else childrenByParent.set(item.parentId, [item]);
27235
+ } else {
27236
+ roots.push(item);
27237
+ }
27238
+ }
27239
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
27240
+ FlatTreeNodeItem,
27241
+ {
27242
+ item,
27243
+ depth: 0,
27244
+ indent,
27245
+ childrenByParent,
27246
+ onNodeSelect,
27247
+ defaultExpanded: true
27248
+ },
27249
+ item.id
27250
+ )) });
27251
+ }
27252
+ if (!tree || tree.length === 0) return null;
27091
27253
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: tree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(
27092
27254
  TreeNodeItem,
27093
27255
  {
@@ -28418,7 +28580,7 @@ var init_debug = __esm({
28418
28580
  logger.createLogger("almadar:ui:debug:game-state");
28419
28581
  }
28420
28582
  });
28421
- var isRelationsDebugEnabled, RelationSelect;
28583
+ var isRelationsDebugEnabled, MANY_CARDINALITIES, RelationSelect;
28422
28584
  var init_RelationSelect = __esm({
28423
28585
  "components/core/molecules/RelationSelect.tsx"() {
28424
28586
  "use client";
@@ -28432,6 +28594,11 @@ var init_RelationSelect = __esm({
28432
28594
  init_Typography();
28433
28595
  init_debug();
28434
28596
  isRelationsDebugEnabled = () => isDebugEnabled();
28597
+ MANY_CARDINALITIES = [
28598
+ "many",
28599
+ "one-to-many",
28600
+ "many-to-many"
28601
+ ];
28435
28602
  RelationSelect = ({
28436
28603
  value,
28437
28604
  onChange,
@@ -30056,6 +30223,7 @@ var init_MathCanvas = __esm({
30056
30223
  "components/learning/molecules/MathCanvas.tsx"() {
30057
30224
  "use client";
30058
30225
  init_useEventBus();
30226
+ init_perf();
30059
30227
  init_atoms();
30060
30228
  init_Stack();
30061
30229
  init_LearningCanvas();
@@ -30096,17 +30264,21 @@ var init_MathCanvas = __esm({
30096
30264
  error
30097
30265
  }) => {
30098
30266
  const eventBus = useEventBus();
30267
+ const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
30268
+ const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
30269
+ const stableKeyMap = React85.useMemo(() => keyMap, [keyMapKey]);
30270
+ const stableKeyUpMap = React85.useMemo(() => keyUpMap, [keyUpMapKey]);
30099
30271
  React85.useEffect(() => {
30100
- if (!keyMap && !keyUpMap) return;
30272
+ if (!stableKeyMap && !stableKeyUpMap) return;
30101
30273
  const onDown = (e) => {
30102
- const ev = keyMap?.[e.code];
30274
+ const ev = stableKeyMap?.[e.code];
30103
30275
  if (ev) {
30104
30276
  eventBus.emit(`UI:${ev}`, {});
30105
30277
  e.preventDefault();
30106
30278
  }
30107
30279
  };
30108
30280
  const onUp = (e) => {
30109
- const ev = keyUpMap?.[e.code];
30281
+ const ev = stableKeyUpMap?.[e.code];
30110
30282
  if (ev) eventBus.emit(`UI:${ev}`, {});
30111
30283
  };
30112
30284
  window.addEventListener("keydown", onDown);
@@ -30115,8 +30287,9 @@ var init_MathCanvas = __esm({
30115
30287
  window.removeEventListener("keydown", onDown);
30116
30288
  window.removeEventListener("keyup", onUp);
30117
30289
  };
30118
- }, [keyMap, keyUpMap, eventBus]);
30290
+ }, [stableKeyMap, stableKeyUpMap, eventBus]);
30119
30291
  const derivedShapes = React85.useMemo(() => {
30292
+ const _perfT = ui.perfStart("mathcanvas:derive");
30120
30293
  const out = [];
30121
30294
  const margin = 24;
30122
30295
  const plotW = width - margin * 2;
@@ -30360,6 +30533,7 @@ var init_MathCanvas = __esm({
30360
30533
  }
30361
30534
  }
30362
30535
  out.push(...shapes);
30536
+ ui.perfEnd("mathcanvas:derive", _perfT);
30363
30537
  return out;
30364
30538
  }, [
30365
30539
  width,
@@ -36575,7 +36749,7 @@ var init_RichBlockEditor = __esm({
36575
36749
  {
36576
36750
  variant: "bordered",
36577
36751
  padding: "none",
36578
- className: cn("flex flex-col", className),
36752
+ className: cn("flex flex-col text-card-foreground", className),
36579
36753
  children: [
36580
36754
  enableBlocks && showToolbar && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
36581
36755
  Box,
@@ -41803,6 +41977,7 @@ var init_DetailPanel = __esm({
41803
41977
  "use client";
41804
41978
  init_atoms();
41805
41979
  init_Box();
41980
+ init_Input();
41806
41981
  init_Stack();
41807
41982
  init_SimpleGrid();
41808
41983
  init_Menu();
@@ -41818,6 +41993,7 @@ var init_DetailPanel = __esm({
41818
41993
  ReactMarkdown2 = React85.lazy(() => import('react-markdown'));
41819
41994
  DetailPanel = ({
41820
41995
  title: propTitle,
41996
+ onTitleCommit,
41821
41997
  subtitle,
41822
41998
  status,
41823
41999
  avatar,
@@ -41839,6 +42015,7 @@ var init_DetailPanel = __esm({
41839
42015
  }) => {
41840
42016
  const eventBus = useEventBus();
41841
42017
  const { t } = hooks.useTranslate();
42018
+ const [titleDraft, setTitleDraft] = React85__namespace.default.useState(null);
41842
42019
  const isFieldDefArray = (arr) => {
41843
42020
  if (!arr || arr.length === 0) return false;
41844
42021
  const first = arr[0];
@@ -42059,6 +42236,50 @@ var init_DetailPanel = __esm({
42059
42236
  }),
42060
42237
  status && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: status.variant ?? "default", children: status.label })
42061
42238
  ] });
42239
+ const commitTitle = () => {
42240
+ if (!onTitleCommit) return;
42241
+ const next = (titleDraft ?? "").trim();
42242
+ setTitleDraft(null);
42243
+ if (!next || next === title) return;
42244
+ onTitleCommit(next, normalizedData?.id !== void 0 ? String(normalizedData.id) : "");
42245
+ };
42246
+ const titleNode = onTitleCommit && titleDraft !== null ? /* @__PURE__ */ jsxRuntime.jsx(
42247
+ Input,
42248
+ {
42249
+ value: titleDraft,
42250
+ autoFocus: true,
42251
+ "aria-label": t("common.title"),
42252
+ className: "h-auto py-1 text-3xl font-bold tracking-tight",
42253
+ onChange: (e) => setTitleDraft(e.target.value),
42254
+ onBlur: commitTitle,
42255
+ onKeyDown: (e) => {
42256
+ if (e.key === "Enter") {
42257
+ e.preventDefault();
42258
+ commitTitle();
42259
+ } else if (e.key === "Escape") {
42260
+ e.preventDefault();
42261
+ setTitleDraft(null);
42262
+ }
42263
+ },
42264
+ "data-testid": "detail-title-input"
42265
+ }
42266
+ ) : onTitleCommit ? /* @__PURE__ */ jsxRuntime.jsx(
42267
+ Box,
42268
+ {
42269
+ role: "button",
42270
+ tabIndex: 0,
42271
+ className: "cursor-text rounded px-1 -mx-1 transition-colors hover:bg-muted/40",
42272
+ onClick: () => setTitleDraft(title ?? ""),
42273
+ onKeyDown: (e) => {
42274
+ if (e.key === "Enter" || e.key === " ") {
42275
+ e.preventDefault();
42276
+ setTitleDraft(title ?? "");
42277
+ }
42278
+ },
42279
+ "data-testid": "detail-title-editable",
42280
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" })
42281
+ }
42282
+ ) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" });
42062
42283
  const content = /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className: "p-6", children: [
42063
42284
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "start", gap: "md", children: [
42064
42285
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", gap: "sm", className: "min-w-0", children: [
@@ -42078,7 +42299,7 @@ var init_DetailPanel = __esm({
42078
42299
  avatar,
42079
42300
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "min-w-0", children: [
42080
42301
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "center", gap: "sm", wrap: true, children: [
42081
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", weight: "bold", children: title || "Details" }),
42302
+ titleNode,
42082
42303
  statusBadges
42083
42304
  ] }),
42084
42305
  subtitle && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: subtitle })
@@ -42414,6 +42635,9 @@ function determineInputType(field) {
42414
42635
  if (field.type === "relation" || field.relation) {
42415
42636
  return "relation";
42416
42637
  }
42638
+ if (field.type === "array") {
42639
+ return "array";
42640
+ }
42417
42641
  if (field.type === "enum" || field.values || getEnumOptions(field).length > 0) {
42418
42642
  return "select";
42419
42643
  }
@@ -42489,6 +42713,7 @@ var init_Form = __esm({
42489
42713
  init_Typography();
42490
42714
  init_Icon();
42491
42715
  init_RelationSelect();
42716
+ init_TagInput();
42492
42717
  init_UploadDropZone();
42493
42718
  init_Alert();
42494
42719
  init_useEventBus();
@@ -42568,7 +42793,7 @@ var init_Form = __esm({
42568
42793
  values: "values" in f3 ? f3.values : void 0,
42569
42794
  min: f3.min,
42570
42795
  max: f3.max,
42571
- relation: "relation" in f3 ? { entity: f3.relation.entity } : void 0
42796
+ relation: "relation" in f3 ? { entity: f3.relation.entity, cardinality: f3.relation.cardinality } : void 0
42572
42797
  })
42573
42798
  );
42574
42799
  }, [entity, fields]);
@@ -42635,7 +42860,7 @@ var init_Form = __esm({
42635
42860
  });
42636
42861
  debug(
42637
42862
  "forms",
42638
- `Calculation triggered: ${calc.variableName} = ${value}`
42863
+ `Calculation triggered: ${calc.variableName} = ${String(value)}`
42639
42864
  );
42640
42865
  }
42641
42866
  });
@@ -42803,7 +43028,7 @@ var init_Form = __esm({
42803
43028
  values: "values" in entityField ? entityField.values : void 0,
42804
43029
  min: entityField.min,
42805
43030
  max: entityField.max,
42806
- relation: "relation" in entityField ? { entity: entityField.relation.entity } : void 0
43031
+ relation: "relation" in entityField ? { entity: entityField.relation.entity, cardinality: entityField.relation.cardinality } : void 0
42807
43032
  };
42808
43033
  }
42809
43034
  return { name: field, type: "string" };
@@ -42915,6 +43140,22 @@ var init_Form = __esm({
42915
43140
  case "relation": {
42916
43141
  const relationOptions = relationsData[fieldName] || [];
42917
43142
  const relationLoading = relationsLoading[fieldName] || false;
43143
+ if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
43144
+ const selectedValues = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : [];
43145
+ return /* @__PURE__ */ jsxRuntime.jsx(
43146
+ Select,
43147
+ {
43148
+ ...commonProps,
43149
+ multiple: true,
43150
+ searchable: true,
43151
+ clearable: true,
43152
+ options: [...relationOptions],
43153
+ value: selectedValues,
43154
+ onValueChange: (value) => handleChange(fieldName, Array.isArray(value) ? value : [value]),
43155
+ placeholder: field.placeholder || `Select ${label}...`
43156
+ }
43157
+ );
43158
+ }
42918
43159
  return /* @__PURE__ */ jsxRuntime.jsx(
42919
43160
  RelationSelect,
42920
43161
  {
@@ -42929,6 +43170,18 @@ var init_Form = __esm({
42929
43170
  }
42930
43171
  );
42931
43172
  }
43173
+ case "array": {
43174
+ const arrayValue = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : currentValue != null && currentValue !== "" ? [String(currentValue)] : [];
43175
+ return /* @__PURE__ */ jsxRuntime.jsx(
43176
+ TagInput,
43177
+ {
43178
+ placeholder: field.placeholder,
43179
+ disabled: isLoading,
43180
+ value: arrayValue,
43181
+ onChange: (next) => handleChange(fieldName, [...next])
43182
+ }
43183
+ );
43184
+ }
42932
43185
  case "number":
42933
43186
  return /* @__PURE__ */ jsxRuntime.jsx(
42934
43187
  Input,
@@ -48497,7 +48750,10 @@ function enrichFormFields(fields, entityDef) {
48497
48750
  enriched.values = entityField.enumValues;
48498
48751
  }
48499
48752
  if (entityField.relation) {
48500
- enriched.relation = entityField.relation.entity;
48753
+ enriched.relation = {
48754
+ entity: entityField.relation.entity,
48755
+ cardinality: entityField.relation.cardinality
48756
+ };
48501
48757
  }
48502
48758
  return enriched;
48503
48759
  }
@@ -48525,7 +48781,10 @@ function enrichFormFields(fields, entityDef) {
48525
48781
  }
48526
48782
  }
48527
48783
  if (!obj.relation && entityField.relation) {
48528
- enriched.relation = entityField.relation.entity;
48784
+ enriched.relation = {
48785
+ entity: entityField.relation.entity,
48786
+ cardinality: entityField.relation.cardinality
48787
+ };
48529
48788
  }
48530
48789
  return enriched;
48531
48790
  }
@@ -48540,7 +48799,12 @@ function enrichDetailFields(fields, entityDef) {
48540
48799
  const meta = { type: entityField.type };
48541
48800
  const values = entityField.values ?? entityField.enumValues;
48542
48801
  if (values && values.length > 0) meta.values = values;
48543
- if (entityField.relation) meta.relation = entityField.relation.entity;
48802
+ if (entityField.relation) {
48803
+ meta.relation = {
48804
+ entity: entityField.relation.entity,
48805
+ cardinality: entityField.relation.cardinality
48806
+ };
48807
+ }
48544
48808
  return meta;
48545
48809
  };
48546
48810
  return fields.map((field) => {
@@ -49094,6 +49358,33 @@ function isPlainConfigObject(value) {
49094
49358
  const proto = Object.getPrototypeOf(value);
49095
49359
  return proto === Object.prototype || proto === null;
49096
49360
  }
49361
+ function subtreeHasTraitRef(value) {
49362
+ if (isEvaluatorResolvedData(value)) return false;
49363
+ const cached = traitRefPresenceCache.get(value);
49364
+ if (cached !== void 0) return cached;
49365
+ let found = false;
49366
+ const children = Array.isArray(value) ? value : Object.values(value);
49367
+ for (const child of children) {
49368
+ if (typeof child === "string" && TRAIT_BINDING_RE.test(child)) {
49369
+ found = true;
49370
+ break;
49371
+ }
49372
+ if (core.isRenderBindingMarker(child)) continue;
49373
+ if (Array.isArray(child)) {
49374
+ if (subtreeHasTraitRef(child)) {
49375
+ found = true;
49376
+ break;
49377
+ }
49378
+ } else if (child !== null && typeof child === "object" && isPlainConfigObject(child)) {
49379
+ if (subtreeHasTraitRef(child)) {
49380
+ found = true;
49381
+ break;
49382
+ }
49383
+ }
49384
+ }
49385
+ traitRefPresenceCache.set(value, found);
49386
+ return found;
49387
+ }
49097
49388
  function substituteTraitRefsDeep(value, pathKey) {
49098
49389
  if (core.isRenderBindingMarker(value)) return value;
49099
49390
  if (typeof value === "string") {
@@ -49108,11 +49399,13 @@ function substituteTraitRefsDeep(value, pathKey) {
49108
49399
  return value;
49109
49400
  }
49110
49401
  if (Array.isArray(value)) {
49402
+ if (!subtreeHasTraitRef(value)) return value;
49111
49403
  return value.map(
49112
49404
  (item, i) => substituteTraitRefsDeep(item, `${pathKey}[${i}]`)
49113
49405
  );
49114
49406
  }
49115
49407
  if (typeof value === "object" && isPlainConfigObject(value)) {
49408
+ if (!subtreeHasTraitRef(value)) return value;
49116
49409
  const out = {};
49117
49410
  for (const [k, v] of Object.entries(value)) {
49118
49411
  out[k] = substituteTraitRefsDeep(v, `${pathKey}.${k}`);
@@ -49139,6 +49432,10 @@ function renderPatternProps(props, onDismiss, propsSchema) {
49139
49432
  };
49140
49433
  rendered[key] = /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content: childContent, onDismiss });
49141
49434
  } else if (Array.isArray(value)) {
49435
+ if (!isEvaluatorResolvedData(value) && value.some((el) => isPatternConfig(el))) ; else if (!subtreeHasTraitRef(value)) {
49436
+ rendered[key] = value;
49437
+ continue;
49438
+ }
49142
49439
  const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
49143
49440
  rendered[key] = value.map((item, i) => {
49144
49441
  const el = item;
@@ -49401,7 +49698,7 @@ function UISlotRenderer({
49401
49698
  }
49402
49699
  return wrapped;
49403
49700
  }
49404
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
49701
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
49405
49702
  var init_UISlotRenderer = __esm({
49406
49703
  "components/core/organisms/UISlotRenderer.tsx"() {
49407
49704
  "use client";
@@ -49475,6 +49772,7 @@ var init_UISlotRenderer = __esm({
49475
49772
  "alert",
49476
49773
  "dialog"
49477
49774
  ]);
49775
+ traitRefPresenceCache = /* @__PURE__ */ new WeakMap();
49478
49776
  UISlotRenderer.displayName = "UISlotRenderer";
49479
49777
  }
49480
49778
  });
@@ -49602,6 +49900,23 @@ function createClientEffectHandlers(options) {
49602
49900
  })
49603
49901
  };
49604
49902
  }
49903
+
49904
+ // lib/event-queue-coalesce.ts
49905
+ function enqueueEvent(queue, entry) {
49906
+ if (entry.tick !== void 0) {
49907
+ const pending = queue.find(
49908
+ (e) => e.tick !== void 0 && e.eventKey === entry.eventKey && e.targetTrait === entry.targetTrait
49909
+ );
49910
+ if (pending !== void 0) {
49911
+ pending.payload = entry.payload;
49912
+ return;
49913
+ }
49914
+ }
49915
+ queue.push(entry);
49916
+ }
49917
+
49918
+ // hooks/useTraitStateMachine.ts
49919
+ init_perf();
49605
49920
  var lambdaLog = logger.createLogger("almadar:ui:fn-form-lambda");
49606
49921
  function isOperatorCall(value) {
49607
49922
  const first = value[0];
@@ -50347,12 +50662,13 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50347
50662
  entityId
50348
50663
  };
50349
50664
  const emittedDuringExec = [];
50665
+ const tickName = flushEvent.startsWith("tick:") ? flushEvent.slice(5) : void 0;
50350
50666
  const baseEmit = handlers.emit;
50351
50667
  const trackingHandlers = {
50352
50668
  ...handlers,
50353
50669
  emit: (event, eventPayload, source) => {
50354
50670
  emittedDuringExec.push(event);
50355
- baseEmit(event, eventPayload, source);
50671
+ baseEmit(event, eventPayload, tickName !== void 0 ? { ...source, tick: tickName } : source);
50356
50672
  }
50357
50673
  };
50358
50674
  if (traitName === "Hero") {
@@ -50459,6 +50775,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50459
50775
  }, [eventBus]);
50460
50776
  React85.useEffect(() => {
50461
50777
  const scheduler = runtime.createTickScheduler();
50778
+ const timedTick = (key, fn) => () => ui.perfTimeAsync(key, fn);
50462
50779
  const pureWriterTickKeys = /* @__PURE__ */ new Set();
50463
50780
  for (const group of sharedGroups.values()) {
50464
50781
  const ticksByInterval = /* @__PURE__ */ new Map();
@@ -50475,12 +50792,12 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50475
50792
  }
50476
50793
  for (const entries of ticksByInterval.values()) {
50477
50794
  const interval = entries[0].tick.interval;
50478
- const onDue = () => {
50795
+ const onDue = timedTick(`tick:shared:${group.storeKey}@${String(interval)}`, () => {
50479
50796
  const writers = entries.map(
50480
50797
  ({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
50481
50798
  );
50482
50799
  runTickFrame(group.storeKey, writers, sharedEntityStore);
50483
- };
50800
+ });
50484
50801
  if (interval === "frame") {
50485
50802
  scheduler.add(0, onDue);
50486
50803
  } else if (typeof interval === "number") {
@@ -50498,21 +50815,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50498
50815
  if (sharedKey !== void 0 && pureWriterTickKeys.has(`${binding.trait.name}::${tick.name}`)) {
50499
50816
  continue;
50500
50817
  }
50818
+ const tickKey = `tick:${binding.trait.name}::${tick.name}`;
50501
50819
  if (tick.interval === "frame") {
50502
- scheduler.add(0, () => runTickEffects(tick, binding));
50820
+ scheduler.add(0, timedTick(tickKey, () => runTickEffects(tick, binding)));
50503
50821
  } else if (typeof tick.interval === "number") {
50504
- scheduler.add(tick.interval, () => runTickEffects(tick, binding));
50822
+ scheduler.add(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
50505
50823
  } else if (runtime.isValidCronExpression(tick.interval)) {
50506
- scheduler.addCron(tick.interval, () => runTickEffects(tick, binding));
50824
+ scheduler.addCron(tick.interval, timedTick(tickKey, () => runTickEffects(tick, binding)));
50507
50825
  } else {
50508
- scheduler.add(runtime.parseDurationString(tick.interval), () => runTickEffects(tick, binding));
50826
+ scheduler.add(runtime.parseDurationString(tick.interval), timedTick(tickKey, () => runTickEffects(tick, binding)));
50509
50827
  }
50510
50828
  }
50511
50829
  }
50512
50830
  return () => scheduler.stopAll();
50513
50831
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
50514
- const processEventQueued = React85.useCallback(async (eventKey, payload, targetTrait) => {
50832
+ const processEventQueued = React85.useCallback(async (eventKey, payload, targetTrait, tick, sourceTrait) => {
50515
50833
  const normalizedEvent = normalizeEventKey(eventKey);
50834
+ const _perfT0 = ui.perfStart("processEvent:total");
50516
50835
  const bindings = traitBindingsRef.current;
50517
50836
  const currentManager = managerRef.current;
50518
50837
  crossTraitLog.debug("processEvent:enter", () => ({
@@ -50536,6 +50855,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50536
50855
  entityByTrait[name] = { ...sharedEntityStore.getSnapshot(sharedKey) };
50537
50856
  }
50538
50857
  }
50858
+ const _perfT1 = ui.perfStart("processEvent:guardMatch");
50539
50859
  const results = currentManager.sendEvent(
50540
50860
  normalizedEvent,
50541
50861
  payload,
@@ -50544,6 +50864,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50544
50864
  void 0,
50545
50865
  targetTrait
50546
50866
  );
50867
+ ui.perfEnd("processEvent:guardMatch", _perfT1);
50547
50868
  crossTraitLog.debug("processEvent:results", {
50548
50869
  event: normalizedEvent,
50549
50870
  executedCount: results.length,
@@ -50593,6 +50914,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50593
50914
  transition: `${result.previousState} -> ${result.newState}`,
50594
50915
  effects: JSON.stringify(result.effects)
50595
50916
  }));
50917
+ const _perfT2 = ui.perfStart("processEvent:executeAll");
50596
50918
  const emittedDuringExec = await executeTransitionEffects({
50597
50919
  binding,
50598
50920
  // upstream gap: /runtime TransitionResult.effects is unknown[] — they are SExpr at runtime (see Almadar_UI_Gaps.md)
@@ -50604,6 +50926,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50604
50926
  syncOnly: false,
50605
50927
  log: stateLog
50606
50928
  });
50929
+ ui.perfEnd("processEvent:executeAll", _perfT2);
50607
50930
  emittedByTrait.set(traitName, emittedDuringExec);
50608
50931
  for (const emittedKey of emittedDuringExec) {
50609
50932
  bridgeEchoPendingRef.current.set(
@@ -50700,23 +51023,31 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50700
51023
  if (orbital) dispatchedOrbitals.add(orbital);
50701
51024
  }
50702
51025
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
50703
- await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
51026
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
50704
51027
  }
51028
+ ui.perfEnd("processEvent:total", _perfT0);
51029
+ ui.perfEnd(`event:${normalizedEvent}`, _perfT0);
50705
51030
  }, [entities, eventBus, sharedEntityStore]);
50706
51031
  const drainEventQueue = React85.useCallback(async () => {
50707
51032
  if (processingRef.current) return;
50708
51033
  processingRef.current = true;
51034
+ const _perfT = ui.perfStart("drain:pass");
51035
+ let _perfN = 0;
50709
51036
  try {
50710
51037
  while (eventQueueRef.current.length > 0) {
50711
51038
  const entry = eventQueueRef.current.shift();
50712
- await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
51039
+ _perfN++;
51040
+ await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait, entry.tick, entry.sourceTrait);
50713
51041
  }
50714
51042
  } finally {
50715
51043
  processingRef.current = false;
51044
+ ui.perfEnd("drain:pass", _perfT);
51045
+ ui.perfGauge("drain:passEntries", _perfN);
50716
51046
  }
50717
51047
  }, [processEventQueued]);
50718
- const enqueueAndDrain = React85.useCallback((eventKey, payload, targetTrait) => {
50719
- eventQueueRef.current.push({ eventKey, payload, targetTrait });
51048
+ const enqueueAndDrain = React85.useCallback((eventKey, payload, targetTrait, tick, sourceTrait) => {
51049
+ enqueueEvent(eventQueueRef.current, { eventKey, payload, targetTrait, tick, sourceTrait });
51050
+ ui.perfGauge("queue:depthAtEnqueue", eventQueueRef.current.length);
50720
51051
  void drainEventQueue();
50721
51052
  }, [drainEventQueue]);
50722
51053
  React85.useCallback((eventKey, payload) => {
@@ -50769,7 +51100,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50769
51100
  crossTraitLog.debug("self:fire-server-cascade", { traitName, busKey: selfBusKey, eventKey });
50770
51101
  }
50771
51102
  crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
50772
- enqueueAndDrain(eventKey, event.payload, traitName);
51103
+ enqueueAndDrain(eventKey, event.payload, traitName, event.source?.tick, event.source?.trait);
50773
51104
  });
50774
51105
  unsubscribes.push(() => {
50775
51106
  crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
@@ -50787,7 +51118,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50787
51118
  const bareKey = `UI:${eventKey}`;
50788
51119
  const unsub = eventBus.on(bareKey, (event) => {
50789
51120
  crossTraitLog.debug("bare-cascade:fire", { bareKey, eventKey });
50790
- enqueueAndDrain(eventKey, event.payload);
51121
+ enqueueAndDrain(eventKey, event.payload, void 0, event.source?.tick, event.source?.trait);
50791
51122
  });
50792
51123
  unsubscribes.push(() => {
50793
51124
  crossTraitLog.debug("bare-cascade:unsubscribe", { bareKey, eventKey });
@@ -50821,7 +51152,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50821
51152
  enqueueAndDrain(
50822
51153
  listen.triggers,
50823
51154
  core.applyListenPayloadMapping(listen.payloadMapping, event.payload, evaluator.evaluateListenPayloadExpr),
50824
- binding.trait.name
51155
+ binding.trait.name,
51156
+ event.source?.tick,
51157
+ event.source?.trait
50825
51158
  );
50826
51159
  });
50827
51160
  unsubscribes.push(() => {
@@ -51116,7 +51449,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
51116
51449
  [serverActiveTraits]
51117
51450
  );
51118
51451
  const uiSlots = context.useUISlots();
51119
- const onEventProcessed = React85.useCallback(async (event, payload, dispatchedOrbitals) => {
51452
+ const onEventProcessed = React85.useCallback((event, payload, dispatchedOrbitals, tick, sourceTrait) => {
51120
51453
  if (!bridge.connected || !orbitalNames?.length) return;
51121
51454
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
51122
51455
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -51126,9 +51459,14 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
51126
51459
  dispatchedOrbitalsSize: dispatchedOrbitals?.size ?? 0
51127
51460
  }));
51128
51461
  for (const name of targets) {
51129
- const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
51130
- recordServerResponse(name, event, meta);
51131
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
51462
+ if (tick !== void 0) {
51463
+ void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
51464
+ continue;
51465
+ }
51466
+ void bridge.sendEvent(name, event, withActiveTraits(payload)).then(({ effects, meta }) => {
51467
+ recordServerResponse(name, event, meta);
51468
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
51469
+ });
51132
51470
  }
51133
51471
  }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits]);
51134
51472
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, navigateBack: onNavigateBack, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams } : { navigate: onNavigate, navigateBack: onNavigateBack, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, initPayload: routeParams };
@@ -51446,14 +51784,15 @@ function OrbPreview({
51446
51784
  return pattern.replace(/:([A-Za-z0-9_]+)/g, (whole, key) => routeParams[key] ?? whole);
51447
51785
  }, [currentPagePath, routeParams]);
51448
51786
  const navStackRef = React85.useRef(null);
51449
- const handleNavigate = React85.useCallback((path) => {
51787
+ const handleNavigate = React85.useCallback((path, navState) => {
51450
51788
  const hit = providers.matchPathAmong(pages, path, (entry) => entry.page.path);
51451
51789
  const match = hit?.candidate;
51452
- const params = hit?.params ?? {};
51790
+ const params = { ...hit?.params ?? {}, ...navState ?? {} };
51453
51791
  navLog.debug("handleNavigate", () => ({
51454
51792
  path,
51455
51793
  matched: match?.page.name ?? null,
51456
51794
  params,
51795
+ navState: navState ? JSON.stringify(navState) : void 0,
51457
51796
  availablePaths: pages.map((p) => p.page.path)
51458
51797
  }));
51459
51798
  if (match?.page.name) {
@@ -51468,9 +51807,9 @@ function OrbPreview({
51468
51807
  }
51469
51808
  }, [pages]);
51470
51809
  const handleNavigateEffect = React85.useCallback(
51471
- (path, _params, crumb) => {
51810
+ (path, params, crumb) => {
51472
51811
  navStackRef.current?.beginNavigate(path, crumb);
51473
- handleNavigate(path);
51812
+ handleNavigate(path, params);
51474
51813
  },
51475
51814
  [handleNavigate]
51476
51815
  );
@@ -51601,11 +51940,13 @@ function BrowserPlayground({
51601
51940
  unregister: async () => {
51602
51941
  runtime.unregisterAll();
51603
51942
  },
51604
- sendEvent: async (orbitalName, event, payload) => {
51943
+ sendEvent: async (orbitalName, event, payload, _clientId, tick, sourceTrait) => {
51605
51944
  await registrationReady;
51606
51945
  return runtime.processOrbitalEvent(orbitalName, {
51607
51946
  event,
51608
- payload
51947
+ payload,
51948
+ tick,
51949
+ sourceTrait
51609
51950
  // @almadar/runtime OrbitalEventResponse.clientEffects uses a wider ClientEffectTuple than
51610
51951
  // ServerBridge's local definition — cast at this boundary (upstream fix queued).
51611
51952
  });
@@ -51623,17 +51964,9 @@ function BrowserPlayground({
51623
51964
  }
51624
51965
  );
51625
51966
  }
51626
- function usePerfBuffer() {
51627
- return React85.useSyncExternalStore(ui.perfStore.subscribe, ui.perfStore.getSnapshot, ui.perfStore.getSnapshot);
51628
- }
51629
- var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
51630
- ui.pushPerfEntry({
51631
- name: `profiler:${id}:${phase}`,
51632
- durationMs: actualDuration,
51633
- ts: commitTime,
51634
- detail: { baseDuration }
51635
- });
51636
- };
51967
+
51968
+ // runtime/index.ts
51969
+ init_perf();
51637
51970
 
51638
51971
  Object.defineProperty(exports, "EntitySchemaProvider", {
51639
51972
  enumerable: true,
@@ -51711,7 +52044,6 @@ exports.BrowserPlayground = BrowserPlayground;
51711
52044
  exports.OrbPreview = OrbPreview;
51712
52045
  exports.clearSchemaCache = clearSchemaCache;
51713
52046
  exports.createClientEffectHandlers = createClientEffectHandlers;
51714
- exports.profilerOnRender = profilerOnRender;
51715
52047
  exports.usePerfBuffer = usePerfBuffer;
51716
52048
  exports.useResolvedSchema = useResolvedSchema;
51717
52049
  exports.useTraitStateMachine = useTraitStateMachine;