@ai-matrx/summon 0.0.0 → 0.1.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +21 -0
  3. package/README.md +379 -1
  4. package/dist/closeBinding-C6raC3qJ.d.cts +20 -0
  5. package/dist/closeBinding-C6raC3qJ.d.ts +20 -0
  6. package/dist/dialog.cjs +60 -0
  7. package/dist/dialog.cjs.map +1 -0
  8. package/dist/dialog.d.cts +16 -0
  9. package/dist/dialog.d.ts +16 -0
  10. package/dist/dialog.js +38 -0
  11. package/dist/dialog.js.map +1 -0
  12. package/dist/errors-eJrKXq33.d.cts +30 -0
  13. package/dist/errors-eJrKXq33.d.ts +30 -0
  14. package/dist/handles.cjs +411 -0
  15. package/dist/handles.cjs.map +1 -0
  16. package/dist/handles.d.cts +166 -0
  17. package/dist/handles.d.ts +166 -0
  18. package/dist/handles.js +388 -0
  19. package/dist/handles.js.map +1 -0
  20. package/dist/index.cjs +2383 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +1259 -0
  23. package/dist/index.d.ts +1259 -0
  24. package/dist/index.js +2363 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/lite.cjs +460 -0
  27. package/dist/lite.cjs.map +1 -0
  28. package/dist/lite.d.cts +56 -0
  29. package/dist/lite.d.ts +56 -0
  30. package/dist/lite.js +444 -0
  31. package/dist/lite.js.map +1 -0
  32. package/dist/next.cjs +61 -0
  33. package/dist/next.cjs.map +1 -0
  34. package/dist/next.d.cts +5 -0
  35. package/dist/next.d.ts +5 -0
  36. package/dist/next.js +39 -0
  37. package/dist/next.js.map +1 -0
  38. package/dist/persistence-data.cjs +143 -0
  39. package/dist/persistence-data.cjs.map +1 -0
  40. package/dist/persistence-data.d.cts +72 -0
  41. package/dist/persistence-data.d.ts +72 -0
  42. package/dist/persistence-data.js +120 -0
  43. package/dist/persistence-data.js.map +1 -0
  44. package/dist/persistence.cjs +1073 -0
  45. package/dist/persistence.cjs.map +1 -0
  46. package/dist/persistence.d.cts +236 -0
  47. package/dist/persistence.d.ts +236 -0
  48. package/dist/persistence.js +1077 -0
  49. package/dist/persistence.js.map +1 -0
  50. package/dist/react.cjs +738 -0
  51. package/dist/react.cjs.map +1 -0
  52. package/dist/react.d.cts +266 -0
  53. package/dist/react.d.ts +266 -0
  54. package/dist/react.js +723 -0
  55. package/dist/react.js.map +1 -0
  56. package/dist/sheet.cjs +71 -0
  57. package/dist/sheet.cjs.map +1 -0
  58. package/dist/sheet.d.cts +17 -0
  59. package/dist/sheet.d.ts +17 -0
  60. package/dist/sheet.js +49 -0
  61. package/dist/sheet.js.map +1 -0
  62. package/dist/styles.css +78 -0
  63. package/dist/testing.cjs +132 -0
  64. package/dist/testing.cjs.map +1 -0
  65. package/dist/testing.d.cts +44 -0
  66. package/dist/testing.d.ts +44 -0
  67. package/dist/testing.js +115 -0
  68. package/dist/testing.js.map +1 -0
  69. package/dist/url.cjs +362 -0
  70. package/dist/url.cjs.map +1 -0
  71. package/dist/url.d.cts +153 -0
  72. package/dist/url.d.ts +153 -0
  73. package/dist/url.js +352 -0
  74. package/dist/url.js.map +1 -0
  75. package/dist/window-host.cjs +123 -0
  76. package/dist/window-host.cjs.map +1 -0
  77. package/dist/window-host.d.cts +72 -0
  78. package/dist/window-host.d.ts +72 -0
  79. package/dist/window-host.js +104 -0
  80. package/dist/window-host.js.map +1 -0
  81. package/dist/window.cjs +3977 -0
  82. package/dist/window.cjs.map +1 -0
  83. package/dist/window.d.cts +796 -0
  84. package/dist/window.d.ts +796 -0
  85. package/dist/window.js +4002 -0
  86. package/dist/window.js.map +1 -0
  87. package/package.json +239 -1
package/dist/react.cjs ADDED
@@ -0,0 +1,738 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/react.ts
22
+ var react_exports = {};
23
+ __export(react_exports, {
24
+ CALLBACK_GROUP_KEY: () => CALLBACK_GROUP_KEY,
25
+ DEFAULT_IMPORT_TIMEOUT_MS: () => DEFAULT_IMPORT_TIMEOUT_MS,
26
+ DEFAULT_KEEP_ALIVE_LIMIT: () => DEFAULT_KEEP_ALIVE_LIMIT,
27
+ DEFAULT_STALL_WARN_MS: () => DEFAULT_STALL_WARN_MS,
28
+ OverlayErrorBoundary: () => OverlayErrorBoundary,
29
+ OverlayErrorFallback: () => OverlayErrorFallback,
30
+ OverlayLoadingFallback: () => OverlayLoadingFallback,
31
+ SummonHost: () => SummonHost,
32
+ SummonProvider: () => SummonProvider,
33
+ SummonStoreContext: () => SummonStoreContext,
34
+ chunkTimeoutError: () => chunkTimeoutError,
35
+ defineOverlay: () => defineOverlay,
36
+ normalizeOverlayError: () => normalizeOverlayError,
37
+ useCloseOnNavigation: () => useCloseOnNavigation,
38
+ useCloseOverlay: () => useCloseOverlay,
39
+ useIsOverlayOpen: () => useIsOverlayOpen,
40
+ useOpenOverlay: () => useOpenOverlay,
41
+ useOptionalOverlayInstance: () => useOptionalOverlayInstance,
42
+ useOptionalSummonPorts: () => useOptionalSummonPorts,
43
+ useOptionalSummonStore: () => useOptionalSummonStore,
44
+ useOverlayInstance: () => useOverlayInstance,
45
+ useOverlayState: () => useOverlayState,
46
+ useSummonDispatch: () => useSummonDispatch,
47
+ useSummonPorts: () => useSummonPorts,
48
+ useSummonSelector: () => useSummonSelector,
49
+ useSummonStore: () => useSummonStore
50
+ });
51
+ module.exports = __toCommonJS(react_exports);
52
+
53
+ // src/react/SummonProvider.tsx
54
+ var import_react5 = require("react");
55
+ var import_react_redux2 = require("react-redux");
56
+ var import_summon2 = require("@ai-matrx/summon");
57
+ var import_summon3 = require("@ai-matrx/summon");
58
+
59
+ // src/react/context.ts
60
+ var import_react = require("react");
61
+ var SummonStoreContext = (0, import_react.createContext)(null);
62
+ SummonStoreContext.displayName = "SummonStoreContext";
63
+ var SummonPortsContext = (0, import_react.createContext)(null);
64
+ SummonPortsContext.displayName = "SummonPortsContext";
65
+ var SummonConfigContext = (0, import_react.createContext)(null);
66
+ SummonConfigContext.displayName = "SummonConfigContext";
67
+
68
+ // src/react/fallbacks.tsx
69
+ var import_react3 = require("react");
70
+ var import_design_system = require("@ai-matrx/design-system");
71
+
72
+ // src/react/storeHooks.ts
73
+ var import_react2 = require("react");
74
+ var import_react_redux = require("react-redux");
75
+ var import_summon = require("@ai-matrx/summon");
76
+ var useSummonSelector = (0, import_react_redux.createSelectorHook)(SummonStoreContext).withTypes();
77
+ var useSummonDispatch = (0, import_react_redux.createDispatchHook)(SummonStoreContext).withTypes();
78
+ var useSummonStore = (0, import_react_redux.createStoreHook)(SummonStoreContext).withTypes();
79
+ var MISSING_PROVIDER = "[summon] No <SummonProvider> above this component. Wrap the app (or the part that opens overlays) in <SummonProvider>.";
80
+ function useSummonPorts() {
81
+ const ports = (0, import_react2.useContext)(SummonPortsContext);
82
+ if (!ports) throw new Error(MISSING_PROVIDER);
83
+ return ports;
84
+ }
85
+ function useOptionalSummonPorts() {
86
+ return (0, import_react2.useContext)(SummonPortsContext) ?? import_summon.DEFAULT_PORTS;
87
+ }
88
+ function useOptionalSummonStore() {
89
+ const context = (0, import_react2.useContext)(SummonStoreContext);
90
+ return context ? context.store : null;
91
+ }
92
+ function useSummonConfig() {
93
+ const config = (0, import_react2.useContext)(SummonConfigContext);
94
+ if (!config) throw new Error(MISSING_PROVIDER);
95
+ return config;
96
+ }
97
+
98
+ // src/react/fallbacks.tsx
99
+ var import_jsx_runtime = require("react/jsx-runtime");
100
+ function normalizeOverlayError(error) {
101
+ if (error instanceof Error) {
102
+ const isChunkLoadError = error.name === "ChunkLoadError" || /Loading chunk [\w-]+ failed|Failed to fetch dynamically imported module|Importing a module script failed/i.test(
103
+ error.message
104
+ );
105
+ return { name: error.name, message: error.message, stack: error.stack ?? null, isChunkLoadError };
106
+ }
107
+ return { name: "Error", message: typeof error === "string" ? error : String(error), stack: null, isChunkLoadError: false };
108
+ }
109
+ function OverlayLoadingFallback({ overlayId, label }) {
110
+ const ports = useSummonPorts();
111
+ const { stallWarnMs } = useSummonConfig();
112
+ (0, import_react3.useEffect)(() => {
113
+ const timer = setTimeout(() => {
114
+ ports.errors.capture(new Error(`Overlay "${overlayId}" still loading after ${stallWarnMs}ms`), {
115
+ stage: "overlay-load-stall",
116
+ overlayId,
117
+ level: "warning"
118
+ });
119
+ }, stallWarnMs);
120
+ return () => clearTimeout(timer);
121
+ }, [ports, overlayId, stallWarnMs]);
122
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-none fixed inset-0 z-[2147483000] flex items-center justify-center p-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
123
+ "div",
124
+ {
125
+ role: "status",
126
+ "aria-label": `Loading ${label}`,
127
+ className: "pointer-events-auto flex items-center gap-3 rounded-xl border border-border bg-card px-5 py-4 text-sm text-muted-foreground shadow-[var(--elevation-2)]",
128
+ children: [
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, className: "size-4 animate-spin rounded-full border-2 border-border border-t-primary" }),
130
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Loading\u2026" })
131
+ ]
132
+ }
133
+ ) });
134
+ }
135
+ function OverlayErrorFallback({ label, error, retry, close, extras }) {
136
+ const e = normalizeOverlayError(error);
137
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-none fixed inset-0 z-[2147483001] flex items-center justify-center p-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
138
+ "div",
139
+ {
140
+ role: "alertdialog",
141
+ "aria-label": `${label} failed to load`,
142
+ className: "pointer-events-auto flex max-h-[85dvh] w-full max-w-lg flex-col overflow-hidden rounded-xl border border-destructive bg-card shadow-[var(--elevation-3)]",
143
+ children: [
144
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "border-b border-border px-5 py-4", children: [
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", { className: "text-sm font-semibold text-foreground", children: [
146
+ label,
147
+ " failed to load"
148
+ ] }),
149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-1 text-xs text-muted-foreground", children: e.isChunkLoadError ? "Part of it did not finish loading." : "It hit an error while rendering." })
150
+ ] }),
151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-h-0 flex-1 overflow-auto px-5 py-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-lg bg-muted px-3 py-2 font-mono text-xs text-foreground", children: [
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-destructive", children: e.name }),
153
+ ": ",
154
+ e.message
155
+ ] }) }),
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-2 border-t border-border px-5 py-3", children: [
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: extras }),
158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_design_system.Button, { variant: "ghost", size: "sm", onClick: close, children: "Close" }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_design_system.Button, { variant: "primary", size: "sm", onClick: retry, children: "Try again" }),
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
162
+ import_design_system.Button,
163
+ {
164
+ variant: "outline",
165
+ size: "sm",
166
+ onClick: () => {
167
+ if (typeof window !== "undefined") window.location.reload();
168
+ },
169
+ children: "Reload page"
170
+ }
171
+ )
172
+ ] })
173
+ ] })
174
+ ]
175
+ }
176
+ ) });
177
+ }
178
+
179
+ // src/react/keepAlive.ts
180
+ var KeepAliveRegistry = class {
181
+ order = [];
182
+ evictors = /* @__PURE__ */ new Map();
183
+ limit;
184
+ constructor(limit) {
185
+ this.limit = limit;
186
+ }
187
+ setLimit(limit) {
188
+ this.limit = Math.max(0, Math.floor(limit));
189
+ this.trim();
190
+ }
191
+ /** Mark `key` as hidden-alive (most recent). `evict` unmounts it when the cap is exceeded. */
192
+ retain(key, evict) {
193
+ this.drop(key);
194
+ this.order.push(key);
195
+ this.evictors.set(key, evict);
196
+ this.trim();
197
+ }
198
+ /** `key` reopened or its slot went away: no longer counted. */
199
+ release(key) {
200
+ this.drop(key);
201
+ }
202
+ size() {
203
+ return this.order.length;
204
+ }
205
+ drop(key) {
206
+ const at = this.order.indexOf(key);
207
+ if (at >= 0) this.order.splice(at, 1);
208
+ this.evictors.delete(key);
209
+ }
210
+ trim() {
211
+ while (this.order.length > this.limit) {
212
+ const oldest = this.order.shift();
213
+ if (oldest === void 0) return;
214
+ const evict = this.evictors.get(oldest);
215
+ this.evictors.delete(oldest);
216
+ evict?.();
217
+ }
218
+ }
219
+ };
220
+
221
+ // src/react/loaderCache.ts
222
+ var import_react4 = require("react");
223
+ function chunkTimeoutError(key, timeoutMs) {
224
+ const error = new Error(
225
+ `Overlay "${key}" render module did not load within ${timeoutMs}ms. The import() did not resolve before the recovery timeout.`
226
+ );
227
+ error.name = "ChunkLoadError";
228
+ return error;
229
+ }
230
+ function raceTimeout(key, real, timeoutMs) {
231
+ real.catch(() => void 0);
232
+ let timer;
233
+ const timeout = new Promise((_, reject) => {
234
+ timer = setTimeout(() => reject(chunkTimeoutError(key, timeoutMs)), timeoutMs);
235
+ });
236
+ return Promise.race([real, timeout]).finally(() => {
237
+ if (timer !== void 0) clearTimeout(timer);
238
+ });
239
+ }
240
+ var LoaderCache = class {
241
+ entries = /* @__PURE__ */ new Map();
242
+ options;
243
+ constructor(options) {
244
+ this.options = options;
245
+ }
246
+ create(key, loader, attempt) {
247
+ const Component2 = (0, import_react4.lazy)(() => {
248
+ this.options.onImportStart?.(key, attempt);
249
+ return raceTimeout(key, loader(), this.options.timeoutMs());
250
+ });
251
+ return { attempt, Component: Component2 };
252
+ }
253
+ /** The current lazy component for `key`, created on first request. */
254
+ get(key, loader) {
255
+ const existing = this.entries.get(key);
256
+ if (existing) return existing;
257
+ const created = this.create(key, loader, 0);
258
+ this.entries.set(key, created);
259
+ return created;
260
+ }
261
+ /** Replace `key`'s lazy with a fresh one so the next render re-imports. */
262
+ retry(key, loader, failedAttempt) {
263
+ const existing = this.entries.get(key);
264
+ if (existing && existing.attempt > failedAttempt) return;
265
+ const next = this.create(key, loader, failedAttempt + 1);
266
+ this.entries.set(key, next);
267
+ }
268
+ };
269
+
270
+ // src/react/SummonProvider.tsx
271
+ var import_jsx_runtime2 = require("react/jsx-runtime");
272
+ var DEFAULT_KEEP_ALIVE_LIMIT = 8;
273
+ var DEFAULT_IMPORT_TIMEOUT_MS = 12e3;
274
+ var DEFAULT_STALL_WARN_MS = 1e4;
275
+ function SummonProvider({
276
+ store,
277
+ ports,
278
+ fallbacks,
279
+ errorReportExtras,
280
+ renderWrapper,
281
+ keepAliveLimit = DEFAULT_KEEP_ALIVE_LIMIT,
282
+ importTimeoutMs = DEFAULT_IMPORT_TIMEOUT_MS,
283
+ stallWarnMs = DEFAULT_STALL_WARN_MS,
284
+ children
285
+ }) {
286
+ const resolvedPorts = (0, import_react5.useMemo)(() => (0, import_summon2.resolvePorts)(ports), [ports]);
287
+ const [privateStore] = (0, import_react5.useState)(() => store ? null : (0, import_summon3.createSummonStore)({ ports: resolvedPorts }));
288
+ const activeStore = store ?? privateStore;
289
+ const timeoutRef = (0, import_react5.useRef)(importTimeoutMs);
290
+ timeoutRef.current = importTimeoutMs;
291
+ const [keepAlive] = (0, import_react5.useState)(() => new KeepAliveRegistry(keepAliveLimit));
292
+ const [loaders] = (0, import_react5.useState)(() => new LoaderCache({ timeoutMs: () => timeoutRef.current }));
293
+ (0, import_react5.useEffect)(() => keepAlive.setLimit(keepAliveLimit), [keepAlive, keepAliveLimit]);
294
+ const loading = fallbacks?.loading ?? OverlayLoadingFallback;
295
+ const error = fallbacks?.error ?? OverlayErrorFallback;
296
+ const config = (0, import_react5.useMemo)(
297
+ () => ({
298
+ loadingFallback: loading,
299
+ errorFallback: error,
300
+ errorReportExtras: errorReportExtras ?? null,
301
+ renderWrapper: renderWrapper ?? null,
302
+ importTimeoutMs,
303
+ stallWarnMs,
304
+ keepAlive,
305
+ loaders
306
+ }),
307
+ [loading, error, errorReportExtras, renderWrapper, importTimeoutMs, stallWarnMs, keepAlive, loaders]
308
+ );
309
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_redux2.Provider, { store: activeStore, context: SummonStoreContext, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SummonPortsContext.Provider, { value: resolvedPorts, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SummonConfigContext.Provider, { value: config, children }) }) });
310
+ }
311
+
312
+ // src/react/defineOverlay.tsx
313
+ var import_react9 = require("react");
314
+ var import_summon7 = require("@ai-matrx/summon");
315
+ var import_summon8 = require("@ai-matrx/summon");
316
+
317
+ // src/react/dispatchers.ts
318
+ var import_summon4 = require("@ai-matrx/summon");
319
+ var import_summon5 = require("@ai-matrx/summon");
320
+ function newInstanceId(overlayId) {
321
+ return `${overlayId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
322
+ }
323
+ function dispatchOpen(dispatch, overlayId, instanceId, data) {
324
+ dispatch(
325
+ (0, import_summon4.openOverlay)({
326
+ overlayId,
327
+ instanceId,
328
+ data: data ?? null
329
+ })
330
+ );
331
+ }
332
+ function dispatchClose(dispatch, overlayId, instanceId = import_summon5.DEFAULT_INSTANCE_ID) {
333
+ dispatch((0, import_summon4.closeOverlay)({ overlayId, instanceId }));
334
+ }
335
+
336
+ // src/react/events.ts
337
+ var import_handles = require("@ai-matrx/summon/handles");
338
+ function openOverlayEventChannel(owner, getHandlers) {
339
+ const channel = (0, import_handles.createEventChannel)({ owner });
340
+ channel.subscribe((message) => {
341
+ const handlers = getHandlers();
342
+ handlers?.[message.type]?.(message.payload);
343
+ });
344
+ return channel.id;
345
+ }
346
+ function emitOverlayEvent(channelId, type, payload) {
347
+ (0, import_handles.getEventChannel)(channelId)?.emit({ type, payload });
348
+ }
349
+
350
+ // src/react/instanceBody.tsx
351
+ var import_react8 = require("react");
352
+
353
+ // src/react/OverlayErrorBoundary.tsx
354
+ var import_react6 = require("react");
355
+ var import_jsx_runtime3 = require("react/jsx-runtime");
356
+ var CLEAR = { error: null, hasError: false, componentStack: null };
357
+ var OverlayErrorBoundary = class extends import_react6.Component {
358
+ state = CLEAR;
359
+ static getDerivedStateFromError(error) {
360
+ return { error, hasError: true };
361
+ }
362
+ componentDidCatch(error, info) {
363
+ const componentStack = info.componentStack ?? null;
364
+ this.setState({ componentStack });
365
+ const e = normalizeOverlayError(error);
366
+ this.props.errors.capture(error, {
367
+ boundary: "OverlayErrorBoundary",
368
+ overlayId: this.props.overlayId,
369
+ instanceId: this.props.instanceId,
370
+ errorName: e.name,
371
+ isChunkLoadError: e.isChunkLoadError,
372
+ componentStack
373
+ });
374
+ }
375
+ retry = () => {
376
+ this.props.onRetry();
377
+ this.setState(CLEAR);
378
+ };
379
+ render() {
380
+ const { hasError, error, componentStack } = this.state;
381
+ if (!hasError) return this.props.children;
382
+ const { overlayId, instanceId, instanceKey, label, extras, onClose } = this.props;
383
+ const Fallback = this.props.fallback;
384
+ const renderedExtras = extras ? extras({ overlayId, instanceId, instanceKey, label, error, componentStack }) : null;
385
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
386
+ Fallback,
387
+ {
388
+ overlayId,
389
+ instanceId,
390
+ label,
391
+ error,
392
+ componentStack,
393
+ retry: this.retry,
394
+ close: onClose,
395
+ extras: renderedExtras
396
+ }
397
+ );
398
+ }
399
+ };
400
+
401
+ // src/react/instance.tsx
402
+ var import_react7 = require("react");
403
+ var import_summon6 = require("@ai-matrx/summon");
404
+ var OverlayInstanceContext = (0, import_react7.createContext)(null);
405
+ OverlayInstanceContext.displayName = "OverlayInstanceContext";
406
+ function useOverlayInstance() {
407
+ const value = (0, import_react7.useContext)(OverlayInstanceContext);
408
+ if (!value) {
409
+ throw new Error("[summon] useOverlayInstance() was called outside a rendered overlay.");
410
+ }
411
+ return value;
412
+ }
413
+ function useOptionalOverlayInstance() {
414
+ return (0, import_react7.useContext)(OverlayInstanceContext);
415
+ }
416
+ function useOverlayState(name, initial) {
417
+ const { instanceKey } = useOverlayInstance();
418
+ const dispatch = useSummonDispatch();
419
+ const store = useSummonStore();
420
+ const stored = useSummonSelector((state) => (0, import_summon6.selectOverlayStateValue)(state, instanceKey, name));
421
+ const value = stored === void 0 ? initial : stored;
422
+ const setValue = (0, import_react7.useCallback)(
423
+ (next) => {
424
+ const current = (0, import_summon6.selectOverlayStateValue)(store.getState(), instanceKey, name);
425
+ const previous = current === void 0 ? initial : current;
426
+ const resolved = typeof next === "function" ? next(previous) : next;
427
+ dispatch((0, import_summon6.setOverlayStateValue)({ key: instanceKey, name, value: resolved }));
428
+ },
429
+ // `initial` is the first-render default, like useState's; its identity never re-binds.
430
+ // eslint-disable-next-line react-hooks/exhaustive-deps
431
+ [dispatch, store, instanceKey, name]
432
+ );
433
+ return [value, setValue];
434
+ }
435
+
436
+ // src/react/instanceBody.tsx
437
+ var import_jsx_runtime4 = require("react/jsx-runtime");
438
+ function LazyRender({ overlayId, instanceId, data, hidden, close, render, attemptRef }) {
439
+ const { loaders } = useSummonConfig();
440
+ const entry = loaders.get(overlayId, render);
441
+ attemptRef.current = entry.attempt;
442
+ const Render = entry.Component;
443
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Render, { overlayId, instanceId, data, isOpen: true, close, hidden });
444
+ }
445
+ function createInstanceBody(overlayId, label, render, parse, refusable) {
446
+ function OverlayInstanceBody({ instanceId, instanceKey, data, hidden, close }) {
447
+ const config = useSummonConfig();
448
+ const ports = useSummonPorts();
449
+ const attemptRef = (0, import_react8.useRef)(0);
450
+ const parsed = (0, import_react8.useMemo)(() => parse(data), [data]);
451
+ const refused = refusable && parsed === null;
452
+ (0, import_react8.useEffect)(() => {
453
+ if (!refused) return;
454
+ if (process.env.NODE_ENV !== "production") {
455
+ ports.notify.warn(`Overlay "${overlayId}" was opened with data its parse() refused; closing it.`, {
456
+ id: `overlays-parse-${instanceKey}`
457
+ });
458
+ }
459
+ close();
460
+ }, [refused, ports, instanceKey, close]);
461
+ const instance = (0, import_react8.useMemo)(
462
+ () => ({ overlayId, instanceId, instanceKey, close, hidden, rawData: data }),
463
+ [instanceId, instanceKey, close, hidden, data]
464
+ );
465
+ if (refused) return null;
466
+ const context = { overlayId, instanceId, instanceKey, label };
467
+ const body = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
468
+ OverlayErrorBoundary,
469
+ {
470
+ overlayId,
471
+ instanceId,
472
+ instanceKey,
473
+ label,
474
+ errors: ports.errors,
475
+ fallback: config.errorFallback,
476
+ extras: config.errorReportExtras,
477
+ onRetry: () => config.loaders.retry(overlayId, render, attemptRef.current),
478
+ onClose: close,
479
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react8.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(config.loadingFallback, { overlayId, label }), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
480
+ LazyRender,
481
+ {
482
+ overlayId,
483
+ instanceId,
484
+ data: parsed,
485
+ hidden,
486
+ close,
487
+ render,
488
+ attemptRef
489
+ }
490
+ ) })
491
+ }
492
+ );
493
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(OverlayInstanceContext.Provider, { value: instance, children: config.renderWrapper ? config.renderWrapper(body, context) : body });
494
+ }
495
+ OverlayInstanceBody.displayName = `Overlay(${overlayId})`;
496
+ return (0, import_react8.memo)(OverlayInstanceBody);
497
+ }
498
+
499
+ // src/react/defineOverlay.tsx
500
+ var CALLBACK_GROUP_KEY = "callbackGroupId";
501
+ function openWith(dispatch, id, multi, options) {
502
+ const instanceId = options?.instanceId ?? (multi ? newInstanceId(id) : import_summon7.DEFAULT_INSTANCE_ID);
503
+ const key = (0, import_summon7.instanceKey)(id, instanceId);
504
+ const data = {};
505
+ const given = options?.data;
506
+ if (typeof given === "object" && given !== null) Object.assign(data, given);
507
+ const handlers = options?.handlers;
508
+ if (handlers) data[CALLBACK_GROUP_KEY] = openOverlayEventChannel(key, () => handlers);
509
+ dispatchOpen(dispatch, id, instanceId, data);
510
+ return { overlayId: id, instanceId, close: () => dispatchClose(dispatch, id, instanceId) };
511
+ }
512
+ function dataSignature(data) {
513
+ try {
514
+ return JSON.stringify(data, (_key, value) => typeof value === "function" ? void 0 : value);
515
+ } catch {
516
+ return String(Date.now());
517
+ }
518
+ }
519
+ function defineOverlay() {
520
+ return function define(spec) {
521
+ const id = spec.id;
522
+ const multi = spec.instance === "multi";
523
+ const parse = spec.parse ?? ((raw) => raw);
524
+ const Body = createInstanceBody(id, spec.label, spec.render, parse, spec.parse !== void 0);
525
+ const meta = spec;
526
+ function useSummon() {
527
+ const dispatch = useSummonDispatch();
528
+ return (0, import_react9.useCallback)((options) => openWith(dispatch, id, multi, options), [dispatch]);
529
+ }
530
+ function useIsOpen(instanceId = import_summon7.DEFAULT_INSTANCE_ID) {
531
+ return useSummonSelector((state) => (0, import_summon8.selectIsOverlayOpen)(state, id, instanceId));
532
+ }
533
+ function useClose() {
534
+ const dispatch = useSummonDispatch();
535
+ return (0, import_react9.useCallback)((instanceId) => dispatchClose(dispatch, id, instanceId ?? import_summon7.DEFAULT_INSTANCE_ID), [dispatch]);
536
+ }
537
+ function useEmit() {
538
+ const instance = useOptionalOverlayInstance();
539
+ const channelId = instance?.overlayId === id ? instance.rawData?.[CALLBACK_GROUP_KEY] : void 0;
540
+ return (0, import_react9.useCallback)(
541
+ (event, payload) => {
542
+ if (typeof channelId === "string") emitOverlayEvent(channelId, event, payload);
543
+ },
544
+ [channelId]
545
+ );
546
+ }
547
+ function Controller(props) {
548
+ const dispatch = useSummonDispatch();
549
+ const [ownInstanceId] = (0, import_react9.useState)(() => multi ? newInstanceId(id) : import_summon7.DEFAULT_INSTANCE_ID);
550
+ const instanceId = props.instanceId ?? ownInstanceId;
551
+ const handlersRef = (0, import_react9.useRef)(props.handlers);
552
+ handlersRef.current = props.handlers;
553
+ const hasHandlers = props.handlers !== void 0;
554
+ const data = (0, import_react9.useMemo)(() => {
555
+ const out = {};
556
+ for (const [key, value] of Object.entries(props)) {
557
+ if (key !== "instanceId" && key !== "handlers") out[key] = value;
558
+ }
559
+ return out;
560
+ }, [props]);
561
+ const signature = dataSignature(data);
562
+ const dataRef = (0, import_react9.useRef)(data);
563
+ dataRef.current = data;
564
+ (0, import_react9.useEffect)(() => {
565
+ const key = (0, import_summon7.instanceKey)(id, instanceId);
566
+ const open = { ...dataRef.current };
567
+ if (hasHandlers) open[CALLBACK_GROUP_KEY] = openOverlayEventChannel(key, () => handlersRef.current);
568
+ dispatchOpen(dispatch, id, instanceId, open);
569
+ return () => dispatchClose(dispatch, id, instanceId);
570
+ }, [dispatch, instanceId, signature, hasHandlers]);
571
+ return null;
572
+ }
573
+ Controller.displayName = `${id}Controller`;
574
+ return {
575
+ ...spec,
576
+ id,
577
+ label: spec.label,
578
+ instance: multi ? "multi" : "single",
579
+ keepAlive: spec.keepAlive ?? "none",
580
+ meta,
581
+ Body,
582
+ useSummon,
583
+ Controller,
584
+ useIsOpen,
585
+ useClose,
586
+ useEmit
587
+ };
588
+ };
589
+ }
590
+
591
+ // src/react/SummonHost.tsx
592
+ var import_react10 = require("react");
593
+ var import_summon9 = require("@ai-matrx/summon");
594
+ var import_summon10 = require("@ai-matrx/summon");
595
+ var import_summon11 = require("@ai-matrx/summon");
596
+ var import_summon12 = require("@ai-matrx/summon");
597
+ var import_jsx_runtime5 = require("react/jsx-runtime");
598
+ function asRawData(data) {
599
+ return typeof data === "object" && data !== null && !Array.isArray(data) ? data : null;
600
+ }
601
+ function sameInstances(a, b) {
602
+ if (a === b) return true;
603
+ if (a.length !== b.length) return false;
604
+ for (let i = 0; i < a.length; i += 1) {
605
+ const x = a[i];
606
+ const y = b[i];
607
+ if (!x || !y || x.instanceId !== y.instanceId || x.data !== y.data) return false;
608
+ }
609
+ return true;
610
+ }
611
+ var NO_RETAINED = /* @__PURE__ */ new Map();
612
+ function useCloseOnNavigation() {
613
+ const ports = useSummonPorts();
614
+ const dispatch = useSummonDispatch();
615
+ (0, import_react10.useEffect)(() => {
616
+ const router = ports.router;
617
+ let previous = router.pathname();
618
+ return router.subscribe(() => {
619
+ const next = router.pathname();
620
+ if (next === previous) return;
621
+ previous = next;
622
+ const overlayIds = (0, import_summon11.overlayIdsBoundToPage)();
623
+ if (overlayIds.length > 0) dispatch((0, import_summon11.closeOverlaysBoundToPage)({ overlayIds }));
624
+ });
625
+ }, [ports, dispatch]);
626
+ }
627
+ var InstanceSlot = (0, import_react10.memo)(function InstanceSlot2({ definition, instanceId, data, hidden }) {
628
+ const dispatch = useSummonDispatch();
629
+ const close = (0, import_react10.useCallback)(() => dispatchClose(dispatch, definition.id, instanceId), [dispatch, definition.id, instanceId]);
630
+ const Body = definition.Body;
631
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Body, { instanceId, instanceKey: (0, import_summon12.instanceKey)(definition.id, instanceId), data, hidden, close });
632
+ });
633
+ var DefinitionSlot = (0, import_react10.memo)(function DefinitionSlot2({ definition }) {
634
+ const { keepAlive } = useSummonConfig();
635
+ const dispatch = useSummonDispatch();
636
+ const open = useSummonSelector((state) => (0, import_summon11.selectOpenInstances)(state, definition.id), sameInstances);
637
+ const keepHidden = definition.keepAlive === "hidden";
638
+ const [retained, setRetained] = (0, import_react10.useState)(NO_RETAINED);
639
+ const [previousOpen, setPreviousOpen] = (0, import_react10.useState)(open);
640
+ if (previousOpen !== open) {
641
+ setPreviousOpen(open);
642
+ if (keepHidden) {
643
+ const openIds = new Set(open.map((instance) => instance.instanceId));
644
+ const next = new Map(retained);
645
+ for (const instance of previousOpen) {
646
+ if (!openIds.has(instance.instanceId)) next.set(instance.instanceId, asRawData(instance.data));
647
+ }
648
+ for (const id of openIds) next.delete(id);
649
+ setRetained(next);
650
+ }
651
+ }
652
+ const registered = (0, import_react10.useRef)(/* @__PURE__ */ new Set());
653
+ (0, import_react10.useEffect)(() => {
654
+ const seen = registered.current;
655
+ for (const instanceId of retained.keys()) {
656
+ if (seen.has(instanceId)) continue;
657
+ seen.add(instanceId);
658
+ const key = (0, import_summon12.instanceKey)(definition.id, instanceId);
659
+ keepAlive.retain(key, () => {
660
+ seen.delete(instanceId);
661
+ (0, import_summon9.releaseOverlayInstance)(dispatch, key);
662
+ setRetained((current) => {
663
+ if (!current.has(instanceId)) return current;
664
+ const next = new Map(current);
665
+ next.delete(instanceId);
666
+ return next;
667
+ });
668
+ });
669
+ }
670
+ for (const instanceId of [...seen]) {
671
+ if (retained.has(instanceId)) continue;
672
+ seen.delete(instanceId);
673
+ keepAlive.release((0, import_summon12.instanceKey)(definition.id, instanceId));
674
+ }
675
+ }, [retained, keepAlive, definition.id, dispatch]);
676
+ (0, import_react10.useEffect)(() => {
677
+ const seen = registered.current;
678
+ return () => {
679
+ for (const instanceId of seen) {
680
+ const key = (0, import_summon12.instanceKey)(definition.id, instanceId);
681
+ keepAlive.release(key);
682
+ (0, import_summon9.releaseOverlayInstance)(dispatch, key);
683
+ }
684
+ seen.clear();
685
+ };
686
+ }, [keepAlive, definition.id, dispatch]);
687
+ if (open.length === 0 && retained.size === 0) return null;
688
+ const nodes = [];
689
+ for (const instance of open) {
690
+ const node = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InstanceSlot, { definition, instanceId: instance.instanceId, data: asRawData(instance.data), hidden: false }, instance.instanceId);
691
+ nodes.push(
692
+ keepHidden ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react10.Activity, { mode: "visible", children: node }, instance.instanceId) : node
693
+ );
694
+ }
695
+ if (keepHidden) {
696
+ for (const [instanceId, data] of retained) {
697
+ nodes.push(
698
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react10.Activity, { mode: "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InstanceSlot, { definition, instanceId, data, hidden: true }, instanceId) }, instanceId)
699
+ );
700
+ }
701
+ }
702
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: nodes });
703
+ });
704
+ function SummonHost({ definitions }) {
705
+ const registeredFor = (0, import_react10.useRef)(null);
706
+ if (registeredFor.current !== definitions) {
707
+ (0, import_summon10.registerOverlayMeta)(definitions.map((definition) => definition.meta));
708
+ registeredFor.current = definitions;
709
+ }
710
+ useCloseOnNavigation();
711
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: definitions.map((definition) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DefinitionSlot, { definition }, definition.id)) });
712
+ }
713
+
714
+ // src/react/hooks.ts
715
+ var import_react11 = require("react");
716
+ var import_summon13 = require("@ai-matrx/summon");
717
+ var import_summon14 = require("@ai-matrx/summon");
718
+ var import_summon15 = require("@ai-matrx/summon");
719
+ function useIsOverlayOpen(overlayId, instanceId = import_summon15.DEFAULT_INSTANCE_ID) {
720
+ return useSummonSelector((state) => (0, import_summon14.selectIsOverlayOpen)(state, overlayId, instanceId));
721
+ }
722
+ function useOpenOverlay(overlayId) {
723
+ const dispatch = useSummonDispatch();
724
+ return (0, import_react11.useCallback)(
725
+ (options) => {
726
+ const multi = (0, import_summon13.getOverlayMeta)(overlayId)?.instance === "multi";
727
+ const instanceId = options?.instanceId ?? (multi ? newInstanceId(overlayId) : import_summon15.DEFAULT_INSTANCE_ID);
728
+ dispatchOpen(dispatch, overlayId, instanceId, options?.data ?? null);
729
+ return { overlayId, instanceId, close: () => dispatchClose(dispatch, overlayId, instanceId) };
730
+ },
731
+ [dispatch, overlayId]
732
+ );
733
+ }
734
+ function useCloseOverlay(overlayId, instanceId = import_summon15.DEFAULT_INSTANCE_ID) {
735
+ const dispatch = useSummonDispatch();
736
+ return (0, import_react11.useCallback)(() => dispatchClose(dispatch, overlayId, instanceId), [dispatch, overlayId, instanceId]);
737
+ }
738
+ //# sourceMappingURL=react.cjs.map