@almadar/ui 4.0.1 → 4.2.1

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.
@@ -1014,30 +1014,76 @@ function useEmitEvent() {
1014
1014
  [eventBus]
1015
1015
  );
1016
1016
  }
1017
- var DEFAULT_SLOTS = {
1018
- main: null,
1019
- sidebar: null,
1020
- modal: null,
1021
- drawer: null,
1022
- overlay: null,
1023
- center: null,
1024
- toast: null,
1025
- "hud-top": null,
1026
- "hud-bottom": null,
1027
- "hud-left": null,
1028
- "hud-right": null,
1029
- floating: null
1030
- };
1017
+ var DEFAULT_SOURCE_KEY = "__default__";
1018
+ var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
1019
+ var ALL_SLOTS = [
1020
+ "main",
1021
+ "sidebar",
1022
+ "modal",
1023
+ "drawer",
1024
+ "overlay",
1025
+ "center",
1026
+ "toast",
1027
+ "hud-top",
1028
+ "hud-bottom",
1029
+ "hud-left",
1030
+ "hud-right",
1031
+ "floating"
1032
+ ];
1033
+ var DEFAULT_SLOTS = ALL_SLOTS.reduce(
1034
+ (acc, slot) => {
1035
+ acc[slot] = null;
1036
+ return acc;
1037
+ },
1038
+ {}
1039
+ );
1040
+ var DEFAULT_SOURCES = ALL_SLOTS.reduce(
1041
+ (acc, slot) => {
1042
+ acc[slot] = {};
1043
+ return acc;
1044
+ },
1045
+ {}
1046
+ );
1031
1047
  var idCounter = 0;
1032
1048
  function generateId() {
1033
1049
  return `slot-content-${++idCounter}-${Date.now()}`;
1034
1050
  }
1051
+ function aggregateSlot(sources) {
1052
+ if (!sources) return null;
1053
+ const entries = Object.entries(sources);
1054
+ if (entries.length === 0) return null;
1055
+ if (entries.length === 1) return entries[0][1];
1056
+ const children = entries.map(([, entry]) => ({
1057
+ type: entry.pattern,
1058
+ ...entry.props
1059
+ }));
1060
+ const stackId = `slot-content-stack-${entries.map(([k]) => k).join("-")}`;
1061
+ return {
1062
+ id: stackId,
1063
+ pattern: "stack",
1064
+ props: {
1065
+ direction: "vertical",
1066
+ gap: "lg",
1067
+ children
1068
+ },
1069
+ priority: 0,
1070
+ animation: "fade",
1071
+ sourceTrait: MULTI_SOURCE_STACK_TRAIT
1072
+ };
1073
+ }
1035
1074
  function useUISlotManager() {
1036
- const [slots, setSlots] = useState(DEFAULT_SLOTS);
1075
+ const [sources, setSources] = useState(DEFAULT_SOURCES);
1037
1076
  const subscribersRef = useRef(/* @__PURE__ */ new Set());
1038
1077
  const timersRef = useRef(/* @__PURE__ */ new Map());
1039
1078
  const traitIndexRef = useRef(/* @__PURE__ */ new Map());
1040
1079
  const traitSubscribersRef = useRef(/* @__PURE__ */ new Map());
1080
+ const slots = useMemo(() => {
1081
+ const out = { ...DEFAULT_SLOTS };
1082
+ for (const slot of ALL_SLOTS) {
1083
+ out[slot] = aggregateSlot(sources[slot]);
1084
+ }
1085
+ return out;
1086
+ }, [sources]);
1041
1087
  useEffect(() => {
1042
1088
  return () => {
1043
1089
  timersRef.current.forEach((timer) => clearTimeout(timer));
@@ -1076,103 +1122,160 @@ function useUISlotManager() {
1076
1122
  const unindexTrait = useCallback((traitName) => {
1077
1123
  traitIndexRef.current.delete(traitName);
1078
1124
  }, []);
1079
- const render = useCallback((config) => {
1080
- const id = generateId();
1081
- const content = {
1082
- id,
1083
- pattern: config.pattern,
1084
- props: config.props ?? {},
1085
- priority: config.priority ?? 0,
1086
- animation: config.animation ?? "fade",
1087
- onDismiss: config.onDismiss,
1088
- sourceTrait: config.sourceTrait
1089
- };
1090
- if (config.autoDismissMs && config.autoDismissMs > 0) {
1091
- content.autoDismissAt = Date.now() + config.autoDismissMs;
1092
- const timer = setTimeout(() => {
1093
- setSlots((prev) => {
1094
- if (prev[config.target]?.id === id) {
1095
- content.onDismiss?.();
1096
- notifySubscribers(config.target, null);
1097
- return { ...prev, [config.target]: null };
1098
- }
1099
- return prev;
1100
- });
1101
- timersRef.current.delete(id);
1102
- }, config.autoDismissMs);
1103
- timersRef.current.set(id, timer);
1104
- }
1105
- setSlots((prev) => {
1106
- const existing = prev[config.target];
1107
- if (existing && existing.priority > content.priority) {
1108
- console.warn(
1109
- `[UISlots] Slot "${config.target}" already has higher priority content (${existing.priority} > ${content.priority})`
1110
- );
1111
- return prev;
1112
- }
1113
- if (content.sourceTrait) {
1114
- indexTraitRender(content.sourceTrait, content);
1115
- notifyTraitSubscribers(content.sourceTrait, content);
1125
+ const render = useCallback(
1126
+ (config) => {
1127
+ const id = generateId();
1128
+ const sourceKey = config.sourceTrait ?? DEFAULT_SOURCE_KEY;
1129
+ const content = {
1130
+ id,
1131
+ pattern: config.pattern,
1132
+ props: config.props ?? {},
1133
+ priority: config.priority ?? 0,
1134
+ animation: config.animation ?? "fade",
1135
+ onDismiss: config.onDismiss,
1136
+ sourceTrait: config.sourceTrait
1137
+ };
1138
+ if (config.autoDismissMs && config.autoDismissMs > 0) {
1139
+ content.autoDismissAt = Date.now() + config.autoDismissMs;
1140
+ const timer = setTimeout(() => {
1141
+ setSources((prev) => {
1142
+ const slotSources = prev[config.target];
1143
+ if (slotSources && slotSources[sourceKey]?.id === id) {
1144
+ content.onDismiss?.();
1145
+ const next = { ...slotSources };
1146
+ delete next[sourceKey];
1147
+ const updated = { ...prev, [config.target]: next };
1148
+ notifySubscribers(config.target, aggregateSlot(next));
1149
+ return updated;
1150
+ }
1151
+ return prev;
1152
+ });
1153
+ timersRef.current.delete(id);
1154
+ }, config.autoDismissMs);
1155
+ timersRef.current.set(id, timer);
1116
1156
  }
1117
- notifySubscribers(config.target, content);
1118
- return { ...prev, [config.target]: content };
1119
- });
1120
- return id;
1121
- }, [notifySubscribers, notifyTraitSubscribers, indexTraitRender]);
1122
- const clear = useCallback((slot) => {
1123
- setSlots((prev) => {
1124
- const content = prev[slot];
1125
- if (content) {
1126
- const timer = timersRef.current.get(content.id);
1127
- if (timer) {
1128
- clearTimeout(timer);
1129
- timersRef.current.delete(content.id);
1157
+ setSources((prev) => {
1158
+ const slotSources = prev[config.target] ?? {};
1159
+ const existing = slotSources[sourceKey];
1160
+ if (existing && existing.priority > content.priority) {
1161
+ console.warn(
1162
+ `[UISlots] Slot "${config.target}" source "${sourceKey}" already has higher priority content (${existing.priority} > ${content.priority})`
1163
+ );
1164
+ return prev;
1130
1165
  }
1131
- content.onDismiss?.();
1166
+ const nextSources = {
1167
+ ...slotSources,
1168
+ [sourceKey]: content
1169
+ };
1170
+ const nextAll = { ...prev, [config.target]: nextSources };
1132
1171
  if (content.sourceTrait) {
1133
- unindexTrait(content.sourceTrait);
1134
- notifyTraitSubscribers(content.sourceTrait, null);
1172
+ indexTraitRender(content.sourceTrait, content);
1173
+ notifyTraitSubscribers(content.sourceTrait, content);
1174
+ }
1175
+ notifySubscribers(config.target, aggregateSlot(nextSources));
1176
+ return nextAll;
1177
+ });
1178
+ return id;
1179
+ },
1180
+ [notifySubscribers, notifyTraitSubscribers, indexTraitRender]
1181
+ );
1182
+ const clear = useCallback(
1183
+ (slot) => {
1184
+ setSources((prev) => {
1185
+ const slotSources = prev[slot];
1186
+ if (!slotSources || Object.keys(slotSources).length === 0) {
1187
+ return prev;
1188
+ }
1189
+ for (const content of Object.values(slotSources)) {
1190
+ const timer = timersRef.current.get(content.id);
1191
+ if (timer) {
1192
+ clearTimeout(timer);
1193
+ timersRef.current.delete(content.id);
1194
+ }
1195
+ content.onDismiss?.();
1196
+ if (content.sourceTrait) {
1197
+ unindexTrait(content.sourceTrait);
1198
+ notifyTraitSubscribers(content.sourceTrait, null);
1199
+ }
1135
1200
  }
1136
1201
  notifySubscribers(slot, null);
1137
- }
1138
- return { ...prev, [slot]: null };
1139
- });
1140
- }, [notifySubscribers, notifyTraitSubscribers, unindexTrait]);
1141
- const clearById = useCallback((id) => {
1142
- setSlots((prev) => {
1143
- const entry = Object.entries(prev).find(([, content]) => content?.id === id);
1144
- if (entry) {
1145
- const [slot, content] = entry;
1146
- const timer = timersRef.current.get(id);
1202
+ return { ...prev, [slot]: {} };
1203
+ });
1204
+ },
1205
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
1206
+ );
1207
+ const clearBySource = useCallback(
1208
+ (slot, sourceTrait) => {
1209
+ const sourceKey = sourceTrait;
1210
+ setSources((prev) => {
1211
+ const slotSources = prev[slot];
1212
+ if (!slotSources || !(sourceKey in slotSources)) return prev;
1213
+ const content = slotSources[sourceKey];
1214
+ const timer = timersRef.current.get(content.id);
1147
1215
  if (timer) {
1148
1216
  clearTimeout(timer);
1149
- timersRef.current.delete(id);
1217
+ timersRef.current.delete(content.id);
1150
1218
  }
1151
1219
  content.onDismiss?.();
1152
1220
  if (content.sourceTrait) {
1153
1221
  unindexTrait(content.sourceTrait);
1154
1222
  notifyTraitSubscribers(content.sourceTrait, null);
1155
1223
  }
1156
- notifySubscribers(slot, null);
1157
- return { ...prev, [slot]: null };
1158
- }
1159
- return prev;
1160
- });
1161
- }, [notifySubscribers, notifyTraitSubscribers, unindexTrait]);
1224
+ const nextSources = { ...slotSources };
1225
+ delete nextSources[sourceKey];
1226
+ notifySubscribers(slot, aggregateSlot(nextSources));
1227
+ return { ...prev, [slot]: nextSources };
1228
+ });
1229
+ },
1230
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
1231
+ );
1232
+ const clearById = useCallback(
1233
+ (id) => {
1234
+ setSources((prev) => {
1235
+ for (const slot of ALL_SLOTS) {
1236
+ const slotSources = prev[slot];
1237
+ if (!slotSources) continue;
1238
+ const matchKey = Object.keys(slotSources).find(
1239
+ (k) => slotSources[k].id === id
1240
+ );
1241
+ if (!matchKey) continue;
1242
+ const content = slotSources[matchKey];
1243
+ const timer = timersRef.current.get(id);
1244
+ if (timer) {
1245
+ clearTimeout(timer);
1246
+ timersRef.current.delete(id);
1247
+ }
1248
+ content.onDismiss?.();
1249
+ if (content.sourceTrait) {
1250
+ unindexTrait(content.sourceTrait);
1251
+ notifyTraitSubscribers(content.sourceTrait, null);
1252
+ }
1253
+ const nextSources = { ...slotSources };
1254
+ delete nextSources[matchKey];
1255
+ notifySubscribers(slot, aggregateSlot(nextSources));
1256
+ return { ...prev, [slot]: nextSources };
1257
+ }
1258
+ return prev;
1259
+ });
1260
+ },
1261
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
1262
+ );
1162
1263
  const clearAll = useCallback(() => {
1163
1264
  timersRef.current.forEach((timer) => clearTimeout(timer));
1164
1265
  timersRef.current.clear();
1165
- setSlots((prev) => {
1166
- Object.entries(prev).forEach(([slot, content]) => {
1167
- if (content) {
1266
+ setSources((prev) => {
1267
+ for (const slot of ALL_SLOTS) {
1268
+ const slotSources = prev[slot];
1269
+ if (!slotSources) continue;
1270
+ for (const content of Object.values(slotSources)) {
1168
1271
  content.onDismiss?.();
1169
1272
  if (content.sourceTrait) {
1170
1273
  notifyTraitSubscribers(content.sourceTrait, null);
1171
1274
  }
1172
- notifySubscribers(slot, null);
1173
1275
  }
1174
- });
1175
- return DEFAULT_SLOTS;
1276
+ notifySubscribers(slot, null);
1277
+ }
1278
+ return DEFAULT_SOURCES;
1176
1279
  });
1177
1280
  traitIndexRef.current.clear();
1178
1281
  }, [notifySubscribers, notifyTraitSubscribers]);
@@ -1182,16 +1285,16 @@ function useUISlotManager() {
1182
1285
  subscribersRef.current.delete(callback);
1183
1286
  };
1184
1287
  }, []);
1185
- const hasContent = useCallback((slot) => {
1186
- return slots[slot] !== null;
1187
- }, [slots]);
1188
- const getContent = useCallback((slot) => {
1189
- return slots[slot];
1190
- }, [slots]);
1288
+ const hasContent = useCallback(
1289
+ (slot) => slots[slot] !== null,
1290
+ [slots]
1291
+ );
1292
+ const getContent = useCallback(
1293
+ (slot) => slots[slot],
1294
+ [slots]
1295
+ );
1191
1296
  const getTraitContent = useCallback(
1192
- (traitName) => {
1193
- return traitIndexRef.current.get(traitName) ?? null;
1194
- },
1297
+ (traitName) => traitIndexRef.current.get(traitName) ?? null,
1195
1298
  []
1196
1299
  );
1197
1300
  const subscribeTrait = useCallback(
@@ -1217,6 +1320,7 @@ function useUISlotManager() {
1217
1320
  slots,
1218
1321
  render,
1219
1322
  clear,
1323
+ clearBySource,
1220
1324
  clearById,
1221
1325
  clearAll,
1222
1326
  subscribe: subscribe2,
@@ -1,3 +1,4 @@
1
+ import type { EventPayload } from '@almadar/core';
1
2
  /**
2
3
  * Valid UI slot names
3
4
  */
@@ -6,6 +7,14 @@ export type UISlot = 'main' | 'sidebar' | 'modal' | 'drawer' | 'overlay' | 'cent
6
7
  * Animation types for slot transitions
7
8
  */
8
9
  export type SlotAnimation = 'fade' | 'slide' | 'scale' | 'none';
10
+ /**
11
+ * Pattern-specific props carried by a rendered slot. Pattern authors decide
12
+ * the concrete shape; the slot manager treats this as an opaque record of
13
+ * field-like values sourced from the event-bus payload vocabulary so the
14
+ * same types round-trip through render-ui → useUISlots → UISlotRenderer
15
+ * without a private re-coercion boundary.
16
+ */
17
+ export type SlotProps = Record<string, EventPayload[string] | unknown>;
9
18
  /**
10
19
  * Content rendered in a slot
11
20
  */
@@ -15,7 +24,7 @@ export interface SlotContent {
15
24
  /** Pattern/component type to render */
16
25
  pattern: string;
17
26
  /** Props to pass to the pattern component */
18
- props: Record<string, unknown>;
27
+ props: SlotProps;
19
28
  /** Priority for conflict resolution (higher wins) */
20
29
  priority: number;
21
30
  /** Animation for showing/hiding */
@@ -38,7 +47,7 @@ export interface RenderUIConfig {
38
47
  /** Pattern/component to render */
39
48
  pattern: string;
40
49
  /** Props for the pattern */
41
- props?: Record<string, unknown>;
50
+ props?: SlotProps;
42
51
  /** Priority (default: 0) */
43
52
  priority?: number;
44
53
  /** Animation type */
@@ -68,12 +77,20 @@ export type TraitChangeCallback = (content: SlotContent | null) => void;
68
77
  * UI Slot Manager interface
69
78
  */
70
79
  export interface UISlotManager {
71
- /** Current content for each slot */
80
+ /**
81
+ * Current aggregate content for each slot. Single-source slots hold
82
+ * that source's SlotContent; multi-source slots hold a synthetic
83
+ * `stack` wrapper whose `children` prop is the list of per-source
84
+ * pattern configs in insertion order. Single consumers can continue
85
+ * to read `slots[slot]` without knowing the slot is multi-source.
86
+ */
72
87
  slots: Record<UISlot, SlotContent | null>;
73
88
  /** Render content to a slot */
74
89
  render: (config: RenderUIConfig) => string;
75
- /** Clear a specific slot */
90
+ /** Clear all source entries from a slot */
76
91
  clear: (slot: UISlot) => void;
92
+ /** Clear a single source's contribution from a slot, keeping others */
93
+ clearBySource: (slot: UISlot, sourceTrait: string) => void;
77
94
  /** Clear content by ID */
78
95
  clearById: (id: string) => void;
79
96
  /** Clear all slots */
@@ -82,7 +99,7 @@ export interface UISlotManager {
82
99
  subscribe: (callback: SlotChangeCallback) => () => void;
83
100
  /** Check if a slot has content */
84
101
  hasContent: (slot: UISlot) => boolean;
85
- /** Get content for a slot */
102
+ /** Get aggregated content for a slot (single or synthetic stack) */
86
103
  getContent: (slot: UISlot) => SlotContent | null;
87
104
  /**
88
105
  * Look up the most recent `render-ui` output a given trait produced,
@@ -18578,6 +18578,16 @@ function statusVariant2(value) {
18578
18578
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
18579
18579
  return "default";
18580
18580
  }
18581
+ function resolveBadgeVariant(field, value) {
18582
+ const fromMap = field.colorMap?.[value];
18583
+ if (fromMap) {
18584
+ const normalised = fromMap === "destructive" ? "danger" : fromMap;
18585
+ if (BADGE_VARIANTS.has(normalised)) {
18586
+ return normalised;
18587
+ }
18588
+ }
18589
+ return statusVariant2(value);
18590
+ }
18581
18591
  function formatDate2(value) {
18582
18592
  if (!value) return "";
18583
18593
  const d = new Date(String(value));
@@ -18791,7 +18801,7 @@ function DataGrid({
18791
18801
  if (val === void 0 || val === null) return null;
18792
18802
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
18793
18803
  field.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: field.icon, size: "xs" }),
18794
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
18804
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: String(val) })
18795
18805
  ] }, field.name);
18796
18806
  }) })
18797
18807
  ] }),
@@ -18884,7 +18894,7 @@ function DataGrid({
18884
18894
  )
18885
18895
  ] });
18886
18896
  }
18887
- var gapStyles6;
18897
+ var BADGE_VARIANTS, gapStyles6;
18888
18898
  var init_DataGrid = __esm({
18889
18899
  "components/molecules/DataGrid.tsx"() {
18890
18900
  "use client";
@@ -18899,6 +18909,17 @@ var init_DataGrid = __esm({
18899
18909
  init_Button();
18900
18910
  init_Icon();
18901
18911
  init_InfiniteScrollSentinel();
18912
+ BADGE_VARIANTS = /* @__PURE__ */ new Set([
18913
+ "default",
18914
+ "primary",
18915
+ "secondary",
18916
+ "success",
18917
+ "warning",
18918
+ "danger",
18919
+ "error",
18920
+ "info",
18921
+ "neutral"
18922
+ ]);
18902
18923
  gapStyles6 = {
18903
18924
  none: "gap-0",
18904
18925
  sm: "gap-2",
@@ -18533,6 +18533,16 @@ function statusVariant2(value) {
18533
18533
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
18534
18534
  return "default";
18535
18535
  }
18536
+ function resolveBadgeVariant(field, value) {
18537
+ const fromMap = field.colorMap?.[value];
18538
+ if (fromMap) {
18539
+ const normalised = fromMap === "destructive" ? "danger" : fromMap;
18540
+ if (BADGE_VARIANTS.has(normalised)) {
18541
+ return normalised;
18542
+ }
18543
+ }
18544
+ return statusVariant2(value);
18545
+ }
18536
18546
  function formatDate2(value) {
18537
18547
  if (!value) return "";
18538
18548
  const d = new Date(String(value));
@@ -18746,7 +18756,7 @@ function DataGrid({
18746
18756
  if (val === void 0 || val === null) return null;
18747
18757
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
18748
18758
  field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
18749
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
18759
+ /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: String(val) })
18750
18760
  ] }, field.name);
18751
18761
  }) })
18752
18762
  ] }),
@@ -18839,7 +18849,7 @@ function DataGrid({
18839
18849
  )
18840
18850
  ] });
18841
18851
  }
18842
- var gapStyles6;
18852
+ var BADGE_VARIANTS, gapStyles6;
18843
18853
  var init_DataGrid = __esm({
18844
18854
  "components/molecules/DataGrid.tsx"() {
18845
18855
  "use client";
@@ -18854,6 +18864,17 @@ var init_DataGrid = __esm({
18854
18864
  init_Button();
18855
18865
  init_Icon();
18856
18866
  init_InfiniteScrollSentinel();
18867
+ BADGE_VARIANTS = /* @__PURE__ */ new Set([
18868
+ "default",
18869
+ "primary",
18870
+ "secondary",
18871
+ "success",
18872
+ "warning",
18873
+ "danger",
18874
+ "error",
18875
+ "info",
18876
+ "neutral"
18877
+ ]);
18857
18878
  gapStyles6 = {
18858
18879
  none: "gap-0",
18859
18880
  sm: "gap-2",
@@ -18382,6 +18382,16 @@ function statusVariant2(value) {
18382
18382
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
18383
18383
  return "default";
18384
18384
  }
18385
+ function resolveBadgeVariant(field, value) {
18386
+ const fromMap = field.colorMap?.[value];
18387
+ if (fromMap) {
18388
+ const normalised = fromMap === "destructive" ? "danger" : fromMap;
18389
+ if (BADGE_VARIANTS.has(normalised)) {
18390
+ return normalised;
18391
+ }
18392
+ }
18393
+ return statusVariant2(value);
18394
+ }
18385
18395
  function formatDate2(value) {
18386
18396
  if (!value) return "";
18387
18397
  const d = new Date(String(value));
@@ -18595,7 +18605,7 @@ function DataGrid({
18595
18605
  if (val === void 0 || val === null) return null;
18596
18606
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
18597
18607
  field.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: field.icon, size: "xs" }),
18598
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
18608
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: String(val) })
18599
18609
  ] }, field.name);
18600
18610
  }) })
18601
18611
  ] }),
@@ -18688,7 +18698,7 @@ function DataGrid({
18688
18698
  )
18689
18699
  ] });
18690
18700
  }
18691
- var gapStyles6;
18701
+ var BADGE_VARIANTS, gapStyles6;
18692
18702
  var init_DataGrid = __esm({
18693
18703
  "components/molecules/DataGrid.tsx"() {
18694
18704
  "use client";
@@ -18703,6 +18713,17 @@ var init_DataGrid = __esm({
18703
18713
  init_Button();
18704
18714
  init_Icon();
18705
18715
  init_InfiniteScrollSentinel();
18716
+ BADGE_VARIANTS = /* @__PURE__ */ new Set([
18717
+ "default",
18718
+ "primary",
18719
+ "secondary",
18720
+ "success",
18721
+ "warning",
18722
+ "danger",
18723
+ "error",
18724
+ "info",
18725
+ "neutral"
18726
+ ]);
18706
18727
  gapStyles6 = {
18707
18728
  none: "gap-0",
18708
18729
  sm: "gap-2",
@@ -37828,6 +37849,34 @@ var init_UISlotRenderer = __esm({
37828
37849
 
37829
37850
  // hooks/index.ts
37830
37851
  init_useEventBus();
37852
+ var ALL_SLOTS = [
37853
+ "main",
37854
+ "sidebar",
37855
+ "modal",
37856
+ "drawer",
37857
+ "overlay",
37858
+ "center",
37859
+ "toast",
37860
+ "hud-top",
37861
+ "hud-bottom",
37862
+ "hud-left",
37863
+ "hud-right",
37864
+ "floating"
37865
+ ];
37866
+ ALL_SLOTS.reduce(
37867
+ (acc, slot) => {
37868
+ acc[slot] = null;
37869
+ return acc;
37870
+ },
37871
+ {}
37872
+ );
37873
+ ALL_SLOTS.reduce(
37874
+ (acc, slot) => {
37875
+ acc[slot] = {};
37876
+ return acc;
37877
+ },
37878
+ {}
37879
+ );
37831
37880
 
37832
37881
  // hooks/useUIEvents.ts
37833
37882
  init_useEventBus();
@@ -38870,13 +38919,11 @@ function SlotBridge() {
38870
38919
  return null;
38871
38920
  }
38872
38921
  function applyServerEffects(effects, uiSlots, onNavigate) {
38873
- const perSlotRenders = /* @__PURE__ */ new Map();
38874
38922
  for (const eff of effects) {
38875
38923
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
38876
38924
  const patternRecord = eff.pattern;
38877
38925
  const { type: patternType, children, ...inlineProps } = patternRecord;
38878
38926
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
38879
- const sourceTrait = eff.traitName ?? "server";
38880
38927
  uiSlots.render({
38881
38928
  target: eff.slot,
38882
38929
  pattern: patternType,
@@ -38884,38 +38931,12 @@ function applyServerEffects(effects, uiSlots, onNavigate) {
38884
38931
  ...inlineProps,
38885
38932
  ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
38886
38933
  },
38887
- sourceTrait
38888
- });
38889
- const bucket = perSlotRenders.get(eff.slot) ?? [];
38890
- bucket.push({
38891
- sourceTrait,
38892
- pattern: {
38893
- type: patternType,
38894
- ...inlineProps,
38895
- ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
38896
- }
38934
+ sourceTrait: eff.traitName ?? "server"
38897
38935
  });
38898
- perSlotRenders.set(eff.slot, bucket);
38899
38936
  } else if (eff.type === "navigate" && eff.route && onNavigate) {
38900
38937
  onNavigate(eff.route, eff.params);
38901
38938
  }
38902
38939
  }
38903
- for (const [slot, bucket] of perSlotRenders) {
38904
- const distinctSources = new Set(bucket.map((b) => b.sourceTrait));
38905
- if (distinctSources.size <= 1) continue;
38906
- uiSlots.render({
38907
- target: slot,
38908
- pattern: "stack",
38909
- props: {
38910
- direction: "vertical",
38911
- gap: "lg",
38912
- children: bucket.map((b) => b.pattern)
38913
- },
38914
- // Use a synthetic wrapper source trait; individual traits' frames
38915
- // already live in the per-trait index from the per-effect loop.
38916
- sourceTrait: "__multi_source_stack__"
38917
- });
38918
- }
38919
38940
  }
38920
38941
  function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence }) {
38921
38942
  const slotsActions = useSlotsActions();