@ai-matrx/summon 0.1.10 → 0.1.11

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/window-host.ts","../src/window/config.tsx","../src/window/WindowTraySync.tsx","../src/window/popout/PopoutContext.tsx","../src/window/popout/usePopoutContainer.ts","../src/window/useOverlaySurfaceRenderAck.ts","../src/window/tray/trayPreviewRegistry.ts"],"sourcesContent":["// @ai-matrx/summon/window-host — the light half of the window section, safe to mount at the app\n// root: window configuration (WindowsProvider), the viewport/tray sync, and tray-preview\n// registration. Importing it never loads WindowPanel; the window section reads this section's\n// context through the package name, so both share one context.\nexport {\n WindowsProvider,\n useWindowConfig,\n WindowSelectionSurface,\n DEFAULT_WINDOW_CONFIG,\n DEFAULT_ESCAPE_YIELD_SELECTOR,\n DEFAULT_MOBILE_TOP_OFFSET,\n type WindowConfig,\n type WindowsProviderProps,\n type WindowSelectionSurfaceProps,\n} from \"./window/config\";\nexport { WindowTraySync } from \"./window/WindowTraySync\";\n// Hooks a render module calls inside a window, light enough for the boot graph. The popout context\n// lives HERE (one identity): the window section imports it through the package name.\nexport { usePopout, PopoutContext, type PopoutContextValue, type PopoutMode } from \"./window/popout/PopoutContext\";\nexport { usePopoutContainer } from \"./window/popout/usePopoutContainer\";\nexport { useOverlaySurfaceRenderAck } from \"./window/useOverlaySurfaceRenderAck\";\nexport {\n registerTrayPreview,\n getTrayPreviewEntry,\n resetTrayPreviews,\n type TrayPreviewContext,\n type TrayPreviewEntry,\n type TraySnapshotCapture,\n} from \"./window/tray/trayPreviewRegistry\";\n","\"use client\";\n\n/**\n * WindowsProvider — the window section's host options, read by every WindowPanel through context.\n * Everything has a working default, so no provider is needed at all; a host passes only what it\n * has (AI Matrx: its right-click selection menu and its header offset).\n */\nimport { createContext, useContext, useMemo, type ComponentType, type ReactNode } from \"react\";\nimport { getSummonHostConfig } from \"@ai-matrx/summon/root\";\n\nexport interface WindowSelectionSurfaceProps {\n readonly children: ReactNode;\n}\n\nexport interface WindowConfig {\n /**\n * Visible elements matching this selector own the Escape key: a window with `closeOnEscape`\n * yields to them (menus, popovers, listboxes and open dialogs always do). Default\n * `[data-overlay-escape-yield]` — mark any host surface that must take Escape first.\n */\n readonly escapeYieldSelector: string;\n /** Top offset of a phone fullscreen window (below the host's header). Default `var(--header-height, 0px)`. */\n readonly mobileTopOffset: string;\n /**\n * Wraps every window body (desktop, popped out, and all three phone presentations). Default:\n * passthrough. AI Matrx injects its selection toolbar + right-click menu here.\n */\n readonly selectionSurface: ComponentType<WindowSelectionSurfaceProps>;\n}\n\nfunction PassthroughSurface({ children }: WindowSelectionSurfaceProps) {\n return <>{children}</>;\n}\n\nexport const DEFAULT_ESCAPE_YIELD_SELECTOR = \"[data-overlay-escape-yield]\";\nexport const DEFAULT_MOBILE_TOP_OFFSET = \"var(--header-height, 0px)\";\n\nexport const DEFAULT_WINDOW_CONFIG: WindowConfig = {\n escapeYieldSelector: DEFAULT_ESCAPE_YIELD_SELECTOR,\n mobileTopOffset: DEFAULT_MOBILE_TOP_OFFSET,\n selectionSurface: PassthroughSurface,\n};\n\nconst WindowConfigContext = createContext<WindowConfig | null>(null);\nWindowConfigContext.displayName = \"WindowConfigContext\";\n\nexport interface WindowsProviderProps extends Partial<WindowConfig> {\n readonly children?: ReactNode;\n}\n\nexport function WindowsProvider({ escapeYieldSelector, mobileTopOffset, selectionSurface, children }: WindowsProviderProps) {\n const value = useMemo<WindowConfig>(\n () => ({\n escapeYieldSelector: escapeYieldSelector ?? DEFAULT_WINDOW_CONFIG.escapeYieldSelector,\n mobileTopOffset: mobileTopOffset ?? DEFAULT_WINDOW_CONFIG.mobileTopOffset,\n selectionSurface: selectionSurface ?? DEFAULT_WINDOW_CONFIG.selectionSurface,\n }),\n [escapeYieldSelector, mobileTopOffset, selectionSurface],\n );\n return <WindowConfigContext.Provider value={value}>{children}</WindowConfigContext.Provider>;\n}\n\n/** The nearest WindowsProvider's options, else the ones configured at `<SummonRoot windows>`, else defaults. */\nexport function useWindowConfig(): WindowConfig {\n const fromContext = useContext(WindowConfigContext);\n if (fromContext) return fromContext;\n const host = getSummonHostConfig().windows;\n if (!host) return DEFAULT_WINDOW_CONFIG;\n return {\n escapeYieldSelector: host.escapeYieldSelector ?? DEFAULT_WINDOW_CONFIG.escapeYieldSelector,\n mobileTopOffset: host.mobileTopOffset ?? DEFAULT_WINDOW_CONFIG.mobileTopOffset,\n selectionSurface: host.selectionSurface ?? DEFAULT_WINDOW_CONFIG.selectionSurface,\n };\n}\n\n/** The configured selection surface, as a component. */\nexport function WindowSelectionSurface({ children }: WindowSelectionSurfaceProps) {\n const Surface = useWindowConfig().selectionSurface;\n return <Surface>{children}</Surface>;\n}\n","\"use client\";\n\n/**\n * WindowTraySync\n *\n * Mounts ONCE in the root layout. Does nothing — no listener, no dispatch — until a window\n * exists (one boolean selector), then attaches a single debounced resize listener that:\n * 1. Recomputes the positions of all minimized windows (tray slots can change\n * when chips-per-row shifts on viewport resize).\n * 2. Clamps every docked windowed window into the new viewport so windows\n * positioned for a larger screen stay reachable after a downsize.\n *\n * Zero re-renders — fires and forgets.\n *\n * Debounce: 500ms — a continuous window-drag generates exactly one dispatch\n * when the user stops moving. Short bursts (e.g. snapping) are ignored.\n *\n * Usage (in both authenticated and SSR root layouts):\n * <WindowTraySync />\n */\n\nimport { useEffect } from \"react\";\nimport { useSummonDispatch, useSummonSelector } from \"@ai-matrx/summon/react\";\nimport {\n recomputeTrayPositions,\n clampAllWindowRects,\n} from \"@ai-matrx/summon\";\nimport { safeViewportDims, type WindowManagerState } from \"@ai-matrx/summon\";\n\nconst DEBOUNCE_MS = 500;\n\n/** Any window registered or waiting to restore: until then there is no geometry to keep in view. */\nexport function selectAnyWindowGeometry(state: { windowManager?: WindowManagerState }): boolean {\n const wm = state.windowManager;\n if (!wm) return false;\n for (const _ in wm.windows) return true;\n for (const _ in wm.pendingRestores) return true;\n return false;\n}\n\nexport function WindowTraySync() {\n const dispatch = useSummonDispatch();\n const active = useSummonSelector(selectAnyWindowGeometry);\n\n useEffect(() => {\n if (!active) return;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const syncWindowGeometry = () => {\n // A resize that measures 0×0 is a hidden/undisplayed tab or a\n // display:none ancestor, never a real screen. Bail WITHOUT dispatching:\n // reacting to it would rewrite every window rect and every tray slot\n // against invented dimensions, and the next real measurement would\n // then have to undo it. No measurement, no write.\n const { vw, vh, degenerate } = safeViewportDims();\n if (degenerate) return;\n dispatch(\n recomputeTrayPositions({ viewportWidth: vw, viewportHeight: vh }),\n );\n dispatch(clampAllWindowRects({ viewportWidth: vw, viewportHeight: vh }));\n };\n\n // This island may load after the viewport already changed. Synchronising\n // once on mount closes that event-listener gap for persisted windows.\n syncWindowGeometry();\n\n const handleResize = () => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n syncWindowGeometry();\n }, DEBOUNCE_MS);\n };\n\n window.addEventListener(\"resize\", handleResize, { passive: true });\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n if (timer !== null) clearTimeout(timer);\n };\n }, [dispatch, active]);\n\n return null;\n}\n\nexport default WindowTraySync;\n","\"use client\";\n\n/**\n * PopoutContext — exposes the popout `Document` / `Window` / portal target\n * to descendants of a popped-out subtree.\n *\n * The default value (`popoutDocument: null`, etc.) means \"I'm rendered in\n * the parent document.\" Descendants that need to behave differently inside\n * a popout (e.g. Radix portals retargeting their content) call\n * `usePopout()` and branch on the values.\n *\n * **Architectural note:** because `createPortal` preserves the parent\n * React tree, every ancestor `Provider` cascades into the popout — Redux,\n * Theme, Tooltip, Router, all work without re-wrapping. This context is\n * for the *opposite* direction: telling popped-out content \"you're inside\n * a popout window, here's the document/window/container you should use\n * instead of the global `document`/`window`.\"\n */\nimport { createContext, useContext } from \"react\";\n\nexport type PopoutMode = \"pip\" | \"popup\";\n\nexport interface PopoutContextValue {\n /**\n * The popout `Document`. `null` when this subtree is rendered in the\n * parent (the default).\n */\n popoutDocument: Document | null;\n /**\n * The popout `Window`. `null` when this subtree is rendered in the parent.\n * Use for `addEventListener`, `matchMedia`, `innerWidth`, etc., when the\n * popout-scoped answer is what you need.\n */\n popoutWindow: Window | null;\n /**\n * Element to use as the Radix `Portal.container` prop so floating UI\n * (tooltips, popovers, dropdowns) renders inside the popout instead of\n * the parent `document.body`. Returns `null` outside a popout —\n * `usePopoutContainer` coerces this to `undefined` so Radix falls back\n * to its default container automatically.\n */\n popoutContainer: HTMLElement | null;\n /**\n * Which popout mechanism is in use. `null` outside a popout.\n * Currently only used for diagnostics; downstream components may branch\n * on it later (e.g. show different chrome for `popup` vs `pip`).\n */\n mode: PopoutMode | null;\n}\n\nconst DEFAULT_VALUE: PopoutContextValue = {\n popoutDocument: null,\n popoutWindow: null,\n popoutContainer: null,\n mode: null,\n};\n\nexport const PopoutContext =\n createContext<PopoutContextValue>(DEFAULT_VALUE);\n\n/** Read the current popout context. Always defined — never throws. */\nexport function usePopout(): PopoutContextValue {\n return useContext(PopoutContext);\n}\n","\"use client\";\n\n/**\n * usePopoutContainer — return the element Radix portals (Tooltip, Popover,\n * Dropdown, Dialog, Select, AlertDialog, HoverCard) should mount into.\n *\n * **The problem this solves:** by default every Radix `Portal` mounts to\n * `document.body`. When a tooltip-trigger lives inside a popped-out window\n * but the popover content portals to the *parent* document, the tooltip\n * appears in the wrong window — typically invisible to the user.\n *\n * **The fix:** wrappers around Radix primitives call this hook and pass\n * the result as `<Primitive.Portal container={...} />`. Inside a popout,\n * the result is the popout's `<body>`. Outside a popout, the result is\n * `undefined`, which Radix interprets as \"use the default\" — meaning no\n * behavior change for any of the thousands of existing usages.\n *\n * **Why `undefined` instead of `null`?** Radix's `container` prop accepts\n * `HTMLElement | null | undefined`. Passing `null` actively tries to\n * portal into a `null` target and crashes. Passing `undefined` falls\n * through to the default (`document.body`). The `?? undefined` coerces\n * the `null` we get from `PopoutContext` outside a popout into the\n * Radix-friendly default-fallthrough value.\n */\nimport { usePopout } from \"./PopoutContext\";\n\nexport function usePopoutContainer(): HTMLElement | undefined {\n return usePopout().popoutContainer ?? undefined;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { ackOverlaySurfaceRender, clearOverlaySurfaceRender } from \"@ai-matrx/summon\";\n\n/**\n * Acknowledge a visible presentation that intentionally replaces WindowPanel.\n *\n * Registered singleton window overlays normally prove they are on screen by mounting a\n * WindowPanel and registering geometry. Mobile drawers, sheets, and full-screen\n * viewers have no window-manager geometry, so their composition root must use\n * this hook while that alternate surface is active.\n */\nexport function useOverlaySurfaceRenderAck(\n overlayId: string,\n active: boolean,\n): void {\n useEffect(() => {\n if (!active) return undefined;\n ackOverlaySurfaceRender(overlayId);\n return () => clearOverlaySurfaceRender(overlayId);\n }, [active, overlayId]);\n}\n","/**\n * Tray previews — overlay id → a semantic minimized preview and/or a custom snapshot capture.\n *\n * The host registers its previews once (AI Matrx keeps its own preview components in the app):\n *\n * registerTrayPreview(\"notesWindow\", NotesTrayPreview);\n * registerTrayPreview(\"canvasWindow\", { captureTraySnapshot: captureCanvas });\n *\n * A window with a preview component skips snapshot capture entirely (cheaper, higher fidelity).\n * A window with neither gets the default capture (`defaultTraySnapshotCapture`, html-to-image when\n * the host installed it). State lives in a global slot (kit globalSlot, D9).\n */\nimport type { ComponentType } from \"react\";\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\n\nexport interface TrayPreviewContext {\n /** The window's allowlisted semantic data (open data merged with what `onCollectData` returned). */\n readonly data: Readonly<Record<string, unknown>>;\n /** Stable overlay id (the registry key). */\n readonly overlayId: string;\n /** Instance id — `\"default\"` for singleton windows. */\n readonly instanceId: string;\n /** The display title. */\n readonly title: string;\n}\n\nexport type TraySnapshotCapture = (bodyEl: HTMLElement) => Promise<Blob | null>;\n\nexport interface TrayPreviewEntry {\n readonly Preview?: ComponentType<TrayPreviewContext>;\n readonly captureTraySnapshot?: TraySnapshotCapture;\n}\n\nconst EMPTY: TrayPreviewEntry = {};\n\nconst registry = (): Map<string, TrayPreviewEntry> =>\n globalSlot<Map<string, TrayPreviewEntry>>(\"ai-matrx.summon.window.trayPreviews\", () => new Map());\n\n/**\n * Register the minimized preview for an overlay (or any window id). Pass a component, or an entry\n * with `Preview` and/or `captureTraySnapshot`. Re-registering replaces. Returns an unregister.\n */\nexport function registerTrayPreview(\n overlayId: string,\n preview: ComponentType<TrayPreviewContext> | TrayPreviewEntry,\n): () => void {\n const entry: TrayPreviewEntry = typeof preview === \"function\" ? { Preview: preview } : preview;\n registry().set(overlayId, entry);\n return () => {\n if (registry().get(overlayId) === entry) registry().delete(overlayId);\n };\n}\n\nexport function getTrayPreviewEntry(overlayId: string): TrayPreviewEntry {\n return registry().get(overlayId) ?? EMPTY;\n}\n\n/** Test-only. */\nexport function resetTrayPreviews(): void {\n registry().clear();\n}\n"],"mappings":"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","names":["import_react","import_summon","import_react","import_react","import_summon"]}
1
+ {"version":3,"sources":["../src/window-host.ts","../src/window/config.tsx","../src/window/WindowTraySync.tsx","../src/window/tray/trayPreviewRegistry.ts"],"sourcesContent":["// @ai-matrx/summon/window-host — the light half of the window section, safe to mount at the app\n// root: window configuration (WindowsProvider), the viewport/tray sync, and tray-preview\n// registration. Importing it never loads WindowPanel; the window section reads this section's\n// context through the package name, so both share one context.\nexport {\n WindowsProvider,\n useWindowConfig,\n WindowSelectionSurface,\n DEFAULT_WINDOW_CONFIG,\n DEFAULT_ESCAPE_YIELD_SELECTOR,\n DEFAULT_MOBILE_TOP_OFFSET,\n type WindowConfig,\n type WindowsProviderProps,\n type WindowSelectionSurfaceProps,\n} from \"./window/config\";\nexport { WindowTraySync } from \"./window/WindowTraySync\";\n// Hooks a render module calls inside a window, light enough for the boot graph. The popout context\n// lives HERE (one identity): the window section imports it through the package name.\nexport { usePopout, PopoutContext, usePopoutContainer, useOverlaySurfaceRenderAck, type PopoutContextValue, type PopoutMode } from \"@ai-matrx/summon/root\";\nexport {\n registerTrayPreview,\n getTrayPreviewEntry,\n resetTrayPreviews,\n type TrayPreviewContext,\n type TrayPreviewEntry,\n type TraySnapshotCapture,\n} from \"./window/tray/trayPreviewRegistry\";\n","\"use client\";\n\n/**\n * WindowsProvider — the window section's host options, read by every WindowPanel through context.\n * Everything has a working default, so no provider is needed at all; a host passes only what it\n * has (AI Matrx: its right-click selection menu and its header offset).\n */\nimport { createContext, useContext, useMemo, type ComponentType, type ReactNode } from \"react\";\nimport { getSummonHostConfig } from \"@ai-matrx/summon/root\";\n\nexport interface WindowSelectionSurfaceProps {\n readonly children: ReactNode;\n}\n\nexport interface WindowConfig {\n /**\n * Visible elements matching this selector own the Escape key: a window with `closeOnEscape`\n * yields to them (menus, popovers, listboxes and open dialogs always do). Default\n * `[data-overlay-escape-yield]` — mark any host surface that must take Escape first.\n */\n readonly escapeYieldSelector: string;\n /** Top offset of a phone fullscreen window (below the host's header). Default `var(--header-height, 0px)`. */\n readonly mobileTopOffset: string;\n /**\n * Wraps every window body (desktop, popped out, and all three phone presentations). Default:\n * passthrough. AI Matrx injects its selection toolbar + right-click menu here.\n */\n readonly selectionSurface: ComponentType<WindowSelectionSurfaceProps>;\n}\n\nfunction PassthroughSurface({ children }: WindowSelectionSurfaceProps) {\n return <>{children}</>;\n}\n\nexport const DEFAULT_ESCAPE_YIELD_SELECTOR = \"[data-overlay-escape-yield]\";\nexport const DEFAULT_MOBILE_TOP_OFFSET = \"var(--header-height, 0px)\";\n\nexport const DEFAULT_WINDOW_CONFIG: WindowConfig = {\n escapeYieldSelector: DEFAULT_ESCAPE_YIELD_SELECTOR,\n mobileTopOffset: DEFAULT_MOBILE_TOP_OFFSET,\n selectionSurface: PassthroughSurface,\n};\n\nconst WindowConfigContext = createContext<WindowConfig | null>(null);\nWindowConfigContext.displayName = \"WindowConfigContext\";\n\nexport interface WindowsProviderProps extends Partial<WindowConfig> {\n readonly children?: ReactNode;\n}\n\nexport function WindowsProvider({ escapeYieldSelector, mobileTopOffset, selectionSurface, children }: WindowsProviderProps) {\n const value = useMemo<WindowConfig>(\n () => ({\n escapeYieldSelector: escapeYieldSelector ?? DEFAULT_WINDOW_CONFIG.escapeYieldSelector,\n mobileTopOffset: mobileTopOffset ?? DEFAULT_WINDOW_CONFIG.mobileTopOffset,\n selectionSurface: selectionSurface ?? DEFAULT_WINDOW_CONFIG.selectionSurface,\n }),\n [escapeYieldSelector, mobileTopOffset, selectionSurface],\n );\n return <WindowConfigContext.Provider value={value}>{children}</WindowConfigContext.Provider>;\n}\n\n/** The nearest WindowsProvider's options, else the ones configured at `<SummonRoot windows>`, else defaults. */\nexport function useWindowConfig(): WindowConfig {\n const fromContext = useContext(WindowConfigContext);\n if (fromContext) return fromContext;\n const host = getSummonHostConfig().windows;\n if (!host) return DEFAULT_WINDOW_CONFIG;\n return {\n escapeYieldSelector: host.escapeYieldSelector ?? DEFAULT_WINDOW_CONFIG.escapeYieldSelector,\n mobileTopOffset: host.mobileTopOffset ?? DEFAULT_WINDOW_CONFIG.mobileTopOffset,\n selectionSurface: host.selectionSurface ?? DEFAULT_WINDOW_CONFIG.selectionSurface,\n };\n}\n\n/** The configured selection surface, as a component. */\nexport function WindowSelectionSurface({ children }: WindowSelectionSurfaceProps) {\n const Surface = useWindowConfig().selectionSurface;\n return <Surface>{children}</Surface>;\n}\n","\"use client\";\n\n/**\n * WindowTraySync\n *\n * Mounts ONCE in the root layout. Does nothing — no listener, no dispatch — until a window\n * exists (one boolean selector), then attaches a single debounced resize listener that:\n * 1. Recomputes the positions of all minimized windows (tray slots can change\n * when chips-per-row shifts on viewport resize).\n * 2. Clamps every docked windowed window into the new viewport so windows\n * positioned for a larger screen stay reachable after a downsize.\n *\n * Zero re-renders — fires and forgets.\n *\n * Debounce: 500ms — a continuous window-drag generates exactly one dispatch\n * when the user stops moving. Short bursts (e.g. snapping) are ignored.\n *\n * Usage (in both authenticated and SSR root layouts):\n * <WindowTraySync />\n */\n\nimport { useEffect } from \"react\";\nimport { useSummonDispatch, useSummonSelector } from \"@ai-matrx/summon/react\";\nimport {\n recomputeTrayPositions,\n clampAllWindowRects,\n} from \"@ai-matrx/summon\";\nimport { safeViewportDims, type WindowManagerState } from \"@ai-matrx/summon\";\n\nconst DEBOUNCE_MS = 500;\n\n/** Any window registered or waiting to restore: until then there is no geometry to keep in view. */\nexport function selectAnyWindowGeometry(state: { windowManager?: WindowManagerState }): boolean {\n const wm = state.windowManager;\n if (!wm) return false;\n for (const _ in wm.windows) return true;\n for (const _ in wm.pendingRestores) return true;\n return false;\n}\n\nexport function WindowTraySync() {\n const dispatch = useSummonDispatch();\n const active = useSummonSelector(selectAnyWindowGeometry);\n\n useEffect(() => {\n if (!active) return;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const syncWindowGeometry = () => {\n // A resize that measures 0×0 is a hidden/undisplayed tab or a\n // display:none ancestor, never a real screen. Bail WITHOUT dispatching:\n // reacting to it would rewrite every window rect and every tray slot\n // against invented dimensions, and the next real measurement would\n // then have to undo it. No measurement, no write.\n const { vw, vh, degenerate } = safeViewportDims();\n if (degenerate) return;\n dispatch(\n recomputeTrayPositions({ viewportWidth: vw, viewportHeight: vh }),\n );\n dispatch(clampAllWindowRects({ viewportWidth: vw, viewportHeight: vh }));\n };\n\n // This island may load after the viewport already changed. Synchronising\n // once on mount closes that event-listener gap for persisted windows.\n syncWindowGeometry();\n\n const handleResize = () => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n syncWindowGeometry();\n }, DEBOUNCE_MS);\n };\n\n window.addEventListener(\"resize\", handleResize, { passive: true });\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n if (timer !== null) clearTimeout(timer);\n };\n }, [dispatch, active]);\n\n return null;\n}\n\nexport default WindowTraySync;\n","/**\n * Tray previews — overlay id → a semantic minimized preview and/or a custom snapshot capture.\n *\n * The host registers its previews once (AI Matrx keeps its own preview components in the app):\n *\n * registerTrayPreview(\"notesWindow\", NotesTrayPreview);\n * registerTrayPreview(\"canvasWindow\", { captureTraySnapshot: captureCanvas });\n *\n * A window with a preview component skips snapshot capture entirely (cheaper, higher fidelity).\n * A window with neither gets the default capture (`defaultTraySnapshotCapture`, html-to-image when\n * the host installed it). State lives in a global slot (kit globalSlot, D9).\n */\nimport type { ComponentType } from \"react\";\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\n\nexport interface TrayPreviewContext {\n /** The window's allowlisted semantic data (open data merged with what `onCollectData` returned). */\n readonly data: Readonly<Record<string, unknown>>;\n /** Stable overlay id (the registry key). */\n readonly overlayId: string;\n /** Instance id — `\"default\"` for singleton windows. */\n readonly instanceId: string;\n /** The display title. */\n readonly title: string;\n}\n\nexport type TraySnapshotCapture = (bodyEl: HTMLElement) => Promise<Blob | null>;\n\nexport interface TrayPreviewEntry {\n readonly Preview?: ComponentType<TrayPreviewContext>;\n readonly captureTraySnapshot?: TraySnapshotCapture;\n}\n\nconst EMPTY: TrayPreviewEntry = {};\n\nconst registry = (): Map<string, TrayPreviewEntry> =>\n globalSlot<Map<string, TrayPreviewEntry>>(\"ai-matrx.summon.window.trayPreviews\", () => new Map());\n\n/**\n * Register the minimized preview for an overlay (or any window id). Pass a component, or an entry\n * with `Preview` and/or `captureTraySnapshot`. Re-registering replaces. Returns an unregister.\n */\nexport function registerTrayPreview(\n overlayId: string,\n preview: ComponentType<TrayPreviewContext> | TrayPreviewEntry,\n): () => void {\n const entry: TrayPreviewEntry = typeof preview === \"function\" ? { Preview: preview } : preview;\n registry().set(overlayId, entry);\n return () => {\n if (registry().get(overlayId) === entry) registry().delete(overlayId);\n };\n}\n\nexport function getTrayPreviewEntry(overlayId: string): TrayPreviewEntry {\n return registry().get(overlayId) ?? EMPTY;\n}\n\n/** Test-only. */\nexport function resetTrayPreviews(): void {\n registry().clear();\n}\n"],"mappings":"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","names":["import_react","import_summon","import_root"]}
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType, ReactNode } from 'react';
3
+ export { PopoutContext, PopoutContextValue, PopoutMode, useOverlaySurfaceRenderAck, usePopout, usePopoutContainer } from '@ai-matrx/summon/root';
3
4
 
4
5
  interface WindowSelectionSurfaceProps {
5
6
  readonly children: ReactNode;
@@ -33,50 +34,6 @@ declare function WindowSelectionSurface({ children }: WindowSelectionSurfaceProp
33
34
 
34
35
  declare function WindowTraySync(): null;
35
36
 
36
- type PopoutMode = "pip" | "popup";
37
- interface PopoutContextValue {
38
- /**
39
- * The popout `Document`. `null` when this subtree is rendered in the
40
- * parent (the default).
41
- */
42
- popoutDocument: Document | null;
43
- /**
44
- * The popout `Window`. `null` when this subtree is rendered in the parent.
45
- * Use for `addEventListener`, `matchMedia`, `innerWidth`, etc., when the
46
- * popout-scoped answer is what you need.
47
- */
48
- popoutWindow: Window | null;
49
- /**
50
- * Element to use as the Radix `Portal.container` prop so floating UI
51
- * (tooltips, popovers, dropdowns) renders inside the popout instead of
52
- * the parent `document.body`. Returns `null` outside a popout —
53
- * `usePopoutContainer` coerces this to `undefined` so Radix falls back
54
- * to its default container automatically.
55
- */
56
- popoutContainer: HTMLElement | null;
57
- /**
58
- * Which popout mechanism is in use. `null` outside a popout.
59
- * Currently only used for diagnostics; downstream components may branch
60
- * on it later (e.g. show different chrome for `popup` vs `pip`).
61
- */
62
- mode: PopoutMode | null;
63
- }
64
- declare const PopoutContext: react.Context<PopoutContextValue>;
65
- /** Read the current popout context. Always defined — never throws. */
66
- declare function usePopout(): PopoutContextValue;
67
-
68
- declare function usePopoutContainer(): HTMLElement | undefined;
69
-
70
- /**
71
- * Acknowledge a visible presentation that intentionally replaces WindowPanel.
72
- *
73
- * Registered singleton window overlays normally prove they are on screen by mounting a
74
- * WindowPanel and registering geometry. Mobile drawers, sheets, and full-screen
75
- * viewers have no window-manager geometry, so their composition root must use
76
- * this hook while that alternate surface is active.
77
- */
78
- declare function useOverlaySurfaceRenderAck(overlayId: string, active: boolean): void;
79
-
80
37
  /**
81
38
  * Tray previews — overlay id → a semantic minimized preview and/or a custom snapshot capture.
82
39
  *
@@ -114,4 +71,4 @@ declare function getTrayPreviewEntry(overlayId: string): TrayPreviewEntry;
114
71
  /** Test-only. */
115
72
  declare function resetTrayPreviews(): void;
116
73
 
117
- export { DEFAULT_ESCAPE_YIELD_SELECTOR, DEFAULT_MOBILE_TOP_OFFSET, DEFAULT_WINDOW_CONFIG, PopoutContext, type PopoutContextValue, type PopoutMode, type TrayPreviewContext, type TrayPreviewEntry, type TraySnapshotCapture, type WindowConfig, WindowSelectionSurface, type WindowSelectionSurfaceProps, WindowTraySync, WindowsProvider, type WindowsProviderProps, getTrayPreviewEntry, registerTrayPreview, resetTrayPreviews, useOverlaySurfaceRenderAck, usePopout, usePopoutContainer, useWindowConfig };
74
+ export { DEFAULT_ESCAPE_YIELD_SELECTOR, DEFAULT_MOBILE_TOP_OFFSET, DEFAULT_WINDOW_CONFIG, type TrayPreviewContext, type TrayPreviewEntry, type TraySnapshotCapture, type WindowConfig, WindowSelectionSurface, type WindowSelectionSurfaceProps, WindowTraySync, WindowsProvider, type WindowsProviderProps, getTrayPreviewEntry, registerTrayPreview, resetTrayPreviews, useWindowConfig };
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType, ReactNode } from 'react';
3
+ export { PopoutContext, PopoutContextValue, PopoutMode, useOverlaySurfaceRenderAck, usePopout, usePopoutContainer } from '@ai-matrx/summon/root';
3
4
 
4
5
  interface WindowSelectionSurfaceProps {
5
6
  readonly children: ReactNode;
@@ -33,50 +34,6 @@ declare function WindowSelectionSurface({ children }: WindowSelectionSurfaceProp
33
34
 
34
35
  declare function WindowTraySync(): null;
35
36
 
36
- type PopoutMode = "pip" | "popup";
37
- interface PopoutContextValue {
38
- /**
39
- * The popout `Document`. `null` when this subtree is rendered in the
40
- * parent (the default).
41
- */
42
- popoutDocument: Document | null;
43
- /**
44
- * The popout `Window`. `null` when this subtree is rendered in the parent.
45
- * Use for `addEventListener`, `matchMedia`, `innerWidth`, etc., when the
46
- * popout-scoped answer is what you need.
47
- */
48
- popoutWindow: Window | null;
49
- /**
50
- * Element to use as the Radix `Portal.container` prop so floating UI
51
- * (tooltips, popovers, dropdowns) renders inside the popout instead of
52
- * the parent `document.body`. Returns `null` outside a popout —
53
- * `usePopoutContainer` coerces this to `undefined` so Radix falls back
54
- * to its default container automatically.
55
- */
56
- popoutContainer: HTMLElement | null;
57
- /**
58
- * Which popout mechanism is in use. `null` outside a popout.
59
- * Currently only used for diagnostics; downstream components may branch
60
- * on it later (e.g. show different chrome for `popup` vs `pip`).
61
- */
62
- mode: PopoutMode | null;
63
- }
64
- declare const PopoutContext: react.Context<PopoutContextValue>;
65
- /** Read the current popout context. Always defined — never throws. */
66
- declare function usePopout(): PopoutContextValue;
67
-
68
- declare function usePopoutContainer(): HTMLElement | undefined;
69
-
70
- /**
71
- * Acknowledge a visible presentation that intentionally replaces WindowPanel.
72
- *
73
- * Registered singleton window overlays normally prove they are on screen by mounting a
74
- * WindowPanel and registering geometry. Mobile drawers, sheets, and full-screen
75
- * viewers have no window-manager geometry, so their composition root must use
76
- * this hook while that alternate surface is active.
77
- */
78
- declare function useOverlaySurfaceRenderAck(overlayId: string, active: boolean): void;
79
-
80
37
  /**
81
38
  * Tray previews — overlay id → a semantic minimized preview and/or a custom snapshot capture.
82
39
  *
@@ -114,4 +71,4 @@ declare function getTrayPreviewEntry(overlayId: string): TrayPreviewEntry;
114
71
  /** Test-only. */
115
72
  declare function resetTrayPreviews(): void;
116
73
 
117
- export { DEFAULT_ESCAPE_YIELD_SELECTOR, DEFAULT_MOBILE_TOP_OFFSET, DEFAULT_WINDOW_CONFIG, PopoutContext, type PopoutContextValue, type PopoutMode, type TrayPreviewContext, type TrayPreviewEntry, type TraySnapshotCapture, type WindowConfig, WindowSelectionSurface, type WindowSelectionSurfaceProps, WindowTraySync, WindowsProvider, type WindowsProviderProps, getTrayPreviewEntry, registerTrayPreview, resetTrayPreviews, useOverlaySurfaceRenderAck, usePopout, usePopoutContainer, useWindowConfig };
74
+ export { DEFAULT_ESCAPE_YIELD_SELECTOR, DEFAULT_MOBILE_TOP_OFFSET, DEFAULT_WINDOW_CONFIG, type TrayPreviewContext, type TrayPreviewEntry, type TraySnapshotCapture, type WindowConfig, WindowSelectionSurface, type WindowSelectionSurfaceProps, WindowTraySync, WindowsProvider, type WindowsProviderProps, getTrayPreviewEntry, registerTrayPreview, resetTrayPreviews, useWindowConfig };
@@ -90,34 +90,8 @@ function WindowTraySync() {
90
90
  return null;
91
91
  }
92
92
 
93
- // src/window/popout/PopoutContext.tsx
94
- import { createContext as createContext2, useContext as useContext2 } from "react";
95
- var DEFAULT_VALUE = {
96
- popoutDocument: null,
97
- popoutWindow: null,
98
- popoutContainer: null,
99
- mode: null
100
- };
101
- var PopoutContext = createContext2(DEFAULT_VALUE);
102
- function usePopout() {
103
- return useContext2(PopoutContext);
104
- }
105
-
106
- // src/window/popout/usePopoutContainer.ts
107
- function usePopoutContainer() {
108
- return usePopout().popoutContainer ?? void 0;
109
- }
110
-
111
- // src/window/useOverlaySurfaceRenderAck.ts
112
- import { useEffect as useEffect2 } from "react";
113
- import { ackOverlaySurfaceRender, clearOverlaySurfaceRender } from "@ai-matrx/summon";
114
- function useOverlaySurfaceRenderAck(overlayId, active) {
115
- useEffect2(() => {
116
- if (!active) return void 0;
117
- ackOverlaySurfaceRender(overlayId);
118
- return () => clearOverlaySurfaceRender(overlayId);
119
- }, [active, overlayId]);
120
- }
93
+ // src/window-host.ts
94
+ import { usePopout, PopoutContext, usePopoutContainer, useOverlaySurfaceRenderAck } from "@ai-matrx/summon/root";
121
95
 
122
96
  // src/window/tray/trayPreviewRegistry.ts
123
97
  import { globalSlot } from "@ai-matrx/kit/global-slot";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/window/config.tsx","../src/window/WindowTraySync.tsx","../src/window/popout/PopoutContext.tsx","../src/window/popout/usePopoutContainer.ts","../src/window/useOverlaySurfaceRenderAck.ts","../src/window/tray/trayPreviewRegistry.ts"],"sourcesContent":["\"use client\";\n\n/**\n * WindowsProvider — the window section's host options, read by every WindowPanel through context.\n * Everything has a working default, so no provider is needed at all; a host passes only what it\n * has (AI Matrx: its right-click selection menu and its header offset).\n */\nimport { createContext, useContext, useMemo, type ComponentType, type ReactNode } from \"react\";\nimport { getSummonHostConfig } from \"@ai-matrx/summon/root\";\n\nexport interface WindowSelectionSurfaceProps {\n readonly children: ReactNode;\n}\n\nexport interface WindowConfig {\n /**\n * Visible elements matching this selector own the Escape key: a window with `closeOnEscape`\n * yields to them (menus, popovers, listboxes and open dialogs always do). Default\n * `[data-overlay-escape-yield]` — mark any host surface that must take Escape first.\n */\n readonly escapeYieldSelector: string;\n /** Top offset of a phone fullscreen window (below the host's header). Default `var(--header-height, 0px)`. */\n readonly mobileTopOffset: string;\n /**\n * Wraps every window body (desktop, popped out, and all three phone presentations). Default:\n * passthrough. AI Matrx injects its selection toolbar + right-click menu here.\n */\n readonly selectionSurface: ComponentType<WindowSelectionSurfaceProps>;\n}\n\nfunction PassthroughSurface({ children }: WindowSelectionSurfaceProps) {\n return <>{children}</>;\n}\n\nexport const DEFAULT_ESCAPE_YIELD_SELECTOR = \"[data-overlay-escape-yield]\";\nexport const DEFAULT_MOBILE_TOP_OFFSET = \"var(--header-height, 0px)\";\n\nexport const DEFAULT_WINDOW_CONFIG: WindowConfig = {\n escapeYieldSelector: DEFAULT_ESCAPE_YIELD_SELECTOR,\n mobileTopOffset: DEFAULT_MOBILE_TOP_OFFSET,\n selectionSurface: PassthroughSurface,\n};\n\nconst WindowConfigContext = createContext<WindowConfig | null>(null);\nWindowConfigContext.displayName = \"WindowConfigContext\";\n\nexport interface WindowsProviderProps extends Partial<WindowConfig> {\n readonly children?: ReactNode;\n}\n\nexport function WindowsProvider({ escapeYieldSelector, mobileTopOffset, selectionSurface, children }: WindowsProviderProps) {\n const value = useMemo<WindowConfig>(\n () => ({\n escapeYieldSelector: escapeYieldSelector ?? DEFAULT_WINDOW_CONFIG.escapeYieldSelector,\n mobileTopOffset: mobileTopOffset ?? DEFAULT_WINDOW_CONFIG.mobileTopOffset,\n selectionSurface: selectionSurface ?? DEFAULT_WINDOW_CONFIG.selectionSurface,\n }),\n [escapeYieldSelector, mobileTopOffset, selectionSurface],\n );\n return <WindowConfigContext.Provider value={value}>{children}</WindowConfigContext.Provider>;\n}\n\n/** The nearest WindowsProvider's options, else the ones configured at `<SummonRoot windows>`, else defaults. */\nexport function useWindowConfig(): WindowConfig {\n const fromContext = useContext(WindowConfigContext);\n if (fromContext) return fromContext;\n const host = getSummonHostConfig().windows;\n if (!host) return DEFAULT_WINDOW_CONFIG;\n return {\n escapeYieldSelector: host.escapeYieldSelector ?? DEFAULT_WINDOW_CONFIG.escapeYieldSelector,\n mobileTopOffset: host.mobileTopOffset ?? DEFAULT_WINDOW_CONFIG.mobileTopOffset,\n selectionSurface: host.selectionSurface ?? DEFAULT_WINDOW_CONFIG.selectionSurface,\n };\n}\n\n/** The configured selection surface, as a component. */\nexport function WindowSelectionSurface({ children }: WindowSelectionSurfaceProps) {\n const Surface = useWindowConfig().selectionSurface;\n return <Surface>{children}</Surface>;\n}\n","\"use client\";\n\n/**\n * WindowTraySync\n *\n * Mounts ONCE in the root layout. Does nothing — no listener, no dispatch — until a window\n * exists (one boolean selector), then attaches a single debounced resize listener that:\n * 1. Recomputes the positions of all minimized windows (tray slots can change\n * when chips-per-row shifts on viewport resize).\n * 2. Clamps every docked windowed window into the new viewport so windows\n * positioned for a larger screen stay reachable after a downsize.\n *\n * Zero re-renders — fires and forgets.\n *\n * Debounce: 500ms — a continuous window-drag generates exactly one dispatch\n * when the user stops moving. Short bursts (e.g. snapping) are ignored.\n *\n * Usage (in both authenticated and SSR root layouts):\n * <WindowTraySync />\n */\n\nimport { useEffect } from \"react\";\nimport { useSummonDispatch, useSummonSelector } from \"@ai-matrx/summon/react\";\nimport {\n recomputeTrayPositions,\n clampAllWindowRects,\n} from \"@ai-matrx/summon\";\nimport { safeViewportDims, type WindowManagerState } from \"@ai-matrx/summon\";\n\nconst DEBOUNCE_MS = 500;\n\n/** Any window registered or waiting to restore: until then there is no geometry to keep in view. */\nexport function selectAnyWindowGeometry(state: { windowManager?: WindowManagerState }): boolean {\n const wm = state.windowManager;\n if (!wm) return false;\n for (const _ in wm.windows) return true;\n for (const _ in wm.pendingRestores) return true;\n return false;\n}\n\nexport function WindowTraySync() {\n const dispatch = useSummonDispatch();\n const active = useSummonSelector(selectAnyWindowGeometry);\n\n useEffect(() => {\n if (!active) return;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const syncWindowGeometry = () => {\n // A resize that measures 0×0 is a hidden/undisplayed tab or a\n // display:none ancestor, never a real screen. Bail WITHOUT dispatching:\n // reacting to it would rewrite every window rect and every tray slot\n // against invented dimensions, and the next real measurement would\n // then have to undo it. No measurement, no write.\n const { vw, vh, degenerate } = safeViewportDims();\n if (degenerate) return;\n dispatch(\n recomputeTrayPositions({ viewportWidth: vw, viewportHeight: vh }),\n );\n dispatch(clampAllWindowRects({ viewportWidth: vw, viewportHeight: vh }));\n };\n\n // This island may load after the viewport already changed. Synchronising\n // once on mount closes that event-listener gap for persisted windows.\n syncWindowGeometry();\n\n const handleResize = () => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n syncWindowGeometry();\n }, DEBOUNCE_MS);\n };\n\n window.addEventListener(\"resize\", handleResize, { passive: true });\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n if (timer !== null) clearTimeout(timer);\n };\n }, [dispatch, active]);\n\n return null;\n}\n\nexport default WindowTraySync;\n","\"use client\";\n\n/**\n * PopoutContext — exposes the popout `Document` / `Window` / portal target\n * to descendants of a popped-out subtree.\n *\n * The default value (`popoutDocument: null`, etc.) means \"I'm rendered in\n * the parent document.\" Descendants that need to behave differently inside\n * a popout (e.g. Radix portals retargeting their content) call\n * `usePopout()` and branch on the values.\n *\n * **Architectural note:** because `createPortal` preserves the parent\n * React tree, every ancestor `Provider` cascades into the popout — Redux,\n * Theme, Tooltip, Router, all work without re-wrapping. This context is\n * for the *opposite* direction: telling popped-out content \"you're inside\n * a popout window, here's the document/window/container you should use\n * instead of the global `document`/`window`.\"\n */\nimport { createContext, useContext } from \"react\";\n\nexport type PopoutMode = \"pip\" | \"popup\";\n\nexport interface PopoutContextValue {\n /**\n * The popout `Document`. `null` when this subtree is rendered in the\n * parent (the default).\n */\n popoutDocument: Document | null;\n /**\n * The popout `Window`. `null` when this subtree is rendered in the parent.\n * Use for `addEventListener`, `matchMedia`, `innerWidth`, etc., when the\n * popout-scoped answer is what you need.\n */\n popoutWindow: Window | null;\n /**\n * Element to use as the Radix `Portal.container` prop so floating UI\n * (tooltips, popovers, dropdowns) renders inside the popout instead of\n * the parent `document.body`. Returns `null` outside a popout —\n * `usePopoutContainer` coerces this to `undefined` so Radix falls back\n * to its default container automatically.\n */\n popoutContainer: HTMLElement | null;\n /**\n * Which popout mechanism is in use. `null` outside a popout.\n * Currently only used for diagnostics; downstream components may branch\n * on it later (e.g. show different chrome for `popup` vs `pip`).\n */\n mode: PopoutMode | null;\n}\n\nconst DEFAULT_VALUE: PopoutContextValue = {\n popoutDocument: null,\n popoutWindow: null,\n popoutContainer: null,\n mode: null,\n};\n\nexport const PopoutContext =\n createContext<PopoutContextValue>(DEFAULT_VALUE);\n\n/** Read the current popout context. Always defined — never throws. */\nexport function usePopout(): PopoutContextValue {\n return useContext(PopoutContext);\n}\n","\"use client\";\n\n/**\n * usePopoutContainer — return the element Radix portals (Tooltip, Popover,\n * Dropdown, Dialog, Select, AlertDialog, HoverCard) should mount into.\n *\n * **The problem this solves:** by default every Radix `Portal` mounts to\n * `document.body`. When a tooltip-trigger lives inside a popped-out window\n * but the popover content portals to the *parent* document, the tooltip\n * appears in the wrong window — typically invisible to the user.\n *\n * **The fix:** wrappers around Radix primitives call this hook and pass\n * the result as `<Primitive.Portal container={...} />`. Inside a popout,\n * the result is the popout's `<body>`. Outside a popout, the result is\n * `undefined`, which Radix interprets as \"use the default\" — meaning no\n * behavior change for any of the thousands of existing usages.\n *\n * **Why `undefined` instead of `null`?** Radix's `container` prop accepts\n * `HTMLElement | null | undefined`. Passing `null` actively tries to\n * portal into a `null` target and crashes. Passing `undefined` falls\n * through to the default (`document.body`). The `?? undefined` coerces\n * the `null` we get from `PopoutContext` outside a popout into the\n * Radix-friendly default-fallthrough value.\n */\nimport { usePopout } from \"./PopoutContext\";\n\nexport function usePopoutContainer(): HTMLElement | undefined {\n return usePopout().popoutContainer ?? undefined;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { ackOverlaySurfaceRender, clearOverlaySurfaceRender } from \"@ai-matrx/summon\";\n\n/**\n * Acknowledge a visible presentation that intentionally replaces WindowPanel.\n *\n * Registered singleton window overlays normally prove they are on screen by mounting a\n * WindowPanel and registering geometry. Mobile drawers, sheets, and full-screen\n * viewers have no window-manager geometry, so their composition root must use\n * this hook while that alternate surface is active.\n */\nexport function useOverlaySurfaceRenderAck(\n overlayId: string,\n active: boolean,\n): void {\n useEffect(() => {\n if (!active) return undefined;\n ackOverlaySurfaceRender(overlayId);\n return () => clearOverlaySurfaceRender(overlayId);\n }, [active, overlayId]);\n}\n","/**\n * Tray previews — overlay id → a semantic minimized preview and/or a custom snapshot capture.\n *\n * The host registers its previews once (AI Matrx keeps its own preview components in the app):\n *\n * registerTrayPreview(\"notesWindow\", NotesTrayPreview);\n * registerTrayPreview(\"canvasWindow\", { captureTraySnapshot: captureCanvas });\n *\n * A window with a preview component skips snapshot capture entirely (cheaper, higher fidelity).\n * A window with neither gets the default capture (`defaultTraySnapshotCapture`, html-to-image when\n * the host installed it). State lives in a global slot (kit globalSlot, D9).\n */\nimport type { ComponentType } from \"react\";\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\n\nexport interface TrayPreviewContext {\n /** The window's allowlisted semantic data (open data merged with what `onCollectData` returned). */\n readonly data: Readonly<Record<string, unknown>>;\n /** Stable overlay id (the registry key). */\n readonly overlayId: string;\n /** Instance id — `\"default\"` for singleton windows. */\n readonly instanceId: string;\n /** The display title. */\n readonly title: string;\n}\n\nexport type TraySnapshotCapture = (bodyEl: HTMLElement) => Promise<Blob | null>;\n\nexport interface TrayPreviewEntry {\n readonly Preview?: ComponentType<TrayPreviewContext>;\n readonly captureTraySnapshot?: TraySnapshotCapture;\n}\n\nconst EMPTY: TrayPreviewEntry = {};\n\nconst registry = (): Map<string, TrayPreviewEntry> =>\n globalSlot<Map<string, TrayPreviewEntry>>(\"ai-matrx.summon.window.trayPreviews\", () => new Map());\n\n/**\n * Register the minimized preview for an overlay (or any window id). Pass a component, or an entry\n * with `Preview` and/or `captureTraySnapshot`. Re-registering replaces. Returns an unregister.\n */\nexport function registerTrayPreview(\n overlayId: string,\n preview: ComponentType<TrayPreviewContext> | TrayPreviewEntry,\n): () => void {\n const entry: TrayPreviewEntry = typeof preview === \"function\" ? { Preview: preview } : preview;\n registry().set(overlayId, entry);\n return () => {\n if (registry().get(overlayId) === entry) registry().delete(overlayId);\n };\n}\n\nexport function getTrayPreviewEntry(overlayId: string): TrayPreviewEntry {\n return registry().get(overlayId) ?? EMPTY;\n}\n\n/** Test-only. */\nexport function resetTrayPreviews(): void {\n registry().clear();\n}\n"],"mappings":"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","names":["createContext","useContext","useEffect"]}
1
+ {"version":3,"sources":["../src/window/config.tsx","../src/window/WindowTraySync.tsx","../src/window-host.ts","../src/window/tray/trayPreviewRegistry.ts"],"sourcesContent":["\"use client\";\n\n/**\n * WindowsProvider — the window section's host options, read by every WindowPanel through context.\n * Everything has a working default, so no provider is needed at all; a host passes only what it\n * has (AI Matrx: its right-click selection menu and its header offset).\n */\nimport { createContext, useContext, useMemo, type ComponentType, type ReactNode } from \"react\";\nimport { getSummonHostConfig } from \"@ai-matrx/summon/root\";\n\nexport interface WindowSelectionSurfaceProps {\n readonly children: ReactNode;\n}\n\nexport interface WindowConfig {\n /**\n * Visible elements matching this selector own the Escape key: a window with `closeOnEscape`\n * yields to them (menus, popovers, listboxes and open dialogs always do). Default\n * `[data-overlay-escape-yield]` — mark any host surface that must take Escape first.\n */\n readonly escapeYieldSelector: string;\n /** Top offset of a phone fullscreen window (below the host's header). Default `var(--header-height, 0px)`. */\n readonly mobileTopOffset: string;\n /**\n * Wraps every window body (desktop, popped out, and all three phone presentations). Default:\n * passthrough. AI Matrx injects its selection toolbar + right-click menu here.\n */\n readonly selectionSurface: ComponentType<WindowSelectionSurfaceProps>;\n}\n\nfunction PassthroughSurface({ children }: WindowSelectionSurfaceProps) {\n return <>{children}</>;\n}\n\nexport const DEFAULT_ESCAPE_YIELD_SELECTOR = \"[data-overlay-escape-yield]\";\nexport const DEFAULT_MOBILE_TOP_OFFSET = \"var(--header-height, 0px)\";\n\nexport const DEFAULT_WINDOW_CONFIG: WindowConfig = {\n escapeYieldSelector: DEFAULT_ESCAPE_YIELD_SELECTOR,\n mobileTopOffset: DEFAULT_MOBILE_TOP_OFFSET,\n selectionSurface: PassthroughSurface,\n};\n\nconst WindowConfigContext = createContext<WindowConfig | null>(null);\nWindowConfigContext.displayName = \"WindowConfigContext\";\n\nexport interface WindowsProviderProps extends Partial<WindowConfig> {\n readonly children?: ReactNode;\n}\n\nexport function WindowsProvider({ escapeYieldSelector, mobileTopOffset, selectionSurface, children }: WindowsProviderProps) {\n const value = useMemo<WindowConfig>(\n () => ({\n escapeYieldSelector: escapeYieldSelector ?? DEFAULT_WINDOW_CONFIG.escapeYieldSelector,\n mobileTopOffset: mobileTopOffset ?? DEFAULT_WINDOW_CONFIG.mobileTopOffset,\n selectionSurface: selectionSurface ?? DEFAULT_WINDOW_CONFIG.selectionSurface,\n }),\n [escapeYieldSelector, mobileTopOffset, selectionSurface],\n );\n return <WindowConfigContext.Provider value={value}>{children}</WindowConfigContext.Provider>;\n}\n\n/** The nearest WindowsProvider's options, else the ones configured at `<SummonRoot windows>`, else defaults. */\nexport function useWindowConfig(): WindowConfig {\n const fromContext = useContext(WindowConfigContext);\n if (fromContext) return fromContext;\n const host = getSummonHostConfig().windows;\n if (!host) return DEFAULT_WINDOW_CONFIG;\n return {\n escapeYieldSelector: host.escapeYieldSelector ?? DEFAULT_WINDOW_CONFIG.escapeYieldSelector,\n mobileTopOffset: host.mobileTopOffset ?? DEFAULT_WINDOW_CONFIG.mobileTopOffset,\n selectionSurface: host.selectionSurface ?? DEFAULT_WINDOW_CONFIG.selectionSurface,\n };\n}\n\n/** The configured selection surface, as a component. */\nexport function WindowSelectionSurface({ children }: WindowSelectionSurfaceProps) {\n const Surface = useWindowConfig().selectionSurface;\n return <Surface>{children}</Surface>;\n}\n","\"use client\";\n\n/**\n * WindowTraySync\n *\n * Mounts ONCE in the root layout. Does nothing — no listener, no dispatch — until a window\n * exists (one boolean selector), then attaches a single debounced resize listener that:\n * 1. Recomputes the positions of all minimized windows (tray slots can change\n * when chips-per-row shifts on viewport resize).\n * 2. Clamps every docked windowed window into the new viewport so windows\n * positioned for a larger screen stay reachable after a downsize.\n *\n * Zero re-renders — fires and forgets.\n *\n * Debounce: 500ms — a continuous window-drag generates exactly one dispatch\n * when the user stops moving. Short bursts (e.g. snapping) are ignored.\n *\n * Usage (in both authenticated and SSR root layouts):\n * <WindowTraySync />\n */\n\nimport { useEffect } from \"react\";\nimport { useSummonDispatch, useSummonSelector } from \"@ai-matrx/summon/react\";\nimport {\n recomputeTrayPositions,\n clampAllWindowRects,\n} from \"@ai-matrx/summon\";\nimport { safeViewportDims, type WindowManagerState } from \"@ai-matrx/summon\";\n\nconst DEBOUNCE_MS = 500;\n\n/** Any window registered or waiting to restore: until then there is no geometry to keep in view. */\nexport function selectAnyWindowGeometry(state: { windowManager?: WindowManagerState }): boolean {\n const wm = state.windowManager;\n if (!wm) return false;\n for (const _ in wm.windows) return true;\n for (const _ in wm.pendingRestores) return true;\n return false;\n}\n\nexport function WindowTraySync() {\n const dispatch = useSummonDispatch();\n const active = useSummonSelector(selectAnyWindowGeometry);\n\n useEffect(() => {\n if (!active) return;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const syncWindowGeometry = () => {\n // A resize that measures 0×0 is a hidden/undisplayed tab or a\n // display:none ancestor, never a real screen. Bail WITHOUT dispatching:\n // reacting to it would rewrite every window rect and every tray slot\n // against invented dimensions, and the next real measurement would\n // then have to undo it. No measurement, no write.\n const { vw, vh, degenerate } = safeViewportDims();\n if (degenerate) return;\n dispatch(\n recomputeTrayPositions({ viewportWidth: vw, viewportHeight: vh }),\n );\n dispatch(clampAllWindowRects({ viewportWidth: vw, viewportHeight: vh }));\n };\n\n // This island may load after the viewport already changed. Synchronising\n // once on mount closes that event-listener gap for persisted windows.\n syncWindowGeometry();\n\n const handleResize = () => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n syncWindowGeometry();\n }, DEBOUNCE_MS);\n };\n\n window.addEventListener(\"resize\", handleResize, { passive: true });\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n if (timer !== null) clearTimeout(timer);\n };\n }, [dispatch, active]);\n\n return null;\n}\n\nexport default WindowTraySync;\n","// @ai-matrx/summon/window-host — the light half of the window section, safe to mount at the app\n// root: window configuration (WindowsProvider), the viewport/tray sync, and tray-preview\n// registration. Importing it never loads WindowPanel; the window section reads this section's\n// context through the package name, so both share one context.\nexport {\n WindowsProvider,\n useWindowConfig,\n WindowSelectionSurface,\n DEFAULT_WINDOW_CONFIG,\n DEFAULT_ESCAPE_YIELD_SELECTOR,\n DEFAULT_MOBILE_TOP_OFFSET,\n type WindowConfig,\n type WindowsProviderProps,\n type WindowSelectionSurfaceProps,\n} from \"./window/config\";\nexport { WindowTraySync } from \"./window/WindowTraySync\";\n// Hooks a render module calls inside a window, light enough for the boot graph. The popout context\n// lives HERE (one identity): the window section imports it through the package name.\nexport { usePopout, PopoutContext, usePopoutContainer, useOverlaySurfaceRenderAck, type PopoutContextValue, type PopoutMode } from \"@ai-matrx/summon/root\";\nexport {\n registerTrayPreview,\n getTrayPreviewEntry,\n resetTrayPreviews,\n type TrayPreviewContext,\n type TrayPreviewEntry,\n type TraySnapshotCapture,\n} from \"./window/tray/trayPreviewRegistry\";\n","/**\n * Tray previews — overlay id → a semantic minimized preview and/or a custom snapshot capture.\n *\n * The host registers its previews once (AI Matrx keeps its own preview components in the app):\n *\n * registerTrayPreview(\"notesWindow\", NotesTrayPreview);\n * registerTrayPreview(\"canvasWindow\", { captureTraySnapshot: captureCanvas });\n *\n * A window with a preview component skips snapshot capture entirely (cheaper, higher fidelity).\n * A window with neither gets the default capture (`defaultTraySnapshotCapture`, html-to-image when\n * the host installed it). State lives in a global slot (kit globalSlot, D9).\n */\nimport type { ComponentType } from \"react\";\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\n\nexport interface TrayPreviewContext {\n /** The window's allowlisted semantic data (open data merged with what `onCollectData` returned). */\n readonly data: Readonly<Record<string, unknown>>;\n /** Stable overlay id (the registry key). */\n readonly overlayId: string;\n /** Instance id — `\"default\"` for singleton windows. */\n readonly instanceId: string;\n /** The display title. */\n readonly title: string;\n}\n\nexport type TraySnapshotCapture = (bodyEl: HTMLElement) => Promise<Blob | null>;\n\nexport interface TrayPreviewEntry {\n readonly Preview?: ComponentType<TrayPreviewContext>;\n readonly captureTraySnapshot?: TraySnapshotCapture;\n}\n\nconst EMPTY: TrayPreviewEntry = {};\n\nconst registry = (): Map<string, TrayPreviewEntry> =>\n globalSlot<Map<string, TrayPreviewEntry>>(\"ai-matrx.summon.window.trayPreviews\", () => new Map());\n\n/**\n * Register the minimized preview for an overlay (or any window id). Pass a component, or an entry\n * with `Preview` and/or `captureTraySnapshot`. Re-registering replaces. Returns an unregister.\n */\nexport function registerTrayPreview(\n overlayId: string,\n preview: ComponentType<TrayPreviewContext> | TrayPreviewEntry,\n): () => void {\n const entry: TrayPreviewEntry = typeof preview === \"function\" ? { Preview: preview } : preview;\n registry().set(overlayId, entry);\n return () => {\n if (registry().get(overlayId) === entry) registry().delete(overlayId);\n };\n}\n\nexport function getTrayPreviewEntry(overlayId: string): TrayPreviewEntry {\n return registry().get(overlayId) ?? EMPTY;\n}\n\n/** Test-only. */\nexport function resetTrayPreviews(): void {\n registry().clear();\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/summon",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Render any React component by dispatch and never mount it until it is needed: single or multi instance, handles for callbacks and refs beside Redux, keep-alive and saved component state, address-bar links bound to data, optional persistence, and a full windowing desktop in the browser.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -184,9 +184,9 @@
184
184
  },
185
185
  "dependencies": {
186
186
  "@ai-matrx/kit": "latest",
187
- "@ai-matrx/design-system": "latest",
188
187
  "@ai-matrx/icons": "latest",
189
- "@ai-matrx/data": "latest"
188
+ "@ai-matrx/data": "latest",
189
+ "@ai-matrx/design-system": "latest"
190
190
  },
191
191
  "peerDependencies": {
192
192
  "@reduxjs/toolkit": ">=2.0.0",
@@ -268,5 +268,5 @@
268
268
  "check:package": "pnpm build && node ../scripts/check-exports-agreement.mjs && publint && pnpm verify:tarball && node ../scripts/check-tarball-relative-imports.mjs --self-test && node ../scripts/check-tarball-relative-imports.mjs --pack . && node ../scripts/check-package-rules.mjs --self-test && node ../scripts/check-package-rules.mjs .",
269
269
  "verify:tarball": "node scripts/verify-tarball.mjs"
270
270
  },
271
- "gitHead": "24a38ba9270ef406d289f06c8172aaa2d9b6decb"
271
+ "gitHead": "eab7a68c6ebdea8f05fe374380fca7f2ae70c899"
272
272
  }