@almadar/ui 5.121.2 → 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.
@@ -3271,9 +3271,9 @@ function getGlobalEventBus() {
3271
3271
  function useEventBus() {
3272
3272
  const context = React90.useContext(providers.EventBusContext);
3273
3273
  const baseBus = context ?? getGlobalEventBus() ?? fallbackEventBus;
3274
- const scope = providers.useTraitScope();
3274
+ const chain = providers.useTraitScopeChain();
3275
3275
  return React90.useMemo(() => {
3276
- if (!scope) {
3276
+ if (chain.length === 0) {
3277
3277
  return {
3278
3278
  ...baseBus,
3279
3279
  emit: (type, payload, source) => {
@@ -3289,22 +3289,31 @@ function useEventBus() {
3289
3289
  emit: (type, payload, source) => {
3290
3290
  if (typeof type === "string" && type.startsWith("UI:")) {
3291
3291
  const tail = type.slice(3);
3292
- const qualified = tail.includes(".") ? type : `UI:${scope.orbital}.${scope.trait}.${tail}`;
3293
- if (qualified !== type) {
3294
- scopeLog.info("emit:qualified", {
3292
+ const isQualified = tail.includes(".");
3293
+ const event = isQualified ? tail.split(".").slice(2).join(".") : tail;
3294
+ if (!event) {
3295
+ baseBus.emit(type, payload, source);
3296
+ return;
3297
+ }
3298
+ const keys = /* @__PURE__ */ new Set();
3299
+ if (isQualified) keys.add(type);
3300
+ for (const sc of chain) {
3301
+ keys.add(`UI:${sc.orbital}.${sc.trait}.${event}`);
3302
+ }
3303
+ if (keys.size > 1) {
3304
+ scopeLog.info("emit:fan-out", {
3295
3305
  from: type,
3296
- to: qualified,
3297
- scopeOrbital: scope.orbital,
3298
- scopeTrait: scope.trait
3306
+ keys: Array.from(keys),
3307
+ chainDepth: chain.length
3299
3308
  });
3300
3309
  }
3301
- baseBus.emit(qualified, payload, source);
3310
+ for (const key of keys) baseBus.emit(key, payload, source);
3302
3311
  return;
3303
3312
  }
3304
3313
  baseBus.emit(type, payload, source);
3305
3314
  }
3306
3315
  };
3307
- }, [baseBus, scope]);
3316
+ }, [baseBus, chain]);
3308
3317
  }
3309
3318
  function useEventListener(event, handler) {
3310
3319
  const eventBus = useEventBus();
@@ -4661,13 +4670,13 @@ var init_Icon = __esm({
4661
4670
  style
4662
4671
  }) => {
4663
4672
  const directIcon = typeof icon === "string" ? void 0 : icon;
4664
- const effectiveName = typeof icon === "string" ? icon : name;
4673
+ const effectiveName = typeof icon === "string" && icon !== "" ? icon : name;
4674
+ const effectiveStrokeWidth = strokeWidth != null && strokeWidth > 0 ? strokeWidth : void 0;
4665
4675
  const family = useIconFamily();
4666
4676
  const RenderedComponent = React90__namespace.default.useMemo(() => {
4667
4677
  if (directIcon) return null;
4668
4678
  return effectiveName ? resolveIconForFamily(effectiveName) : null;
4669
4679
  }, [directIcon, effectiveName, family]);
4670
- const effectiveStrokeWidth = strokeWidth ?? void 0;
4671
4680
  const inlineStyle = {
4672
4681
  ...effectiveStrokeWidth === void 0 ? { strokeWidth: "var(--icon-stroke-width, 2)" } : {},
4673
4682
  ...style
@@ -5149,6 +5158,15 @@ var init_Modal = __esm({
5149
5158
  const [dragY, setDragY] = React90.useState(0);
5150
5159
  const dragStartY = React90.useRef(0);
5151
5160
  const isDragging = React90.useRef(false);
5161
+ const [closing, setClosing] = React90.useState(false);
5162
+ const wasOpenRef = React90.useRef(isOpen);
5163
+ React90.useEffect(() => {
5164
+ if (wasOpenRef.current && !isOpen) setClosing(true);
5165
+ wasOpenRef.current = isOpen;
5166
+ }, [isOpen]);
5167
+ const handleAnimEnd = (e) => {
5168
+ if (closing && e.target === e.currentTarget) setClosing(false);
5169
+ };
5152
5170
  React90.useEffect(() => {
5153
5171
  if (isOpen) {
5154
5172
  previousActiveElement.current = document.activeElement;
@@ -5182,7 +5200,11 @@ var init_Modal = __esm({
5182
5200
  document.body.style.overflow = "";
5183
5201
  };
5184
5202
  }, [isOpen]);
5185
- if (!isOpen || typeof document === "undefined") return null;
5203
+ if (typeof document === "undefined") return null;
5204
+ const renderOpen = isOpen || closing;
5205
+ if (!renderOpen) return null;
5206
+ const dialogAnim = closing ? "animate-modal-out" : "animate-modal-in";
5207
+ const overlayAnim = closing ? "animate-overlay-out" : "animate-overlay-in";
5186
5208
  const handleClose = () => {
5187
5209
  if (closeEvent) eventBus.emit(`UI:${closeEvent}`, {});
5188
5210
  onClose();
@@ -5199,7 +5221,8 @@ var init_Modal = __esm({
5199
5221
  className: cn(
5200
5222
  "fixed inset-0 z-[1000]",
5201
5223
  "flex items-start justify-center px-4 pb-4 pt-[10vh]",
5202
- "max-sm:items-stretch max-sm:p-0 max-sm:pt-0"
5224
+ "max-sm:items-stretch max-sm:p-0 max-sm:pt-0",
5225
+ overlayAnim
5203
5226
  ),
5204
5227
  style: { backgroundColor: "rgba(0, 0, 0, 0.6)" },
5205
5228
  onClick: handleOverlayClick,
@@ -5227,8 +5250,10 @@ var init_Modal = __esm({
5227
5250
  // full height, no rounded corners, no min-width.
5228
5251
  "max-sm:max-w-none max-sm:max-h-none max-sm:w-full max-sm:h-full max-sm:rounded-none",
5229
5252
  lookStyles[look],
5230
- className
5253
+ className,
5254
+ dialogAnim
5231
5255
  ),
5256
+ onAnimationEnd: handleAnimEnd,
5232
5257
  style: dragY > 0 ? {
5233
5258
  transform: `translateY(${dragY}px)`,
5234
5259
  transition: isDragging.current ? "none" : "transform 200ms ease-out"
@@ -5337,6 +5362,7 @@ var init_Overlay = __esm({
5337
5362
  className: cn(
5338
5363
  "fixed inset-0 z-40",
5339
5364
  blur && "backdrop-blur-sm",
5365
+ "animate-overlay-in",
5340
5366
  className
5341
5367
  ),
5342
5368
  style: { backgroundColor: "rgba(0, 0, 0, 0.6)" },
@@ -6749,7 +6775,7 @@ var init_Input = __esm({
6749
6775
  eventBus.emit(`UI:${action}`, { value: e.currentTarget.value });
6750
6776
  }
6751
6777
  };
6752
- const wrapField = (field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
6778
+ const wrapField = (field, fullWidth = true) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: fullWidth ? "w-full" : "w-fit", children: [
6753
6779
  label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-foreground mb-1", children: label }),
6754
6780
  field,
6755
6781
  (helperText || error) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-xs", error ? "text-error" : "text-muted-foreground"), children: error ?? helperText })
@@ -6809,7 +6835,8 @@ var init_Input = __esm({
6809
6835
  ),
6810
6836
  ...props
6811
6837
  }
6812
- )
6838
+ ),
6839
+ false
6813
6840
  );
6814
6841
  }
6815
6842
  return wrapField(
@@ -9052,6 +9079,18 @@ var init_RangeSlider = __esm({
9052
9079
  function easeOut(t) {
9053
9080
  return t * (2 - t);
9054
9081
  }
9082
+ function formatDisplay(value, format, decimals) {
9083
+ switch (format) {
9084
+ case "currency":
9085
+ return `$${value.toFixed(2)}`;
9086
+ case "percent":
9087
+ return `${Math.round(value)}%`;
9088
+ case "number":
9089
+ return value.toLocaleString();
9090
+ default:
9091
+ return value.toFixed(decimals);
9092
+ }
9093
+ }
9055
9094
  var AnimatedCounter;
9056
9095
  var init_AnimatedCounter = __esm({
9057
9096
  "components/marketing/atoms/AnimatedCounter.tsx"() {
@@ -9063,6 +9102,7 @@ var init_AnimatedCounter = __esm({
9063
9102
  duration = 600,
9064
9103
  prefix,
9065
9104
  suffix,
9105
+ format,
9066
9106
  className
9067
9107
  }) => {
9068
9108
  const numericRaw = typeof rawValue === "number" ? rawValue : Number.parseFloat(String(rawValue ?? ""));
@@ -9078,6 +9118,10 @@ var init_AnimatedCounter = __esm({
9078
9118
  setDisplayValue(to);
9079
9119
  return;
9080
9120
  }
9121
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
9122
+ setDisplayValue(to);
9123
+ return;
9124
+ }
9081
9125
  const startTime = performance.now();
9082
9126
  const diff = to - from;
9083
9127
  function animate(currentTime) {
@@ -9099,7 +9143,7 @@ var init_AnimatedCounter = __esm({
9099
9143
  };
9100
9144
  }, [value, duration]);
9101
9145
  const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
9102
- const formattedValue = displayValue.toFixed(decimalPlaces);
9146
+ const formattedValue = formatDisplay(displayValue, format, decimalPlaces);
9103
9147
  return /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
9104
9148
  prefix,
9105
9149
  formattedValue,
@@ -10378,14 +10422,29 @@ function recordTransition(trace) {
10378
10422
  "pass"
10379
10423
  );
10380
10424
  } else {
10381
- const hasRenderUI = entry.effects.some((e) => e.type === "render-ui");
10382
- if (hasRenderUI) {
10383
- registerCheck(
10384
- checkId,
10385
- `INIT transition for "${entry.traitName}" missing fetch effect`,
10386
- "fail",
10387
- "Entity-bound render-ui without a fetch effect will show empty data"
10425
+ const rendersEntityData = entry.effects.some(
10426
+ (e) => e.type === "render-ui" && JSON.stringify(e.args).includes("@entity.")
10427
+ );
10428
+ if (rendersEntityData) {
10429
+ const siblingSeeds = getState().transitions.some(
10430
+ (t) => t.event === "INIT" && t.effects.some(
10431
+ (e) => e.type === "fetch" || e.type === "set" && typeof e.args[0] === "string" && e.args[0].startsWith("@entity.")
10432
+ )
10388
10433
  );
10434
+ if (siblingSeeds) {
10435
+ registerCheck(
10436
+ checkId,
10437
+ `INIT transition for "${entry.traitName}" has sibling entity-seed (shared entity)`,
10438
+ "pass"
10439
+ );
10440
+ } else {
10441
+ registerCheck(
10442
+ checkId,
10443
+ `INIT transition for "${entry.traitName}" missing fetch effect`,
10444
+ "fail",
10445
+ "Entity-bound render-ui without a fetch effect will show empty data"
10446
+ );
10447
+ }
10389
10448
  }
10390
10449
  }
10391
10450
  }
@@ -11530,7 +11589,7 @@ function GameMenu({
11530
11589
  logo,
11531
11590
  className
11532
11591
  }) {
11533
- const resolvedOptions = options ?? menuItems ?? DEFAULT_MENU_OPTIONS;
11592
+ const resolvedOptions = (options?.length ? options : void 0) ?? (menuItems?.length ? menuItems : void 0) ?? DEFAULT_MENU_OPTIONS;
11534
11593
  const eventBus = useEventBus();
11535
11594
  const handleOptionClick = React90__namespace.useCallback(
11536
11595
  (option) => {
@@ -12928,6 +12987,10 @@ function Canvas2D({
12928
12987
  window.removeEventListener("keyup", onUp);
12929
12988
  };
12930
12989
  }, [keyMap, keyUpMap, eventBus]);
12990
+ React90.useEffect(() => {
12991
+ if (!keyMap && !keyUpMap) return;
12992
+ canvasRef.current?.focus();
12993
+ }, [keyMap, keyUpMap]);
12931
12994
  if (error) {
12932
12995
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("flex items-center justify-center w-full h-full bg-[var(--color-card)] rounded-container", className), children: /* @__PURE__ */ jsxRuntime.jsxs(Stack, { direction: "vertical", gap: "md", align: "center", children: [
12933
12996
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "alert-circle", size: "xl" }),
@@ -12976,7 +13039,7 @@ function Canvas2D({
12976
13039
  onWheel: gestureHandlers.onWheel,
12977
13040
  onContextMenu: (e) => e.preventDefault(),
12978
13041
  className: "cursor-pointer touch-none",
12979
- tabIndex: isFree ? 0 : void 0,
13042
+ tabIndex: isFree || keyMap || keyUpMap ? 0 : void 0,
12980
13043
  style: {
12981
13044
  width: viewportSize.width,
12982
13045
  height: viewportSize.height
@@ -16400,7 +16463,8 @@ var init_LoadingState = __esm({
16400
16463
  LoadingState = ({
16401
16464
  title,
16402
16465
  message,
16403
- className
16466
+ className,
16467
+ fullPage = false
16404
16468
  }) => {
16405
16469
  const { t } = hooks.useTranslate();
16406
16470
  const displayMessage = message ?? t("common.loading");
@@ -16409,7 +16473,8 @@ var init_LoadingState = __esm({
16409
16473
  {
16410
16474
  align: "center",
16411
16475
  className: cn(
16412
- "justify-center py-12",
16476
+ "justify-center",
16477
+ fullPage ? "fixed inset-0 z-[1000] py-12 bg-background/80 backdrop-blur-sm animate-overlay-in" : "py-12",
16413
16478
  className
16414
16479
  ),
16415
16480
  children: [
@@ -19611,7 +19676,7 @@ var init_BookCoverPage = __esm({
19611
19676
  size: "lg",
19612
19677
  action: "BOOK_START",
19613
19678
  className: "mt-8",
19614
- children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: t("book.startReading") })
19679
+ children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "inherit", children: t("book.startReading") })
19615
19680
  }
19616
19681
  )
19617
19682
  ]
@@ -21923,8 +21988,9 @@ var init_Chart = __esm({
21923
21988
  align: "center",
21924
21989
  flex: true,
21925
21990
  className: "min-w-0",
21991
+ style: { height: "100%" },
21926
21992
  children: [
21927
- /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: histogram ? "none" : "xs", align: "end", className: "w-full", style: { height: "100%" }, children: series.map((s, sIdx) => {
21993
+ /* @__PURE__ */ jsxRuntime.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) => {
21928
21994
  const value = valueAt(s, label);
21929
21995
  const barHeight = value / maxValue * 100;
21930
21996
  const color = seriesColor(s, sIdx);
@@ -21937,6 +22003,8 @@ var init_Chart = __esm({
21937
22003
  ),
21938
22004
  style: {
21939
22005
  height: `${barHeight}%`,
22006
+ // Cap width so a lone category doesn't paint the whole plot as one solid block; narrow columns are unaffected (flex-1 binds first).
22007
+ ...!histogram && { maxWidth: 72 },
21940
22008
  backgroundColor: color
21941
22009
  },
21942
22010
  onClick: () => onPointClick?.(
@@ -21971,8 +22039,9 @@ var init_Chart = __esm({
21971
22039
  align: "center",
21972
22040
  flex: true,
21973
22041
  className: "min-w-0",
22042
+ style: { height: "100%" },
21974
22043
  children: [
21975
- /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", className: "w-full", style: { height: "100%" }, justify: "end", children: series.map((s, sIdx) => {
22044
+ /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", className: "w-full flex-1 min-h-0", justify: "end", children: series.map((s, sIdx) => {
21976
22045
  const value = valueAt(s, label);
21977
22046
  const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
21978
22047
  const color = seriesColor(s, sIdx);
@@ -22017,6 +22086,7 @@ var init_Chart = __esm({
22017
22086
  const innerRadius = donut ? radius * 0.6 : 0;
22018
22087
  const center = size / 2;
22019
22088
  const segments = React90.useMemo(() => {
22089
+ if (!Number.isFinite(total) || total <= 0) return [];
22020
22090
  let currentAngle = -Math.PI / 2;
22021
22091
  return data.map((point, idx) => {
22022
22092
  const angle = point.value / total * 2 * Math.PI;
@@ -22028,13 +22098,25 @@ var init_Chart = __esm({
22028
22098
  const y1 = center + radius * Math.sin(startAngle);
22029
22099
  const x2 = center + radius * Math.cos(endAngle);
22030
22100
  const y2 = center + radius * Math.sin(endAngle);
22101
+ const fullCircle = angle >= 2 * Math.PI - 1e-9;
22102
+ const midAngle = startAngle + angle / 2;
22103
+ const xm = center + radius * Math.cos(midAngle);
22104
+ const ym = center + radius * Math.sin(midAngle);
22031
22105
  let d;
22032
22106
  if (innerRadius > 0) {
22033
22107
  const ix1 = center + innerRadius * Math.cos(startAngle);
22034
22108
  const iy1 = center + innerRadius * Math.sin(startAngle);
22035
22109
  const ix2 = center + innerRadius * Math.cos(endAngle);
22036
22110
  const iy2 = center + innerRadius * Math.sin(endAngle);
22037
- 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`;
22111
+ if (fullCircle) {
22112
+ const ixm = center + innerRadius * Math.cos(midAngle);
22113
+ const iym = center + innerRadius * Math.sin(midAngle);
22114
+ 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`;
22115
+ } else {
22116
+ 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`;
22117
+ }
22118
+ } else if (fullCircle) {
22119
+ d = `M ${center} ${center} L ${x1} ${y1} A ${radius} ${radius} 0 1 1 ${xm} ${ym} A ${radius} ${radius} 0 1 1 ${x2} ${y2} Z`;
22038
22120
  } else {
22039
22121
  d = `M ${center} ${center} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`;
22040
22122
  }
@@ -25061,10 +25143,10 @@ function DataGrid({
25061
25143
  ] }),
25062
25144
  badgeFields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap", children: badgeFields.map((field) => {
25063
25145
  const val = getNestedValue(itemData, field.name);
25064
- if (val === void 0 || val === null) return null;
25146
+ if (val === void 0 || val === null || val === "") return null;
25065
25147
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
25066
25148
  field.icon && renderIconInput(field.icon, { size: "xs" }),
25067
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: String(val) })
25149
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: formatValue(val, field.format) })
25068
25150
  ] }, field.name);
25069
25151
  }) })
25070
25152
  ] }),
@@ -32554,6 +32636,11 @@ var init_VoteStack = __esm({
32554
32636
  {
32555
32637
  className: cn(
32556
32638
  "inline-flex items-center justify-center",
32639
+ // Shrink-wrap in stretch contexts (slot/sidecar wrappers are
32640
+ // flex-column with stretch alignment): without this the root spans
32641
+ // the full wrapper width and the count row's `w-full` turns the
32642
+ // compact pill into a page-wide band.
32643
+ "w-fit",
32557
32644
  variant === "vertical" ? "flex-col" : "flex-row",
32558
32645
  "rounded-sm",
32559
32646
  "border-[length:var(--border-width)] border-border",
@@ -36050,7 +36137,7 @@ var init_Sidebar = __esm({
36050
36137
  isActive ? [
36051
36138
  "bg-primary text-primary-foreground",
36052
36139
  "font-medium shadow-sm",
36053
- "border-primary translate-x-1 -translate-y-0.5"
36140
+ "border-primary translate-x-1 rtl:-translate-x-1 -translate-y-0.5"
36054
36141
  ].join(" ") : [
36055
36142
  "text-foreground",
36056
36143
  "hover:bg-muted hover:border-border",
@@ -36060,10 +36147,10 @@ var init_Sidebar = __esm({
36060
36147
  title: collapsed ? item.label : void 0,
36061
36148
  children: [
36062
36149
  item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm", className: cn("min-w-[20px] flex-shrink-0", isActive && "text-primary-foreground") }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm", className: cn("min-w-[20px] flex-shrink-0", isActive && "text-primary-foreground") })),
36063
- !collapsed && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: isActive ? "inherit" : "primary", className: "font-medium truncate flex-1 text-left", children: item.label }),
36150
+ !collapsed && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: isActive ? "inherit" : "primary", className: "font-medium truncate flex-1 text-start", children: item.label }),
36064
36151
  !collapsed && item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "danger", size: "sm", children: item.badge }),
36065
36152
  collapsed && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn(
36066
- "absolute left-full ml-2 px-2 py-1 text-xs opacity-0 group-hover:opacity-100",
36153
+ "absolute start-full ms-2 px-2 py-1 text-xs opacity-0 group-hover:opacity-100",
36067
36154
  "pointer-events-none whitespace-nowrap z-50 transition-opacity",
36068
36155
  "bg-primary text-primary-foreground",
36069
36156
  "border-[length:var(--border-width-thin)] border-border",
@@ -36118,7 +36205,7 @@ var init_Sidebar = __esm({
36118
36205
  as: "aside",
36119
36206
  className: cn(
36120
36207
  "flex flex-col h-full",
36121
- "bg-card border-r border-border",
36208
+ "bg-card border-e border-border",
36122
36209
  "transition-all duration-300 ease-in-out",
36123
36210
  collapsed ? "w-20" : "w-64",
36124
36211
  className
@@ -37279,7 +37366,7 @@ function resolveColor3(color, el) {
37279
37366
  function truncateLabel(label) {
37280
37367
  return label.length > MAX_LABEL_CHARS ? label.slice(0, MAX_LABEL_CHARS - 1) + "\u2026" : label;
37281
37368
  }
37282
- var GROUP_COLORS2, labelMeasureCtx, MAX_LABEL_CHARS, GraphCanvas;
37369
+ var GROUP_COLORS2, GROUP_GRAVITY, UNGROUPED_GRAVITY, labelMeasureCtx, MAX_LABEL_CHARS, NO_NODES, NO_EDGES, NO_SIM, GraphCanvas;
37283
37370
  var init_GraphCanvas = __esm({
37284
37371
  "components/core/molecules/GraphCanvas.tsx"() {
37285
37372
  "use client";
@@ -37299,13 +37386,18 @@ var init_GraphCanvas = __esm({
37299
37386
  "var(--color-info)",
37300
37387
  "var(--color-accent)"
37301
37388
  ];
37389
+ GROUP_GRAVITY = 0.3;
37390
+ UNGROUPED_GRAVITY = 0.02;
37302
37391
  labelMeasureCtx = null;
37303
37392
  MAX_LABEL_CHARS = 22;
37393
+ NO_NODES = [];
37394
+ NO_EDGES = [];
37395
+ NO_SIM = [];
37304
37396
  GraphCanvas = ({
37305
37397
  title,
37306
- nodes: propNodes = [],
37307
- edges: propEdges = [],
37308
- similarity: propSimilarity = [],
37398
+ nodes: propNodes = NO_NODES,
37399
+ edges: propEdges = NO_EDGES,
37400
+ similarity: propSimilarity = NO_SIM,
37309
37401
  height = 400,
37310
37402
  showLabels = true,
37311
37403
  interactive = true,
@@ -37355,6 +37447,7 @@ var init_GraphCanvas = __esm({
37355
37447
  const interactionRef = React90.useRef({
37356
37448
  mode: "none",
37357
37449
  dragNodeId: null,
37450
+ pressedNodeId: null,
37358
37451
  startMouse: { x: 0, y: 0 },
37359
37452
  startOffset: { x: 0, y: 0 },
37360
37453
  downPos: { x: 0, y: 0 }
@@ -37402,6 +37495,11 @@ var init_GraphCanvas = __esm({
37402
37495
  () => [...new Set(propNodes.map((n) => n.group).filter(Boolean))],
37403
37496
  [propNodes]
37404
37497
  );
37498
+ const nodesKey = React90.useMemo(() => propNodes.map((n) => n.id).join(""), [propNodes]);
37499
+ const edgesKey = React90.useMemo(
37500
+ () => propEdges.map((e) => `${e.source}${e.target}`).join(""),
37501
+ [propEdges]
37502
+ );
37405
37503
  React90.useEffect(() => {
37406
37504
  const canvas = canvasRef.current;
37407
37505
  if (!canvas || propNodes.length === 0) return;
@@ -37485,7 +37583,7 @@ var init_GraphCanvas = __esm({
37485
37583
  const edgeLinks = propEdges.filter((e) => present.has(e.source) && present.has(e.target)).map((e) => ({ source: e.source, target: e.target, weight: clamp01(e.weight ?? 1) }));
37486
37584
  const simLinks = activeSimilarity.filter((p) => present.has(p.source) && present.has(p.target)).map((p) => ({ source: p.source, target: p.target, weight: clamp01(p.weight) }));
37487
37585
  const collideRadius = (n) => Math.max(n.size || 8, (n.labelW ?? 0) / 2) + nodeSpacing / 2;
37488
- const simulation = d3Force.forceSimulation(simNodes).force("edge", d3Force.forceLink(edgeLinks).id((d) => d.id).distance((l) => linkDistance * (0.35 + (1 - l.weight) * 0.3)).strength(0.9)).force("similarity", d3Force.forceLink(simLinks).id((d) => d.id).distance((l) => linkDistance * (1.35 - 0.55 * l.weight)).strength(0.03)).force("charge", d3Force.forceManyBody().strength(-repulsion * 0.5)).force("collide", d3Force.forceCollide().radius(collideRadius).strength(0.9)).force("x", d3Force.forceX((n) => groupTarget.get(n.group ?? "")?.x ?? w / 2).strength((n) => multiCluster && n.group ? 0.14 : 0.02)).force("y", d3Force.forceY((n) => groupTarget.get(n.group ?? "")?.y ?? h / 2).strength((n) => multiCluster && n.group ? 0.14 : 0.02)).stop();
37586
+ const simulation = d3Force.forceSimulation(simNodes).force("edge", d3Force.forceLink(edgeLinks).id((d) => d.id).distance((l) => linkDistance * (0.35 + (1 - l.weight) * 0.3)).strength(0.9)).force("similarity", d3Force.forceLink(simLinks).id((d) => d.id).distance((l) => linkDistance * (1.35 - 0.55 * l.weight)).strength(0.03)).force("charge", d3Force.forceManyBody().strength(-repulsion * 0.5)).force("collide", d3Force.forceCollide().radius(collideRadius).strength(0.9)).force("x", d3Force.forceX((n) => groupTarget.get(n.group ?? "")?.x ?? w / 2).strength((n) => multiCluster && n.group ? GROUP_GRAVITY : UNGROUPED_GRAVITY)).force("y", d3Force.forceY((n) => groupTarget.get(n.group ?? "")?.y ?? h / 2).strength((n) => multiCluster && n.group ? GROUP_GRAVITY : UNGROUPED_GRAVITY)).stop();
37489
37587
  for (let i = 0; i < 300; i++) simulation.tick();
37490
37588
  const pad = nodeSpacing / 2;
37491
37589
  const rectsOf = (n) => {
@@ -37582,7 +37680,7 @@ var init_GraphCanvas = __esm({
37582
37680
  return () => {
37583
37681
  cancelAnimationFrame(animRef.current);
37584
37682
  };
37585
- }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
37683
+ }, [nodesKey, edgesKey, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
37586
37684
  React90.useEffect(() => {
37587
37685
  const canvas = canvasRef.current;
37588
37686
  if (!canvas) return;
@@ -37712,6 +37810,7 @@ var init_GraphCanvas = __esm({
37712
37810
  const cancelSinglePointer = React90.useCallback(() => {
37713
37811
  interactionRef.current.mode = "none";
37714
37812
  interactionRef.current.dragNodeId = null;
37813
+ interactionRef.current.pressedNodeId = null;
37715
37814
  }, []);
37716
37815
  const handlePointerDown = React90.useCallback(
37717
37816
  (e) => {
@@ -37722,6 +37821,7 @@ var init_GraphCanvas = __esm({
37722
37821
  state.downPos = { x: e.clientX, y: e.clientY };
37723
37822
  state.startMouse = { x: e.clientX, y: e.clientY };
37724
37823
  state.startOffset = { ...offset };
37824
+ state.pressedNodeId = node?.id ?? null;
37725
37825
  if (draggable && node) {
37726
37826
  state.mode = "dragging";
37727
37827
  state.dragNodeId = node.id;
@@ -37773,7 +37873,8 @@ var init_GraphCanvas = __esm({
37773
37873
  if (moved < 4) {
37774
37874
  const coords = toCoords(e);
37775
37875
  if (!coords) return;
37776
- const node = nodeAt(coords.graphX, coords.graphY);
37876
+ const node = (state.pressedNodeId ? nodesRef.current.find((n) => n.id === state.pressedNodeId) : void 0) ?? nodeAt(coords.graphX, coords.graphY);
37877
+ state.pressedNodeId = null;
37777
37878
  if (node) {
37778
37879
  if (node.badge && node.badge > 1 && onBadgeClick) {
37779
37880
  const r2 = (node.size || 8) + (selectedNodeId === node.id ? 4 : 0);
@@ -37793,6 +37894,7 @@ var init_GraphCanvas = __esm({
37793
37894
  );
37794
37895
  const handlePointerLeave = React90.useCallback(() => {
37795
37896
  setHoveredNode(null);
37897
+ interactionRef.current.pressedNodeId = null;
37796
37898
  }, []);
37797
37899
  const gestureHandlers = useCanvasGestures({
37798
37900
  canvasRef,
@@ -40744,8 +40846,17 @@ var init_MediaGallery = __esm({
40744
40846
  const eventBus = useEventBus();
40745
40847
  const { t } = hooks.useTranslate();
40746
40848
  const [lightboxItem, setLightboxItem] = React90.useState(null);
40849
+ const [failedIds, setFailedIds] = React90.useState(/* @__PURE__ */ new Set());
40747
40850
  const closeLightbox = React90.useCallback(() => setLightboxItem(null), []);
40748
40851
  useEventListener("UI:LIGHTBOX_CLOSE", closeLightbox);
40852
+ const handleImageError = React90.useCallback((id) => {
40853
+ setFailedIds((prev) => {
40854
+ if (prev.has(id)) return prev;
40855
+ const next = new Set(prev);
40856
+ next.add(id);
40857
+ return next;
40858
+ });
40859
+ }, []);
40749
40860
  const handleItemClick = React90.useCallback(
40750
40861
  (item) => {
40751
40862
  if (selectable) {
@@ -40761,7 +40872,7 @@ var init_MediaGallery = __esm({
40761
40872
  );
40762
40873
  const entityData = Array.isArray(entity) ? entity : [];
40763
40874
  const items = React90__namespace.default.useMemo(() => {
40764
- if (propItems) return propItems;
40875
+ if (propItems && propItems.length > 0) return propItems;
40765
40876
  if (entityData.length === 0) return [];
40766
40877
  return entityData.map((record, idx) => {
40767
40878
  return {
@@ -40844,13 +40955,14 @@ var init_MediaGallery = __esm({
40844
40955
  ),
40845
40956
  onClick: () => handleItemClick(item),
40846
40957
  children: [
40847
- /* @__PURE__ */ jsxRuntime.jsx(
40958
+ failedIds.has(item.id) ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full h-full flex items-center justify-center bg-muted text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Image, size: "lg" }) }) : /* @__PURE__ */ jsxRuntime.jsx(
40848
40959
  "img",
40849
40960
  {
40850
40961
  src: item.thumbnail || item.src,
40851
40962
  alt: item.alt || item.caption || "",
40852
40963
  className: "w-full h-full object-cover",
40853
- loading: "lazy"
40964
+ loading: "lazy",
40965
+ onError: () => handleImageError(item.id)
40854
40966
  }
40855
40967
  ),
40856
40968
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -44357,6 +44469,11 @@ var init_TeamOrganism = __esm({
44357
44469
  TeamOrganism.displayName = "TeamOrganism";
44358
44470
  }
44359
44471
  });
44472
+ function formatDate4(value) {
44473
+ const d = new Date(value);
44474
+ if (isNaN(d.getTime())) return value;
44475
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
44476
+ }
44360
44477
  var lookStyles10, STATUS_STYLES2, Timeline;
44361
44478
  var init_Timeline = __esm({
44362
44479
  "components/core/organisms/Timeline.tsx"() {
@@ -44484,7 +44601,7 @@ var init_Timeline = __esm({
44484
44601
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: cn("flex-1 min-w-0", !isLast && "pb-6"), children: [
44485
44602
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { justify: "between", align: "start", wrap: true, children: [
44486
44603
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", weight: "semibold", children: item.title }),
44487
- item.date && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: item.date })
44604
+ item.date && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "flex-shrink-0", children: formatDate4(item.date) })
44488
44605
  ] }),
44489
44606
  item.description && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: item.description }),
44490
44607
  item.tags && item.tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", wrap: true, children: item.tags.map((tag, tagIdx) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: tag }, tagIdx)) }),
@@ -45696,7 +45813,7 @@ function substituteTraitRefsDeep(value, pathKey) {
45696
45813
  }
45697
45814
  return value;
45698
45815
  }
45699
- function renderPatternProps(props, onDismiss) {
45816
+ function renderPatternProps(props, onDismiss, propsSchema) {
45700
45817
  const rendered = {};
45701
45818
  for (const [key, value] of Object.entries(props)) {
45702
45819
  if (key === "children") {
@@ -45714,9 +45831,10 @@ function renderPatternProps(props, onDismiss) {
45714
45831
  };
45715
45832
  rendered[key] = /* @__PURE__ */ jsxRuntime.jsx(SlotContentRenderer, { content: childContent, onDismiss });
45716
45833
  } else if (Array.isArray(value)) {
45834
+ const isDataArray = propsSchema?.[key]?.items?.types?.includes("object") ?? false;
45717
45835
  rendered[key] = value.map((item, i) => {
45718
45836
  const el = item;
45719
- if (isPatternConfig(el)) {
45837
+ if (!isDataArray && isPatternConfig(el)) {
45720
45838
  const nestedProps = {};
45721
45839
  for (const [k, v] of Object.entries(el)) {
45722
45840
  if (k !== "type") nestedProps[k] = v;
@@ -45804,14 +45922,15 @@ function SlotContentRenderer({
45804
45922
  );
45805
45923
  }
45806
45924
  }
45807
- const renderedProps = renderPatternProps(restProps, onDismiss);
45808
45925
  const patternDef = patterns.getPatternDefinition(content.pattern);
45809
45926
  const propsSchema = patternDef?.propsSchema;
45927
+ const renderedProps = renderPatternProps(restProps, onDismiss, propsSchema);
45810
45928
  if (propsSchema) {
45811
45929
  for (const [propKey, propValue] of Object.entries(renderedProps)) {
45812
45930
  if (typeof propValue !== "string") continue;
45813
45931
  const propDef = propsSchema[propKey];
45814
45932
  if (!propDef || propDef.kind !== "callback") continue;
45933
+ if (propValue === "") continue;
45815
45934
  renderedProps[propKey] = ui.wrapCallbackForEvent(
45816
45935
  `UI:${propValue}`,
45817
45936
  propDef.callbackArgs,
@@ -49606,6 +49725,7 @@ function useSharedEntityStore() {
49606
49725
  }
49607
49726
  return storeRef.current;
49608
49727
  }
49728
+ React90.createContext(null);
49609
49729
  function runTickFrame(entityId, orderedWriters, store) {
49610
49730
  let scratch = store.getSnapshot(entityId);
49611
49731
  for (const writer of orderedWriters) {
@@ -49703,6 +49823,11 @@ var SYNC_TICK_OPERATORS = /* @__PURE__ */ new Set([
49703
49823
  "do",
49704
49824
  "when"
49705
49825
  ]);
49826
+ var REACTIVE_REPAINT_PATTERN_TYPES = /* @__PURE__ */ new Set([
49827
+ "canvas",
49828
+ "game-hud",
49829
+ "game-shell"
49830
+ ]);
49706
49831
  var LIFECYCLE_EVENTS = ["INIT", "LOAD", "$MOUNT"];
49707
49832
  function toTraitDefinition(binding) {
49708
49833
  return {
@@ -49955,6 +50080,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
49955
50080
  }, [traitStates]);
49956
50081
  const traitSnapshotDataRef = React90.useRef(/* @__PURE__ */ new Map());
49957
50082
  const traitFieldStatesRef = React90.useRef(/* @__PURE__ */ new Map());
50083
+ const lastCanvasRenderUiRef = React90.useRef(/* @__PURE__ */ new Map());
50084
+ const executeTransitionEffectsRef = React90.useRef(null);
49958
50085
  React90.useEffect(() => {
49959
50086
  const mgr = managerRef.current;
49960
50087
  const bindings = traitBindingsRef.current;
@@ -49995,11 +50122,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
49995
50122
  bindTraitStateGetter((traitName) => {
49996
50123
  const allStates = newManager.getAllStates();
49997
50124
  if (allStates instanceof Map) {
49998
- const val = allStates.get(traitName);
49999
- return typeof val === "string" ? val : void 0;
50125
+ return allStates.get(traitName)?.currentState;
50000
50126
  }
50001
- const state = newManager.getState(traitName);
50002
- return typeof state === "string" ? state : void 0;
50127
+ return newManager.getState(traitName)?.currentState;
50003
50128
  });
50004
50129
  const snapshotUnregs = [];
50005
50130
  for (const binding of traitBindingsRef.current) {
@@ -50084,6 +50209,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50084
50209
  const existing = pendingSlots.get(slot) || [];
50085
50210
  existing.push({ pattern, props: props || {} });
50086
50211
  pendingSlots.set(slot, existing);
50212
+ const patternType = pattern?.type;
50213
+ if (patternType !== void 0 && REACTIVE_REPAINT_PATTERN_TYPES.has(patternType)) {
50214
+ const rawForSlot = effects.filter(
50215
+ (e) => Array.isArray(e) && (e[0] === "render-ui" || e[0] === "render") && e[1] === slot
50216
+ );
50217
+ if (rawForSlot.length > 0) {
50218
+ lastCanvasRenderUiRef.current.set(traitName, rawForSlot);
50219
+ }
50220
+ }
50087
50221
  },
50088
50222
  clearSlot: (slot) => {
50089
50223
  pendingSlots.set(slot, []);
@@ -50154,11 +50288,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50154
50288
  notify: clientHandlers.notify
50155
50289
  };
50156
50290
  }
50291
+ const sharedWrites = [];
50157
50292
  const baseSet = handlers.set;
50158
50293
  handlers = {
50159
50294
  ...handlers,
50160
50295
  set: async (targetId, field, value) => {
50161
50296
  if (baseSet) await baseSet(targetId, field, value);
50297
+ if (sharedKey !== void 0) {
50298
+ sharedWrites.push({ field, value });
50299
+ }
50162
50300
  log11.debug("set:write", {
50163
50301
  traitName,
50164
50302
  field,
@@ -50234,7 +50372,10 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50234
50372
  });
50235
50373
  }
50236
50374
  if (sharedKey !== void 0) {
50237
- sharedEntityStore.commit(sharedKey, liveEntity);
50375
+ sharedEntityStore.commit(
50376
+ sharedKey,
50377
+ core.mergeEntityFrame(sharedEntityStore.getSnapshot(sharedKey), sharedWrites)
50378
+ );
50238
50379
  }
50239
50380
  log11.debug("effects:executed", () => ({
50240
50381
  traitName,
@@ -50260,6 +50401,38 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50260
50401
  entity: binding.linkedEntity
50261
50402
  });
50262
50403
  }
50404
+ if (pendingSlots.size === 0 && effectsCallOp(effects, SHARED_ENTITY_WRITE_OPS)) {
50405
+ const stashed = lastCanvasRenderUiRef.current.get(traitName);
50406
+ if (stashed !== void 0 && stashed.length > 0) {
50407
+ await executor.executeAll(stashed);
50408
+ for (const [slot, patterns] of pendingSlots) {
50409
+ flushSlot(traitName, slot, patterns, {
50410
+ event: flushEvent,
50411
+ state: previousState,
50412
+ entity: binding.linkedEntity
50413
+ });
50414
+ }
50415
+ }
50416
+ if (sharedKey !== void 0 && executeTransitionEffectsRef.current !== null) {
50417
+ for (const sibling of traitBindingsRef.current) {
50418
+ const siblingName = sibling.trait.name;
50419
+ if (siblingName === traitName) continue;
50420
+ if (sharedKeyByTraitNameRef.current.get(siblingName) !== sharedKey) continue;
50421
+ const siblingStash = lastCanvasRenderUiRef.current.get(siblingName);
50422
+ if (siblingStash === void 0 || siblingStash.length === 0) continue;
50423
+ const siblingState = traitStatesRef.current.get(siblingName)?.currentState ?? "";
50424
+ await executeTransitionEffectsRef.current({
50425
+ binding: sibling,
50426
+ effects: siblingStash,
50427
+ previousState: siblingState,
50428
+ newState: siblingState,
50429
+ flushEvent: `${flushEvent}:repaint`,
50430
+ syncOnly,
50431
+ log: log11
50432
+ });
50433
+ }
50434
+ }
50435
+ }
50263
50436
  } catch (error) {
50264
50437
  log11.error("effects:error", {
50265
50438
  traitName,
@@ -50271,6 +50444,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50271
50444
  }
50272
50445
  return emittedDuringExec;
50273
50446
  }, [eventBus, flushSlot, sharedEntityStore]);
50447
+ React90.useEffect(() => {
50448
+ executeTransitionEffectsRef.current = executeTransitionEffects;
50449
+ }, [executeTransitionEffects]);
50274
50450
  const runTickEffects = React90.useCallback((tick, binding) => {
50275
50451
  const traitName = binding.trait.name;
50276
50452
  const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
@@ -50333,6 +50509,24 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50333
50509
  ({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
50334
50510
  );
50335
50511
  runTickFrame(group.storeKey, writers, sharedEntityStore);
50512
+ const repaint = executeTransitionEffectsRef.current;
50513
+ if (repaint !== null) {
50514
+ for (const renderBinding of group.renderBindings) {
50515
+ const renderTraitName = renderBinding.trait.name;
50516
+ const stash = lastCanvasRenderUiRef.current.get(renderTraitName);
50517
+ if (stash === void 0 || stash.length === 0) continue;
50518
+ const renderState = traitStatesRef.current.get(renderTraitName)?.currentState ?? "";
50519
+ void repaint({
50520
+ binding: renderBinding,
50521
+ effects: stash,
50522
+ previousState: renderState,
50523
+ newState: renderState,
50524
+ flushEvent: "tick:repaint",
50525
+ syncOnly: true,
50526
+ log: sharedEntityLog
50527
+ });
50528
+ }
50529
+ }
50336
50530
  };
50337
50531
  if (interval === "frame") {
50338
50532
  scheduler.add(0, onDue);
@@ -50382,6 +50576,13 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50382
50576
  entityByTrait[name] = fields;
50383
50577
  }
50384
50578
  }
50579
+ for (const binding of bindings) {
50580
+ const name = binding.trait.name;
50581
+ const sharedKey = sharedKeyByTraitNameRef.current.get(name);
50582
+ if (sharedKey !== void 0) {
50583
+ entityByTrait[name] = { ...sharedEntityStore.getSnapshot(sharedKey) };
50584
+ }
50585
+ }
50385
50586
  const results = currentManager.sendEvent(
50386
50587
  normalizedEvent,
50387
50588
  payload,
@@ -50535,7 +50736,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50535
50736
  }
50536
50737
  await onEventProcessed(normalizedEvent, payload, dispatchedOrbitals);
50537
50738
  }
50538
- }, [entities, eventBus]);
50739
+ }, [entities, eventBus, sharedEntityStore]);
50539
50740
  const drainEventQueue = React90.useCallback(async () => {
50540
50741
  if (processingRef.current) return;
50541
50742
  processingRef.current = true;
@@ -50605,6 +50806,24 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
50605
50806
  });
50606
50807
  }
50607
50808
  }
50809
+ const bareCascadeKeys = /* @__PURE__ */ new Set();
50810
+ for (const binding of traitBindings) {
50811
+ for (const transition of binding.trait.transitions) {
50812
+ const eventKey = transition.event;
50813
+ if (LIFECYCLE_EVENTS.includes(eventKey)) continue;
50814
+ if (bareCascadeKeys.has(eventKey)) continue;
50815
+ bareCascadeKeys.add(eventKey);
50816
+ const bareKey = `UI:${eventKey}`;
50817
+ const unsub = eventBus.on(bareKey, (event) => {
50818
+ crossTraitLog.debug("bare-cascade:fire", { bareKey, eventKey });
50819
+ enqueueAndDrain(eventKey, event.payload);
50820
+ });
50821
+ unsubscribes.push(() => {
50822
+ crossTraitLog.debug("bare-cascade:unsubscribe", { bareKey, eventKey });
50823
+ unsub();
50824
+ });
50825
+ }
50826
+ }
50608
50827
  for (const binding of traitBindings) {
50609
50828
  const ownOrbital = orbitalsByTrait?.[binding.trait.name];
50610
50829
  const listens = binding.trait.listens ?? [];
@@ -50688,9 +50907,16 @@ function normalizeChild(child) {
50688
50907
  props: { ...rest, ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {} }
50689
50908
  };
50690
50909
  }
50691
- function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits) {
50910
+ function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraits) {
50692
50911
  for (const eff of effects) {
50693
50912
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
50913
+ if (eff.traitName && activeTraits && !activeTraits.has(eff.traitName)) {
50914
+ xOrbitalLog.debug("slot:off-page-trait-skipped", {
50915
+ sourceTrait: eff.traitName,
50916
+ slot: eff.slot
50917
+ });
50918
+ continue;
50919
+ }
50694
50920
  const patternRecord = eff.pattern;
50695
50921
  const { type: patternType, children, ...inlineProps } = patternRecord;
50696
50922
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
@@ -50731,8 +50957,38 @@ function applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits) {
50731
50957
  }
50732
50958
  }
50733
50959
  }
50734
- function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits }) {
50960
+ function collectServerActiveTraits(ir, allTraits, mountedTraitNames) {
50961
+ if (!ir?.pages || ir.pages.size <= 1) return void 0;
50962
+ const pageBound = /* @__PURE__ */ new Set();
50963
+ for (const page of ir.pages.values()) {
50964
+ const queue = page.traits.map((b) => b.trait.name).filter((n) => !!n);
50965
+ for (const name of queue) {
50966
+ if (pageBound.has(name)) continue;
50967
+ pageBound.add(name);
50968
+ const rt = allTraits.get(name);
50969
+ if (!rt) continue;
50970
+ queue.push(...ui.collectTraitRefsFromResolvedTrait(rt));
50971
+ }
50972
+ }
50973
+ const active = new Set(mountedTraitNames);
50974
+ for (const name of allTraits.keys()) {
50975
+ if (!pageBound.has(name)) active.add(name);
50976
+ }
50977
+ return active;
50978
+ }
50979
+ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, serverActiveTraits }) {
50735
50980
  const bridge = providers.useServerBridge();
50981
+ const activeTraitNames = React90.useMemo(
50982
+ () => new Set(traits2.map((b) => b.trait.name).filter((n) => !!n)),
50983
+ [traits2]
50984
+ );
50985
+ const withActiveTraits = React90.useCallback(
50986
+ (payload) => {
50987
+ if (!serverActiveTraits || serverActiveTraits.size === 0) return payload;
50988
+ return { ...payload ?? {}, _activeTraits: Array.from(serverActiveTraits) };
50989
+ },
50990
+ [serverActiveTraits]
50991
+ );
50736
50992
  const uiSlots = context.useUISlots();
50737
50993
  const onEventProcessed = React90.useCallback(async (event, payload, dispatchedOrbitals) => {
50738
50994
  if (!bridge.connected || !orbitalNames?.length) return;
@@ -50744,11 +51000,11 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
50744
51000
  dispatchedOrbitalsSize: dispatchedOrbitals?.size ?? 0
50745
51001
  }));
50746
51002
  for (const name of targets) {
50747
- const { effects, meta } = await bridge.sendEvent(name, event, payload);
51003
+ const { effects, meta } = await bridge.sendEvent(name, event, withActiveTraits(payload));
50748
51004
  recordServerResponse(name, event, meta);
50749
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits);
51005
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames);
50750
51006
  }
50751
- }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, embeddedTraits]);
51007
+ }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, embeddedTraits, activeTraitNames, withActiveTraits]);
50752
51008
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, traitConfigsByName, orbitalsByTrait, embeddedTraits } : { navigate: onNavigate, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits };
50753
51009
  const { sendEvent } = useTraitStateMachine(traits2, uiSlots, opts);
50754
51010
  const initSentRef = React90.useRef(false);
@@ -50785,7 +51041,7 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
50785
51041
  initSentRef.current = true;
50786
51042
  (async () => {
50787
51043
  for (const name of orbitalNames) {
50788
- const { effects, meta } = await bridge.sendEvent(name, "INIT", {});
51044
+ const { effects, meta } = await bridge.sendEvent(name, "INIT", withActiveTraits({}));
50789
51045
  recordServerResponse(name, "INIT", meta);
50790
51046
  const effectTraces = [
50791
51047
  { type: "fetch", args: [], status: "executed" },
@@ -50803,10 +51059,10 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFa
50803
51059
  effects: effectTraces,
50804
51060
  timestamp: Date.now()
50805
51061
  });
50806
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits);
51062
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames);
50807
51063
  }
50808
51064
  })();
50809
- }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, embeddedTraits]);
51065
+ }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, embeddedTraits, activeTraitNames, withActiveTraits]);
50810
51066
  return null;
50811
51067
  }
50812
51068
  function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavigate, onLocalFallback, persistence }) {
@@ -50893,6 +51149,14 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavi
50893
51149
  }
50894
51150
  return Array.from(set);
50895
51151
  }, [allPageTraits, orbitalsByTrait]);
51152
+ const serverActiveTraits = React90.useMemo(
51153
+ () => collectServerActiveTraits(
51154
+ ir,
51155
+ allTraits,
51156
+ new Set(allPageTraits.map((b) => b.trait.name).filter((n) => !!n))
51157
+ ),
51158
+ [ir, allTraits, allPageTraits]
51159
+ );
50896
51160
  React90.useEffect(() => {
50897
51161
  const traitNames = allPageTraits.map((b) => b.trait.name);
50898
51162
  const orbitalsByTraitForPage = {};
@@ -50902,7 +51166,7 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavi
50902
51166
  }
50903
51167
  xOrbitalLog.info("SchemaRunner:mount", {
50904
51168
  pageName,
50905
- traitNames,
51169
+ traitNames: traitNames.join(","),
50906
51170
  orbitalsByTraitForPage,
50907
51171
  pageOrbitalNames: pageOrbitalNames.join(",")
50908
51172
  });
@@ -50947,6 +51211,7 @@ function SchemaRunner({ schema, serverUrl, transport, mockData, pageName, onNavi
50947
51211
  traitConfigsByName,
50948
51212
  orbitalsByTrait,
50949
51213
  embeddedTraits,
51214
+ serverActiveTraits,
50950
51215
  onNavigate,
50951
51216
  onLocalFallback,
50952
51217
  persistence