@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.
- package/bundling.d.mts +18 -0
- package/bundling.mjs +28 -0
- package/dist/ArbitraryClassCompiler-CO_iASBn.d.ts +195 -0
- package/dist/AvlGlyph-EPJKFEO3.js +6 -0
- package/dist/AvlStateMachine-RKZIKNGC.js +9 -0
- package/dist/AvlTransitionDetail-63SD6GZF.js +11 -0
- package/dist/AvlTransitionExplainer-ZBTLOM3T.js +12 -0
- package/dist/BehaviorView-YS5G4GNB.js +11 -0
- package/dist/Canvas3DHost-BkZQB5l2.d.ts +552 -0
- package/dist/EventBusProvider-BFFHfN4O.d.ts +296 -0
- package/dist/FileTree-BCFnBEuQ.d.ts +254 -0
- package/dist/FlowCanvas-3JRMNJLV.css +1756 -0
- package/dist/FlowCanvas-AUMTWMNG.js +26 -0
- package/dist/GameAudioProvider-ceM_n0ey.d.ts +90 -0
- package/dist/KaTeX_AMS-Regular-CYEKBG2K.woff +0 -0
- package/dist/KaTeX_AMS-Regular-JKX5W2C4.ttf +0 -0
- package/dist/KaTeX_AMS-Regular-U6PRYMIZ.woff2 +0 -0
- package/dist/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 +0 -0
- package/dist/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff +0 -0
- package/dist/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf +0 -0
- package/dist/KaTeX_Caligraphic-Regular-3LKEU76G.woff +0 -0
- package/dist/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf +0 -0
- package/dist/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 +0 -0
- package/dist/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 +0 -0
- package/dist/KaTeX_Fraktur-Bold-T4SWXBMT.woff +0 -0
- package/dist/KaTeX_Fraktur-Bold-WGHVTYOR.ttf +0 -0
- package/dist/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 +0 -0
- package/dist/KaTeX_Fraktur-Regular-5U4OPH2X.ttf +0 -0
- package/dist/KaTeX_Fraktur-Regular-PQMHCIK6.woff +0 -0
- package/dist/KaTeX_Main-Bold-2GA4IZIN.woff +0 -0
- package/dist/KaTeX_Main-Bold-W5FBVCZM.ttf +0 -0
- package/dist/KaTeX_Main-Bold-YP5VVQRP.woff2 +0 -0
- package/dist/KaTeX_Main-BoldItalic-4P4C7HJH.woff +0 -0
- package/dist/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 +0 -0
- package/dist/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf +0 -0
- package/dist/KaTeX_Main-Italic-I43T2HSR.ttf +0 -0
- package/dist/KaTeX_Main-Italic-RELBIK7M.woff2 +0 -0
- package/dist/KaTeX_Main-Italic-SASNQFN2.woff +0 -0
- package/dist/KaTeX_Main-Regular-ARRPAO67.woff2 +0 -0
- package/dist/KaTeX_Main-Regular-P5I74A2A.woff +0 -0
- package/dist/KaTeX_Main-Regular-W74P5G27.ttf +0 -0
- package/dist/KaTeX_Math-BoldItalic-6EBV3DK5.woff +0 -0
- package/dist/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 +0 -0
- package/dist/KaTeX_Math-BoldItalic-VB447A4D.ttf +0 -0
- package/dist/KaTeX_Math-Italic-6KGCHLFN.woff2 +0 -0
- package/dist/KaTeX_Math-Italic-KKK3USB2.woff +0 -0
- package/dist/KaTeX_Math-Italic-SON4MRCA.ttf +0 -0
- package/dist/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 +0 -0
- package/dist/KaTeX_SansSerif-Bold-STQ6RXC7.ttf +0 -0
- package/dist/KaTeX_SansSerif-Bold-X5M5EMOD.woff +0 -0
- package/dist/KaTeX_SansSerif-Italic-HMPFTM52.woff2 +0 -0
- package/dist/KaTeX_SansSerif-Italic-PSN4QKYX.woff +0 -0
- package/dist/KaTeX_SansSerif-Italic-WTBAZBGY.ttf +0 -0
- package/dist/KaTeX_SansSerif-Regular-2TL3USAE.ttf +0 -0
- package/dist/KaTeX_SansSerif-Regular-OQCII6EP.woff +0 -0
- package/dist/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 +0 -0
- package/dist/KaTeX_Script-Regular-72OLXYNA.ttf +0 -0
- package/dist/KaTeX_Script-Regular-A5IFOEBS.woff +0 -0
- package/dist/KaTeX_Script-Regular-APUWIHLP.woff2 +0 -0
- package/dist/KaTeX_Size1-Regular-4HRHTS65.woff +0 -0
- package/dist/KaTeX_Size1-Regular-5LRUTBFT.woff2 +0 -0
- package/dist/KaTeX_Size1-Regular-7K6AASVL.ttf +0 -0
- package/dist/KaTeX_Size2-Regular-222HN3GT.ttf +0 -0
- package/dist/KaTeX_Size2-Regular-K5ZHAIS6.woff +0 -0
- package/dist/KaTeX_Size2-Regular-LELKET5D.woff2 +0 -0
- package/dist/KaTeX_Size3-Regular-TLFPAHDE.woff +0 -0
- package/dist/KaTeX_Size3-Regular-UFCO6WCA.ttf +0 -0
- package/dist/KaTeX_Size3-Regular-WQRQ47UD.woff2 +0 -0
- package/dist/KaTeX_Size4-Regular-7PGNVPQK.ttf +0 -0
- package/dist/KaTeX_Size4-Regular-CDMV7U5C.woff2 +0 -0
- package/dist/KaTeX_Size4-Regular-PKMWZHNC.woff +0 -0
- package/dist/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf +0 -0
- package/dist/KaTeX_Typewriter-Regular-MJMFSK64.woff +0 -0
- package/dist/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 +0 -0
- package/dist/KernelEngineContext-CcxGYtlr.d.ts +33 -0
- package/dist/LayersPanel-M2LHWUFF.js +6 -0
- package/dist/ModuleCard-QHDGK5XK.js +7 -0
- package/dist/OrbInspector-MHLJ6OEE.css +1756 -0
- package/dist/OrbInspector-QCPEPUPH.js +26 -0
- package/dist/Select-B-iDdnvE.d.ts +185 -0
- package/dist/ServerBridge-Bqsj-lOU.d.ts +124 -0
- package/dist/ThemeContext-DJmO59xd.d.ts +147 -0
- package/dist/UISlotContext-Dl_ewwCI.d.ts +79 -0
- package/dist/UISlotRenderer-C7WWOK43.js +26 -0
- package/dist/UISlotRenderer-KVFO6J7R.css +1756 -0
- package/dist/auth/browser/index.d.ts +2 -0
- package/dist/auth/browser/index.js +2 -0
- package/dist/auth/react/index.d.ts +56 -0
- package/dist/auth/react/index.js +157 -0
- package/dist/avl/index.css +1756 -0
- package/dist/avl/index.d.ts +2248 -0
- package/dist/avl/index.js +2693 -0
- package/dist/avl-3d-context-BycmO6tG.d.ts +201 -0
- package/dist/avl-schema-parser-CtJxJ9KU.d.ts +132 -0
- package/dist/avl-zoom-state-CKMVuBFg.d.ts +17 -0
- package/dist/chunk-3BBUBJFF.js +339 -0
- package/dist/chunk-3S54FWRC.js +131 -0
- package/dist/chunk-3VZ36ZKO.js +775 -0
- package/dist/chunk-4T3P43NS.js +1133 -0
- package/dist/chunk-4YXBKHHM.js +11488 -0
- package/dist/chunk-57DC34LY.js +525 -0
- package/dist/chunk-5QTYQTPS.js +15 -0
- package/dist/chunk-A2LR6VQQ.js +349 -0
- package/dist/chunk-AB3X7QTE.js +505 -0
- package/dist/chunk-AE7VCQB6.js +561 -0
- package/dist/chunk-AGXD77SL.js +98 -0
- package/dist/chunk-AU7O65OX.js +66 -0
- package/dist/chunk-CPLN6NBQ.js +701 -0
- package/dist/chunk-DZBQA2OW.js +176 -0
- package/dist/chunk-E6KA4J6N.js +227 -0
- package/dist/chunk-EOWMARUB.js +16 -0
- package/dist/chunk-G7DCAZ22.js +19 -0
- package/dist/chunk-GFXCPUMX.js +1334 -0
- package/dist/chunk-HH5KSMH5.js +311 -0
- package/dist/chunk-ILQXJ2AR.js +280 -0
- package/dist/chunk-ILZ7ORX7.js +432 -0
- package/dist/chunk-K4MOPSEL.js +674 -0
- package/dist/chunk-LHP2TK7O.js +47587 -0
- package/dist/chunk-P4ZFUQFF.js +93 -0
- package/dist/chunk-PCR37T3U.js +296 -0
- package/dist/chunk-PRXQGGCO.js +580 -0
- package/dist/chunk-REXYVBUO.js +191 -0
- package/dist/chunk-ROICKAU7.js +24 -0
- package/dist/chunk-SP5CDJ6K.js +338 -0
- package/dist/chunk-TUPZWTRE.js +1051 -0
- package/dist/chunk-TXAUOLWZ.js +490 -0
- package/dist/chunk-TZZBINQY.js +82 -0
- package/dist/chunk-UTDC2NEA.js +14 -0
- package/dist/chunk-UU57VNS2.js +1053 -0
- package/dist/chunk-UXLE3IPR.js +421 -0
- package/dist/chunk-VO2TTLS5.js +94 -0
- package/dist/chunk-X3DMMX27.js +11 -0
- package/dist/chunk-YHMRX6YA.js +455 -0
- package/dist/components/index.css +1756 -0
- package/dist/components/index.d.ts +11299 -0
- package/dist/components/index.js +1110 -0
- package/dist/context/index.d.ts +8 -0
- package/dist/context/index.js +8 -0
- package/dist/core.d.ts +2599 -0
- package/dist/core.js +3 -0
- package/dist/design-classes/index.cjs +431 -0
- package/dist/design-classes/index.d.cts +165 -0
- package/dist/design-classes/index.d.ts +165 -0
- package/dist/design-classes/index.js +399 -0
- package/dist/emojiItems-QGSNAABW.js +20 -0
- package/dist/fx-CmQjnHmT.d.ts +371 -0
- package/dist/hooks/index.d.ts +971 -0
- package/dist/hooks/index.js +8 -0
- package/dist/index.d.ts +408 -0
- package/dist/index.js +178 -0
- package/dist/katexPlugin-FA7NX4PE.css +1161 -0
- package/dist/katexPlugin-NCDKOOQY.js +2 -0
- package/dist/kernel-client-TZKGHJRY.js +516 -0
- package/dist/kernel-wire-wNdAPfhJ.d.ts +88 -0
- package/dist/lib/index.d.ts +602 -0
- package/dist/lib/index.js +1289 -0
- package/dist/paintDispatch-CuJLsr9Y.d.ts +688 -0
- package/dist/parseLessonSegments-GpCeMrb2.d.ts +334 -0
- package/dist/perf-CKiBKbkw.d.ts +22 -0
- package/dist/perf-Dr28-AgT.d.ts +57 -0
- package/dist/prism-X5YWKXO5.js +136 -0
- package/dist/providers/index.css +1756 -0
- package/dist/providers/index.d.ts +355 -0
- package/dist/providers/index.js +29 -0
- package/dist/renderer/index.d.ts +465 -0
- package/dist/renderer/index.js +4 -0
- package/dist/runtime/index.css +1756 -0
- package/dist/runtime/index.d.ts +239 -0
- package/dist/runtime/index.js +28 -0
- package/dist/slot-host-CPzrxLo9.d.ts +48 -0
- package/dist/ssr/index.cjs +9739 -0
- package/dist/ssr/index.d.cts +464 -0
- package/dist/ssr/index.d.ts +464 -0
- package/dist/ssr/index.js +9703 -0
- package/dist/storage/browser/index.d.ts +1 -0
- package/dist/storage/browser/index.js +2 -0
- package/dist/test-support.d.ts +42 -0
- package/dist/test-support.js +181 -0
- package/dist/themeTokens-CRd8AEDF.d.ts +125 -0
- package/dist/three/index.css +503 -0
- package/dist/three/index.d.ts +1185 -0
- package/dist/three/index.js +3908 -0
- package/dist/useUISlots-CiobNFGb.d.ts +211 -0
- package/dist/verification-C5pTeMJv.d.ts +66 -0
- package/dist/verificationRegistry-CVrcxG1s.d.ts +122 -0
- package/dist/wrapCallbackForEvent-B9QLbhxX.d.ts +19 -0
- package/index.css +199 -0
- package/package.json +273 -3
- package/src/core/lib/band-shapes.json +7 -0
- package/src/core/locales/ar.json +1276 -0
- package/src/core/locales/en.json +1276 -0
- package/src/core/locales/index.ts +54 -0
- package/src/core/locales/sl.json +1276 -0
- package/src/core/tokens/native-tokens.json +369 -0
- package/tailwind-compile.cjs +29 -0
- package/tailwind-preset.cjs +1020 -0
- package/themes/_base.css +451 -0
- package/themes/_contract.md +307 -0
- package/themes/_fonts-kenney.css +11 -0
- package/themes/_fonts.css +12 -0
- package/themes/_foundation.css +105 -0
- package/themes/_head.css +22 -0
- package/themes/_presets.css +67 -0
- package/themes/almadar-heritage.css +455 -0
- package/themes/almadar-website.css +269 -0
- package/themes/almadar.css +269 -0
- package/themes/aqua.css +455 -0
- package/themes/arabesque.css +459 -0
- package/themes/art-deco.css +457 -0
- package/themes/art-nouveau.css +459 -0
- package/themes/atelier.css +463 -0
- package/themes/bauhaus.css +461 -0
- package/themes/bloomberg-dense.css +463 -0
- package/themes/blueprint.css +457 -0
- package/themes/clay.css +449 -0
- package/themes/comic.css +453 -0
- package/themes/constructivist.css +457 -0
- package/themes/corporate.css +449 -0
- package/themes/cyberpunk.css +453 -0
- package/themes/frutiger-aero.css +458 -0
- package/themes/game-adventure.css +201 -0
- package/themes/game-rpg.css +203 -0
- package/themes/game-sci-fi.css +201 -0
- package/themes/game-ui-pack.css +200 -0
- package/themes/gazette.css +449 -0
- package/themes/glass.css +457 -0
- package/themes/gothic.css +453 -0
- package/themes/index.css +21 -0
- package/themes/ink-wash.css +459 -0
- package/themes/kawaii.css +459 -0
- package/themes/kflow.css +310 -0
- package/themes/kiosk.css +449 -0
- package/themes/linear-clean.css +463 -0
- package/themes/luxury.css +451 -0
- package/themes/mac-classic.css +455 -0
- package/themes/material.css +463 -0
- package/themes/memphis.css +453 -0
- package/themes/mid-century.css +455 -0
- package/themes/minimalist.css +449 -0
- package/themes/mudcloth.css +459 -0
- package/themes/nata.css +310 -0
- package/themes/neon.css +453 -0
- package/themes/neumorphic.css +451 -0
- package/themes/newsprint.css +455 -0
- package/themes/notion-editorial.css +463 -0
- package/themes/orb.css +465 -0
- package/themes/pixel.css +449 -0
- package/themes/prism.css +309 -0
- package/themes/rawi.css +310 -0
- package/themes/retro.css +452 -0
- package/themes/risograph.css +463 -0
- package/themes/scandi.css +469 -0
- package/themes/sketch.css +455 -0
- package/themes/solarpunk.css +469 -0
- package/themes/steampunk.css +451 -0
- package/themes/swiss.css +455 -0
- package/themes/terminal.css +466 -0
- package/themes/trait-wars.css +487 -0
- package/themes/ukiyo-e.css +459 -0
- package/themes/vaporwave.css +455 -0
- package/themes/victorian.css +453 -0
- package/themes/wabi-sabi.css +459 -0
- package/themes/win95.css +455 -0
- package/themes/wireframe.css +439 -0
- package/themes/zine.css +455 -0
- 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 };
|