@almadar/ui 5.121.3 → 5.121.4

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.
@@ -1,6 +1,6 @@
1
1
  import * as React81 from 'react';
2
- import React81__default, { useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, createContext, useSyncExternalStore, lazy, useLayoutEffect, useId } from 'react';
3
- import { EventBusContext, useTraitScope, useEntitySchemaOptional, useEntitySchema, getAllPages, OrbitalProvider, TraitScopeProvider, ServerBridgeProvider, useCurrentPagePath, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge } from '@almadar/ui/providers';
2
+ import React81__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, lazy, useLayoutEffect, useId } from 'react';
3
+ import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntitySchema, getAllPages, OrbitalProvider, TraitScopeProvider, ServerBridgeProvider, useCurrentPagePath, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge } from '@almadar/ui/providers';
4
4
  export { EntitySchemaProvider, ServerBridgeProvider, TraitContext, TraitProvider, useEntitySchema, useEntitySchemaOptional, useServerBridge, useTrait, useTraitContext } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import { clsx } from 'clsx';
@@ -39,7 +39,7 @@ import ReactMarkdown from 'react-markdown';
39
39
  import remarkGfm from 'remark-gfm';
40
40
  import remarkMath from 'remark-math';
41
41
  import rehypeKatex from 'rehype-katex';
42
- import { isCircuitEvent, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, mergeEntityFrame, isInlineTrait, isSExpr, isEventPayloadValue } from '@almadar/core';
42
+ import { mergeEntityFrame, isCircuitEvent, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, walkSExpr, buildResolvedTraitConfigs, isInlineTrait, isSExpr, isEventPayloadValue } from '@almadar/core';
43
43
  import { DndContext, pointerWithin, rectIntersection, closestCorners, useSensors, useSensor, PointerSensor, KeyboardSensor, useDroppable } from '@dnd-kit/core';
44
44
  import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
45
45
  import { CSS } from '@dnd-kit/utilities';
@@ -98,9 +98,9 @@ function getGlobalEventBus() {
98
98
  function useEventBus() {
99
99
  const context = useContext(EventBusContext);
100
100
  const baseBus = context ?? getGlobalEventBus() ?? fallbackEventBus;
101
- const scope = useTraitScope();
101
+ const chain = useTraitScopeChain();
102
102
  return useMemo(() => {
103
- if (!scope) {
103
+ if (chain.length === 0) {
104
104
  return {
105
105
  ...baseBus,
106
106
  emit: (type, payload, source) => {
@@ -116,22 +116,31 @@ function useEventBus() {
116
116
  emit: (type, payload, source) => {
117
117
  if (typeof type === "string" && type.startsWith("UI:")) {
118
118
  const tail = type.slice(3);
119
- const qualified = tail.includes(".") ? type : `UI:${scope.orbital}.${scope.trait}.${tail}`;
120
- if (qualified !== type) {
121
- scopeLog.info("emit:qualified", {
119
+ const isQualified = tail.includes(".");
120
+ const event = isQualified ? tail.split(".").slice(2).join(".") : tail;
121
+ if (!event) {
122
+ baseBus.emit(type, payload, source);
123
+ return;
124
+ }
125
+ const keys = /* @__PURE__ */ new Set();
126
+ if (isQualified) keys.add(type);
127
+ for (const sc of chain) {
128
+ keys.add(`UI:${sc.orbital}.${sc.trait}.${event}`);
129
+ }
130
+ if (keys.size > 1) {
131
+ scopeLog.info("emit:fan-out", {
122
132
  from: type,
123
- to: qualified,
124
- scopeOrbital: scope.orbital,
125
- scopeTrait: scope.trait
133
+ keys: Array.from(keys),
134
+ chainDepth: chain.length
126
135
  });
127
136
  }
128
- baseBus.emit(qualified, payload, source);
137
+ for (const key of keys) baseBus.emit(key, payload, source);
129
138
  return;
130
139
  }
131
140
  baseBus.emit(type, payload, source);
132
141
  }
133
142
  };
134
- }, [baseBus, scope]);
143
+ }, [baseBus, chain]);
135
144
  }
136
145
  function useEventListener(event, handler) {
137
146
  const eventBus = useEventBus();
@@ -1148,13 +1157,13 @@ var init_Icon = __esm({
1148
1157
  style
1149
1158
  }) => {
1150
1159
  const directIcon = typeof icon === "string" ? void 0 : icon;
1151
- const effectiveName = typeof icon === "string" ? icon : name;
1160
+ const effectiveName = typeof icon === "string" && icon !== "" ? icon : name;
1161
+ const effectiveStrokeWidth = strokeWidth != null && strokeWidth > 0 ? strokeWidth : void 0;
1152
1162
  const family = useIconFamily();
1153
1163
  const RenderedComponent = React81__default.useMemo(() => {
1154
1164
  if (directIcon) return null;
1155
1165
  return effectiveName ? resolveIconForFamily(effectiveName) : null;
1156
1166
  }, [directIcon, effectiveName, family]);
1157
- const effectiveStrokeWidth = strokeWidth ?? void 0;
1158
1167
  const inlineStyle = {
1159
1168
  ...effectiveStrokeWidth === void 0 ? { strokeWidth: "var(--icon-stroke-width, 2)" } : {},
1160
1169
  ...style
@@ -1754,6 +1763,15 @@ var init_Modal = __esm({
1754
1763
  const [dragY, setDragY] = useState(0);
1755
1764
  const dragStartY = useRef(0);
1756
1765
  const isDragging = useRef(false);
1766
+ const [closing, setClosing] = useState(false);
1767
+ const wasOpenRef = useRef(isOpen);
1768
+ useEffect(() => {
1769
+ if (wasOpenRef.current && !isOpen) setClosing(true);
1770
+ wasOpenRef.current = isOpen;
1771
+ }, [isOpen]);
1772
+ const handleAnimEnd = (e) => {
1773
+ if (closing && e.target === e.currentTarget) setClosing(false);
1774
+ };
1757
1775
  useEffect(() => {
1758
1776
  if (isOpen) {
1759
1777
  previousActiveElement.current = document.activeElement;
@@ -1787,7 +1805,11 @@ var init_Modal = __esm({
1787
1805
  document.body.style.overflow = "";
1788
1806
  };
1789
1807
  }, [isOpen]);
1790
- if (!isOpen || typeof document === "undefined") return null;
1808
+ if (typeof document === "undefined") return null;
1809
+ const renderOpen = isOpen || closing;
1810
+ if (!renderOpen) return null;
1811
+ const dialogAnim = closing ? "animate-modal-out" : "animate-modal-in";
1812
+ const overlayAnim = closing ? "animate-overlay-out" : "animate-overlay-in";
1791
1813
  const handleClose = () => {
1792
1814
  if (closeEvent) eventBus.emit(`UI:${closeEvent}`, {});
1793
1815
  onClose();
@@ -1804,7 +1826,8 @@ var init_Modal = __esm({
1804
1826
  className: cn(
1805
1827
  "fixed inset-0 z-[1000]",
1806
1828
  "flex items-start justify-center px-4 pb-4 pt-[10vh]",
1807
- "max-sm:items-stretch max-sm:p-0 max-sm:pt-0"
1829
+ "max-sm:items-stretch max-sm:p-0 max-sm:pt-0",
1830
+ overlayAnim
1808
1831
  ),
1809
1832
  style: { backgroundColor: "rgba(0, 0, 0, 0.6)" },
1810
1833
  onClick: handleOverlayClick,
@@ -1832,8 +1855,10 @@ var init_Modal = __esm({
1832
1855
  // full height, no rounded corners, no min-width.
1833
1856
  "max-sm:max-w-none max-sm:max-h-none max-sm:w-full max-sm:h-full max-sm:rounded-none",
1834
1857
  lookStyles[look],
1835
- className
1858
+ className,
1859
+ dialogAnim
1836
1860
  ),
1861
+ onAnimationEnd: handleAnimEnd,
1837
1862
  style: dragY > 0 ? {
1838
1863
  transform: `translateY(${dragY}px)`,
1839
1864
  transition: isDragging.current ? "none" : "transform 200ms ease-out"
@@ -1942,6 +1967,7 @@ var init_Overlay = __esm({
1942
1967
  className: cn(
1943
1968
  "fixed inset-0 z-40",
1944
1969
  blur && "backdrop-blur-sm",
1970
+ "animate-overlay-in",
1945
1971
  className
1946
1972
  ),
1947
1973
  style: { backgroundColor: "rgba(0, 0, 0, 0.6)" },
@@ -3354,7 +3380,7 @@ var init_Input = __esm({
3354
3380
  eventBus.emit(`UI:${action}`, { value: e.currentTarget.value });
3355
3381
  }
3356
3382
  };
3357
- const wrapField = (field) => /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
3383
+ const wrapField = (field, fullWidth = true) => /* @__PURE__ */ jsxs("div", { className: fullWidth ? "w-full" : "w-fit", children: [
3358
3384
  label && /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
3359
3385
  field,
3360
3386
  (helperText || error) && /* @__PURE__ */ jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
@@ -3414,7 +3440,8 @@ var init_Input = __esm({
3414
3440
  ),
3415
3441
  ...props
3416
3442
  }
3417
- )
3443
+ ),
3444
+ false
3418
3445
  );
3419
3446
  }
3420
3447
  return wrapField(
@@ -5742,6 +5769,18 @@ var init_RangeSlider = __esm({
5742
5769
  function easeOut(t) {
5743
5770
  return t * (2 - t);
5744
5771
  }
5772
+ function formatDisplay(value, format, decimals) {
5773
+ switch (format) {
5774
+ case "currency":
5775
+ return `$${value.toFixed(2)}`;
5776
+ case "percent":
5777
+ return `${Math.round(value)}%`;
5778
+ case "number":
5779
+ return value.toLocaleString();
5780
+ default:
5781
+ return value.toFixed(decimals);
5782
+ }
5783
+ }
5745
5784
  var AnimatedCounter;
5746
5785
  var init_AnimatedCounter = __esm({
5747
5786
  "components/marketing/atoms/AnimatedCounter.tsx"() {
@@ -5753,6 +5792,7 @@ var init_AnimatedCounter = __esm({
5753
5792
  duration = 600,
5754
5793
  prefix,
5755
5794
  suffix,
5795
+ format,
5756
5796
  className
5757
5797
  }) => {
5758
5798
  const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
@@ -5768,6 +5808,10 @@ var init_AnimatedCounter = __esm({
5768
5808
  setDisplayValue(to);
5769
5809
  return;
5770
5810
  }
5811
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
5812
+ setDisplayValue(to);
5813
+ return;
5814
+ }
5771
5815
  const startTime = performance.now();
5772
5816
  const diff = to - from;
5773
5817
  function animate(currentTime) {
@@ -5789,7 +5833,7 @@ var init_AnimatedCounter = __esm({
5789
5833
  };
5790
5834
  }, [value, duration]);
5791
5835
  const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
5792
- const formattedValue = displayValue.toFixed(decimalPlaces);
5836
+ const formattedValue = formatDisplay(displayValue, format, decimalPlaces);
5793
5837
  return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
5794
5838
  prefix,
5795
5839
  formattedValue,
@@ -7033,14 +7077,29 @@ function recordTransition(trace) {
7033
7077
  "pass"
7034
7078
  );
7035
7079
  } else {
7036
- const hasRenderUI = entry.effects.some((e) => e.type === "render-ui");
7037
- if (hasRenderUI) {
7038
- registerCheck(
7039
- checkId,
7040
- `INIT transition for "${entry.traitName}" missing fetch effect`,
7041
- "fail",
7042
- "Entity-bound render-ui without a fetch effect will show empty data"
7080
+ const rendersEntityData = entry.effects.some(
7081
+ (e) => e.type === "render-ui" && JSON.stringify(e.args).includes("@entity.")
7082
+ );
7083
+ if (rendersEntityData) {
7084
+ const siblingSeeds = getState().transitions.some(
7085
+ (t) => t.event === "INIT" && t.effects.some(
7086
+ (e) => e.type === "fetch" || e.type === "set" && typeof e.args[0] === "string" && e.args[0].startsWith("@entity.")
7087
+ )
7043
7088
  );
7089
+ if (siblingSeeds) {
7090
+ registerCheck(
7091
+ checkId,
7092
+ `INIT transition for "${entry.traitName}" has sibling entity-seed (shared entity)`,
7093
+ "pass"
7094
+ );
7095
+ } else {
7096
+ registerCheck(
7097
+ checkId,
7098
+ `INIT transition for "${entry.traitName}" missing fetch effect`,
7099
+ "fail",
7100
+ "Entity-bound render-ui without a fetch effect will show empty data"
7101
+ );
7102
+ }
7044
7103
  }
7045
7104
  }
7046
7105
  }
@@ -8185,7 +8244,7 @@ function GameMenu({
8185
8244
  logo,
8186
8245
  className
8187
8246
  }) {
8188
- const resolvedOptions = options ?? menuItems ?? DEFAULT_MENU_OPTIONS;
8247
+ const resolvedOptions = (options?.length ? options : void 0) ?? (menuItems?.length ? menuItems : void 0) ?? DEFAULT_MENU_OPTIONS;
8189
8248
  const eventBus = useEventBus();
8190
8249
  const handleOptionClick = React81.useCallback(
8191
8250
  (option) => {
@@ -9476,6 +9535,10 @@ function Canvas2D({
9476
9535
  window.removeEventListener("keyup", onUp);
9477
9536
  };
9478
9537
  }, [keyMap, keyUpMap, eventBus]);
9538
+ useEffect(() => {
9539
+ if (!keyMap && !keyUpMap) return;
9540
+ canvasRef.current?.focus();
9541
+ }, [keyMap, keyUpMap]);
9479
9542
  if (error) {
9480
9543
  return /* @__PURE__ */ jsx(Box, { className: cn("flex items-center justify-center w-full h-full bg-[var(--color-card)] rounded-container", className), children: /* @__PURE__ */ jsxs(Stack, { direction: "vertical", gap: "md", align: "center", children: [
9481
9544
  /* @__PURE__ */ jsx(Icon, { name: "alert-circle", size: "xl" }),
@@ -9524,7 +9587,7 @@ function Canvas2D({
9524
9587
  onWheel: gestureHandlers.onWheel,
9525
9588
  onContextMenu: (e) => e.preventDefault(),
9526
9589
  className: "cursor-pointer touch-none",
9527
- tabIndex: isFree ? 0 : void 0,
9590
+ tabIndex: isFree || keyMap || keyUpMap ? 0 : void 0,
9528
9591
  style: {
9529
9592
  width: viewportSize.width,
9530
9593
  height: viewportSize.height
@@ -13775,7 +13838,8 @@ var init_LoadingState = __esm({
13775
13838
  LoadingState = ({
13776
13839
  title,
13777
13840
  message,
13778
- className
13841
+ className,
13842
+ fullPage = false
13779
13843
  }) => {
13780
13844
  const { t } = useTranslate();
13781
13845
  const displayMessage = message ?? t("common.loading");
@@ -13784,7 +13848,8 @@ var init_LoadingState = __esm({
13784
13848
  {
13785
13849
  align: "center",
13786
13850
  className: cn(
13787
- "justify-center py-12",
13851
+ "justify-center",
13852
+ fullPage ? "fixed inset-0 z-[1000] py-12 bg-background/80 backdrop-blur-sm animate-overlay-in" : "py-12",
13788
13853
  className
13789
13854
  ),
13790
13855
  children: [
@@ -16936,7 +17001,7 @@ var init_BookCoverPage = __esm({
16936
17001
  size: "lg",
16937
17002
  action: "BOOK_START",
16938
17003
  className: "mt-8",
16939
- children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: t("book.startReading") })
17004
+ children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "inherit", children: t("book.startReading") })
16940
17005
  }
16941
17006
  )
16942
17007
  ]
@@ -19181,8 +19246,9 @@ var init_Chart = __esm({
19181
19246
  align: "center",
19182
19247
  flex: true,
19183
19248
  className: "min-w-0",
19249
+ style: { height: "100%" },
19184
19250
  children: [
19185
- /* @__PURE__ */ jsx(HStack, { gap: histogram ? "none" : "xs", align: "end", className: "w-full", style: { height: "100%" }, children: series.map((s, sIdx) => {
19251
+ /* @__PURE__ */ jsx(HStack, { gap: histogram ? "none" : "xs", align: "end", justify: histogram ? void 0 : "center", className: "w-full flex-1 min-h-0", children: series.map((s, sIdx) => {
19186
19252
  const value = valueAt(s, label);
19187
19253
  const barHeight = value / maxValue * 100;
19188
19254
  const color = seriesColor(s, sIdx);
@@ -19195,6 +19261,8 @@ var init_Chart = __esm({
19195
19261
  ),
19196
19262
  style: {
19197
19263
  height: `${barHeight}%`,
19264
+ // Cap width so a lone category doesn't paint the whole plot as one solid block; narrow columns are unaffected (flex-1 binds first).
19265
+ ...!histogram && { maxWidth: 72 },
19198
19266
  backgroundColor: color
19199
19267
  },
19200
19268
  onClick: () => onPointClick?.(
@@ -19229,8 +19297,9 @@ var init_Chart = __esm({
19229
19297
  align: "center",
19230
19298
  flex: true,
19231
19299
  className: "min-w-0",
19300
+ style: { height: "100%" },
19232
19301
  children: [
19233
- /* @__PURE__ */ jsx(VStack, { gap: "none", className: "w-full", style: { height: "100%" }, justify: "end", children: series.map((s, sIdx) => {
19302
+ /* @__PURE__ */ jsx(VStack, { gap: "none", className: "w-full flex-1 min-h-0", justify: "end", children: series.map((s, sIdx) => {
19234
19303
  const value = valueAt(s, label);
19235
19304
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
19236
19305
  const color = seriesColor(s, sIdx);
@@ -19275,6 +19344,7 @@ var init_Chart = __esm({
19275
19344
  const innerRadius = donut ? radius * 0.6 : 0;
19276
19345
  const center = size / 2;
19277
19346
  const segments = useMemo(() => {
19347
+ if (!Number.isFinite(total) || total <= 0) return [];
19278
19348
  let currentAngle = -Math.PI / 2;
19279
19349
  return data.map((point, idx) => {
19280
19350
  const angle = point.value / total * 2 * Math.PI;
@@ -19286,13 +19356,25 @@ var init_Chart = __esm({
19286
19356
  const y1 = center + radius * Math.sin(startAngle);
19287
19357
  const x2 = center + radius * Math.cos(endAngle);
19288
19358
  const y2 = center + radius * Math.sin(endAngle);
19359
+ const fullCircle = angle >= 2 * Math.PI - 1e-9;
19360
+ const midAngle = startAngle + angle / 2;
19361
+ const xm = center + radius * Math.cos(midAngle);
19362
+ const ym = center + radius * Math.sin(midAngle);
19289
19363
  let d;
19290
19364
  if (innerRadius > 0) {
19291
19365
  const ix1 = center + innerRadius * Math.cos(startAngle);
19292
19366
  const iy1 = center + innerRadius * Math.sin(startAngle);
19293
19367
  const ix2 = center + innerRadius * Math.cos(endAngle);
19294
19368
  const iy2 = center + innerRadius * Math.sin(endAngle);
19295
- d = `M ${ix1} ${iy1} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} L ${ix2} ${iy2} A ${innerRadius} ${innerRadius} 0 ${largeArc} 0 ${ix1} ${iy1} Z`;
19369
+ if (fullCircle) {
19370
+ const ixm = center + innerRadius * Math.cos(midAngle);
19371
+ const iym = center + innerRadius * Math.sin(midAngle);
19372
+ d = `M ${ix1} ${iy1} L ${x1} ${y1} A ${radius} ${radius} 0 1 1 ${xm} ${ym} A ${radius} ${radius} 0 1 1 ${x2} ${y2} L ${ix2} ${iy2} A ${innerRadius} ${innerRadius} 0 1 0 ${ixm} ${iym} A ${innerRadius} ${innerRadius} 0 1 0 ${ix1} ${iy1} Z`;
19373
+ } else {
19374
+ d = `M ${ix1} ${iy1} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} L ${ix2} ${iy2} A ${innerRadius} ${innerRadius} 0 ${largeArc} 0 ${ix1} ${iy1} Z`;
19375
+ }
19376
+ } else if (fullCircle) {
19377
+ d = `M ${center} ${center} L ${x1} ${y1} A ${radius} ${radius} 0 1 1 ${xm} ${ym} A ${radius} ${radius} 0 1 1 ${x2} ${y2} Z`;
19296
19378
  } else {
19297
19379
  d = `M ${center} ${center} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`;
19298
19380
  }
@@ -22305,10 +22387,10 @@ function DataGrid({
22305
22387
  ] }),
22306
22388
  badgeFields.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap", children: badgeFields.map((field) => {
22307
22389
  const val = getNestedValue(itemData, field.name);
22308
- if (val === void 0 || val === null) return null;
22390
+ if (val === void 0 || val === null || val === "") return null;
22309
22391
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
22310
22392
  field.icon && renderIconInput(field.icon, { size: "xs" }),
22311
- /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: String(val) })
22393
+ /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: formatValue(val, field.format) })
22312
22394
  ] }, field.name);
22313
22395
  }) })
22314
22396
  ] }),
@@ -29596,6 +29678,11 @@ var init_VoteStack = __esm({
29596
29678
  {
29597
29679
  className: cn(
29598
29680
  "inline-flex items-center justify-center",
29681
+ // Shrink-wrap in stretch contexts (slot/sidecar wrappers are
29682
+ // flex-column with stretch alignment): without this the root spans
29683
+ // the full wrapper width and the count row's `w-full` turns the
29684
+ // compact pill into a page-wide band.
29685
+ "w-fit",
29599
29686
  variant === "vertical" ? "flex-col" : "flex-row",
29600
29687
  "rounded-sm",
29601
29688
  "border-[length:var(--border-width)] border-border",
@@ -33501,7 +33588,7 @@ var init_Sidebar = __esm({
33501
33588
  isActive ? [
33502
33589
  "bg-primary text-primary-foreground",
33503
33590
  "font-medium shadow-sm",
33504
- "border-primary translate-x-1 -translate-y-0.5"
33591
+ "border-primary translate-x-1 rtl:-translate-x-1 -translate-y-0.5"
33505
33592
  ].join(" ") : [
33506
33593
  "text-foreground",
33507
33594
  "hover:bg-muted hover:border-border",
@@ -33511,10 +33598,10 @@ var init_Sidebar = __esm({
33511
33598
  title: collapsed ? item.label : void 0,
33512
33599
  children: [
33513
33600
  item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm", className: cn("min-w-[20px] flex-shrink-0", isActive && "text-primary-foreground") }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm", className: cn("min-w-[20px] flex-shrink-0", isActive && "text-primary-foreground") })),
33514
- !collapsed && /* @__PURE__ */ jsx(Typography, { variant: "body", color: isActive ? "inherit" : "primary", className: "font-medium truncate flex-1 text-left", children: item.label }),
33601
+ !collapsed && /* @__PURE__ */ jsx(Typography, { variant: "body", color: isActive ? "inherit" : "primary", className: "font-medium truncate flex-1 text-start", children: item.label }),
33515
33602
  !collapsed && item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "danger", size: "sm", children: item.badge }),
33516
33603
  collapsed && /* @__PURE__ */ jsx(Box, { className: cn(
33517
- "absolute left-full ml-2 px-2 py-1 text-xs opacity-0 group-hover:opacity-100",
33604
+ "absolute start-full ms-2 px-2 py-1 text-xs opacity-0 group-hover:opacity-100",
33518
33605
  "pointer-events-none whitespace-nowrap z-50 transition-opacity",
33519
33606
  "bg-primary text-primary-foreground",
33520
33607
  "border-[length:var(--border-width-thin)] border-border",
@@ -33569,7 +33656,7 @@ var init_Sidebar = __esm({
33569
33656
  as: "aside",
33570
33657
  className: cn(
33571
33658
  "flex flex-col h-full",
33572
- "bg-card border-r border-border",
33659
+ "bg-card border-e border-border",
33573
33660
  "transition-all duration-300 ease-in-out",
33574
33661
  collapsed ? "w-20" : "w-64",
33575
33662
  className
@@ -34730,7 +34817,7 @@ function resolveColor3(color, el) {
34730
34817
  function truncateLabel(label) {
34731
34818
  return label.length > MAX_LABEL_CHARS ? label.slice(0, MAX_LABEL_CHARS - 1) + "\u2026" : label;
34732
34819
  }
34733
- var GROUP_COLORS2, GROUP_GRAVITY, UNGROUPED_GRAVITY, labelMeasureCtx, MAX_LABEL_CHARS, GraphCanvas;
34820
+ var GROUP_COLORS2, GROUP_GRAVITY, UNGROUPED_GRAVITY, labelMeasureCtx, MAX_LABEL_CHARS, NO_NODES, NO_EDGES, NO_SIM, GraphCanvas;
34734
34821
  var init_GraphCanvas = __esm({
34735
34822
  "components/core/molecules/GraphCanvas.tsx"() {
34736
34823
  "use client";
@@ -34754,11 +34841,14 @@ var init_GraphCanvas = __esm({
34754
34841
  UNGROUPED_GRAVITY = 0.02;
34755
34842
  labelMeasureCtx = null;
34756
34843
  MAX_LABEL_CHARS = 22;
34844
+ NO_NODES = [];
34845
+ NO_EDGES = [];
34846
+ NO_SIM = [];
34757
34847
  GraphCanvas = ({
34758
34848
  title,
34759
- nodes: propNodes = [],
34760
- edges: propEdges = [],
34761
- similarity: propSimilarity = [],
34849
+ nodes: propNodes = NO_NODES,
34850
+ edges: propEdges = NO_EDGES,
34851
+ similarity: propSimilarity = NO_SIM,
34762
34852
  height = 400,
34763
34853
  showLabels = true,
34764
34854
  interactive = true,
@@ -34808,6 +34898,7 @@ var init_GraphCanvas = __esm({
34808
34898
  const interactionRef = useRef({
34809
34899
  mode: "none",
34810
34900
  dragNodeId: null,
34901
+ pressedNodeId: null,
34811
34902
  startMouse: { x: 0, y: 0 },
34812
34903
  startOffset: { x: 0, y: 0 },
34813
34904
  downPos: { x: 0, y: 0 }
@@ -34855,6 +34946,11 @@ var init_GraphCanvas = __esm({
34855
34946
  () => [...new Set(propNodes.map((n) => n.group).filter(Boolean))],
34856
34947
  [propNodes]
34857
34948
  );
34949
+ const nodesKey = useMemo(() => propNodes.map((n) => n.id).join(""), [propNodes]);
34950
+ const edgesKey = useMemo(
34951
+ () => propEdges.map((e) => `${e.source}${e.target}`).join(""),
34952
+ [propEdges]
34953
+ );
34858
34954
  useEffect(() => {
34859
34955
  const canvas = canvasRef.current;
34860
34956
  if (!canvas || propNodes.length === 0) return;
@@ -35035,7 +35131,7 @@ var init_GraphCanvas = __esm({
35035
35131
  return () => {
35036
35132
  cancelAnimationFrame(animRef.current);
35037
35133
  };
35038
- }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35134
+ }, [nodesKey, edgesKey, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35039
35135
  useEffect(() => {
35040
35136
  const canvas = canvasRef.current;
35041
35137
  if (!canvas) return;
@@ -35165,6 +35261,7 @@ var init_GraphCanvas = __esm({
35165
35261
  const cancelSinglePointer = useCallback(() => {
35166
35262
  interactionRef.current.mode = "none";
35167
35263
  interactionRef.current.dragNodeId = null;
35264
+ interactionRef.current.pressedNodeId = null;
35168
35265
  }, []);
35169
35266
  const handlePointerDown = useCallback(
35170
35267
  (e) => {
@@ -35175,6 +35272,7 @@ var init_GraphCanvas = __esm({
35175
35272
  state.downPos = { x: e.clientX, y: e.clientY };
35176
35273
  state.startMouse = { x: e.clientX, y: e.clientY };
35177
35274
  state.startOffset = { ...offset };
35275
+ state.pressedNodeId = node?.id ?? null;
35178
35276
  if (draggable && node) {
35179
35277
  state.mode = "dragging";
35180
35278
  state.dragNodeId = node.id;
@@ -35226,7 +35324,8 @@ var init_GraphCanvas = __esm({
35226
35324
  if (moved < 4) {
35227
35325
  const coords = toCoords(e);
35228
35326
  if (!coords) return;
35229
- const node = nodeAt(coords.graphX, coords.graphY);
35327
+ const node = (state.pressedNodeId ? nodesRef.current.find((n) => n.id === state.pressedNodeId) : void 0) ?? nodeAt(coords.graphX, coords.graphY);
35328
+ state.pressedNodeId = null;
35230
35329
  if (node) {
35231
35330
  if (node.badge && node.badge > 1 && onBadgeClick) {
35232
35331
  const r = (node.size || 8) + (selectedNodeId === node.id ? 4 : 0);
@@ -35246,6 +35345,7 @@ var init_GraphCanvas = __esm({
35246
35345
  );
35247
35346
  const handlePointerLeave = useCallback(() => {
35248
35347
  setHoveredNode(null);
35348
+ interactionRef.current.pressedNodeId = null;
35249
35349
  }, []);
35250
35350
  const gestureHandlers = useCanvasGestures({
35251
35351
  canvasRef,
@@ -38197,8 +38297,17 @@ var init_MediaGallery = __esm({
38197
38297
  const eventBus = useEventBus();
38198
38298
  const { t } = useTranslate();
38199
38299
  const [lightboxItem, setLightboxItem] = useState(null);
38300
+ const [failedIds, setFailedIds] = useState(/* @__PURE__ */ new Set());
38200
38301
  const closeLightbox = useCallback(() => setLightboxItem(null), []);
38201
38302
  useEventListener("UI:LIGHTBOX_CLOSE", closeLightbox);
38303
+ const handleImageError = useCallback((id) => {
38304
+ setFailedIds((prev) => {
38305
+ if (prev.has(id)) return prev;
38306
+ const next = new Set(prev);
38307
+ next.add(id);
38308
+ return next;
38309
+ });
38310
+ }, []);
38202
38311
  const handleItemClick = useCallback(
38203
38312
  (item) => {
38204
38313
  if (selectable) {
@@ -38214,7 +38323,7 @@ var init_MediaGallery = __esm({
38214
38323
  );
38215
38324
  const entityData = Array.isArray(entity) ? entity : [];
38216
38325
  const items = React81__default.useMemo(() => {
38217
- if (propItems) return propItems;
38326
+ if (propItems && propItems.length > 0) return propItems;
38218
38327
  if (entityData.length === 0) return [];
38219
38328
  return entityData.map((record, idx) => {
38220
38329
  return {
@@ -38297,13 +38406,14 @@ var init_MediaGallery = __esm({
38297
38406
  ),
38298
38407
  onClick: () => handleItemClick(item),
38299
38408
  children: [
38300
- /* @__PURE__ */ jsx(
38409
+ failedIds.has(item.id) ? /* @__PURE__ */ jsx(Box, { className: "w-full h-full flex items-center justify-center bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { icon: Image$1, size: "lg" }) }) : /* @__PURE__ */ jsx(
38301
38410
  "img",
38302
38411
  {
38303
38412
  src: item.thumbnail || item.src,
38304
38413
  alt: item.alt || item.caption || "",
38305
38414
  className: "w-full h-full object-cover",
38306
- loading: "lazy"
38415
+ loading: "lazy",
38416
+ onError: () => handleImageError(item.id)
38307
38417
  }
38308
38418
  ),
38309
38419
  /* @__PURE__ */ jsx(
@@ -41810,6 +41920,11 @@ var init_TeamOrganism = __esm({
41810
41920
  TeamOrganism.displayName = "TeamOrganism";
41811
41921
  }
41812
41922
  });
41923
+ function formatDate4(value) {
41924
+ const d = new Date(value);
41925
+ if (isNaN(d.getTime())) return value;
41926
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
41927
+ }
41813
41928
  var lookStyles10, STATUS_STYLES2, Timeline;
41814
41929
  var init_Timeline = __esm({
41815
41930
  "components/core/organisms/Timeline.tsx"() {
@@ -41937,7 +42052,7 @@ var init_Timeline = __esm({
41937
42052
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: cn("flex-1 min-w-0", !isLast && "pb-6"), children: [
41938
42053
  /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "start", wrap: true, children: [
41939
42054
  /* @__PURE__ */ jsx(Typography, { variant: "body", weight: "semibold", children: item.title }),
41940
- item.date && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: item.date })
42055
+ item.date && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate4(item.date) })
41941
42056
  ] }),
41942
42057
  item.description && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: item.description }),
41943
42058
  item.tags && item.tags.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: item.tags.map((tag, tagIdx) => /* @__PURE__ */ jsx(Badge, { variant: "default", children: tag }, tagIdx)) }),
@@ -43149,7 +43264,7 @@ function substituteTraitRefsDeep(value, pathKey) {
43149
43264
  }
43150
43265
  return value;
43151
43266
  }
43152
- function renderPatternProps(props, onDismiss) {
43267
+ function renderPatternProps(props, onDismiss, propsSchema) {
43153
43268
  const rendered = {};
43154
43269
  for (const [key, value] of Object.entries(props)) {
43155
43270
  if (key === "children") {
@@ -43167,9 +43282,10 @@ function renderPatternProps(props, onDismiss) {
43167
43282
  };
43168
43283
  rendered[key] = /* @__PURE__ */ jsx(SlotContentRenderer, { content: childContent, onDismiss });
43169
43284
  } else if (Array.isArray(value)) {
43285
+ const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
43170
43286
  rendered[key] = value.map((item, i) => {
43171
43287
  const el = item;
43172
- if (isPatternConfig(el)) {
43288
+ if (!isDataArray && isPatternConfig(el)) {
43173
43289
  const nestedProps = {};
43174
43290
  for (const [k, v] of Object.entries(el)) {
43175
43291
  if (k !== "type") nestedProps[k] = v;
@@ -43257,14 +43373,15 @@ function SlotContentRenderer({
43257
43373
  );
43258
43374
  }
43259
43375
  }
43260
- const renderedProps = renderPatternProps(restProps, onDismiss);
43261
43376
  const patternDef = getPatternDefinition(content.pattern);
43262
43377
  const propsSchema = patternDef?.propsSchema;
43378
+ const renderedProps = renderPatternProps(restProps, onDismiss, propsSchema);
43263
43379
  if (propsSchema) {
43264
43380
  for (const [propKey, propValue] of Object.entries(renderedProps)) {
43265
43381
  if (typeof propValue !== "string") continue;
43266
43382
  const propDef = propsSchema[propKey];
43267
43383
  if (!propDef || propDef.kind !== "callback") continue;
43384
+ if (propValue === "") continue;
43268
43385
  renderedProps[propKey] = wrapCallbackForEvent(
43269
43386
  `UI:${propValue}`,
43270
43387
  propDef.callbackArgs,
@@ -43547,6 +43664,7 @@ function useSharedEntityStore() {
43547
43664
  }
43548
43665
  return storeRef.current;
43549
43666
  }
43667
+ createContext(null);
43550
43668
  function runTickFrame(entityId, orderedWriters, store) {
43551
43669
  let scratch = store.getSnapshot(entityId);
43552
43670
  for (const writer of orderedWriters) {
@@ -43782,6 +43900,11 @@ var SYNC_TICK_OPERATORS = /* @__PURE__ */ new Set([
43782
43900
  "do",
43783
43901
  "when"
43784
43902
  ]);
43903
+ var REACTIVE_REPAINT_PATTERN_TYPES = /* @__PURE__ */ new Set([
43904
+ "canvas",
43905
+ "game-hud",
43906
+ "game-shell"
43907
+ ]);
43785
43908
  var LIFECYCLE_EVENTS = ["INIT", "LOAD", "$MOUNT"];
43786
43909
  function toTraitDefinition(binding) {
43787
43910
  return {
@@ -44034,6 +44157,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44034
44157
  }, [traitStates]);
44035
44158
  const traitSnapshotDataRef = useRef(/* @__PURE__ */ new Map());
44036
44159
  const traitFieldStatesRef = useRef(/* @__PURE__ */ new Map());
44160
+ const lastCanvasRenderUiRef = useRef(/* @__PURE__ */ new Map());
44161
+ const executeTransitionEffectsRef = useRef(null);
44037
44162
  useEffect(() => {
44038
44163
  const mgr = managerRef.current;
44039
44164
  const bindings = traitBindingsRef.current;
@@ -44074,11 +44199,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44074
44199
  bindTraitStateGetter((traitName) => {
44075
44200
  const allStates = newManager.getAllStates();
44076
44201
  if (allStates instanceof Map) {
44077
- const val = allStates.get(traitName);
44078
- return typeof val === "string" ? val : void 0;
44202
+ return allStates.get(traitName)?.currentState;
44079
44203
  }
44080
- const state = newManager.getState(traitName);
44081
- return typeof state === "string" ? state : void 0;
44204
+ return newManager.getState(traitName)?.currentState;
44082
44205
  });
44083
44206
  const snapshotUnregs = [];
44084
44207
  for (const binding of traitBindingsRef.current) {
@@ -44163,6 +44286,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44163
44286
  const existing = pendingSlots.get(slot) || [];
44164
44287
  existing.push({ pattern, props: props || {} });
44165
44288
  pendingSlots.set(slot, existing);
44289
+ const patternType = pattern?.type;
44290
+ if (patternType !== void 0 && REACTIVE_REPAINT_PATTERN_TYPES.has(patternType)) {
44291
+ const rawForSlot = effects.filter(
44292
+ (e) => Array.isArray(e) && (e[0] === "render-ui" || e[0] === "render") && e[1] === slot
44293
+ );
44294
+ if (rawForSlot.length > 0) {
44295
+ lastCanvasRenderUiRef.current.set(traitName, rawForSlot);
44296
+ }
44297
+ }
44166
44298
  },
44167
44299
  clearSlot: (slot) => {
44168
44300
  pendingSlots.set(slot, []);
@@ -44233,11 +44365,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44233
44365
  notify: clientHandlers.notify
44234
44366
  };
44235
44367
  }
44368
+ const sharedWrites = [];
44236
44369
  const baseSet = handlers.set;
44237
44370
  handlers = {
44238
44371
  ...handlers,
44239
44372
  set: async (targetId, field, value) => {
44240
44373
  if (baseSet) await baseSet(targetId, field, value);
44374
+ if (sharedKey !== void 0) {
44375
+ sharedWrites.push({ field, value });
44376
+ }
44241
44377
  log9.debug("set:write", {
44242
44378
  traitName,
44243
44379
  field,
@@ -44313,7 +44449,10 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44313
44449
  });
44314
44450
  }
44315
44451
  if (sharedKey !== void 0) {
44316
- sharedEntityStore.commit(sharedKey, liveEntity);
44452
+ sharedEntityStore.commit(
44453
+ sharedKey,
44454
+ mergeEntityFrame(sharedEntityStore.getSnapshot(sharedKey), sharedWrites)
44455
+ );
44317
44456
  }
44318
44457
  log9.debug("effects:executed", () => ({
44319
44458
  traitName,
@@ -44339,6 +44478,38 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44339
44478
  entity: binding.linkedEntity
44340
44479
  });
44341
44480
  }
44481
+ if (pendingSlots.size === 0 && effectsCallOp(effects, SHARED_ENTITY_WRITE_OPS)) {
44482
+ const stashed = lastCanvasRenderUiRef.current.get(traitName);
44483
+ if (stashed !== void 0 && stashed.length > 0) {
44484
+ await executor.executeAll(stashed);
44485
+ for (const [slot, patterns] of pendingSlots) {
44486
+ flushSlot(traitName, slot, patterns, {
44487
+ event: flushEvent,
44488
+ state: previousState,
44489
+ entity: binding.linkedEntity
44490
+ });
44491
+ }
44492
+ }
44493
+ if (sharedKey !== void 0 && executeTransitionEffectsRef.current !== null) {
44494
+ for (const sibling of traitBindingsRef.current) {
44495
+ const siblingName = sibling.trait.name;
44496
+ if (siblingName === traitName) continue;
44497
+ if (sharedKeyByTraitNameRef.current.get(siblingName) !== sharedKey) continue;
44498
+ const siblingStash = lastCanvasRenderUiRef.current.get(siblingName);
44499
+ if (siblingStash === void 0 || siblingStash.length === 0) continue;
44500
+ const siblingState = traitStatesRef.current.get(siblingName)?.currentState ?? "";
44501
+ await executeTransitionEffectsRef.current({
44502
+ binding: sibling,
44503
+ effects: siblingStash,
44504
+ previousState: siblingState,
44505
+ newState: siblingState,
44506
+ flushEvent: `${flushEvent}:repaint`,
44507
+ syncOnly,
44508
+ log: log9
44509
+ });
44510
+ }
44511
+ }
44512
+ }
44342
44513
  } catch (error) {
44343
44514
  log9.error("effects:error", {
44344
44515
  traitName,
@@ -44350,6 +44521,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44350
44521
  }
44351
44522
  return emittedDuringExec;
44352
44523
  }, [eventBus, flushSlot, sharedEntityStore]);
44524
+ useEffect(() => {
44525
+ executeTransitionEffectsRef.current = executeTransitionEffects;
44526
+ }, [executeTransitionEffects]);
44353
44527
  const runTickEffects = useCallback((tick, binding) => {
44354
44528
  const traitName = binding.trait.name;
44355
44529
  const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
@@ -44412,6 +44586,24 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44412
44586
  ({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
44413
44587
  );
44414
44588
  runTickFrame(group.storeKey, writers, sharedEntityStore);
44589
+ const repaint = executeTransitionEffectsRef.current;
44590
+ if (repaint !== null) {
44591
+ for (const renderBinding of group.renderBindings) {
44592
+ const renderTraitName = renderBinding.trait.name;
44593
+ const stash = lastCanvasRenderUiRef.current.get(renderTraitName);
44594
+ if (stash === void 0 || stash.length === 0) continue;
44595
+ const renderState = traitStatesRef.current.get(renderTraitName)?.currentState ?? "";
44596
+ void repaint({
44597
+ binding: renderBinding,
44598
+ effects: stash,
44599
+ previousState: renderState,
44600
+ newState: renderState,
44601
+ flushEvent: "tick:repaint",
44602
+ syncOnly: true,
44603
+ log: sharedEntityLog
44604
+ });
44605
+ }
44606
+ }
44415
44607
  };
44416
44608
  if (interval === "frame") {
44417
44609
  scheduler.add(0, onDue);
@@ -44461,6 +44653,13 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44461
44653
  entityByTrait[name] = fields;
44462
44654
  }
44463
44655
  }
44656
+ for (const binding of bindings) {
44657
+ const name = binding.trait.name;
44658
+ const sharedKey = sharedKeyByTraitNameRef.current.get(name);
44659
+ if (sharedKey !== void 0) {
44660
+ entityByTrait[name] = { ...sharedEntityStore.getSnapshot(sharedKey) };
44661
+ }
44662
+ }
44464
44663
  const results = currentManager.sendEvent(
44465
44664
  normalizedEvent,
44466
44665
  payload,
@@ -44614,7 +44813,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44614
44813
  }
44615
44814
  await onEventProcessed(normalizedEvent, payload, dispatchedOrbitals);
44616
44815
  }
44617
- }, [entities, eventBus]);
44816
+ }, [entities, eventBus, sharedEntityStore]);
44618
44817
  const drainEventQueue = useCallback(async () => {
44619
44818
  if (processingRef.current) return;
44620
44819
  processingRef.current = true;
@@ -44684,6 +44883,24 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44684
44883
  });
44685
44884
  }
44686
44885
  }
44886
+ const bareCascadeKeys = /* @__PURE__ */ new Set();
44887
+ for (const binding of traitBindings) {
44888
+ for (const transition of binding.trait.transitions) {
44889
+ const eventKey = transition.event;
44890
+ if (LIFECYCLE_EVENTS.includes(eventKey)) continue;
44891
+ if (bareCascadeKeys.has(eventKey)) continue;
44892
+ bareCascadeKeys.add(eventKey);
44893
+ const bareKey = `UI:${eventKey}`;
44894
+ const unsub = eventBus.on(bareKey, (event) => {
44895
+ crossTraitLog.debug("bare-cascade:fire", { bareKey, eventKey });
44896
+ enqueueAndDrain(eventKey, event.payload);
44897
+ });
44898
+ unsubscribes.push(() => {
44899
+ crossTraitLog.debug("bare-cascade:unsubscribe", { bareKey, eventKey });
44900
+ unsub();
44901
+ });
44902
+ }
44903
+ }
44687
44904
  for (const binding of traitBindings) {
44688
44905
  const ownOrbital = orbitalsByTrait?.[binding.trait.name];
44689
44906
  const listens = binding.trait.listens ?? [];
@@ -44868,9 +45085,16 @@ function normalizeChild(child) {
44868
45085
  props: { ...rest, ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {} }
44869
45086
  };
44870
45087
  }
44871
- function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits) {
45088
+ function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraits) {
44872
45089
  for (const eff of effects) {
44873
45090
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
45091
+ if (eff.traitName && activeTraits && !activeTraits.has(eff.traitName)) {
45092
+ xOrbitalLog.debug("slot:off-page-trait-skipped", {
45093
+ sourceTrait: eff.traitName,
45094
+ slot: eff.slot
45095
+ });
45096
+ continue;
45097
+ }
44874
45098
  const patternRecord = eff.pattern;
44875
45099
  const { type: patternType, children, ...inlineProps } = patternRecord;
44876
45100
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
@@ -44911,8 +45135,38 @@ function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits) {
44911
45135
  }
44912
45136
  }
44913
45137
  }
44914
- function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits }) {
45138
+ function collectServerActiveTraits(ir, allTraits, mountedTraitNames) {
45139
+ if (!ir?.pages || ir.pages.size <= 1) return void 0;
45140
+ const pageBound = /* @__PURE__ */ new Set();
45141
+ for (const page of ir.pages.values()) {
45142
+ const queue = page.traits.map((b) => b.trait.name).filter((n) => !!n);
45143
+ for (const name of queue) {
45144
+ if (pageBound.has(name)) continue;
45145
+ pageBound.add(name);
45146
+ const rt = allTraits.get(name);
45147
+ if (!rt) continue;
45148
+ queue.push(...collectTraitRefsFromResolvedTrait(rt));
45149
+ }
45150
+ }
45151
+ const active = new Set(mountedTraitNames);
45152
+ for (const name of allTraits.keys()) {
45153
+ if (!pageBound.has(name)) active.add(name);
45154
+ }
45155
+ return active;
45156
+ }
45157
+ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, serverActiveTraits }) {
44915
45158
  const bridge = useServerBridge();
45159
+ const activeTraitNames = useMemo(
45160
+ () => new Set(traits2.map((b) => b.trait.name).filter((n) => !!n)),
45161
+ [traits2]
45162
+ );
45163
+ const withActiveTraits = useCallback(
45164
+ (payload) => {
45165
+ if (!serverActiveTraits || serverActiveTraits.size === 0) return payload;
45166
+ return { ...payload ?? {}, _activeTraits: Array.from(serverActiveTraits) };
45167
+ },
45168
+ [serverActiveTraits]
45169
+ );
44916
45170
  const uiSlots = useUISlots();
44917
45171
  const onEventProcessed = useCallback(async (event, payload, dispatchedOrbitals) => {
44918
45172
  if (!bridge.connected || !orbitalNames?.length) return;
@@ -44924,11 +45178,11 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
44924
45178
  dispatchedOrbitalsSize: dispatchedOrbitals?.size ?? 0
44925
45179
  }));
44926
45180
  for (const name of targets) {
44927
- const { effects, meta } = await bridge.sendEvent(name, event, payload);
45181
+ const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
44928
45182
  recordServerResponse(name, event, meta);
44929
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits);
45183
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames);
44930
45184
  }
44931
- }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, embeddedTraits]);
45185
+ }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, embeddedTraits, activeTraitNames, withActiveTraits]);
44932
45186
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, traitConfigsByName, orbitalsByTrait, embeddedTraits } : { navigate: onNavigate, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits };
44933
45187
  const { sendEvent } = useTraitStateMachine(traits2, uiSlots, opts);
44934
45188
  const initSentRef = useRef(false);
@@ -44965,7 +45219,7 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
44965
45219
  initSentRef.current = true;
44966
45220
  (async () => {
44967
45221
  for (const name of orbitalNames) {
44968
- const { effects, meta } = await bridge.sendEvent(name, "INIT", {});
45222
+ const { effects, meta } = await bridge.sendEvent(name, "INIT", withActiveTraits({}));
44969
45223
  recordServerResponse(name, "INIT", meta);
44970
45224
  const effectTraces = [
44971
45225
  { type: "fetch", args: [], status: "executed" },
@@ -44983,10 +45237,10 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
44983
45237
  effects: effectTraces,
44984
45238
  timestamp: Date.now()
44985
45239
  });
44986
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits);
45240
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames);
44987
45241
  }
44988
45242
  })();
44989
- }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, embeddedTraits]);
45243
+ }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, embeddedTraits, activeTraitNames, withActiveTraits]);
44990
45244
  return null;
44991
45245
  }
44992
45246
  function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavigate, onLocalFallback, persistence }) {
@@ -45073,6 +45327,14 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavi
45073
45327
  }
45074
45328
  return Array.from(set);
45075
45329
  }, [allPageTraits, orbitalsByTrait]);
45330
+ const serverActiveTraits = useMemo(
45331
+ () => collectServerActiveTraits(
45332
+ ir,
45333
+ allTraits,
45334
+ new Set(allPageTraits.map((b) => b.trait.name).filter((n) => !!n))
45335
+ ),
45336
+ [ir, allTraits, allPageTraits]
45337
+ );
45076
45338
  useEffect(() => {
45077
45339
  const traitNames = allPageTraits.map((b) => b.trait.name);
45078
45340
  const orbitalsByTraitForPage = {};
@@ -45082,7 +45344,7 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavi
45082
45344
  }
45083
45345
  xOrbitalLog.info("SchemaRunner:mount", {
45084
45346
  pageName,
45085
- traitNames,
45347
+ traitNames: traitNames.join(","),
45086
45348
  orbitalsByTraitForPage,
45087
45349
  pageOrbitalNames: pageOrbitalNames.join(",")
45088
45350
  });
@@ -45127,6 +45389,7 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavi
45127
45389
  traitConfigsByName,
45128
45390
  orbitalsByTrait,
45129
45391
  embeddedTraits,
45392
+ serverActiveTraits,
45130
45393
  onNavigate,
45131
45394
  onLocalFallback,
45132
45395
  persistence