@almadar/ui 5.156.0 → 5.158.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/avl/index.js CHANGED
@@ -4588,11 +4588,32 @@ function isPlainObject(value) {
4588
4588
  if (typeof value === "function") return false;
4589
4589
  return true;
4590
4590
  }
4591
+ function subtreeHasMarker(value) {
4592
+ const cached = markerPresenceCache.get(value);
4593
+ if (cached !== void 0) return cached;
4594
+ let found = false;
4595
+ const children = Array.isArray(value) ? value : Object.values(value);
4596
+ for (const child of children) {
4597
+ if (isRenderBindingMarker(child)) {
4598
+ found = true;
4599
+ break;
4600
+ }
4601
+ if (Array.isArray(child) || isPlainObject(child)) {
4602
+ if (subtreeHasMarker(child)) {
4603
+ found = true;
4604
+ break;
4605
+ }
4606
+ }
4607
+ }
4608
+ markerPresenceCache.set(value, found);
4609
+ return found;
4610
+ }
4591
4611
  function walkValue(value, scopeTrait, entity, config, state) {
4592
4612
  if (isRenderBindingMarker(value)) {
4593
4613
  return { resolved: resolveMarkerExpression(value.expression, entity, config, state), changed: true };
4594
4614
  }
4595
4615
  if (Array.isArray(value)) {
4616
+ if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
4596
4617
  const out = [];
4597
4618
  let changed = false;
4598
4619
  for (const item of value) {
@@ -4610,6 +4631,7 @@ function walkValue(value, scopeTrait, entity, config, state) {
4610
4631
  return changed ? { resolved: out, changed: true } : { resolved: value, changed: false };
4611
4632
  }
4612
4633
  if (isPlainObject(value)) {
4634
+ if (!subtreeHasMarker(value)) return { resolved: value, changed: false };
4613
4635
  const sourceTrait = value._sourceTrait;
4614
4636
  if (typeof sourceTrait === "string" && sourceTrait !== scopeTrait) {
4615
4637
  return { resolved: value, changed: false };
@@ -4635,9 +4657,11 @@ function resolveRenderBindingMarkers(props, scopeTrait, entity, config, state) {
4635
4657
  }
4636
4658
  return changed ? out : props;
4637
4659
  }
4660
+ var markerPresenceCache;
4638
4661
  var init_resolve_render_bindings = __esm({
4639
4662
  "lib/resolve-render-bindings.ts"() {
4640
4663
  "use client";
4664
+ markerPresenceCache = /* @__PURE__ */ new WeakMap();
4641
4665
  }
4642
4666
  });
4643
4667
  function getCurrentIconFamily() {
@@ -15435,6 +15459,8 @@ function drawShape(ctx, shape, width, height, allShapes) {
15435
15459
  case "path": {
15436
15460
  if (!shape.path) break;
15437
15461
  const p = new Path2D(shape.path);
15462
+ ctx.lineJoin = "round";
15463
+ ctx.lineCap = "round";
15438
15464
  if (fill) {
15439
15465
  ctx.fillStyle = fill;
15440
15466
  ctx.fill(p);
@@ -15643,7 +15669,7 @@ var init_LearningCanvas = __esm({
15643
15669
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
15644
15670
  ctx.clearRect(0, 0, width, height);
15645
15671
  if (backgroundColor) {
15646
- ctx.fillStyle = backgroundColor;
15672
+ ctx.fillStyle = resolveColor2(backgroundColor, ctx, backgroundColor);
15647
15673
  ctx.fillRect(0, 0, width, height);
15648
15674
  }
15649
15675
  for (const shape of derivedShapes) {
@@ -29629,7 +29655,7 @@ function fileIcon(name) {
29629
29655
  return "file";
29630
29656
  }
29631
29657
  }
29632
- var TreeNodeItem, FileTree;
29658
+ var TreeNodeItem, FlatTreeNodeItem, FileTree;
29633
29659
  var init_FileTree = __esm({
29634
29660
  "components/core/molecules/FileTree.tsx"() {
29635
29661
  "use client";
@@ -29714,14 +29740,101 @@ var init_FileTree = __esm({
29714
29740
  )) })
29715
29741
  ] });
29716
29742
  };
29743
+ FlatTreeNodeItem = ({
29744
+ item,
29745
+ depth,
29746
+ indent,
29747
+ childrenByParent,
29748
+ onNodeSelect,
29749
+ defaultExpanded = false
29750
+ }) => {
29751
+ const [expanded, setExpanded] = useState(defaultExpanded || depth < 1);
29752
+ const children = childrenByParent.get(item.id);
29753
+ const hasChildren = !!children && children.length > 0;
29754
+ const handleClick = useCallback(() => {
29755
+ if (hasChildren) setExpanded((prev) => !prev);
29756
+ onNodeSelect?.(item.id);
29757
+ }, [hasChildren, item.id, onNodeSelect]);
29758
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
29759
+ /* @__PURE__ */ jsxs(
29760
+ Box,
29761
+ {
29762
+ className: "flex items-center gap-1.5 py-0.5 px-2 cursor-pointer rounded-sm transition-colors hover:bg-muted",
29763
+ style: { paddingLeft: depth * indent + 8 },
29764
+ onClick: handleClick,
29765
+ role: "treeitem",
29766
+ "aria-expanded": hasChildren ? expanded : void 0,
29767
+ children: [
29768
+ hasChildren ? /* @__PURE__ */ jsx(
29769
+ Icon,
29770
+ {
29771
+ name: expanded ? "chevron-down" : "chevron-right",
29772
+ size: "xs",
29773
+ className: "text-[var(--color-muted-foreground)] flex-shrink-0"
29774
+ }
29775
+ ) : /* @__PURE__ */ jsx(Box, { style: { width: 12, flexShrink: 0 } }),
29776
+ /* @__PURE__ */ jsx(
29777
+ Icon,
29778
+ {
29779
+ name: item.icon ?? (hasChildren ? expanded ? "folder-open" : "folder" : "file"),
29780
+ size: "xs",
29781
+ className: hasChildren ? "text-[var(--color-warning)]" : "text-[var(--color-muted-foreground)]"
29782
+ }
29783
+ ),
29784
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "truncate font-mono text-xs", children: item.label })
29785
+ ]
29786
+ }
29787
+ ),
29788
+ hasChildren && expanded && /* @__PURE__ */ jsx(Box, { role: "group", children: children.map((child) => /* @__PURE__ */ jsx(
29789
+ FlatTreeNodeItem,
29790
+ {
29791
+ item: child,
29792
+ depth: depth + 1,
29793
+ indent,
29794
+ childrenByParent,
29795
+ onNodeSelect
29796
+ },
29797
+ child.id
29798
+ )) })
29799
+ ] });
29800
+ };
29717
29801
  FileTree = ({
29718
29802
  tree,
29803
+ items,
29719
29804
  selectedPath,
29720
29805
  onFileSelect,
29806
+ onNodeSelect,
29721
29807
  className,
29722
29808
  indent = 16
29723
29809
  }) => {
29724
- if (tree.length === 0) return null;
29810
+ if (items) {
29811
+ if (items.length === 0) return null;
29812
+ const ids = new Set(items.map((node) => node.id));
29813
+ const childrenByParent = /* @__PURE__ */ new Map();
29814
+ const roots = [];
29815
+ for (const item of items) {
29816
+ if (item.parentId && ids.has(item.parentId)) {
29817
+ const siblings = childrenByParent.get(item.parentId);
29818
+ if (siblings) siblings.push(item);
29819
+ else childrenByParent.set(item.parentId, [item]);
29820
+ } else {
29821
+ roots.push(item);
29822
+ }
29823
+ }
29824
+ return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: roots.map((item) => /* @__PURE__ */ jsx(
29825
+ FlatTreeNodeItem,
29826
+ {
29827
+ item,
29828
+ depth: 0,
29829
+ indent,
29830
+ childrenByParent,
29831
+ onNodeSelect,
29832
+ defaultExpanded: true
29833
+ },
29834
+ item.id
29835
+ )) });
29836
+ }
29837
+ if (!tree || tree.length === 0) return null;
29725
29838
  return /* @__PURE__ */ jsx(Box, { className: `py-1 overflow-y-auto ${className ?? ""}`, role: "tree", children: tree.map((node) => /* @__PURE__ */ jsx(
29726
29839
  TreeNodeItem,
29727
29840
  {
@@ -31116,7 +31229,7 @@ var init_debug = __esm({
31116
31229
  createLogger("almadar:ui:debug:game-state");
31117
31230
  }
31118
31231
  });
31119
- var isRelationsDebugEnabled, RelationSelect;
31232
+ var isRelationsDebugEnabled, MANY_CARDINALITIES, RelationSelect;
31120
31233
  var init_RelationSelect = __esm({
31121
31234
  "components/core/molecules/RelationSelect.tsx"() {
31122
31235
  "use client";
@@ -31130,6 +31243,11 @@ var init_RelationSelect = __esm({
31130
31243
  init_Typography();
31131
31244
  init_debug();
31132
31245
  isRelationsDebugEnabled = () => isDebugEnabled();
31246
+ MANY_CARDINALITIES = [
31247
+ "many",
31248
+ "one-to-many",
31249
+ "many-to-many"
31250
+ ];
31133
31251
  RelationSelect = ({
31134
31252
  value,
31135
31253
  onChange,
@@ -32769,6 +32887,9 @@ var init_MathCanvas = __esm({
32769
32887
  showAxes = true,
32770
32888
  showGrid = true,
32771
32889
  gridStep = 1,
32890
+ backgroundColor,
32891
+ gridColor = "var(--color-border, #9ca3af)",
32892
+ axisColor = "var(--color-muted-foreground, #374151)",
32772
32893
  showTickLabels = false,
32773
32894
  showCurveLabels = false,
32774
32895
  curves = [],
@@ -32791,17 +32912,21 @@ var init_MathCanvas = __esm({
32791
32912
  error
32792
32913
  }) => {
32793
32914
  const eventBus = useEventBus();
32915
+ const keyMapKey = keyMap ? JSON.stringify(keyMap) : null;
32916
+ const keyUpMapKey = keyUpMap ? JSON.stringify(keyUpMap) : null;
32917
+ const stableKeyMap = useMemo(() => keyMap, [keyMapKey]);
32918
+ const stableKeyUpMap = useMemo(() => keyUpMap, [keyUpMapKey]);
32794
32919
  useEffect(() => {
32795
- if (!keyMap && !keyUpMap) return;
32920
+ if (!stableKeyMap && !stableKeyUpMap) return;
32796
32921
  const onDown = (e) => {
32797
- const ev = keyMap?.[e.code];
32922
+ const ev = stableKeyMap?.[e.code];
32798
32923
  if (ev) {
32799
32924
  eventBus.emit(`UI:${ev}`, {});
32800
32925
  e.preventDefault();
32801
32926
  }
32802
32927
  };
32803
32928
  const onUp = (e) => {
32804
- const ev = keyUpMap?.[e.code];
32929
+ const ev = stableKeyUpMap?.[e.code];
32805
32930
  if (ev) eventBus.emit(`UI:${ev}`, {});
32806
32931
  };
32807
32932
  window.addEventListener("keydown", onDown);
@@ -32810,7 +32935,7 @@ var init_MathCanvas = __esm({
32810
32935
  window.removeEventListener("keydown", onDown);
32811
32936
  window.removeEventListener("keyup", onUp);
32812
32937
  };
32813
- }, [keyMap, keyUpMap, eventBus]);
32938
+ }, [stableKeyMap, stableKeyUpMap, eventBus]);
32814
32939
  const derivedShapes = useMemo(() => {
32815
32940
  const out = [];
32816
32941
  const margin = 24;
@@ -32823,11 +32948,11 @@ var init_MathCanvas = __esm({
32823
32948
  if (showGrid) {
32824
32949
  for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
32825
32950
  const px = mapX(x);
32826
- out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32951
+ out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color: gridColor, opacity: 0.35, lineWidth: 1 });
32827
32952
  }
32828
32953
  for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep) {
32829
32954
  const py = mapY(y);
32830
- out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#9ca3af", opacity: 0.35, lineWidth: 1 });
32955
+ out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: gridColor, opacity: 0.35, lineWidth: 1 });
32831
32956
  }
32832
32957
  }
32833
32958
  if (showTickLabels) {
@@ -32898,8 +33023,8 @@ var init_MathCanvas = __esm({
32898
33023
  });
32899
33024
  }
32900
33025
  if (showAxes) {
32901
- out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
32902
- out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
33026
+ out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: axisColor, lineWidth: 2 });
33027
+ out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: axisColor, lineWidth: 2 });
32903
33028
  }
32904
33029
  for (const guide of guides) {
32905
33030
  const color = guide.color ?? "#9ca3af";
@@ -32922,23 +33047,36 @@ var init_MathCanvas = __esm({
32922
33047
  }
32923
33048
  for (const curve of curves) {
32924
33049
  if (!curve.samples || curve.samples.length < 2) continue;
33050
+ let d = "";
33051
+ let penDown = false;
32925
33052
  let lastInRange;
32926
33053
  for (let i = 1; i < curve.samples.length; i++) {
32927
33054
  const a = curve.samples[i - 1];
32928
33055
  const b = curve.samples[i];
32929
- if (a.x < xMin || a.x > xMax || b.x < xMin || b.x > xMax) continue;
32930
- out.push({
32931
- type: "line",
32932
- x1: mapX(a.x),
32933
- y1: mapY(a.y),
32934
- x2: mapX(b.x),
32935
- y2: mapY(b.y),
32936
- color: curve.color ?? "#2563eb",
32937
- lineWidth: 2,
32938
- dash: curve.dash
32939
- });
32940
- lastInRange = b;
32941
- }
33056
+ if (a.x < xMin && b.x < xMin || a.x > xMax && b.x > xMax) {
33057
+ penDown = false;
33058
+ continue;
33059
+ }
33060
+ const clip = (p, q, xLim) => {
33061
+ const t = q.x === p.x ? 0 : (xLim - p.x) / (q.x - p.x);
33062
+ return { x: xLim, y: p.y + t * (q.y - p.y) };
33063
+ };
33064
+ const ca = a.x < xMin ? clip(a, b, xMin) : a.x > xMax ? clip(a, b, xMax) : a;
33065
+ const cb = b.x < xMin ? clip(a, b, xMin) : b.x > xMax ? clip(a, b, xMax) : b;
33066
+ const pax = mapX(ca.x);
33067
+ const pay = mapY(ca.y);
33068
+ d += `${penDown ? "L" : "M"} ${pax} ${pay} L ${mapX(cb.x)} ${mapY(cb.y)} `;
33069
+ penDown = true;
33070
+ if (b.x >= xMin && b.x <= xMax) lastInRange = b;
33071
+ }
33072
+ if (!d) continue;
33073
+ out.push({
33074
+ type: "path",
33075
+ path: d,
33076
+ color: curve.color ?? "#2563eb",
33077
+ lineWidth: 2,
33078
+ dash: curve.dash
33079
+ });
32942
33080
  if (showCurveLabels && curve.label && lastInRange) {
32943
33081
  out.push({
32944
33082
  type: "text",
@@ -33053,6 +33191,8 @@ var init_MathCanvas = __esm({
33053
33191
  showAxes,
33054
33192
  showGrid,
33055
33193
  gridStep,
33194
+ gridColor,
33195
+ axisColor,
33056
33196
  showTickLabels,
33057
33197
  showCurveLabels,
33058
33198
  curves,
@@ -33072,6 +33212,7 @@ var init_MathCanvas = __esm({
33072
33212
  {
33073
33213
  width,
33074
33214
  height,
33215
+ backgroundColor,
33075
33216
  shapes: derivedShapes,
33076
33217
  readouts,
33077
33218
  traces,
@@ -44822,6 +44963,9 @@ function determineInputType(field) {
44822
44963
  if (field.type === "relation" || field.relation) {
44823
44964
  return "relation";
44824
44965
  }
44966
+ if (field.type === "array") {
44967
+ return "array";
44968
+ }
44825
44969
  if (field.type === "enum" || field.values || getEnumOptions(field).length > 0) {
44826
44970
  return "select";
44827
44971
  }
@@ -44897,6 +45041,7 @@ var init_Form = __esm({
44897
45041
  init_Typography();
44898
45042
  init_Icon();
44899
45043
  init_RelationSelect();
45044
+ init_TagInput();
44900
45045
  init_UploadDropZone();
44901
45046
  init_Alert();
44902
45047
  init_useEventBus();
@@ -44976,7 +45121,7 @@ var init_Form = __esm({
44976
45121
  values: "values" in f3 ? f3.values : void 0,
44977
45122
  min: f3.min,
44978
45123
  max: f3.max,
44979
- relation: "relation" in f3 ? { entity: f3.relation.entity } : void 0
45124
+ relation: "relation" in f3 ? { entity: f3.relation.entity, cardinality: f3.relation.cardinality } : void 0
44980
45125
  })
44981
45126
  );
44982
45127
  }, [entity, fields]);
@@ -45211,7 +45356,7 @@ var init_Form = __esm({
45211
45356
  values: "values" in entityField ? entityField.values : void 0,
45212
45357
  min: entityField.min,
45213
45358
  max: entityField.max,
45214
- relation: "relation" in entityField ? { entity: entityField.relation.entity } : void 0
45359
+ relation: "relation" in entityField ? { entity: entityField.relation.entity, cardinality: entityField.relation.cardinality } : void 0
45215
45360
  };
45216
45361
  }
45217
45362
  return { name: field, type: "string" };
@@ -45323,6 +45468,22 @@ var init_Form = __esm({
45323
45468
  case "relation": {
45324
45469
  const relationOptions = relationsData[fieldName] || [];
45325
45470
  const relationLoading = relationsLoading[fieldName] || false;
45471
+ if (field.relation?.cardinality !== void 0 && MANY_CARDINALITIES.includes(field.relation.cardinality)) {
45472
+ const selectedValues = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : [];
45473
+ return /* @__PURE__ */ jsx(
45474
+ Select,
45475
+ {
45476
+ ...commonProps,
45477
+ multiple: true,
45478
+ searchable: true,
45479
+ clearable: true,
45480
+ options: [...relationOptions],
45481
+ value: selectedValues,
45482
+ onValueChange: (value) => handleChange(fieldName, Array.isArray(value) ? value : [value]),
45483
+ placeholder: field.placeholder || `Select ${label}...`
45484
+ }
45485
+ );
45486
+ }
45326
45487
  return /* @__PURE__ */ jsx(
45327
45488
  RelationSelect,
45328
45489
  {
@@ -45337,6 +45498,18 @@ var init_Form = __esm({
45337
45498
  }
45338
45499
  );
45339
45500
  }
45501
+ case "array": {
45502
+ const arrayValue = Array.isArray(currentValue) ? currentValue.map((v) => String(v)) : currentValue != null && currentValue !== "" ? [String(currentValue)] : [];
45503
+ return /* @__PURE__ */ jsx(
45504
+ TagInput,
45505
+ {
45506
+ placeholder: field.placeholder,
45507
+ disabled: isLoading,
45508
+ value: arrayValue,
45509
+ onChange: (next) => handleChange(fieldName, [...next])
45510
+ }
45511
+ );
45512
+ }
45340
45513
  case "number":
45341
45514
  return /* @__PURE__ */ jsx(
45342
45515
  Input,
@@ -50905,7 +51078,10 @@ function enrichFormFields(fields, entityDef) {
50905
51078
  enriched.values = entityField.enumValues;
50906
51079
  }
50907
51080
  if (entityField.relation) {
50908
- enriched.relation = entityField.relation.entity;
51081
+ enriched.relation = {
51082
+ entity: entityField.relation.entity,
51083
+ cardinality: entityField.relation.cardinality
51084
+ };
50909
51085
  }
50910
51086
  return enriched;
50911
51087
  }
@@ -50933,7 +51109,10 @@ function enrichFormFields(fields, entityDef) {
50933
51109
  }
50934
51110
  }
50935
51111
  if (!obj.relation && entityField.relation) {
50936
- enriched.relation = entityField.relation.entity;
51112
+ enriched.relation = {
51113
+ entity: entityField.relation.entity,
51114
+ cardinality: entityField.relation.cardinality
51115
+ };
50937
51116
  }
50938
51117
  return enriched;
50939
51118
  }
@@ -50948,7 +51127,12 @@ function enrichDetailFields(fields, entityDef) {
50948
51127
  const meta = { type: entityField.type };
50949
51128
  const values = entityField.values ?? entityField.enumValues;
50950
51129
  if (values && values.length > 0) meta.values = values;
50951
- if (entityField.relation) meta.relation = entityField.relation.entity;
51130
+ if (entityField.relation) {
51131
+ meta.relation = {
51132
+ entity: entityField.relation.entity,
51133
+ cardinality: entityField.relation.cardinality
51134
+ };
51135
+ }
50952
51136
  return meta;
50953
51137
  };
50954
51138
  return fields.map((field) => {
@@ -51502,6 +51686,32 @@ function isPlainConfigObject(value) {
51502
51686
  const proto = Object.getPrototypeOf(value);
51503
51687
  return proto === Object.prototype || proto === null;
51504
51688
  }
51689
+ function subtreeHasTraitRef(value) {
51690
+ const cached = traitRefPresenceCache.get(value);
51691
+ if (cached !== void 0) return cached;
51692
+ let found = false;
51693
+ const children = Array.isArray(value) ? value : Object.values(value);
51694
+ for (const child of children) {
51695
+ if (typeof child === "string" && TRAIT_BINDING_RE.test(child)) {
51696
+ found = true;
51697
+ break;
51698
+ }
51699
+ if (isRenderBindingMarker(child)) continue;
51700
+ if (Array.isArray(child)) {
51701
+ if (subtreeHasTraitRef(child)) {
51702
+ found = true;
51703
+ break;
51704
+ }
51705
+ } else if (child !== null && typeof child === "object" && isPlainConfigObject(child)) {
51706
+ if (subtreeHasTraitRef(child)) {
51707
+ found = true;
51708
+ break;
51709
+ }
51710
+ }
51711
+ }
51712
+ traitRefPresenceCache.set(value, found);
51713
+ return found;
51714
+ }
51505
51715
  function substituteTraitRefsDeep(value, pathKey) {
51506
51716
  if (isRenderBindingMarker(value)) return value;
51507
51717
  if (typeof value === "string") {
@@ -51516,11 +51726,13 @@ function substituteTraitRefsDeep(value, pathKey) {
51516
51726
  return value;
51517
51727
  }
51518
51728
  if (Array.isArray(value)) {
51729
+ if (!subtreeHasTraitRef(value)) return value;
51519
51730
  return value.map(
51520
51731
  (item, i) => substituteTraitRefsDeep(item, `${pathKey}[${i}]`)
51521
51732
  );
51522
51733
  }
51523
51734
  if (typeof value === "object" && isPlainConfigObject(value)) {
51735
+ if (!subtreeHasTraitRef(value)) return value;
51524
51736
  const out = {};
51525
51737
  for (const [k, v] of Object.entries(value)) {
51526
51738
  out[k] = substituteTraitRefsDeep(v, `${pathKey}.${k}`);
@@ -51809,7 +52021,7 @@ function UISlotRenderer({
51809
52021
  }
51810
52022
  return wrapped;
51811
52023
  }
51812
- var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN;
52024
+ var scopeWrapLog, TRAIT_BINDING_RE, SuspenseConfigContext, SlotContainedContext, SLOT_SKELETON_MAP, SELF_OVERLAY_PATTERNS, CONTENT_NODE_SLOTS, PATTERNS_WITH_CHILDREN, traitRefPresenceCache;
51813
52025
  var init_UISlotRenderer = __esm({
51814
52026
  "components/core/organisms/UISlotRenderer.tsx"() {
51815
52027
  "use client";
@@ -51883,6 +52095,7 @@ var init_UISlotRenderer = __esm({
51883
52095
  "alert",
51884
52096
  "dialog"
51885
52097
  ]);
52098
+ traitRefPresenceCache = /* @__PURE__ */ new WeakMap();
51886
52099
  UISlotRenderer.displayName = "UISlotRenderer";
51887
52100
  }
51888
52101
  });
@@ -55557,6 +55770,20 @@ function createClientEffectHandlers(options) {
55557
55770
  };
55558
55771
  }
55559
55772
 
55773
+ // lib/event-queue-coalesce.ts
55774
+ function enqueueEvent(queue, entry) {
55775
+ if (entry.tick !== void 0) {
55776
+ const pending = queue.find(
55777
+ (e) => e.tick !== void 0 && e.eventKey === entry.eventKey && e.targetTrait === entry.targetTrait
55778
+ );
55779
+ if (pending !== void 0) {
55780
+ pending.payload = entry.payload;
55781
+ return;
55782
+ }
55783
+ }
55784
+ queue.push(entry);
55785
+ }
55786
+
55560
55787
  // hooks/useTraitStateMachine.ts
55561
55788
  init_traitRegistry();
55562
55789
  init_verificationRegistry();
@@ -56131,12 +56358,13 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56131
56358
  entityId
56132
56359
  };
56133
56360
  const emittedDuringExec = [];
56361
+ const tickName = flushEvent.startsWith("tick:") ? flushEvent.slice(5) : void 0;
56134
56362
  const baseEmit = handlers.emit;
56135
56363
  const trackingHandlers = {
56136
56364
  ...handlers,
56137
56365
  emit: (event, eventPayload, source) => {
56138
56366
  emittedDuringExec.push(event);
56139
- baseEmit(event, eventPayload, source);
56367
+ baseEmit(event, eventPayload, tickName !== void 0 ? { ...source, tick: tickName } : source);
56140
56368
  }
56141
56369
  };
56142
56370
  if (traitName === "Hero") {
@@ -56295,7 +56523,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56295
56523
  }
56296
56524
  return () => scheduler.stopAll();
56297
56525
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
56298
- const processEventQueued = useCallback(async (eventKey, payload, targetTrait) => {
56526
+ const processEventQueued = useCallback(async (eventKey, payload, targetTrait, tick, sourceTrait) => {
56299
56527
  const normalizedEvent = normalizeEventKey(eventKey);
56300
56528
  const bindings = traitBindingsRef.current;
56301
56529
  const currentManager = managerRef.current;
@@ -56484,7 +56712,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56484
56712
  if (orbital) dispatchedOrbitals.add(orbital);
56485
56713
  }
56486
56714
  const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
56487
- await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
56715
+ if (tick !== void 0) {
56716
+ void onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals, tick, sourceTrait);
56717
+ } else {
56718
+ await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
56719
+ }
56488
56720
  }
56489
56721
  }, [entities, eventBus, sharedEntityStore]);
56490
56722
  const drainEventQueue = useCallback(async () => {
@@ -56493,14 +56725,14 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56493
56725
  try {
56494
56726
  while (eventQueueRef.current.length > 0) {
56495
56727
  const entry = eventQueueRef.current.shift();
56496
- await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
56728
+ await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait, entry.tick, entry.sourceTrait);
56497
56729
  }
56498
56730
  } finally {
56499
56731
  processingRef.current = false;
56500
56732
  }
56501
56733
  }, [processEventQueued]);
56502
- const enqueueAndDrain = useCallback((eventKey, payload, targetTrait) => {
56503
- eventQueueRef.current.push({ eventKey, payload, targetTrait });
56734
+ const enqueueAndDrain = useCallback((eventKey, payload, targetTrait, tick, sourceTrait) => {
56735
+ enqueueEvent(eventQueueRef.current, { eventKey, payload, targetTrait, tick, sourceTrait });
56504
56736
  void drainEventQueue();
56505
56737
  }, [drainEventQueue]);
56506
56738
  useCallback((eventKey, payload) => {
@@ -56553,7 +56785,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56553
56785
  crossTraitLog.debug("self:fire-server-cascade", { traitName, busKey: selfBusKey, eventKey });
56554
56786
  }
56555
56787
  crossTraitLog.debug("self:fire", { traitName, busKey: selfBusKey, eventKey });
56556
- enqueueAndDrain(eventKey, event.payload, traitName);
56788
+ enqueueAndDrain(eventKey, event.payload, traitName, event.source?.tick, event.source?.trait);
56557
56789
  });
56558
56790
  unsubscribes.push(() => {
56559
56791
  crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
@@ -56571,7 +56803,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56571
56803
  const bareKey = `UI:${eventKey}`;
56572
56804
  const unsub = eventBus.on(bareKey, (event) => {
56573
56805
  crossTraitLog.debug("bare-cascade:fire", { bareKey, eventKey });
56574
- enqueueAndDrain(eventKey, event.payload);
56806
+ enqueueAndDrain(eventKey, event.payload, void 0, event.source?.tick, event.source?.trait);
56575
56807
  });
56576
56808
  unsubscribes.push(() => {
56577
56809
  crossTraitLog.debug("bare-cascade:unsubscribe", { bareKey, eventKey });
@@ -56605,7 +56837,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
56605
56837
  enqueueAndDrain(
56606
56838
  listen.triggers,
56607
56839
  applyListenPayloadMapping(listen.payloadMapping, event.payload, evaluateListenPayloadExpr),
56608
- binding.trait.name
56840
+ binding.trait.name,
56841
+ event.source?.tick,
56842
+ event.source?.trait
56609
56843
  );
56610
56844
  });
56611
56845
  unsubscribes.push(() => {
@@ -56797,7 +57031,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
56797
57031
  [serverActiveTraits]
56798
57032
  );
56799
57033
  const uiSlots = useUISlots();
56800
- const onEventProcessed = useCallback(async (event, payload, dispatchedOrbitals) => {
57034
+ const onEventProcessed = useCallback(async (event, payload, dispatchedOrbitals, tick, sourceTrait) => {
56801
57035
  if (!bridge.connected || !orbitalNames?.length) return;
56802
57036
  const targets = dispatchedOrbitals && dispatchedOrbitals.size > 0 ? orbitalNames.filter((n) => dispatchedOrbitals.has(n)) : orbitalNames;
56803
57037
  xOrbitalLog.debug("TraitInitializer:fanout", () => ({
@@ -56807,6 +57041,10 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
56807
57041
  dispatchedOrbitalsSize: dispatchedOrbitals?.size ?? 0
56808
57042
  }));
56809
57043
  for (const name of targets) {
57044
+ if (tick !== void 0) {
57045
+ void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
57046
+ continue;
57047
+ }
56810
57048
  const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
56811
57049
  recordServerResponse(name, event, meta);
56812
57050
  applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
@@ -57282,11 +57520,13 @@ function BrowserPlayground({
57282
57520
  unregister: async () => {
57283
57521
  runtime.unregisterAll();
57284
57522
  },
57285
- sendEvent: async (orbitalName, event, payload) => {
57523
+ sendEvent: async (orbitalName, event, payload, _clientId, tick, sourceTrait) => {
57286
57524
  await registrationReady;
57287
57525
  return runtime.processOrbitalEvent(orbitalName, {
57288
57526
  event,
57289
- payload
57527
+ payload,
57528
+ tick,
57529
+ sourceTrait
57290
57530
  // @almadar/runtime OrbitalEventResponse.clientEffects uses a wider ClientEffectTuple than
57291
57531
  // ServerBridge's local definition — cast at this boundary (upstream fix queued).
57292
57532
  });