@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
@@ -0,0 +1,796 @@
1
+ import * as react from 'react';
2
+ import react__default, { ComponentType, SVGAttributes, ReactNode } from 'react';
3
+ import { WindowRect, WindowPersistenceRegistration, MobilePresentation } from '@ai-matrx/summon';
4
+ import { TraySnapshotCapture } from '@ai-matrx/summon/window-host';
5
+ export { DEFAULT_ESCAPE_YIELD_SELECTOR, DEFAULT_MOBILE_TOP_OFFSET, DEFAULT_WINDOW_CONFIG, TrayPreviewContext, TrayPreviewEntry, TraySnapshotCapture, WindowConfig, WindowSelectionSurface, WindowSelectionSurfaceProps, WindowTraySync, WindowsProvider, WindowsProviderProps, getTrayPreviewEntry, registerTrayPreview, resetTrayPreviews, useWindowConfig } from '@ai-matrx/summon/window-host';
6
+
7
+ type ResizeEdge = "e" | "s" | "se" | "w" | "sw" | "n" | "ne" | "nw";
8
+ /** Where to place the window when it first opens. */
9
+ type WindowPosition = "center" | "top-right" | "top-left" | "bottom-right" | "bottom-left";
10
+ declare function resetDefaultWindowCascade(): void;
11
+ declare function resolvePosition(pos: WindowPosition | undefined, w: number, h: number): {
12
+ x: number;
13
+ y: number;
14
+ };
15
+ interface UseWindowPanelOptions {
16
+ /** Stable, unique id. If omitted a React useId-based id is used. */
17
+ id?: string | undefined;
18
+ /** Title shown in the header and tray chip. */
19
+ title?: string | undefined;
20
+ /**
21
+ * Initial width. Accepts pixels (number) or viewport strings ("60vw").
22
+ * Default: 320.
23
+ */
24
+ width?: number | string | undefined;
25
+ /**
26
+ * Initial height. Accepts pixels (number) or viewport strings ("60dvh").
27
+ * Default: 400.
28
+ */
29
+ height?: number | string | undefined;
30
+ /** Minimum width during pointer resize. Default: 180. */
31
+ minWidth?: number | undefined;
32
+ /** Minimum height during pointer resize. Default: 80. */
33
+ minHeight?: number | undefined;
34
+ /** Where to place the window initially. Default: "center". */
35
+ position?: WindowPosition | undefined;
36
+ /** Park a brand-new desktop window in the tray without a visible flash. */
37
+ initialState?: "windowed" | "minimized" | undefined;
38
+ /**
39
+ * The system (not a person) opened this window. It docks in a free gutter beside the page's
40
+ * working column, or parks in the tray when none fits — never over the page. See
41
+ * `utils/autoOpenPlacement.ts`. WindowPanel sets this from `markAutoOpened`.
42
+ */
43
+ autoOpened?: boolean | undefined;
44
+ /**
45
+ * MKT-104: an auto-opened window with no gutter left beside the page closes itself through this
46
+ * instead of parking a tray card over the page's controls.
47
+ */
48
+ onAutoNoRoom?: (() => void) | undefined;
49
+ /**
50
+ * @deprecated Pass `width`, `height`, and `position` instead.
51
+ * Low-level initial rect — overrides width/height/position when provided.
52
+ */
53
+ initialRect?: Partial<WindowRect> | undefined;
54
+ /** Maximum width the user can resize to. */
55
+ maxWidth?: number | undefined;
56
+ /** Maximum height the user can resize to. */
57
+ maxHeight?: number | undefined;
58
+ /**
59
+ * Optional callback fired when the user drags the window outside the
60
+ * viewport long enough to count as a pop-out gesture. Caller is expected
61
+ * to open the popout window synchronously inside the user-gesture stack
62
+ * (i.e. without `await` before the open call).
63
+ *
64
+ * Pass the current rect at the time of release — the popout uses it for
65
+ * sizing.
66
+ *
67
+ * `undefined` disables drag-out detection entirely. Use `undefined` for
68
+ * popouts that aren't supported (mobile, no-capability browsers, etc.)
69
+ * so we don't waste cycles on the per-move evaluation.
70
+ */
71
+ onTriggerPopout?: ((rect: WindowRect) => void) | undefined;
72
+ /** Audited local preservation identity and initial semantic/chrome state. */
73
+ persistence?: WindowPersistenceRegistration | undefined;
74
+ }
75
+ interface UseWindowPanelReturn {
76
+ /** The stable window id. */
77
+ id: string;
78
+ /** Current window state from Redux. */
79
+ windowState: "windowed" | "maximized" | "minimized";
80
+ /** Current windowed rect from Redux. */
81
+ rect: WindowRect;
82
+ /** z-index from Redux. */
83
+ zIndex: number;
84
+ /** Pointerdown handler for the drag handle (header). Works with mouse + touch + pen. */
85
+ onDragStart: (e: React.PointerEvent) => void;
86
+ /** Pointerdown handler factory for resize handles. Works with mouse + touch + pen. */
87
+ onResizeStart: (edge: ResizeEdge) => (e: React.PointerEvent) => void;
88
+ /** Bring this window to the top. */
89
+ onFocus: () => void;
90
+ /** Transition to windowed state. */
91
+ onRestore: () => void;
92
+ /** Transition to maximized state. */
93
+ onMaximize: () => void;
94
+ /** Transition to minimized (tray) state. */
95
+ onMinimize: () => void;
96
+ /** Toggle maximized ↔ windowed. */
97
+ onToggleMaximize: () => void;
98
+ /**
99
+ * True while the user is actively dragging or resizing with the pointer.
100
+ * The shell disables its geometry transition during interaction so the
101
+ * window tracks the pointer 1:1, and re-enables it for programmatic rect
102
+ * changes (snap, arrange, minimize→tray, restore, clamp) which then glide.
103
+ */
104
+ isInteracting: boolean;
105
+ }
106
+ declare function useWindowPanel(opts?: UseWindowPanelOptions): UseWindowPanelReturn;
107
+
108
+ /**
109
+ * WindowPanel — a floating, draggable, resizable, maximizable, minimizable OS-style window, with
110
+ * snap/arrange, a minimized tile with a captured preview, pop-out to a real browser window, and
111
+ * three phone presentations. Ported from matrx-frontend `features/window-panels/WindowPanel.tsx`.
112
+ *
113
+ * Body identity: the body subtree renders into ONE stable detached container through a portal at a
114
+ * fixed position in this component's tree; each presentation (windowed, maximized, minimized,
115
+ * popped out) only decides WHERE that container is attached. So maximize, minimize, pop-out and
116
+ * dock never remount the body — its state, refs, effects and subscriptions survive every move.
117
+ */
118
+
119
+ /**
120
+ * Props that don't participate in the close-binding discrimination.
121
+ * Kept as an intersection base so the discriminated union below can layer
122
+ * the close-binding contract on top without losing intellisense on the
123
+ * common props.
124
+ */
125
+ interface WindowPanelBaseProps extends UseWindowPanelOptions {
126
+ children: react__default.ReactNode;
127
+ title?: string | undefined;
128
+ /** Rich title rendered in the header. Falls back to `title` (string) when omitted. */
129
+ titleNode?: react__default.ReactNode | undefined;
130
+ /** @deprecated Use `actionsLeft` and `actionsRight` instead */
131
+ actions?: react__default.ReactNode | undefined;
132
+ actionsLeft?: react__default.ReactNode | undefined;
133
+ actionsRight?: react__default.ReactNode | undefined;
134
+ bodyClassName?: string | undefined;
135
+ className?: string | undefined;
136
+ /**
137
+ * Rare escape hatch for page-local windows that have no overlay definition
138
+ * but still need an explicit mobile surface. Registered overlays keep their
139
+ * presentation in their definition (`window.mobile`).
140
+ */
141
+ mobilePresentationOverride?: MobilePresentation | undefined;
142
+ /**
143
+ * The surface this window REGISTERS (its own `SurfaceRuntimeProvider`).
144
+ * Marks every root with `data-surface-layer`, so the unregistered-window
145
+ * reader (`features/surfaces/runtime/window-forms.ts`) leaves it alone —
146
+ * one way for an agent to write a field, never two.
147
+ */
148
+ surfaceLayer?: string | undefined;
149
+ /** Phone drawer presentation: size the sheet to the content (max 85dvh). */
150
+ mobileSizeToContent?: boolean | undefined;
151
+ /**
152
+ * Escape closes this window when it is the TOPMOST window and nothing above
153
+ * it (a menu, popover, dialog, the workspace picker) is open to take the
154
+ * key. Opt in for windows whose close is safe (their draft is kept, or
155
+ * there is nothing to lose). Desktop only — a phone sheet has its own.
156
+ */
157
+ closeOnEscape?: boolean | undefined;
158
+ /** Phone drawer presentation: no title-row ✕ (the handle and the window's own Cancel dismiss it). */
159
+ mobileHideClose?: boolean | undefined;
160
+ minWidth?: number | undefined;
161
+ minHeight?: number | undefined;
162
+ urlSyncKey?: string | undefined;
163
+ urlSyncId?: string | undefined;
164
+ urlSyncArgs?: Record<string, string> | undefined;
165
+ /** Content to render in a collapsible left sidebar panel */
166
+ sidebar?: react__default.ReactNode | undefined;
167
+ /** Default width for the sidebar in pixels (default: 200) */
168
+ sidebarDefaultSize?: number | undefined;
169
+ /** Minimum width in pixels before the sidebar collapses (default: 150) */
170
+ sidebarMinSize?: number | undefined;
171
+ /** Whether the sidebar starts open (default: true) */
172
+ defaultSidebarOpen?: boolean | undefined;
173
+ /** Class name applied to the sidebar panel content wrapper */
174
+ sidebarClassName?: string | undefined;
175
+ /**
176
+ * When true, opening the sidebar grows the window width by sidebarDefaultSize
177
+ * and closing it shrinks it back, keeping the body content width constant.
178
+ */
179
+ sidebarExpandsWindow?: boolean | undefined;
180
+ /**
181
+ * Content for a collapsible RIGHT secondary panel — the canonical home for a
182
+ * history / inspector / details pane that belongs to the window, not the body.
183
+ * Resizable, mirrors the left `sidebar`. Desktop only; handle mobile in the
184
+ * consumer (e.g. a Drawer) — see `features/notes` for the reference pattern.
185
+ */
186
+ secondaryPanel?: react__default.ReactNode | undefined;
187
+ /** Whether the secondary panel is open (default: true when `secondaryPanel` is provided). */
188
+ secondaryPanelOpen?: boolean | undefined;
189
+ /** Default width for the secondary panel in pixels (default: 360) */
190
+ secondaryPanelDefaultSize?: number | undefined;
191
+ /** Minimum width in pixels for the secondary panel (default: 240) */
192
+ secondaryPanelMinSize?: number | undefined;
193
+ /** Class name applied to the secondary panel content wrapper */
194
+ secondaryPanelClassName?: string | undefined;
195
+ /**
196
+ * When true, hides the header pop-out affordance (icon + green-dropdown entry).
197
+ * Pop-out is shown by default on desktop when the browser supports it.
198
+ */
199
+ hidePopOutButton?: boolean | undefined;
200
+ /** Content rendered in a full-width footer bar below the body. Renders as a single flex row. For zoned layout, use footerLeft/footerCenter/footerRight instead. */
201
+ footer?: react__default.ReactNode | undefined;
202
+ /** Left-aligned footer content (use instead of `footer` for zoned layout) */
203
+ footerLeft?: react__default.ReactNode | undefined;
204
+ /** Center-aligned footer content */
205
+ footerCenter?: react__default.ReactNode | undefined;
206
+ /** Right-aligned footer content */
207
+ footerRight?: react__default.ReactNode | undefined;
208
+ /**
209
+ * Styling posture for the footer wrapper. Default `"bar"` applies the compact
210
+ * metadata-bar chrome (thin chips, `text-xs`, tiny buttons/icons via descendant
211
+ * selectors) tuned for status rows like `NoteMetadataBar`. That chrome CRUSHES
212
+ * rich content — use `"rich"` when the footer hosts a composer / input bar /
213
+ * anything with full-size buttons or a multi-row textarea (e.g. `SmartAgentInput`).
214
+ * `"rich"` drops the compact descendant selectors and the `bg-muted/40` chrome,
215
+ * leaving just `shrink-0 border-t` so the slot's own content owns its layout.
216
+ */
217
+ footerVariant?: "bar" | "rich" | undefined;
218
+ /**
219
+ * When true, the windowed panel sizes itself to fit its content rather than
220
+ * using the explicit width/height from Redux. A ResizeObserver syncs the
221
+ * measured dimensions back into Redux so drag/snap operations still work.
222
+ * The panel will still respect minWidth/minHeight constraints.
223
+ */
224
+ fitContent?: boolean | undefined;
225
+ /**
226
+ * Called by WindowPanel before a save so the child component can return
227
+ * its current content state for the local refresh-preservation cache.
228
+ * Return value must be a plain object (JSON-serializable).
229
+ */
230
+ onCollectData?: (() => Record<string, unknown>) | undefined;
231
+ /**
232
+ * Optional snapshot capture function for the minimized tray chip.
233
+ * WindowPanel invokes it after minimizing against a briefly retained,
234
+ * offscreen body and stores the resulting Blob in a bounded local cache.
235
+ * Pass this from the window component if it implements custom capture logic.
236
+ */
237
+ captureTraySnapshot?: TraySnapshotCapture | undefined;
238
+ /** Overlay instance used by semantic minimized previews. Defaults to `default`. */
239
+ overlayInstanceId?: string | undefined;
240
+ }
241
+ /**
242
+ * The close-binding contract — enforced by the type system.
243
+ *
244
+ * Every WindowPanel declares HOW it closes. Exactly three shapes are valid:
245
+ *
246
+ * 1. Overlay-managed: `overlayId`. Close dispatches `closeOverlay` for that overlay (and the
247
+ * instance this window renders, when it renders inside an SummonHost instance or passes
248
+ * `overlayInstanceId`). The close-lifecycle middleware tombstones preservation, disposes the
249
+ * instance's handles and runs persistence flushers; the host then unmounts the body. `onClose`
250
+ * is an OPTIONAL hook for extra work.
251
+ * 2. Instance-bound: `boundToOverlay` — the window renders inside an SummonHost-rendered overlay
252
+ * and closes through that instance (`useOptionalOverlayInstance().close`). Rendering it outside
253
+ * an overlay reports an error through the error port and the close button closes nothing.
254
+ * 3. Page-local: `onClose`, REQUIRED, because nothing else will close the window.
255
+ *
256
+ * Passing none of them is a compile error: the silent-failure mode where the X rendered as a dead
257
+ * button (matrx-frontend, Notes incident 2026-05-26). Do NOT make these all optional.
258
+ */
259
+ type WindowPanelCloseBinding = {
260
+ overlayId: string;
261
+ boundToOverlay?: never | undefined;
262
+ /** Optional extra cleanup hook (cancel uploads, save drafts, etc.). */
263
+ onClose?: (() => void) | undefined;
264
+ } | {
265
+ overlayId?: never | undefined;
266
+ /** Close through the enclosing SummonHost instance. */
267
+ boundToOverlay: true;
268
+ onClose?: (() => void) | undefined;
269
+ } | {
270
+ overlayId?: never | undefined;
271
+ boundToOverlay?: never | undefined;
272
+ /** Required when the panel is rendered page-locally (no overlay). */
273
+ onClose: () => void;
274
+ };
275
+ type WindowPanelProps = WindowPanelBaseProps & WindowPanelCloseBinding;
276
+ declare function WindowPanel({ children, title, titleNode, actions, actionsLeft, actionsRight, onClose, bodyClassName, className, mobilePresentationOverride, surfaceLayer, mobileSizeToContent, closeOnEscape, mobileHideClose, minWidth, minHeight, urlSyncKey, urlSyncId, urlSyncArgs, sidebar, sidebarDefaultSize, sidebarMinSize, defaultSidebarOpen, sidebarClassName, sidebarExpandsWindow, secondaryPanel, secondaryPanelOpen, secondaryPanelDefaultSize, secondaryPanelMinSize, secondaryPanelClassName, footer, footerLeft, footerCenter, footerRight, footerVariant, fitContent, overlayId: overlayIdProp, boundToOverlay, onCollectData, captureTraySnapshot, overlayInstanceId: overlayInstanceIdProp, hidePopOutButton, ...hookOpts }: WindowPanelProps): react__default.JSX.Element | null;
277
+
278
+ interface WindowSidebarItem {
279
+ value: string;
280
+ label: string;
281
+ icon: react__default.ElementType;
282
+ description?: string | undefined;
283
+ }
284
+ interface WindowSidebarProps<T extends string = string> {
285
+ items: WindowSidebarItem[];
286
+ activeItem: T;
287
+ onSelect: (value: T) => void;
288
+ className?: string | undefined;
289
+ }
290
+ declare function WindowSidebar<T extends string = string>({ items, activeItem, onSelect, className, }: WindowSidebarProps<T>): react__default.JSX.Element;
291
+
292
+ type LayoutIconType = "left-half" | "right-half" | "top-half" | "bottom-half" | "centre" | "full" | "grid4" | "grid6" | "grid8" | "grid9" | "grid12" | "stackLeft2" | "stackLeft3" | "stackLeft4" | "stackLeft5" | "stackRight2" | "stackRight3" | "stackRight4" | "stackRight5";
293
+ declare function LayoutIcon({ type, className, }: {
294
+ type: LayoutIconType;
295
+ className?: string | undefined;
296
+ }): react.JSX.Element;
297
+ declare function LayoutIconButton({ onClick, type, className, }: {
298
+ onClick: () => void;
299
+ type: LayoutIconType;
300
+ className?: string | undefined;
301
+ }): react.JSX.Element;
302
+
303
+ /**
304
+ * Acknowledge a visible presentation that intentionally replaces WindowPanel.
305
+ *
306
+ * Registered singleton window overlays normally prove they are on screen by mounting a
307
+ * WindowPanel and registering geometry. Mobile drawers, sheets, and full-screen
308
+ * viewers have no window-manager geometry, so their composition root must use
309
+ * this hook while that alternate surface is active.
310
+ */
311
+ declare function useOverlaySurfaceRenderAck(overlayId: string, active: boolean): void;
312
+
313
+ /** Store a local snapshot blob for a window and notify subscribers. */
314
+ declare function setTraySnapshot(id: string, blob: Blob): void;
315
+ /** Clear the snapshot for a given window id. */
316
+ declare function clearTraySnapshot(id: string): void;
317
+ /** Read the snapshot for a given window id, or null. */
318
+ declare function getTraySnapshot(id: string): string | null;
319
+ /** Subscribe to map changes (useSyncExternalStore-shaped). */
320
+ declare function subscribeTraySnapshotMap(listener: () => void): () => void;
321
+
322
+ /**
323
+ * defaultTraySnapshotCapture — the fallback `captureTraySnapshot` for any window without a
324
+ * registered preview, so its minimized tile shows a small image of the real body.
325
+ *
326
+ * `html-to-image` is an OPTIONAL peer, loaded through ONE dynamic import on the first capture: a
327
+ * host that never installs it gets the labelled default tile (no error, no bundle cost).
328
+ *
329
+ * Performance contract (do not weaken when editing):
330
+ * - Runs exactly ONCE per minimize, inside WindowPanel's timeout race. No polling, no network, no
331
+ * storage — the Blob lands in the bounded object-URL cache (16 entries, revoked on restore).
332
+ * - Oversized DOM subtrees are skipped (the clone pass is synchronous and would jank).
333
+ * - Output is downscaled (320px longest edge, 1x pixel density) WebP.
334
+ */
335
+ interface ElementThumbnailOptions {
336
+ /** Longest edge in px (min 64). Default 320. */
337
+ readonly maxEdge?: number;
338
+ /** WebP quality 0–1. Default 0.62. */
339
+ readonly quality?: number;
340
+ }
341
+ /** A small display-ready WebP thumbnail of one element, or null when html-to-image is absent. */
342
+ declare function captureElementThumbnail(element: HTMLElement, options?: ElementThumbnailOptions): Promise<Blob | null>;
343
+ declare function defaultTraySnapshotCapture(bodyEl: HTMLElement): Promise<Blob | null>;
344
+
345
+ interface TrayChipPreviewProps {
346
+ /** Registry key — the overlay id for overlay-managed windows, else the runtime id. */
347
+ registryKey: string;
348
+ /** Unique runtime window id used for the local snapshot cache. */
349
+ snapshotKey: string;
350
+ overlayInstanceId?: string | undefined;
351
+ previewData?: Record<string, unknown> | undefined;
352
+ title: string;
353
+ }
354
+ declare const TrayChipPreview: react.MemoExoticComponent<({ registryKey, snapshotKey, overlayInstanceId, previewData, title, }: TrayChipPreviewProps) => react.JSX.Element>;
355
+
356
+ interface MinimizedWindowContentProps {
357
+ /** Registry key — `overlayId` for overlay-managed windows, else runtime id. */
358
+ registryKey: string;
359
+ /** Unique runtime id for the local screenshot cache. */
360
+ snapshotKey: string;
361
+ overlayInstanceId?: string | undefined;
362
+ previewData?: Record<string, unknown> | undefined;
363
+ /** Window title; falls back to the registry label when not a plain string. */
364
+ title?: string | undefined;
365
+ /** Restore (un-minimize) the window. Wired to a click on the whole body. */
366
+ onRestore?: (() => void) | undefined;
367
+ }
368
+ declare function MinimizedWindowContent({ registryKey, snapshotKey, overlayInstanceId, previewData, title, onRestore, }: MinimizedWindowContentProps): react.JSX.Element;
369
+
370
+ /**
371
+ * Severity scale, loudest-last. Maps onto the captured-error tiers but is
372
+ * intentionally generic so non-error windows can reuse it:
373
+ * neutral — inert / nothing notable (muted)
374
+ * info — active & healthy, "monitoring" (info token)
375
+ * warning — low / silent (yellow)
376
+ * elevated — minor / worth a glance (amber)
377
+ * critical — loud / needs attention (destructive red)
378
+ */
379
+ type TrayStatusTone = "neutral" | "info" | "warning" | "elevated" | "critical";
380
+ interface TrayStatusSegment {
381
+ count: number;
382
+ tone: TrayStatusTone;
383
+ /** Accessible/tooltip label, e.g. "clear errors". */
384
+ label?: string | undefined;
385
+ }
386
+ interface TrayStatusChipProps {
387
+ /** Icon component (any SVG icon taking className) — the glanceable identity/severity mark. */
388
+ icon: ComponentType<SVGAttributes<SVGSVGElement>>;
389
+ /** Overall severity — tints the icon (and the primary count). */
390
+ tone: TrayStatusTone;
391
+ /** Primary number. Omit (or pass undefined) to show only the caption. */
392
+ count?: number | undefined;
393
+ /** Shown when there's no count (e.g. the all-clear / idle state). */
394
+ caption?: string | undefined;
395
+ /** Per-tone breakdown. Only renders when 2+ entries are non-zero. */
396
+ segments?: TrayStatusSegment[] | undefined;
397
+ /** Pulse the icon (e.g. unseen activity since minimize). */
398
+ pulse?: boolean | undefined;
399
+ }
400
+ declare function TrayStatusChip({ icon: Icon, tone, count, caption, segments, pulse, }: TrayStatusChipProps): react.JSX.Element;
401
+
402
+ type PopoutMode = "pip" | "popup";
403
+ interface PopoutContextValue {
404
+ /**
405
+ * The popout `Document`. `null` when this subtree is rendered in the
406
+ * parent (the default).
407
+ */
408
+ popoutDocument: Document | null;
409
+ /**
410
+ * The popout `Window`. `null` when this subtree is rendered in the parent.
411
+ * Use for `addEventListener`, `matchMedia`, `innerWidth`, etc., when the
412
+ * popout-scoped answer is what you need.
413
+ */
414
+ popoutWindow: Window | null;
415
+ /**
416
+ * Element to use as the Radix `Portal.container` prop so floating UI
417
+ * (tooltips, popovers, dropdowns) renders inside the popout instead of
418
+ * the parent `document.body`. Returns `null` outside a popout —
419
+ * `usePopoutContainer` coerces this to `undefined` so Radix falls back
420
+ * to its default container automatically.
421
+ */
422
+ popoutContainer: HTMLElement | null;
423
+ /**
424
+ * Which popout mechanism is in use. `null` outside a popout.
425
+ * Currently only used for diagnostics; downstream components may branch
426
+ * on it later (e.g. show different chrome for `popup` vs `pip`).
427
+ */
428
+ mode: PopoutMode | null;
429
+ }
430
+ declare const PopoutContext: react.Context<PopoutContextValue>;
431
+ /** Read the current popout context. Always defined — never throws. */
432
+ declare function usePopout(): PopoutContextValue;
433
+
434
+ declare function usePopoutContainer(): HTMLElement | undefined;
435
+
436
+ interface OpenPopoutOptions {
437
+ width: number;
438
+ height: number;
439
+ title: string;
440
+ }
441
+ type OpenPopoutResult = {
442
+ ok: true;
443
+ mode: "pip" | "popup";
444
+ } | {
445
+ ok: false;
446
+ reason: "pip-slot-taken" | "popup-blocked" | "no-capability" | "request-failed";
447
+ };
448
+ interface UsePopoutWindowReturn {
449
+ /**
450
+ * Open a popout for this window. MUST be called synchronously inside a
451
+ * user-gesture handler (pointerup, click, keydown) — otherwise the browser
452
+ * rejects the request.
453
+ */
454
+ openPopout(opts: OpenPopoutOptions): Promise<OpenPopoutResult>;
455
+ /**
456
+ * Programmatically close the popout. Triggers the same cleanup path as
457
+ * the user clicking the browser X button. Safe to call when there's no
458
+ * popout open.
459
+ */
460
+ close(): void;
461
+ }
462
+ declare function usePopoutWindow(windowId: string): UsePopoutWindowReturn;
463
+
464
+ type OpenPopoutFn = (opts: OpenPopoutOptions) => Promise<OpenPopoutResult>;
465
+ /** Get the live openPopout for a given window id, or null. */
466
+ declare function getPopoutOpener(windowId: string): OpenPopoutFn | null;
467
+ interface PopoutControlApi {
468
+ /**
469
+ * Pop out a specific window by its id. Must be called inside a
470
+ * user-gesture stack. Returns the same result shape as
471
+ * `usePopoutWindow.openPopout`. If the window isn't currently mounted,
472
+ * returns `{ ok: false, reason: "not-mounted" }`.
473
+ */
474
+ popOut(windowId: string, opts: OpenPopoutOptions): Promise<OpenPopoutResult | {
475
+ ok: false;
476
+ reason: "not-mounted";
477
+ }>;
478
+ /** Dock a popped-out window back into the parent viewport. */
479
+ dock(windowId: string): void;
480
+ }
481
+ declare function usePopoutControl(): PopoutControlApi;
482
+ interface PopOutByIdOptions {
483
+ /** Falls back to 480 if not provided. */
484
+ width?: number | undefined;
485
+ /** Falls back to 320 if not provided. */
486
+ height?: number | undefined;
487
+ /** Falls back to "Window" if not provided. */
488
+ title?: string | undefined;
489
+ }
490
+ declare function buildPopoutOpts(rect: Partial<WindowRect> | undefined, opts: PopOutByIdOptions | undefined): OpenPopoutOptions;
491
+
492
+ /** Get the popout window for a given id, or `null` if none is registered. */
493
+ declare function getPopoutWindow(id: string): Window | null;
494
+ /** Iterate over every currently-registered popout window. */
495
+ declare function forEachPopoutWindow(cb: (win: Window, id: string) => void): void;
496
+ /**
497
+ * Subscribe to map changes. Returns an unsubscribe function.
498
+ * Used internally by `usePopoutWindowRef` for `useSyncExternalStore`.
499
+ */
500
+ declare function subscribePopoutWindowMap(listener: () => void): () => void;
501
+ /**
502
+ * React hook: subscribe to the popout window for a given id and re-render
503
+ * when it changes. Returns the live `Window` reference or `null`.
504
+ *
505
+ * Uses `useSyncExternalStore` for tearing-free reads under React 19 strict
506
+ * mode and concurrent rendering.
507
+ */
508
+ declare function usePopoutWindowRef(id: string): Window | null;
509
+
510
+ /** Read all pending window ids (used on hydration to surface the toast). */
511
+ declare function getPendingPopoutWindowIds(): string[];
512
+ /** Clear ALL pending entries — used after surfacing the recovery toast. */
513
+ declare function clearAllPopoutPending(): void;
514
+
515
+ /**
516
+ * Feature detection for the window-panels pop-out system.
517
+ *
518
+ * Three capability tiers, in order of preference:
519
+ *
520
+ * 1. **`"pip"`** — Document Picture-in-Picture API (Chromium-based browsers,
521
+ * Chrome 116+, Edge 116+, Opera). Frameless, always-on-top floating window.
522
+ * Detected by presence of `window.documentPictureInPicture.requestWindow`.
523
+ *
524
+ * 2. **`"popup"`** — `window.open()` fallback (Safari, Firefox, anything else).
525
+ * Universal browser support. Shows browser chrome (URL bar, tabs).
526
+ * Acceptable degradation: user still gets a separate window for multi-
527
+ * monitor workflows.
528
+ *
529
+ * 3. **`"none"`** — Embedded WebViews or other contexts where neither API is
530
+ * available (rare). The "Pop out" affordance should be hidden entirely.
531
+ *
532
+ * The detection is intentionally simple and synchronous — it runs once on
533
+ * the client at component mount time and the result is stable for the
534
+ * lifetime of the page.
535
+ *
536
+ * SSR safety: every code path branches on `typeof window` first.
537
+ */
538
+ type PopoutCapability = "pip" | "popup" | "none";
539
+ /**
540
+ * Returns the highest-fidelity popout mechanism available in the current
541
+ * browser. Safe to call during SSR (returns `"none"`).
542
+ */
543
+ declare function detectPopoutCapability(): PopoutCapability;
544
+ /**
545
+ * True when the browser supports Document Picture-in-Picture (the preferred
546
+ * popout mode). Convenience wrapper around `detectPopoutCapability()` for
547
+ * use in conditional rendering and feature flags.
548
+ */
549
+ declare function supportsDocumentPictureInPicture(): boolean;
550
+ /**
551
+ * True when popout is available in any form (DPiP or popup).
552
+ * Use this to decide whether to show the "Pop out" affordance at all.
553
+ */
554
+ declare function supportsAnyPopout(): boolean;
555
+
556
+ /**
557
+ * popoutDragDetector — pure logic for "is the user dragging this window
558
+ * out of the viewport hard enough to mean they want to pop it out?"
559
+ *
560
+ * **Threshold strategy:**
561
+ * 1. Cursor must cross outside the viewport rect, AND
562
+ * 2. Cursor must be at least `outsideThreshold` pixels past the edge
563
+ * (a deliberate intent signal, not a stray mouse twitch), AND
564
+ * 3. Cursor must DWELL outside for at least `dwellMs` milliseconds
565
+ * (filters out drags that just transit through the edge en route
566
+ * to a different on-screen position).
567
+ *
568
+ * The dwell timer resets every time the cursor crosses back inside the
569
+ * viewport — a glance outside doesn't latch the candidate state.
570
+ *
571
+ * **Return shape:**
572
+ * - `state` — the next `DragOutState` (replaces the previous on the next call)
573
+ * - `shouldTrigger` — true when the dwell threshold has just been met. The
574
+ * caller should fire popout on `pointerup`, NOT on this flag — the flag
575
+ * is purely for animation/visual feedback (`isCandidate`).
576
+ *
577
+ * Pure function: no DOM access, no side effects. Easy to unit test.
578
+ */
579
+ interface DragOutConfig {
580
+ /** Pixels past the viewport edge before considering the drag "outside". */
581
+ outsideThreshold: number;
582
+ /** Milliseconds the cursor must remain outside before becoming a candidate. */
583
+ dwellMs: number;
584
+ }
585
+ interface DragOutState {
586
+ /** Whether the dwell threshold is currently met (drives visual feedback). */
587
+ isCandidate: boolean;
588
+ /**
589
+ * `performance.now()` timestamp when the cursor first crossed `outsideThreshold`.
590
+ * `null` when the cursor is currently inside (or hasn't been outside yet).
591
+ */
592
+ outsideSinceMs: number | null;
593
+ }
594
+ interface DragOutInput {
595
+ clientX: number;
596
+ clientY: number;
597
+ }
598
+ interface DragOutEvalResult {
599
+ state: DragOutState;
600
+ /**
601
+ * Edge-trigger: `true` ONLY on the first call where `isCandidate` flips
602
+ * from `false` to `true`. Useful if a caller wants to dispatch a one-shot
603
+ * action (e.g. haptic) on candidate entry.
604
+ */
605
+ shouldTrigger: boolean;
606
+ }
607
+ declare const DEFAULT_DRAG_OUT_CONFIG: DragOutConfig;
608
+ declare const INITIAL_DRAG_OUT_STATE: DragOutState;
609
+ /**
610
+ * Evaluate one pointermove event against the previous drag-out state.
611
+ *
612
+ * @param e Pointer event coords (clientX/clientY are viewport-relative)
613
+ * @param prev Previous state from the last call (or `INITIAL_DRAG_OUT_STATE`)
614
+ * @param cfg Threshold config
615
+ * @param vpW Current `window.innerWidth`
616
+ * @param vpH Current `window.innerHeight`
617
+ * @param now Current `performance.now()` reading
618
+ */
619
+ declare function evaluateDragOut(e: DragOutInput, prev: DragOutState, cfg: DragOutConfig, vpW: number, vpH: number, now: number): DragOutEvalResult;
620
+
621
+ interface MobileDrawerSurfaceProps {
622
+ title: ReactNode;
623
+ children: ReactNode;
624
+ onClose: () => void;
625
+ sidebar?: ReactNode | undefined;
626
+ footer?: ReactNode | undefined;
627
+ /** Open state — mirrors the parent's overlay-isOpen flag. */
628
+ isOpen: boolean;
629
+ actionsLeft?: ReactNode | undefined;
630
+ actionsRight?: ReactNode | undefined;
631
+ /** Optional className applied to the body scroll container. */
632
+ bodyClassName?: string | undefined;
633
+ /** Marks the root `data-surface-layer` (see WindowPanel `surfaceLayer`). */
634
+ surfaceLayer?: string | undefined;
635
+ /**
636
+ * Size the sheet to its content (capped at 85dvh) instead of a fixed 85dvh:
637
+ * a short form no longer sits on a band of empty sheet.
638
+ */
639
+ sizeToContent?: boolean | undefined;
640
+ /** No title-row ✕: the drag handle and the window's own Cancel dismiss it. */
641
+ hideClose?: boolean | undefined;
642
+ /**
643
+ * Whether the sidebar should collapse into a nested drawer when the user
644
+ * taps the sidebar toggle. When "inline", the sidebar pushes the body
645
+ * instead. Default: "drawer".
646
+ */
647
+ sidebarAs?: "drawer" | "inline" | undefined;
648
+ }
649
+ declare function MobileDrawerSurface({ title, children, onClose, sidebar, footer, isOpen, actionsLeft, actionsRight, bodyClassName, surfaceLayer, sizeToContent, hideClose, sidebarAs, }: MobileDrawerSurfaceProps): react.JSX.Element;
650
+
651
+ interface MobileCardSurfaceProps {
652
+ title: ReactNode;
653
+ children: ReactNode;
654
+ onClose: () => void;
655
+ footer?: ReactNode | undefined;
656
+ isOpen: boolean;
657
+ actionsRight?: ReactNode | undefined;
658
+ bodyClassName?: string | undefined;
659
+ /** Marks the root `data-surface-layer` (see WindowPanel `surfaceLayer`). */
660
+ surfaceLayer?: string | undefined;
661
+ /** Override the default max-height (60dvh). Accepts any CSS length. */
662
+ maxHeight?: string | undefined;
663
+ }
664
+ declare function MobileCardSurface({ title, children, onClose, footer, isOpen, actionsRight, bodyClassName, surfaceLayer, maxHeight, }: MobileCardSurfaceProps): react.ReactPortal | null;
665
+
666
+ /** Two lines, then an ellipsis — never a one-line `truncate`. */
667
+ declare const MOBILE_WINDOW_TITLE_TEXT = "line-clamp-2 break-words text-sm font-medium leading-snug text-foreground";
668
+ /**
669
+ * The title slot's classes. A text title wraps to two lines; a RICH title (a
670
+ * control, e.g. the Chat window's agent picker) keeps its own layout. Exported
671
+ * for a host whose title element is fixed (the drawer's accessible
672
+ * `DrawerTitle`) — put these classes and `data-window-mobile-title` on it.
673
+ */
674
+ declare function mobileWindowTitleClass(title: ReactNode): string;
675
+ /** The title slot. */
676
+ declare function MobileWindowTitle({ title, className, }: {
677
+ title: ReactNode;
678
+ className?: string | undefined;
679
+ }): react.JSX.Element;
680
+ /** Row 1: whatever leads (close / minimize) and the title, nothing else. */
681
+ declare function MobileWindowTitleRow({ leading, trailing, children, className, }: {
682
+ leading?: ReactNode | undefined;
683
+ /** A close ✕ only — never window actions. */
684
+ trailing?: ReactNode | undefined;
685
+ children: ReactNode;
686
+ className?: string | undefined;
687
+ }): react.JSX.Element;
688
+ /** Row 2: pane toggle + window actions. Absent when both are empty. */
689
+ declare function MobileWindowActionsRow({ leading, actions, className, }: {
690
+ leading?: ReactNode | undefined;
691
+ actions?: ReactNode | undefined;
692
+ className?: string | undefined;
693
+ }): react.JSX.Element | null;
694
+
695
+ /**
696
+ * AUTO-OPENED WINDOW PLACEMENT (MKT-104, Arman 2026-10-15).
697
+ *
698
+ * Some windows open by themselves (debug mode's Workflow run + Watch AI windows) so a person can
699
+ * watch progress live. They must NEVER cover the page's working column or take its clicks. This
700
+ * is the ONE place that decides where such a window goes, for every window that is opened by the
701
+ * system rather than by a person:
702
+ *
703
+ * 1. The caller marks the open with `markAutoOpened(overlayId, instanceId)` (see
704
+ * `features/overlays/openers/openAutoOverlay.ts`); `WindowPanel` consumes the mark.
705
+ * 2. The working column is measured from the page itself: the bounds of every visible control
706
+ * (button, link, field) in the page body. Anything outside that box takes no clicks anyway.
707
+ * 3. The window is docked in the widest free side gutter, shrunk to fit, stacked under any other
708
+ * auto-opened window already there. No gutter wide enough (a narrow screen, a page that
709
+ * fills the width) → it is not opened (`autoOpenHasRoom`), or, when the page grew under it,
710
+ * it closes itself. A person's own press of Watch AI / Workflow run always opens it.
711
+ *
712
+ * Pure module: no Redux import (WindowPanel stays a leaf).
713
+ */
714
+
715
+ /** Narrowest window worth docking; anything tighter goes to the tray. */
716
+ declare const AUTO_OPEN_MIN_WIDTH = 280;
717
+ /** Call BEFORE dispatching the open of a window the system (not a person) opens. */
718
+ declare function markAutoOpened(overlayId: string, instanceId: string): void;
719
+ /** Peek (does not consume, so StrictMode's double render sees the same answer). */
720
+ declare function isAutoOpened(overlayId: string, instanceId: string): boolean;
721
+ /** Forget the mark once the window is gone: a person's later reopen places normally. */
722
+ declare function clearAutoOpened(overlayId: string, instanceId: string): void;
723
+ /** Left/right edge of everything on the page body that takes a click. */
724
+ declare function measureWorkingColumn(): {
725
+ left: number;
726
+ right: number;
727
+ } | null;
728
+ /** Fired (detail `{ overlayId, instanceId }`) when a docked auto-opened window closes itself for lack of room. */
729
+ declare const AUTO_OPEN_NO_ROOM_EVENT = "matrx:auto-open-no-room";
730
+ /**
731
+ * True when a gutter beside the page's working column can hold an auto-opened window right now.
732
+ * Callers that open windows by themselves check this FIRST: with no room the window is not opened
733
+ * (a parked tray tile sits over the page's own bottom-right controls — MKT-104).
734
+ */
735
+ declare function autoOpenHasRoom(): boolean;
736
+ interface AutoOpenPlacement {
737
+ rect: WindowRect;
738
+ /** True when no gutter fits: open parked in the tray instead of over the page. */
739
+ minimized: boolean;
740
+ }
741
+ /**
742
+ * @param siblings rects of auto-opened windows already docked (to stack under them)
743
+ */
744
+ declare function resolveAutoOpenPlacement(args: {
745
+ width: number;
746
+ height: number;
747
+ minWidth: number;
748
+ minHeight: number;
749
+ top: number;
750
+ viewport: {
751
+ vw: number;
752
+ vh: number;
753
+ };
754
+ siblings: WindowRect[];
755
+ }): AutoOpenPlacement;
756
+
757
+ type UnbiddenRefusalCode =
758
+ /** The registry entry declares no `unbiddenHome` (or there is no entry). */
759
+ "no-declared-home"
760
+ /** It has a home; the viewer is not on it. Try again when they are. */
761
+ | "away-from-home";
762
+ interface UnbiddenRaiseVerdict {
763
+ /** True only when this window may raise itself, here, right now. */
764
+ allowed: boolean;
765
+ /** Plain-English why, for logs and for the caller's own warning. */
766
+ reason: string;
767
+ /** Set only when `allowed` is false. */
768
+ code?: UnbiddenRefusalCode | undefined;
769
+ /** The routes this window declared as home, for diagnostics (exact ones end in " (exact)"). */
770
+ home: readonly string[];
771
+ }
772
+ /**
773
+ * True when `pathname` is `route` itself or a page nested under it.
774
+ * `/administration` covers `/administration/spend`; it does not cover
775
+ * `/administration-archive`.
776
+ */
777
+ declare function isUnderRoute(pathname: string, route: string): boolean;
778
+ /**
779
+ * May this window raise ITSELF — with nobody having clicked anything — while
780
+ * the viewer is on `pathname`?
781
+ *
782
+ * Refusals are announced, never swallowed: a window with no declared home
783
+ * warns (that is a registry bug someone must fix), and an away-from-home
784
+ * deferral logs at info (that is the system working as designed).
785
+ */
786
+ declare function mayRaiseUnbidden(overlayId: string, pathname: string | null | undefined): UnbiddenRaiseVerdict;
787
+
788
+ /**
789
+ * Warn when `moduleId` is evaluated during boot. `report` replaces the default console warning
790
+ * (route it to the host's error sink).
791
+ */
792
+ declare function assertLazyLoaded(moduleId: string, report?: (message: string, stack: string) => void): void;
793
+
794
+ declare function sanitizeWindowData(value: unknown, allowedKeys: readonly string[]): Record<string, unknown>;
795
+
796
+ export { AUTO_OPEN_MIN_WIDTH, AUTO_OPEN_NO_ROOM_EVENT, type AutoOpenPlacement, DEFAULT_DRAG_OUT_CONFIG, type DragOutConfig, type DragOutEvalResult, type DragOutInput, type DragOutState, type ElementThumbnailOptions, INITIAL_DRAG_OUT_STATE, LayoutIcon, LayoutIconButton, type LayoutIconType, MOBILE_WINDOW_TITLE_TEXT, MinimizedWindowContent, MobileCardSurface, MobileDrawerSurface, MobileWindowActionsRow, MobileWindowTitle, MobileWindowTitleRow, type OpenPopoutOptions, type OpenPopoutResult, type PopOutByIdOptions, type PopoutCapability, PopoutContext, type PopoutContextValue, type PopoutControlApi, type PopoutMode, type ResizeEdge, TrayChipPreview, TrayStatusChip, type TrayStatusChipProps, type TrayStatusSegment, type TrayStatusTone, type UnbiddenRaiseVerdict, type UnbiddenRefusalCode, type UsePopoutWindowReturn, type UseWindowPanelOptions, type UseWindowPanelReturn, WindowPanel, type WindowPanelBaseProps, type WindowPanelCloseBinding, type WindowPanelProps, type WindowPosition, WindowSidebar, type WindowSidebarItem, assertLazyLoaded, autoOpenHasRoom, buildPopoutOpts, captureElementThumbnail, clearAllPopoutPending, clearAutoOpened, clearTraySnapshot, defaultTraySnapshotCapture, detectPopoutCapability, evaluateDragOut, forEachPopoutWindow, getPendingPopoutWindowIds, getPopoutOpener, getPopoutWindow, getTraySnapshot, isAutoOpened, isUnderRoute, markAutoOpened, mayRaiseUnbidden, measureWorkingColumn, mobileWindowTitleClass, resetDefaultWindowCascade, resolveAutoOpenPlacement, resolvePosition, sanitizeWindowData, setTraySnapshot, subscribePopoutWindowMap, subscribeTraySnapshotMap, supportsAnyPopout, supportsDocumentPictureInPicture, useOverlaySurfaceRenderAck, usePopout, usePopoutContainer, usePopoutControl, usePopoutWindow, usePopoutWindowRef, useWindowPanel };