@almadar/shell-web 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/bundling.d.mts +18 -0
  2. package/bundling.mjs +28 -0
  3. package/dist/ArbitraryClassCompiler-CO_iASBn.d.ts +195 -0
  4. package/dist/AvlGlyph-EPJKFEO3.js +6 -0
  5. package/dist/AvlStateMachine-RKZIKNGC.js +9 -0
  6. package/dist/AvlTransitionDetail-63SD6GZF.js +11 -0
  7. package/dist/AvlTransitionExplainer-ZBTLOM3T.js +12 -0
  8. package/dist/BehaviorView-YS5G4GNB.js +11 -0
  9. package/dist/Canvas3DHost-BkZQB5l2.d.ts +552 -0
  10. package/dist/EventBusProvider-BFFHfN4O.d.ts +296 -0
  11. package/dist/FileTree-BCFnBEuQ.d.ts +254 -0
  12. package/dist/FlowCanvas-3JRMNJLV.css +1756 -0
  13. package/dist/FlowCanvas-AUMTWMNG.js +26 -0
  14. package/dist/GameAudioProvider-ceM_n0ey.d.ts +90 -0
  15. package/dist/KaTeX_AMS-Regular-CYEKBG2K.woff +0 -0
  16. package/dist/KaTeX_AMS-Regular-JKX5W2C4.ttf +0 -0
  17. package/dist/KaTeX_AMS-Regular-U6PRYMIZ.woff2 +0 -0
  18. package/dist/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 +0 -0
  19. package/dist/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff +0 -0
  20. package/dist/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf +0 -0
  21. package/dist/KaTeX_Caligraphic-Regular-3LKEU76G.woff +0 -0
  22. package/dist/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf +0 -0
  23. package/dist/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 +0 -0
  24. package/dist/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 +0 -0
  25. package/dist/KaTeX_Fraktur-Bold-T4SWXBMT.woff +0 -0
  26. package/dist/KaTeX_Fraktur-Bold-WGHVTYOR.ttf +0 -0
  27. package/dist/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 +0 -0
  28. package/dist/KaTeX_Fraktur-Regular-5U4OPH2X.ttf +0 -0
  29. package/dist/KaTeX_Fraktur-Regular-PQMHCIK6.woff +0 -0
  30. package/dist/KaTeX_Main-Bold-2GA4IZIN.woff +0 -0
  31. package/dist/KaTeX_Main-Bold-W5FBVCZM.ttf +0 -0
  32. package/dist/KaTeX_Main-Bold-YP5VVQRP.woff2 +0 -0
  33. package/dist/KaTeX_Main-BoldItalic-4P4C7HJH.woff +0 -0
  34. package/dist/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 +0 -0
  35. package/dist/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf +0 -0
  36. package/dist/KaTeX_Main-Italic-I43T2HSR.ttf +0 -0
  37. package/dist/KaTeX_Main-Italic-RELBIK7M.woff2 +0 -0
  38. package/dist/KaTeX_Main-Italic-SASNQFN2.woff +0 -0
  39. package/dist/KaTeX_Main-Regular-ARRPAO67.woff2 +0 -0
  40. package/dist/KaTeX_Main-Regular-P5I74A2A.woff +0 -0
  41. package/dist/KaTeX_Main-Regular-W74P5G27.ttf +0 -0
  42. package/dist/KaTeX_Math-BoldItalic-6EBV3DK5.woff +0 -0
  43. package/dist/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 +0 -0
  44. package/dist/KaTeX_Math-BoldItalic-VB447A4D.ttf +0 -0
  45. package/dist/KaTeX_Math-Italic-6KGCHLFN.woff2 +0 -0
  46. package/dist/KaTeX_Math-Italic-KKK3USB2.woff +0 -0
  47. package/dist/KaTeX_Math-Italic-SON4MRCA.ttf +0 -0
  48. package/dist/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 +0 -0
  49. package/dist/KaTeX_SansSerif-Bold-STQ6RXC7.ttf +0 -0
  50. package/dist/KaTeX_SansSerif-Bold-X5M5EMOD.woff +0 -0
  51. package/dist/KaTeX_SansSerif-Italic-HMPFTM52.woff2 +0 -0
  52. package/dist/KaTeX_SansSerif-Italic-PSN4QKYX.woff +0 -0
  53. package/dist/KaTeX_SansSerif-Italic-WTBAZBGY.ttf +0 -0
  54. package/dist/KaTeX_SansSerif-Regular-2TL3USAE.ttf +0 -0
  55. package/dist/KaTeX_SansSerif-Regular-OQCII6EP.woff +0 -0
  56. package/dist/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 +0 -0
  57. package/dist/KaTeX_Script-Regular-72OLXYNA.ttf +0 -0
  58. package/dist/KaTeX_Script-Regular-A5IFOEBS.woff +0 -0
  59. package/dist/KaTeX_Script-Regular-APUWIHLP.woff2 +0 -0
  60. package/dist/KaTeX_Size1-Regular-4HRHTS65.woff +0 -0
  61. package/dist/KaTeX_Size1-Regular-5LRUTBFT.woff2 +0 -0
  62. package/dist/KaTeX_Size1-Regular-7K6AASVL.ttf +0 -0
  63. package/dist/KaTeX_Size2-Regular-222HN3GT.ttf +0 -0
  64. package/dist/KaTeX_Size2-Regular-K5ZHAIS6.woff +0 -0
  65. package/dist/KaTeX_Size2-Regular-LELKET5D.woff2 +0 -0
  66. package/dist/KaTeX_Size3-Regular-TLFPAHDE.woff +0 -0
  67. package/dist/KaTeX_Size3-Regular-UFCO6WCA.ttf +0 -0
  68. package/dist/KaTeX_Size3-Regular-WQRQ47UD.woff2 +0 -0
  69. package/dist/KaTeX_Size4-Regular-7PGNVPQK.ttf +0 -0
  70. package/dist/KaTeX_Size4-Regular-CDMV7U5C.woff2 +0 -0
  71. package/dist/KaTeX_Size4-Regular-PKMWZHNC.woff +0 -0
  72. package/dist/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf +0 -0
  73. package/dist/KaTeX_Typewriter-Regular-MJMFSK64.woff +0 -0
  74. package/dist/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 +0 -0
  75. package/dist/KernelEngineContext-CcxGYtlr.d.ts +33 -0
  76. package/dist/LayersPanel-M2LHWUFF.js +6 -0
  77. package/dist/ModuleCard-QHDGK5XK.js +7 -0
  78. package/dist/OrbInspector-MHLJ6OEE.css +1756 -0
  79. package/dist/OrbInspector-QCPEPUPH.js +26 -0
  80. package/dist/Select-B-iDdnvE.d.ts +185 -0
  81. package/dist/ServerBridge-Bqsj-lOU.d.ts +124 -0
  82. package/dist/ThemeContext-DJmO59xd.d.ts +147 -0
  83. package/dist/UISlotContext-Dl_ewwCI.d.ts +79 -0
  84. package/dist/UISlotRenderer-C7WWOK43.js +26 -0
  85. package/dist/UISlotRenderer-KVFO6J7R.css +1756 -0
  86. package/dist/auth/browser/index.d.ts +2 -0
  87. package/dist/auth/browser/index.js +2 -0
  88. package/dist/auth/react/index.d.ts +56 -0
  89. package/dist/auth/react/index.js +157 -0
  90. package/dist/avl/index.css +1756 -0
  91. package/dist/avl/index.d.ts +2248 -0
  92. package/dist/avl/index.js +2693 -0
  93. package/dist/avl-3d-context-BycmO6tG.d.ts +201 -0
  94. package/dist/avl-schema-parser-CtJxJ9KU.d.ts +132 -0
  95. package/dist/avl-zoom-state-CKMVuBFg.d.ts +17 -0
  96. package/dist/chunk-3BBUBJFF.js +339 -0
  97. package/dist/chunk-3S54FWRC.js +131 -0
  98. package/dist/chunk-3VZ36ZKO.js +775 -0
  99. package/dist/chunk-4T3P43NS.js +1133 -0
  100. package/dist/chunk-4YXBKHHM.js +11488 -0
  101. package/dist/chunk-57DC34LY.js +525 -0
  102. package/dist/chunk-5QTYQTPS.js +15 -0
  103. package/dist/chunk-A2LR6VQQ.js +349 -0
  104. package/dist/chunk-AB3X7QTE.js +505 -0
  105. package/dist/chunk-AE7VCQB6.js +561 -0
  106. package/dist/chunk-AGXD77SL.js +98 -0
  107. package/dist/chunk-AU7O65OX.js +66 -0
  108. package/dist/chunk-CPLN6NBQ.js +701 -0
  109. package/dist/chunk-DZBQA2OW.js +176 -0
  110. package/dist/chunk-E6KA4J6N.js +227 -0
  111. package/dist/chunk-EOWMARUB.js +16 -0
  112. package/dist/chunk-G7DCAZ22.js +19 -0
  113. package/dist/chunk-GFXCPUMX.js +1334 -0
  114. package/dist/chunk-HH5KSMH5.js +311 -0
  115. package/dist/chunk-ILQXJ2AR.js +280 -0
  116. package/dist/chunk-ILZ7ORX7.js +432 -0
  117. package/dist/chunk-K4MOPSEL.js +674 -0
  118. package/dist/chunk-LHP2TK7O.js +47587 -0
  119. package/dist/chunk-P4ZFUQFF.js +93 -0
  120. package/dist/chunk-PCR37T3U.js +296 -0
  121. package/dist/chunk-PRXQGGCO.js +580 -0
  122. package/dist/chunk-REXYVBUO.js +191 -0
  123. package/dist/chunk-ROICKAU7.js +24 -0
  124. package/dist/chunk-SP5CDJ6K.js +338 -0
  125. package/dist/chunk-TUPZWTRE.js +1051 -0
  126. package/dist/chunk-TXAUOLWZ.js +490 -0
  127. package/dist/chunk-TZZBINQY.js +82 -0
  128. package/dist/chunk-UTDC2NEA.js +14 -0
  129. package/dist/chunk-UU57VNS2.js +1053 -0
  130. package/dist/chunk-UXLE3IPR.js +421 -0
  131. package/dist/chunk-VO2TTLS5.js +94 -0
  132. package/dist/chunk-X3DMMX27.js +11 -0
  133. package/dist/chunk-YHMRX6YA.js +455 -0
  134. package/dist/components/index.css +1756 -0
  135. package/dist/components/index.d.ts +11299 -0
  136. package/dist/components/index.js +1110 -0
  137. package/dist/context/index.d.ts +8 -0
  138. package/dist/context/index.js +8 -0
  139. package/dist/core.d.ts +2599 -0
  140. package/dist/core.js +3 -0
  141. package/dist/design-classes/index.cjs +431 -0
  142. package/dist/design-classes/index.d.cts +165 -0
  143. package/dist/design-classes/index.d.ts +165 -0
  144. package/dist/design-classes/index.js +399 -0
  145. package/dist/emojiItems-QGSNAABW.js +20 -0
  146. package/dist/fx-CmQjnHmT.d.ts +371 -0
  147. package/dist/hooks/index.d.ts +971 -0
  148. package/dist/hooks/index.js +8 -0
  149. package/dist/index.d.ts +408 -0
  150. package/dist/index.js +178 -0
  151. package/dist/katexPlugin-FA7NX4PE.css +1161 -0
  152. package/dist/katexPlugin-NCDKOOQY.js +2 -0
  153. package/dist/kernel-client-TZKGHJRY.js +516 -0
  154. package/dist/kernel-wire-wNdAPfhJ.d.ts +88 -0
  155. package/dist/lib/index.d.ts +602 -0
  156. package/dist/lib/index.js +1289 -0
  157. package/dist/paintDispatch-CuJLsr9Y.d.ts +688 -0
  158. package/dist/parseLessonSegments-GpCeMrb2.d.ts +334 -0
  159. package/dist/perf-CKiBKbkw.d.ts +22 -0
  160. package/dist/perf-Dr28-AgT.d.ts +57 -0
  161. package/dist/prism-X5YWKXO5.js +136 -0
  162. package/dist/providers/index.css +1756 -0
  163. package/dist/providers/index.d.ts +355 -0
  164. package/dist/providers/index.js +29 -0
  165. package/dist/renderer/index.d.ts +465 -0
  166. package/dist/renderer/index.js +4 -0
  167. package/dist/runtime/index.css +1756 -0
  168. package/dist/runtime/index.d.ts +239 -0
  169. package/dist/runtime/index.js +28 -0
  170. package/dist/slot-host-CPzrxLo9.d.ts +48 -0
  171. package/dist/ssr/index.cjs +9739 -0
  172. package/dist/ssr/index.d.cts +464 -0
  173. package/dist/ssr/index.d.ts +464 -0
  174. package/dist/ssr/index.js +9703 -0
  175. package/dist/storage/browser/index.d.ts +1 -0
  176. package/dist/storage/browser/index.js +2 -0
  177. package/dist/test-support.d.ts +42 -0
  178. package/dist/test-support.js +181 -0
  179. package/dist/themeTokens-CRd8AEDF.d.ts +125 -0
  180. package/dist/three/index.css +503 -0
  181. package/dist/three/index.d.ts +1185 -0
  182. package/dist/three/index.js +3908 -0
  183. package/dist/useUISlots-CiobNFGb.d.ts +211 -0
  184. package/dist/verification-C5pTeMJv.d.ts +66 -0
  185. package/dist/verificationRegistry-CVrcxG1s.d.ts +122 -0
  186. package/dist/wrapCallbackForEvent-B9QLbhxX.d.ts +19 -0
  187. package/index.css +199 -0
  188. package/package.json +273 -3
  189. package/src/core/lib/band-shapes.json +7 -0
  190. package/src/core/locales/ar.json +1276 -0
  191. package/src/core/locales/en.json +1276 -0
  192. package/src/core/locales/index.ts +54 -0
  193. package/src/core/locales/sl.json +1276 -0
  194. package/src/core/tokens/native-tokens.json +369 -0
  195. package/tailwind-compile.cjs +29 -0
  196. package/tailwind-preset.cjs +1020 -0
  197. package/themes/_base.css +451 -0
  198. package/themes/_contract.md +307 -0
  199. package/themes/_fonts-kenney.css +11 -0
  200. package/themes/_fonts.css +12 -0
  201. package/themes/_foundation.css +105 -0
  202. package/themes/_head.css +22 -0
  203. package/themes/_presets.css +67 -0
  204. package/themes/almadar-heritage.css +455 -0
  205. package/themes/almadar-website.css +269 -0
  206. package/themes/almadar.css +269 -0
  207. package/themes/aqua.css +455 -0
  208. package/themes/arabesque.css +459 -0
  209. package/themes/art-deco.css +457 -0
  210. package/themes/art-nouveau.css +459 -0
  211. package/themes/atelier.css +463 -0
  212. package/themes/bauhaus.css +461 -0
  213. package/themes/bloomberg-dense.css +463 -0
  214. package/themes/blueprint.css +457 -0
  215. package/themes/clay.css +449 -0
  216. package/themes/comic.css +453 -0
  217. package/themes/constructivist.css +457 -0
  218. package/themes/corporate.css +449 -0
  219. package/themes/cyberpunk.css +453 -0
  220. package/themes/frutiger-aero.css +458 -0
  221. package/themes/game-adventure.css +201 -0
  222. package/themes/game-rpg.css +203 -0
  223. package/themes/game-sci-fi.css +201 -0
  224. package/themes/game-ui-pack.css +200 -0
  225. package/themes/gazette.css +449 -0
  226. package/themes/glass.css +457 -0
  227. package/themes/gothic.css +453 -0
  228. package/themes/index.css +21 -0
  229. package/themes/ink-wash.css +459 -0
  230. package/themes/kawaii.css +459 -0
  231. package/themes/kflow.css +310 -0
  232. package/themes/kiosk.css +449 -0
  233. package/themes/linear-clean.css +463 -0
  234. package/themes/luxury.css +451 -0
  235. package/themes/mac-classic.css +455 -0
  236. package/themes/material.css +463 -0
  237. package/themes/memphis.css +453 -0
  238. package/themes/mid-century.css +455 -0
  239. package/themes/minimalist.css +449 -0
  240. package/themes/mudcloth.css +459 -0
  241. package/themes/nata.css +310 -0
  242. package/themes/neon.css +453 -0
  243. package/themes/neumorphic.css +451 -0
  244. package/themes/newsprint.css +455 -0
  245. package/themes/notion-editorial.css +463 -0
  246. package/themes/orb.css +465 -0
  247. package/themes/pixel.css +449 -0
  248. package/themes/prism.css +309 -0
  249. package/themes/rawi.css +310 -0
  250. package/themes/retro.css +452 -0
  251. package/themes/risograph.css +463 -0
  252. package/themes/scandi.css +469 -0
  253. package/themes/sketch.css +455 -0
  254. package/themes/solarpunk.css +469 -0
  255. package/themes/steampunk.css +451 -0
  256. package/themes/swiss.css +455 -0
  257. package/themes/terminal.css +466 -0
  258. package/themes/trait-wars.css +487 -0
  259. package/themes/ukiyo-e.css +459 -0
  260. package/themes/vaporwave.css +455 -0
  261. package/themes/victorian.css +453 -0
  262. package/themes/wabi-sabi.css +459 -0
  263. package/themes/win95.css +455 -0
  264. package/themes/wireframe.css +439 -0
  265. package/themes/zine.css +455 -0
  266. package/README.md +0 -3
@@ -0,0 +1,455 @@
1
+ import { isNotificationSlot } from './chunk-P4ZFUQFF.js';
2
+ import React, { useState, useRef, useMemo, useEffect, useCallback } from 'react';
3
+ import { RENDERED_SLOTS, isRenderBindingMarker } from '@almadar/core';
4
+ import { createLogger } from '@almadar/logger';
5
+
6
+ function expressionEqual(a, b) {
7
+ if (Object.is(a, b)) return true;
8
+ if (Array.isArray(a) && Array.isArray(b)) {
9
+ if (a.length !== b.length) return false;
10
+ for (let i = 0; i < a.length; i++) {
11
+ if (!expressionEqual(a[i], b[i])) return false;
12
+ }
13
+ return true;
14
+ }
15
+ if (a !== null && b !== null && typeof a === "object" && typeof b === "object" && !Array.isArray(a) && !Array.isArray(b) && !(a instanceof Date) && !(b instanceof Date)) {
16
+ const aKeys = Object.keys(a);
17
+ const bKeys = Object.keys(b);
18
+ if (aKeys.length !== bKeys.length) return false;
19
+ for (const key of aKeys) {
20
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
21
+ if (!expressionEqual(
22
+ a[key],
23
+ b[key]
24
+ )) return false;
25
+ }
26
+ return true;
27
+ }
28
+ return false;
29
+ }
30
+ function isPlainSlotObject(value) {
31
+ if (value === null || value === void 0 || typeof value !== "object") return false;
32
+ if (Array.isArray(value)) return false;
33
+ if (React.isValidElement(value)) return false;
34
+ if (value instanceof Date) return false;
35
+ if (isRenderBindingMarker(value)) return false;
36
+ return true;
37
+ }
38
+ function shareValue(prev, next) {
39
+ if (Object.is(prev, next)) return { value: prev, equal: true };
40
+ const prevMarker = isRenderBindingMarker(prev) ? prev : void 0;
41
+ const nextMarker = isRenderBindingMarker(next) ? next : void 0;
42
+ if (prevMarker !== void 0 || nextMarker !== void 0) {
43
+ if (prevMarker !== void 0 && nextMarker !== void 0 && expressionEqual(prevMarker.expression, nextMarker.expression)) {
44
+ return { value: prev, equal: true };
45
+ }
46
+ return { value: next, equal: false };
47
+ }
48
+ if (prev instanceof Date || next instanceof Date) {
49
+ return prev instanceof Date && next instanceof Date && prev.getTime() === next.getTime() ? { value: prev, equal: true } : { value: next, equal: false };
50
+ }
51
+ if (typeof prev === "function" || typeof next === "function") return { value: next, equal: false };
52
+ if (React.isValidElement(prev) || React.isValidElement(next)) return { value: next, equal: false };
53
+ if (Array.isArray(prev) && Array.isArray(next)) {
54
+ let equal = prev.length === next.length;
55
+ const out = new Array(next.length);
56
+ for (let i = 0; i < next.length; i++) {
57
+ const child = shareValue(prev[i], next[i]);
58
+ out[i] = child.value;
59
+ if (!child.equal) equal = false;
60
+ }
61
+ return equal ? { value: prev, equal: true } : { value: out, equal: false };
62
+ }
63
+ if (isPlainSlotObject(prev) && isPlainSlotObject(next)) {
64
+ const prevKeys = Object.keys(prev);
65
+ const nextKeys = Object.keys(next);
66
+ let equal = prevKeys.length === nextKeys.length;
67
+ const out = {};
68
+ for (const key of nextKeys) {
69
+ if (!Object.prototype.hasOwnProperty.call(prev, key)) {
70
+ equal = false;
71
+ out[key] = next[key];
72
+ continue;
73
+ }
74
+ const child = shareValue(prev[key], next[key]);
75
+ out[key] = child.value;
76
+ if (!child.equal) equal = false;
77
+ }
78
+ return equal ? { value: prev, equal: true } : { value: out, equal: false };
79
+ }
80
+ return { value: next, equal: false };
81
+ }
82
+ function reconcileSlotProps(prev, next) {
83
+ if (typeof prev === "string") return { value: next, equal: prev === next };
84
+ if (typeof next === "string") return { value: next, equal: false };
85
+ const result = shareValue(prev, next);
86
+ return { value: result.value, equal: result.equal };
87
+ }
88
+
89
+ // src/hooks/useUISlots.ts
90
+ var log = createLogger("almadar:ui:ui-slots");
91
+ var DEFAULT_SOURCE_KEY = "__default__";
92
+ var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
93
+ var ALL_SLOTS = RENDERED_SLOTS;
94
+ var DEFAULT_SLOTS = ALL_SLOTS.reduce(
95
+ (acc, slot) => {
96
+ acc[slot] = null;
97
+ return acc;
98
+ },
99
+ {}
100
+ );
101
+ var DEFAULT_SOURCES = ALL_SLOTS.reduce(
102
+ (acc, slot) => {
103
+ acc[slot] = {};
104
+ return acc;
105
+ },
106
+ {}
107
+ );
108
+ function aggregateSlot(sources) {
109
+ if (!sources) return null;
110
+ const entries = Object.entries(sources);
111
+ if (entries.length === 0) return null;
112
+ if (entries.length === 1) return entries[0][1];
113
+ const children = entries.map(
114
+ ([sourceKey, entry]) => (
115
+ // A bare-string payload (`(render-ui main "@trait.X")`) is kept as the
116
+ // raw string child — the children walker lifts `@trait.X` strings to
117
+ // TraitFrames — spreading it would explode it into indexed characters.
118
+ typeof entry.props === "string" ? entry.props : {
119
+ type: entry.pattern,
120
+ ...entry.props,
121
+ ...sourceKey !== DEFAULT_SOURCE_KEY && { _sourceTrait: sourceKey }
122
+ }
123
+ )
124
+ );
125
+ log.info("aggregateSlot:multi-source", {
126
+ writers: entries.map(([k]) => k).join(","),
127
+ patterns: entries.map(([, e]) => e.pattern).join(",")
128
+ });
129
+ const stackId = `slot-content-stack-${entries.map(([k]) => k).join("-")}`;
130
+ return {
131
+ id: stackId,
132
+ pattern: "stack",
133
+ props: {
134
+ direction: "vertical",
135
+ gap: "lg",
136
+ children
137
+ },
138
+ priority: 0,
139
+ sourceTrait: MULTI_SOURCE_STACK_TRAIT
140
+ };
141
+ }
142
+ function useUISlotManager() {
143
+ const [sources, setSources] = useState(DEFAULT_SOURCES);
144
+ const subscribersRef = useRef(/* @__PURE__ */ new Set());
145
+ const timersRef = useRef(/* @__PURE__ */ new Map());
146
+ const traitIndexRef = useRef(/* @__PURE__ */ new Map());
147
+ const traitSubscribersRef = useRef(/* @__PURE__ */ new Map());
148
+ const slots = useMemo(() => {
149
+ const out = { ...DEFAULT_SLOTS };
150
+ for (const slot of ALL_SLOTS) {
151
+ out[slot] = aggregateSlot(sources[slot]);
152
+ }
153
+ return out;
154
+ }, [sources]);
155
+ useEffect(() => {
156
+ return () => {
157
+ timersRef.current.forEach((timer) => clearTimeout(timer));
158
+ timersRef.current.clear();
159
+ };
160
+ }, []);
161
+ const notifySubscribers = useCallback((slot, content) => {
162
+ subscribersRef.current.forEach((callback) => {
163
+ try {
164
+ callback(slot, content);
165
+ } catch (error) {
166
+ log.error("Subscriber error", { error: error instanceof Error ? error : String(error) });
167
+ }
168
+ });
169
+ }, []);
170
+ const notifyTraitSubscribers = useCallback(
171
+ (traitName, content) => {
172
+ const subs = traitSubscribersRef.current.get(traitName);
173
+ if (!subs) return;
174
+ subs.forEach((callback) => {
175
+ try {
176
+ callback(content);
177
+ } catch (error) {
178
+ log.error("Trait subscriber error", { traitName, error: error instanceof Error ? error : String(error) });
179
+ }
180
+ });
181
+ },
182
+ []
183
+ );
184
+ const indexTraitRender = useCallback(
185
+ (traitName, content) => {
186
+ traitIndexRef.current.set(traitName, content);
187
+ },
188
+ []
189
+ );
190
+ const unindexTrait = useCallback((traitName) => {
191
+ traitIndexRef.current.delete(traitName);
192
+ }, []);
193
+ const render = useCallback(
194
+ (config) => {
195
+ const sourceKey = config.sourceTrait ?? DEFAULT_SOURCE_KEY;
196
+ const id = `slot-content-${config.target}-${sourceKey}`;
197
+ const content = {
198
+ id,
199
+ pattern: config.pattern,
200
+ props: config.props ?? {},
201
+ priority: config.priority ?? 0,
202
+ onDismiss: config.onDismiss,
203
+ sourceTrait: config.sourceTrait,
204
+ slot: config.target,
205
+ transitionEvent: config.transitionEvent,
206
+ fromState: config.fromState,
207
+ entity: config.entity
208
+ };
209
+ if (config.autoDismissMs && config.autoDismissMs > 0) {
210
+ content.autoDismissAt = Date.now() + config.autoDismissMs;
211
+ const prevTimer = timersRef.current.get(id);
212
+ if (prevTimer) clearTimeout(prevTimer);
213
+ const timer = setTimeout(() => {
214
+ setSources((prev) => {
215
+ const slotSources = prev[config.target];
216
+ if (slotSources && slotSources[sourceKey] === content) {
217
+ content.onDismiss?.();
218
+ const next = { ...slotSources };
219
+ delete next[sourceKey];
220
+ const updated = { ...prev, [config.target]: next };
221
+ notifySubscribers(config.target, aggregateSlot(next));
222
+ return updated;
223
+ }
224
+ return prev;
225
+ });
226
+ timersRef.current.delete(id);
227
+ }, config.autoDismissMs);
228
+ timersRef.current.set(id, timer);
229
+ }
230
+ setSources((prev) => {
231
+ const slotSources = prev[config.target] ?? {};
232
+ const existing = slotSources[sourceKey];
233
+ if (existing && existing.priority > content.priority) {
234
+ log.warn("Slot already has higher priority content", {
235
+ slot: config.target,
236
+ sourceKey,
237
+ existingPriority: existing.priority,
238
+ newPriority: content.priority
239
+ });
240
+ return prev;
241
+ }
242
+ if (existing && existing.priority === content.priority && existing.pattern === content.pattern && existing.transitionEvent === content.transitionEvent && existing.fromState === content.fromState && existing.entity === content.entity && existing.nodeId === content.nodeId && existing.autoDismissAt === void 0 && content.autoDismissAt === void 0 && existing.onDismiss === void 0 === (content.onDismiss === void 0)) {
243
+ const reconciled = reconcileSlotProps(existing.props, content.props);
244
+ if (reconciled.equal) {
245
+ log.debug("slot:flush-bail", { slot: config.target, sourceKey, pattern: content.pattern });
246
+ return prev;
247
+ }
248
+ content.props = reconciled.value;
249
+ }
250
+ const nextSources = {
251
+ ...slotSources,
252
+ [sourceKey]: content
253
+ };
254
+ const nextAll = { ...prev, [config.target]: nextSources };
255
+ const priorWriters = Object.keys(slotSources);
256
+ if (priorWriters.length === 1 && priorWriters[0] !== sourceKey) {
257
+ log.warn("slot:contention", {
258
+ slot: config.target,
259
+ writers: [priorWriters[0], sourceKey],
260
+ patternTypes: [slotSources[priorWriters[0]].pattern, content.pattern]
261
+ });
262
+ }
263
+ if (content.sourceTrait && !isNotificationSlot(config.target)) {
264
+ indexTraitRender(content.sourceTrait, content);
265
+ notifyTraitSubscribers(content.sourceTrait, content);
266
+ }
267
+ log.info("slot:written", {
268
+ slot: config.target,
269
+ sourceKey,
270
+ sourceTrait: content.sourceTrait,
271
+ patternType: content.pattern,
272
+ priority: content.priority
273
+ });
274
+ notifySubscribers(config.target, aggregateSlot(nextSources));
275
+ return nextAll;
276
+ });
277
+ return id;
278
+ },
279
+ [notifySubscribers, notifyTraitSubscribers, indexTraitRender]
280
+ );
281
+ const clear = useCallback(
282
+ (slot) => {
283
+ setSources((prev) => {
284
+ const slotSources = prev[slot];
285
+ if (!slotSources || Object.keys(slotSources).length === 0) {
286
+ return prev;
287
+ }
288
+ for (const content of Object.values(slotSources)) {
289
+ const timer = timersRef.current.get(content.id);
290
+ if (timer) {
291
+ clearTimeout(timer);
292
+ timersRef.current.delete(content.id);
293
+ }
294
+ content.onDismiss?.();
295
+ if (content.sourceTrait && !isNotificationSlot(slot)) {
296
+ unindexTrait(content.sourceTrait);
297
+ notifyTraitSubscribers(content.sourceTrait, null);
298
+ }
299
+ }
300
+ notifySubscribers(slot, null);
301
+ return { ...prev, [slot]: {} };
302
+ });
303
+ },
304
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
305
+ );
306
+ const clearBySource = useCallback(
307
+ (slot, sourceTrait) => {
308
+ const sourceKey = sourceTrait;
309
+ setSources((prev) => {
310
+ const slotSources = prev[slot];
311
+ if (!slotSources || !(sourceKey in slotSources)) {
312
+ const embedded = traitIndexRef.current.get(sourceTrait);
313
+ if (embedded !== void 0 && embedded.slot === slot) {
314
+ unindexTrait(sourceTrait);
315
+ notifyTraitSubscribers(sourceTrait, null);
316
+ log.info("slot:cleared-embedded", { slot, sourceTrait, lastPatternType: embedded.pattern });
317
+ return prev;
318
+ }
319
+ log.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
320
+ return prev;
321
+ }
322
+ const content = slotSources[sourceKey];
323
+ const timer = timersRef.current.get(content.id);
324
+ if (timer) {
325
+ clearTimeout(timer);
326
+ timersRef.current.delete(content.id);
327
+ }
328
+ content.onDismiss?.();
329
+ if (content.sourceTrait && !isNotificationSlot(slot)) {
330
+ unindexTrait(content.sourceTrait);
331
+ notifyTraitSubscribers(content.sourceTrait, null);
332
+ }
333
+ const nextSources = { ...slotSources };
334
+ delete nextSources[sourceKey];
335
+ log.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
336
+ notifySubscribers(slot, aggregateSlot(nextSources));
337
+ return { ...prev, [slot]: nextSources };
338
+ });
339
+ },
340
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
341
+ );
342
+ const clearById = useCallback(
343
+ (id) => {
344
+ setSources((prev) => {
345
+ for (const slot of ALL_SLOTS) {
346
+ const slotSources = prev[slot];
347
+ if (!slotSources) continue;
348
+ const matchKey = Object.keys(slotSources).find(
349
+ (k) => slotSources[k].id === id
350
+ );
351
+ if (!matchKey) continue;
352
+ const content = slotSources[matchKey];
353
+ const timer = timersRef.current.get(id);
354
+ if (timer) {
355
+ clearTimeout(timer);
356
+ timersRef.current.delete(id);
357
+ }
358
+ content.onDismiss?.();
359
+ if (content.sourceTrait && !isNotificationSlot(slot)) {
360
+ unindexTrait(content.sourceTrait);
361
+ notifyTraitSubscribers(content.sourceTrait, null);
362
+ }
363
+ const nextSources = { ...slotSources };
364
+ delete nextSources[matchKey];
365
+ notifySubscribers(slot, aggregateSlot(nextSources));
366
+ return { ...prev, [slot]: nextSources };
367
+ }
368
+ return prev;
369
+ });
370
+ },
371
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
372
+ );
373
+ const clearAll = useCallback(() => {
374
+ timersRef.current.forEach((timer) => clearTimeout(timer));
375
+ timersRef.current.clear();
376
+ setSources((prev) => {
377
+ for (const slot of ALL_SLOTS) {
378
+ const slotSources = prev[slot];
379
+ if (!slotSources) continue;
380
+ for (const content of Object.values(slotSources)) {
381
+ content.onDismiss?.();
382
+ if (content.sourceTrait) {
383
+ notifyTraitSubscribers(content.sourceTrait, null);
384
+ }
385
+ }
386
+ notifySubscribers(slot, null);
387
+ }
388
+ return DEFAULT_SOURCES;
389
+ });
390
+ traitIndexRef.current.clear();
391
+ }, [notifySubscribers, notifyTraitSubscribers]);
392
+ const subscribe = useCallback((callback) => {
393
+ subscribersRef.current.add(callback);
394
+ return () => {
395
+ subscribersRef.current.delete(callback);
396
+ };
397
+ }, []);
398
+ const hasContent = useCallback(
399
+ (slot) => slots[slot] !== null,
400
+ [slots]
401
+ );
402
+ const getContent = useCallback(
403
+ (slot) => slots[slot] ?? null,
404
+ [slots]
405
+ );
406
+ const getTraitContent = useCallback(
407
+ (traitName) => traitIndexRef.current.get(traitName) ?? null,
408
+ []
409
+ );
410
+ const subscribeTrait = useCallback(
411
+ (traitName, callback) => {
412
+ let set = traitSubscribersRef.current.get(traitName);
413
+ if (!set) {
414
+ set = /* @__PURE__ */ new Set();
415
+ traitSubscribersRef.current.set(traitName, set);
416
+ }
417
+ set.add(callback);
418
+ return () => {
419
+ const s = traitSubscribersRef.current.get(traitName);
420
+ if (!s) return;
421
+ s.delete(callback);
422
+ if (s.size === 0) {
423
+ traitSubscribersRef.current.delete(traitName);
424
+ }
425
+ };
426
+ },
427
+ []
428
+ );
429
+ const updateTraitContent = useCallback(
430
+ (traitName, content) => {
431
+ const id = `slot-content-trait-${traitName}`;
432
+ const fullContent = { ...content, id, sourceTrait: traitName };
433
+ indexTraitRender(traitName, fullContent);
434
+ notifyTraitSubscribers(traitName, fullContent);
435
+ return id;
436
+ },
437
+ [indexTraitRender, notifyTraitSubscribers]
438
+ );
439
+ return {
440
+ slots,
441
+ render,
442
+ clear,
443
+ clearBySource,
444
+ clearById,
445
+ clearAll,
446
+ subscribe,
447
+ hasContent,
448
+ getContent,
449
+ getTraitContent,
450
+ subscribeTrait,
451
+ updateTraitContent
452
+ };
453
+ }
454
+
455
+ export { ALL_SLOTS, DEFAULT_SLOTS, useUISlotManager };