@ai-matrx/summon 0.1.17 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dock.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dock/Dock.tsx","../src/dock/useDock.ts","../src/dock/geometry.ts","../src/dock/store.ts"],"sourcesContent":["/**\n * `<Dock>` — the component form of `useDock()`: a fixed, chrome-less element that\n * moves. Drag it by a `<DockHandle>` inside it, or by the whole dock with\n * `handle=\"self\"`. `useDockContext()` reaches the dock's API from anywhere inside\n * (a \"Move back to the corner\" menu item calls `reset()`).\n */\n\nimport { createContext, useContext, type CSSProperties, type HTMLAttributes, type ReactNode } from \"react\";\nimport { useDock, type DockApi, type UseDockOptions } from \"./useDock\";\n\nconst DockContext = createContext<DockApi | null>(null);\n\n/** The enclosing dock's API; throws outside a `<Dock>`. */\nexport function useDockContext(): DockApi {\n const dock = useContext(DockContext);\n if (!dock) throw new Error(\"[summon] useDockContext() must be used inside <Dock>\");\n return dock;\n}\n\nexport interface DockProps extends UseDockOptions, Omit<HTMLAttributes<HTMLDivElement>, \"id\" | \"children\" | \"draggable\"> {\n /** \"self\": the whole dock is the drag handle. Default: only `<DockHandle>`s drag. */\n handle?: \"self\" | \"inner\";\n children?: ReactNode | ((dock: DockApi) => ReactNode);\n}\n\nconst OPTION_KEYS = [\n \"id\", \"corner\", \"defaultPosition\", \"anchor\", \"position\", \"onPositionChange\", \"persist\", \"store\", \"draggable\",\n \"touch\", \"threshold\", \"clamp\", \"avoid\", \"snapToCorner\", \"zIndex\", \"raiseOnPress\", \"ignore\",\n] as const satisfies readonly (keyof UseDockOptions)[];\n\nexport function Dock(props: DockProps): ReactNode {\n const options = {} as UseDockOptions;\n const rest: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(props)) {\n if ((OPTION_KEYS as readonly string[]).includes(key)) (options as unknown as Record<string, unknown>)[key] = value;\n else if (key !== \"handle\" && key !== \"children\" && key !== \"style\") rest[key] = value;\n }\n const dock = useDock(options);\n const self = props.handle === \"self\";\n const style: CSSProperties = { ...props.style, ...dock.style, ...(self ? dock.handleProps.style : {}) };\n return (\n <DockContext.Provider value={dock}>\n <div\n {...(rest as HTMLAttributes<HTMLDivElement>)}\n ref={dock.ref}\n data-dock={props.id}\n data-dragging={dock.dragging ? \"\" : undefined}\n style={style}\n {...(self ? { onPointerDown: dock.handleProps.onPointerDown, \"data-dock-handle\": \"\" } : {})}\n >\n {typeof props.children === \"function\" ? props.children(dock) : props.children}\n </div>\n </DockContext.Provider>\n );\n}\n\nexport interface DockHandleProps extends HTMLAttributes<HTMLDivElement> {\n children?: ReactNode;\n}\n\n/** The part of a `<Dock>` you drag it by. Buttons and fields inside stay controls. */\nexport function DockHandle({ style, onPointerDown, ...rest }: DockHandleProps): ReactNode {\n const dock = useDockContext();\n return (\n <div\n {...rest}\n {...dock.handleProps}\n style={{ ...style, ...dock.handleProps.style }}\n onPointerDown={(event) => {\n onPointerDown?.(event);\n dock.handleProps.onPointerDown(event);\n }}\n />\n );\n}\n","/**\n * `useDock()` — the one primitive for floating chrome-less cards, pills and docks.\n *\n * THE RULE (paid for in the Workflow Studio's FloatingChatPanel and chat's\n * ChatPanel): a pointer move paints the CSS `translate` property inside ONE\n * requestAnimationFrame and React state is committed EXACTLY ONCE, on release.\n * Writing state per pointer event re-renders whatever streams inside the dock\n * sixty times a second. `translate` composes with the host's own `transform`\n * and with any `left/right/top/bottom` it computes, so a host that builds its\n * own placement (CSS variables, lifts) still drags without re-rendering.\n *\n * What else it carries:\n * - a drag is never a click: below `threshold` px the gesture stays a click;\n * past it the trailing click is swallowed once;\n * - the position is an offset from a corner, clamped to the viewport on every\n * render (a position saved on a wide monitor never strands the dock), and\n * can snap to the nearest corner on release;\n * - it remembers its place per dock id (the store chain in ./store) or reports\n * it to a controlled host (`position` + `onPositionChange`, e.g. per-user sync);\n * - it joins the window manager's z-order lane on press (raised above every\n * window, below modals) when a SummonProvider is present;\n * - THE CURSOR LAW: the drag holds `grabbing` with design-system `holdDragCursor`;\n * - zero cost until mounted: no listener exists outside an active press.\n */\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type PointerEvent as ReactPointerEvent,\n} from \"react\";\nimport { holdDragCursor } from \"@ai-matrx/design-system\";\nimport { raiseTransientWindow, releaseTransientWindow } from \"@ai-matrx/summon\";\nimport { useOptionalSummonStore } from \"@ai-matrx/summon/react\";\nimport {\n avoidExclusions,\n clampOffset,\n DEFAULT_DOCK_MARGIN,\n DEFAULT_DRAG_THRESHOLD,\n fromScreen,\n nearestCorner,\n samePosition,\n toScreen,\n type DockClamp,\n type DockCorner,\n type DockOffset,\n type DockPosition,\n type DockSize,\n type ScreenPoint,\n type ScreenRect,\n} from \"./geometry\";\nimport { memoryOnlyDockStore, useDockStore, type DockPositionStore } from \"./store\";\n\n/** CSS placement used while the dock has no position (never moved, no `defaultPosition`). */\nexport interface DockAnchor {\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n /** Centre horizontally (`left: 50%` + translateX(-50%)). */\n centerX?: boolean;\n}\n\n/** Controls inside the handle that stay controls — a press there never starts a drag. */\nexport const DEFAULT_DOCK_IGNORE = \"button,a,input,select,textarea,[contenteditable='true'],[data-no-drag]\";\n\nexport interface UseDockOptions {\n /** Identity: the remembered position, the z-order lane entry. Unique per dock. */\n id: string;\n /** The corner positions are measured from. Default \"bottom-right\". */\n corner?: DockCorner;\n /** Resting offset from `corner` before the person moves it. Omit to rest on `anchor`. */\n defaultPosition?: DockOffset | null;\n /** CSS resting place when there is no position at all. */\n anchor?: DockAnchor;\n /** Controlled position (the host owns storage, e.g. a per-user preference). `null` = resting. */\n position?: DockPosition | null;\n /** Told once per drag, on release, and on `reset()` (with `null`). */\n onPositionChange?: (position: DockPosition | null) => void;\n /** Remember the position across reloads (uncontrolled only). Default true. */\n persist?: boolean;\n /** This dock's own store; otherwise the nearest `<DockProvider>`, else localStorage. */\n store?: DockPositionStore;\n /** Default true. Pass false on surfaces that must not move (a phone sheet). */\n draggable?: boolean;\n /** Allow touch drags. Default true; a dock that fights page scroll passes false. */\n touch?: boolean;\n /** Px a pointer must travel before a press becomes a drag. Default 4. */\n threshold?: number;\n clamp?: DockClamp;\n /** Fixed controls the dock must stay off (nearest clear spot wins). */\n avoid?: { selector: string; gap?: number };\n /** On release, re-anchor to the nearest corner. Default false. */\n snapToCorner?: boolean;\n /** z-index before the first press raises it into the window lane. */\n zIndex?: number;\n /** Raise into the window manager's z-order lane on press. Default true. */\n raiseOnPress?: boolean;\n /** Selector of controls inside the handle that never start a drag. `null` = every press may drag. */\n ignore?: string | null;\n}\n\nexport interface DockHandleBindings {\n onPointerDown(event: ReactPointerEvent<HTMLElement>): void;\n style: CSSProperties;\n \"data-dock-handle\": \"\";\n}\n\nexport interface DockApi {\n /** Attach to the element that MOVES. */\n ref: (element: HTMLElement | null) => void;\n element: HTMLElement | null;\n /** Spread onto the moving element: fixed placement (placed or anchored) + z-index. */\n style: CSSProperties;\n /** Spread onto the element you drag BY (or the whole dock). */\n handleProps: DockHandleBindings;\n /** The clamped offset from `corner`; null while resting on `anchor`. */\n offset: DockOffset | null;\n /** The corner `offset` is measured from. */\n corner: DockCorner;\n /** True from the moment a press passes the threshold until release. */\n dragging: boolean;\n /** The person has moved it (a remembered or controlled position exists). */\n moved: boolean;\n /** Current z-index: the lane's once raised, else the `zIndex` option. */\n zIndex: number | undefined;\n /** Forget the position; the dock returns to its resting place. */\n reset(): void;\n}\n\nconst SSR_VIEWPORT: DockSize = { width: 1280, height: 800 };\nconst noopSubscribe = () => () => undefined;\n\ninterface Geometry {\n viewport: DockSize;\n size: DockSize;\n}\n\ninterface Press {\n pointerId: number;\n startX: number;\n startY: number;\n /** The dock's top-left when pressed. */\n origin: ScreenPoint;\n size: DockSize;\n viewport: DockSize;\n active: boolean;\n latest: ScreenPoint;\n release: (() => void) | null;\n cleanup: () => void;\n}\n\nfunction readViewport(): DockSize {\n return typeof window === \"undefined\" ? SSR_VIEWPORT : { width: window.innerWidth, height: window.innerHeight };\n}\n\nfunction measure(element: HTMLElement | null): DockSize {\n return { width: element?.offsetWidth ?? 0, height: element?.offsetHeight ?? 0 };\n}\n\nfunction excludedRects(avoid: UseDockOptions[\"avoid\"], self: HTMLElement | null): ScreenRect[] {\n if (!avoid || typeof document === \"undefined\") return [];\n return [...document.querySelectorAll<HTMLElement>(avoid.selector)]\n .filter((target) => target !== self && !self?.contains(target))\n .map((target) => target.getBoundingClientRect())\n .filter((rect) => rect.width > 0 && rect.height > 0);\n}\n\nexport function useDock(options: UseDockOptions): DockApi {\n const {\n id,\n corner = \"bottom-right\",\n defaultPosition = null,\n anchor,\n position: controlled,\n onPositionChange,\n persist = true,\n draggable = true,\n touch = true,\n threshold = DEFAULT_DRAG_THRESHOLD,\n clamp,\n avoid,\n snapToCorner = false,\n zIndex: baseZ,\n raiseOnPress = true,\n ignore = DEFAULT_DOCK_IGNORE,\n } = options;\n const isControlled = controlled !== undefined;\n const providedStore = useDockStore(options.store);\n const store = persist && !isControlled ? providedStore : memoryOnlyDockStore;\n\n const [element, setElement] = useState<HTMLElement | null>(null);\n const [local, setLocal] = useState<DockPosition | null>(null);\n /** A controlled commit, shown until the host's `position` changes (it may update async). */\n const [pending, setPending] = useState<{ value: DockPosition | null; forProp: DockPosition | null } | null>(null);\n const [geometry, setGeometry] = useState<Geometry>({ viewport: SSR_VIEWPORT, size: { width: 0, height: 0 } });\n const [nudge, setNudge] = useState<ScreenPoint | null>(null);\n const [dragging, setDragging] = useState(false);\n const pressRef = useRef<Press | null>(null);\n const frameRef = useRef<number | null>(null);\n const adoptingRef = useRef(false);\n const restoredRef = useRef<string | null>(null);\n\n // ── The position, resolved ───────────────────────────────────────────────\n const chosen: DockPosition | null = isControlled\n ? pending && samePosition(pending.forProp, controlled) ? pending.value : controlled\n : local;\n const raw: DockPosition | null = chosen ?? defaultPosition;\n const rawCorner = raw?.corner ?? corner;\n const offset = raw ? clampOffset(raw, rawCorner, geometry.size, geometry.viewport, clamp) : null;\n\n // Live refs for the press listeners (written in effects, never during render).\n const live = useRef({ offset, rawCorner, clamp, avoid, threshold, snapToCorner, onPositionChange, isControlled, controlled, store, id });\n useEffect(() => {\n live.current = { offset, rawCorner, clamp, avoid, threshold, snapToCorner, onPositionChange, isControlled, controlled, store, id };\n });\n\n // Restore the remembered position BEFORE paint, once per id, as soon as the element exists.\n useLayoutEffect(() => {\n if (!element || restoredRef.current === id) return;\n restoredRef.current = id;\n if (isControlled) return;\n setLocal(store.read(id));\n }, [element, id, isControlled, store]);\n\n // Track the viewport and the dock's own size (expanded/collapsed content), re-clamping on change.\n useLayoutEffect(() => {\n if (!element) return undefined;\n let frame: number | null = null;\n const read = () => {\n const next: Geometry = { viewport: readViewport(), size: measure(element) };\n setGeometry((current) =>\n current.viewport.width === next.viewport.width &&\n current.viewport.height === next.viewport.height &&\n current.size.width === next.size.width &&\n current.size.height === next.size.height\n ? current\n : next,\n );\n };\n const schedule = () => {\n if (frame !== null) return;\n frame = window.requestAnimationFrame(() => {\n frame = null;\n read();\n });\n };\n read();\n const observer = typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(schedule);\n observer?.observe(element);\n window.addEventListener(\"resize\", schedule);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", schedule);\n if (frame !== null) window.cancelAnimationFrame(frame);\n };\n }, [element]);\n\n // Exclusions: a placed dock is moved to the nearest clear spot at render; a resting (anchored)\n // dock gets a measured nudge, re-read whenever the page under it changes.\n const avoidSelector = avoid?.selector;\n const avoidGap = avoid?.gap;\n useLayoutEffect(() => {\n if (!element || !avoidSelector || offset) {\n setNudge((current) => (current === null ? current : null));\n return undefined;\n }\n let frame: number | null = null;\n let applied: ScreenPoint | null = null;\n const reconcile = () => {\n const rect = element.getBoundingClientRect();\n const size = measure(element);\n const viewport = readViewport();\n const base = { left: rect.left - (applied?.left ?? 0), top: rect.top - (applied?.top ?? 0) };\n const keep = (p: ScreenPoint) => {\n const o = clampOffset(fromScreen(p, \"top-left\", size, viewport), \"top-left\", size, viewport, live.current.clamp);\n return toScreen(o, \"top-left\", size, viewport);\n };\n const clear = avoidExclusions(base, size, excludedRects({ selector: avoidSelector }, element), avoidGap ?? DEFAULT_DOCK_MARGIN, keep);\n const next = { left: Math.round(clear.left - base.left), top: Math.round(clear.top - base.top) };\n applied = next.left === 0 && next.top === 0 ? null : next;\n setNudge((current) =>\n (current === null && applied === null) || (current && applied && current.left === applied.left && current.top === applied.top)\n ? current\n : applied,\n );\n };\n const schedule = () => {\n if (frame !== null) return;\n frame = window.requestAnimationFrame(() => {\n frame = null;\n reconcile();\n });\n };\n reconcile();\n const mutations = typeof MutationObserver === \"undefined\" ? null : new MutationObserver(schedule);\n mutations?.observe(document.body, { childList: true, subtree: true });\n window.addEventListener(\"resize\", schedule);\n window.addEventListener(\"scroll\", schedule, true);\n return () => {\n mutations?.disconnect();\n window.removeEventListener(\"resize\", schedule);\n window.removeEventListener(\"scroll\", schedule, true);\n if (frame !== null) window.cancelAnimationFrame(frame);\n };\n }, [element, avoidSelector, avoidGap, offset === null]); // eslint-disable-line react-hooks/exhaustive-deps\n\n /** A placed dock that would cover an excluded control renders at the nearest clear spot. */\n let placed: ScreenPoint | null = null;\n if (offset) {\n placed = toScreen(offset, rawCorner, geometry.size, geometry.viewport);\n if (avoid && element) {\n const size = geometry.size;\n const viewport = geometry.viewport;\n const keep = (p: ScreenPoint) =>\n toScreen(clampOffset(fromScreen(p, \"top-left\", size, viewport), \"top-left\", size, viewport, clamp), \"top-left\", size, viewport);\n placed = avoidExclusions(placed, size, excludedRects(avoid, element), avoid.gap ?? DEFAULT_DOCK_MARGIN, keep);\n }\n }\n const renderedOffset = placed && offset ? fromScreen(placed, rawCorner, geometry.size, geometry.viewport) : offset;\n\n // The committed position replaces the paint offset in the SAME commit: clear `translate` now.\n useLayoutEffect(() => {\n if (!adoptingRef.current) return;\n adoptingRef.current = false;\n element?.style.removeProperty(\"translate\");\n });\n\n // ── The z-order lane ────────────────────────────────────────────────────\n const summonStore = useOptionalSummonStore();\n const laneZ = useSyncExternalStore(\n summonStore ? summonStore.subscribe : noopSubscribe,\n () => (summonStore ? (summonStore.getState() as { windowManager?: { transientZ?: Record<string, number> } }).windowManager?.transientZ?.[id] : undefined),\n () => undefined,\n );\n useEffect(() => {\n if (!summonStore) return undefined;\n return () => {\n const state = summonStore.getState() as { windowManager?: { transientZ?: Record<string, number> } };\n if (state.windowManager?.transientZ?.[id] !== undefined) summonStore.dispatch(releaseTransientWindow(id));\n };\n }, [summonStore, id]);\n const zIndex = laneZ ?? baseZ;\n\n // ── The drag ────────────────────────────────────────────────────────────\n useEffect(\n () => () => {\n pressRef.current?.cleanup();\n pressRef.current = null;\n },\n [],\n );\n\n const commit = useCallback((next: DockPosition | null) => {\n const current = live.current;\n adoptingRef.current = true;\n if (current.isControlled) setPending({ value: next, forProp: current.controlled ?? null });\n else {\n setLocal(next);\n current.store.write(current.id, next);\n }\n current.onPositionChange?.(next);\n }, []);\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n if (!draggable || event.button !== 0 || !element) return;\n if (!touch && event.pointerType === \"touch\") return;\n const target = event.target as HTMLElement | null;\n if (ignore && target) {\n const hit = target.closest(ignore);\n if (hit && hit !== event.currentTarget && event.currentTarget.contains(hit)) return;\n }\n pressRef.current?.cleanup();\n if (raiseOnPress && summonStore) summonStore.dispatch(raiseTransientWindow(id));\n // A press on bare chrome must not start a text selection; a press on a control keeps its default.\n if (!target?.closest(\"button,a,input,select,textarea,label,[role=button]\")) event.preventDefault();\n\n const rect = element.getBoundingClientRect();\n const viewport = readViewport();\n const size = measure(element);\n const press: Press = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n origin: { left: rect.left, top: rect.top },\n size,\n viewport,\n active: false,\n latest: { left: rect.left, top: rect.top },\n release: null,\n cleanup: () => undefined,\n };\n // A placed dock starts from its committed offset (the host may add lifts on top of it);\n // a resting dock starts from where it is on screen.\n const startOffset: DockOffset =\n live.current.offset ?? fromScreen(press.origin, live.current.rawCorner, size, viewport);\n const startCorner = live.current.rawCorner;\n const startScreen = toScreen(startOffset, startCorner, size, viewport);\n\n const paint = () => {\n frameRef.current = null;\n const active = pressRef.current;\n if (!active || !active.active) return;\n element.style.translate = `${active.latest.left - startScreen.left}px ${active.latest.top - startScreen.top}px`;\n };\n const move = (e: PointerEvent) => {\n if (e.pointerId !== press.pointerId) return;\n const dx = e.clientX - press.startX;\n const dy = e.clientY - press.startY;\n const opts = live.current;\n if (!press.active) {\n if (Math.abs(dx) < opts.threshold && Math.abs(dy) < opts.threshold) return;\n press.active = true;\n press.release = holdDragCursor(\"grabbing\");\n setDragging(true);\n }\n const desired = fromScreen({ left: startScreen.left + dx, top: startScreen.top + dy }, startCorner, size, viewport);\n let next = toScreen(clampOffset(desired, startCorner, size, viewport, opts.clamp), startCorner, size, viewport);\n if (opts.avoid) {\n const keep = (p: ScreenPoint) =>\n toScreen(clampOffset(fromScreen(p, \"top-left\", size, viewport), \"top-left\", size, viewport, opts.clamp), \"top-left\", size, viewport);\n next = avoidExclusions(next, size, excludedRects(opts.avoid, element), opts.avoid.gap ?? DEFAULT_DOCK_MARGIN, keep);\n }\n press.latest = next;\n if (frameRef.current === null) {\n if (typeof window.requestAnimationFrame === \"function\") frameRef.current = window.requestAnimationFrame(paint);\n else paint();\n }\n };\n const end = (e: PointerEvent) => {\n if (e.pointerId !== press.pointerId) return;\n press.cleanup();\n pressRef.current = null;\n if (!press.active) return;\n press.release?.();\n setDragging(false);\n const opts = live.current;\n const finalCorner = opts.snapToCorner ? nearestCorner(press.latest, size, viewport) : startCorner;\n const finalOffset = clampOffset(fromScreen(press.latest, finalCorner, size, viewport), finalCorner, size, viewport, opts.clamp);\n commit(finalCorner === corner ? { x: finalOffset.x, y: finalOffset.y } : { ...finalOffset, corner: finalCorner });\n // The click the browser fires next is the tail of this drag, never an intent.\n const swallow = (click: MouseEvent) => {\n click.stopPropagation();\n click.preventDefault();\n };\n window.addEventListener(\"click\", swallow, { capture: true, once: true });\n window.setTimeout(() => window.removeEventListener(\"click\", swallow, { capture: true }), 0);\n };\n press.cleanup = () => {\n window.removeEventListener(\"pointermove\", move);\n window.removeEventListener(\"pointerup\", end);\n window.removeEventListener(\"pointercancel\", end);\n if (frameRef.current !== null) {\n window.cancelAnimationFrame?.(frameRef.current);\n frameRef.current = null;\n }\n if (!press.active) return;\n press.release?.();\n press.release = null;\n };\n pressRef.current = press;\n window.addEventListener(\"pointermove\", move);\n window.addEventListener(\"pointerup\", end);\n window.addEventListener(\"pointercancel\", end);\n },\n [commit, corner, draggable, element, id, ignore, raiseOnPress, summonStore, touch],\n );\n\n const reset = useCallback(() => {\n adoptingRef.current = true;\n commit(null);\n }, [commit]);\n\n // ── Placement ───────────────────────────────────────────────────────────\n let style: CSSProperties;\n if (renderedOffset) {\n const left = rawCorner.endsWith(\"left\");\n const top = rawCorner.startsWith(\"top\");\n style = {\n position: \"fixed\",\n left: left ? renderedOffset.x : \"auto\",\n right: left ? \"auto\" : renderedOffset.x,\n top: top ? renderedOffset.y : \"auto\",\n bottom: top ? \"auto\" : renderedOffset.y,\n };\n } else {\n const transform = [anchor?.centerX ? \"translateX(-50%)\" : null, nudge ? `translate(${nudge.left}px, ${nudge.top}px)` : null]\n .filter(Boolean)\n .join(\" \");\n style = {\n position: \"fixed\",\n top: anchor?.top,\n bottom: anchor?.bottom,\n left: anchor?.centerX ? \"50%\" : anchor?.left,\n right: anchor?.right,\n ...(transform ? { transform } : {}),\n };\n }\n if (zIndex !== undefined) style.zIndex = zIndex;\n\n return {\n ref: setElement,\n element,\n style,\n handleProps: {\n onPointerDown,\n style: draggable ? { cursor: \"grab\", touchAction: touch ? \"none\" : \"auto\" } : {},\n \"data-dock-handle\": \"\",\n },\n offset: renderedOffset,\n corner: rawCorner,\n dragging,\n moved: chosen !== null,\n zIndex,\n reset,\n };\n}\n","/**\n * Dock geometry — the pure half of a dock: corner-relative offsets, the clamp\n * that keeps a dock reachable, the nearest-corner snap and the exclusion\n * avoidance. No DOM reads except `avoidExclusions`, which is handed the\n * rectangles it must stay off.\n *\n * A dock's position is stored as an OFFSET FROM A CORNER, not as left/top: a\n * dock resting 12px from the bottom-right keeps that relationship when the\n * window is resized, and a position saved on a wide monitor comes back next to\n * the same corner on a laptop.\n */\n\nexport type DockCorner = \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n\n/** Distance in px from the corner's horizontal (`x`) and vertical (`y`) edges. */\nexport interface DockOffset {\n x: number;\n y: number;\n}\n\n/** A remembered position: an offset and the corner it is measured from (the dock's corner when omitted). */\nexport interface DockPosition extends DockOffset {\n corner?: DockCorner;\n}\n\nexport interface DockSize {\n width: number;\n height: number;\n}\n\nexport interface DockClamp {\n /** Smallest gap kept between the dock and the viewport edge. Default 8. */\n margin?: number;\n /**\n * How much of the dock must stay on screen. Default: all of it. A dock that\n * may hang off an edge (a pill you can park half-hidden) passes its grab area.\n */\n minVisible?: DockSize;\n}\n\nexport interface ScreenPoint {\n left: number;\n top: number;\n}\n\nexport interface ScreenRect extends ScreenPoint {\n right: number;\n bottom: number;\n}\n\nexport const DEFAULT_DOCK_MARGIN = 8;\n\n/** A pointer that moved less than this is a click, not a drag. */\nexport const DEFAULT_DRAG_THRESHOLD = 4;\n\nconst fromLeft = (corner: DockCorner): boolean => corner.endsWith(\"left\");\nconst fromTop = (corner: DockCorner): boolean => corner.startsWith(\"top\");\n\n/** The offset → the dock's top-left in viewport px. */\nexport function toScreen(offset: DockOffset, corner: DockCorner, size: DockSize, viewport: DockSize): ScreenPoint {\n return {\n left: fromLeft(corner) ? offset.x : viewport.width - size.width - offset.x,\n top: fromTop(corner) ? offset.y : viewport.height - size.height - offset.y,\n };\n}\n\n/** A top-left in viewport px → the offset from `corner`. */\nexport function fromScreen(point: ScreenPoint, corner: DockCorner, size: DockSize, viewport: DockSize): DockOffset {\n return {\n x: fromLeft(corner) ? point.left : viewport.width - size.width - point.left,\n y: fromTop(corner) ? point.top : viewport.height - size.height - point.top,\n };\n}\n\n/**\n * Keep the offset inside the viewport. When the dock is larger than the room\n * there is, its TOP and LEFT stay visible — that is where a header, a grip\n * and a close button live, and an unreachable close button is how a movable\n * notice turns into an unclosable one.\n */\nexport function clampOffset(\n offset: DockOffset,\n corner: DockCorner,\n size: DockSize,\n viewport: DockSize,\n clamp: DockClamp = {},\n): DockOffset {\n const margin = clamp.margin ?? DEFAULT_DOCK_MARGIN;\n const visible = clamp.minVisible ?? size;\n const axis = (value: number, room: number, extent: number, nearIsStart: boolean): number => {\n const min = margin;\n const max = room - extent - margin;\n if (max < min) return Math.round(nearIsStart ? min : max);\n return Math.round(Math.min(Math.max(value, min), max));\n };\n return {\n x: axis(offset.x, viewport.width, visible.width, fromLeft(corner)),\n y: axis(offset.y, viewport.height, visible.height, fromTop(corner)),\n };\n}\n\n/** The corner nearest the dock's centre — where a dropped dock re-anchors when snapping is on. */\nexport function nearestCorner(point: ScreenPoint, size: DockSize, viewport: DockSize): DockCorner {\n const top = point.top + size.height / 2 < viewport.height / 2;\n const left = point.left + size.width / 2 < viewport.width / 2;\n return `${top ? \"top\" : \"bottom\"}-${left ? \"left\" : \"right\"}`;\n}\n\nfunction overlaps(a: ScreenRect, b: ScreenRect): boolean {\n return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top;\n}\n\n/**\n * Keep the dock off fixed controls it must never cover (a table's pager, a\n * composer) without reserving page space. A clear position is returned\n * untouched; a covering one moves to the NEAREST clear candidate — never\n * further than it has to.\n */\nexport function avoidExclusions(\n point: ScreenPoint,\n size: DockSize,\n excluded: readonly ScreenRect[],\n gap: number,\n keepOnScreen: (point: ScreenPoint) => ScreenPoint,\n): ScreenPoint {\n const bounded = keepOnScreen(point);\n if (excluded.length === 0) return bounded;\n const rectFor = (candidate: ScreenPoint): ScreenRect => ({\n left: candidate.left,\n top: candidate.top,\n right: candidate.left + size.width,\n bottom: candidate.top + size.height,\n });\n const isClear = (candidate: ScreenPoint) => !excluded.some((target) => overlaps(rectFor(candidate), target));\n if (isClear(bounded)) return bounded;\n const clear = excluded\n .flatMap((target) => [\n { left: bounded.left, top: target.top - size.height - gap },\n { left: bounded.left, top: target.bottom + gap },\n { left: target.left - size.width - gap, top: bounded.top },\n { left: target.right + gap, top: bounded.top },\n ])\n .map(keepOnScreen)\n .filter(isClear);\n if (clear.length === 0) return bounded;\n const distance = (candidate: ScreenPoint) => Math.hypot(candidate.left - bounded.left, candidate.top - bounded.top);\n return clear.reduce((nearest, candidate) => (distance(candidate) < distance(nearest) ? candidate : nearest));\n}\n\nexport function samePosition(a: DockPosition | null | undefined, b: DockPosition | null | undefined): boolean {\n if (!a || !b) return a === b || (!a && !b);\n return a.x === b.x && a.y === b.y && a.corner === b.corner;\n}\n","/**\n * Where a dock remembers its position.\n *\n * The package never decides WHICH storage a host uses: a host that keeps\n * browser state behind its own declared keys (or syncs it per user) hands a\n * `DockPositionStore` to `<DockProvider>`, or to one dock through its `store`\n * option. With neither, positions live in this browser's localStorage under\n * `matrx:summon:dock:<id>` — every read and write is SSR-safe and never throws\n * (private mode, quota and disabled storage degrade to \"nothing stored\").\n */\n\nimport { createContext, createElement, useContext, type ReactNode } from \"react\";\nimport type { DockCorner, DockPosition } from \"./geometry\";\n\nexport interface DockPositionStore {\n read(id: string): DockPosition | null;\n /** `null` forgets the position (the dock goes back to its resting place). */\n write(id: string, position: DockPosition | null): void;\n}\n\nexport const DOCK_STORAGE_PREFIX = \"matrx:summon:dock:\";\n\nconst CORNERS: readonly DockCorner[] = [\"top-left\", \"top-right\", \"bottom-left\", \"bottom-right\"];\n\n/** Accept only a well-formed position; anything else reads as \"nothing stored\". */\nexport function parseDockPosition(value: unknown): DockPosition | null {\n if (!value || typeof value !== \"object\") return null;\n const { x, y, corner } = value as Record<string, unknown>;\n if (typeof x !== \"number\" || typeof y !== \"number\" || !Number.isFinite(x) || !Number.isFinite(y)) return null;\n if (corner !== undefined && !CORNERS.includes(corner as DockCorner)) return null;\n return corner === undefined ? { x, y } : { x, y, corner: corner as DockCorner };\n}\n\nfunction storage(): Storage | null {\n try {\n return typeof window === \"undefined\" ? null : window.localStorage;\n } catch {\n return null;\n }\n}\n\n/** The default store: one localStorage key per dock id. */\nexport function createLocalDockStore(prefix: string = DOCK_STORAGE_PREFIX): DockPositionStore {\n return {\n read(id) {\n try {\n const raw = storage()?.getItem(prefix + id);\n return raw ? parseDockPosition(JSON.parse(raw)) : null;\n } catch {\n return null;\n }\n },\n write(id, position) {\n try {\n const area = storage();\n if (!area) return;\n if (position) area.setItem(prefix + id, JSON.stringify(position));\n else area.removeItem(prefix + id);\n } catch {\n // Storage refused: the in-memory position still applies for this page.\n }\n },\n };\n}\n\n/** A store that remembers nothing — for docks whose place is per-moment (a toast, a stacked widget). */\nexport const memoryOnlyDockStore: DockPositionStore = { read: () => null, write: () => undefined };\n\nconst defaultStore = createLocalDockStore();\n\nconst DockStoreContext = createContext<DockPositionStore | null>(null);\n\nexport interface DockProviderProps {\n store: DockPositionStore;\n children?: ReactNode;\n}\n\n/** Hand every dock below one position store (the host's declared keys, a per-user sync). */\nexport function DockProvider({ store, children }: DockProviderProps): ReactNode {\n return createElement(DockStoreContext.Provider, { value: store }, children);\n}\n\n/** The store a dock uses: its own option, else the nearest provider, else localStorage. */\nexport function useDockStore(own?: DockPositionStore): DockPositionStore {\n const provided = useContext(DockStoreContext);\n return own ?? provided ?? defaultStore;\n}\n"],"mappings":"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","names":["createContext","useContext","createContext","useContext"]}
1
+ {"version":3,"sources":["../src/dock/Dock.tsx","../src/dock/useDock.ts","../src/dock/geometry.ts","../src/dock/store.ts"],"sourcesContent":["/**\n * `<Dock>` — the component form of `useDock()`: a fixed, chrome-less element that\n * moves. Drag it by a `<DockHandle>` inside it, or by the whole dock with\n * `handle=\"self\"`. `useDockContext()` reaches the dock's API from anywhere inside\n * (a \"Move back to the corner\" menu item calls `reset()`).\n */\n\nimport { createContext, useContext, type CSSProperties, type HTMLAttributes, type ReactNode } from \"react\";\nimport { useDock, type DockApi, type UseDockOptions } from \"./useDock\";\n\nconst DockContext = createContext<DockApi | null>(null);\n\n/** The enclosing dock's API; throws outside a `<Dock>`. */\nexport function useDockContext(): DockApi {\n const dock = useContext(DockContext);\n if (!dock) throw new Error(\"[summon] useDockContext() must be used inside <Dock>\");\n return dock;\n}\n\nexport interface DockProps extends UseDockOptions, Omit<HTMLAttributes<HTMLDivElement>, \"id\" | \"children\" | \"draggable\"> {\n /** \"self\": the whole dock is the drag handle. Default: only `<DockHandle>`s drag. */\n handle?: \"self\" | \"inner\";\n children?: ReactNode | ((dock: DockApi) => ReactNode);\n}\n\nconst OPTION_KEYS = [\n \"id\", \"corner\", \"defaultPosition\", \"anchor\", \"position\", \"onPositionChange\", \"persist\", \"store\", \"draggable\",\n \"touch\", \"threshold\", \"clamp\", \"avoid\", \"snapToCorner\", \"zIndex\", \"raiseOnPress\", \"ignore\",\n] as const satisfies readonly (keyof UseDockOptions)[];\n\nexport function Dock(props: DockProps): ReactNode {\n const options = {} as UseDockOptions;\n const rest: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(props)) {\n if ((OPTION_KEYS as readonly string[]).includes(key)) (options as unknown as Record<string, unknown>)[key] = value;\n else if (key !== \"handle\" && key !== \"children\" && key !== \"style\") rest[key] = value;\n }\n const dock = useDock(options);\n const self = props.handle === \"self\";\n const style: CSSProperties = { ...props.style, ...dock.style, ...(self ? dock.handleProps.style : {}) };\n return (\n <DockContext.Provider value={dock}>\n <div\n {...(rest as HTMLAttributes<HTMLDivElement>)}\n ref={dock.ref}\n data-dock={props.id}\n data-dragging={dock.dragging ? \"\" : undefined}\n style={style}\n {...(self ? { onPointerDown: dock.handleProps.onPointerDown, \"data-dock-handle\": \"\" } : {})}\n >\n {typeof props.children === \"function\" ? props.children(dock) : props.children}\n </div>\n </DockContext.Provider>\n );\n}\n\nexport interface DockHandleProps extends HTMLAttributes<HTMLDivElement> {\n children?: ReactNode;\n}\n\n/** The part of a `<Dock>` you drag it by. Buttons and fields inside stay controls. */\nexport function DockHandle({ style, onPointerDown, ...rest }: DockHandleProps): ReactNode {\n const dock = useDockContext();\n return (\n <div\n {...rest}\n {...dock.handleProps}\n style={{ ...style, ...dock.handleProps.style }}\n onPointerDown={(event) => {\n onPointerDown?.(event);\n dock.handleProps.onPointerDown(event);\n }}\n />\n );\n}\n","/**\n * `useDock()` — the one primitive for floating chrome-less cards, pills and docks.\n *\n * THE RULE (paid for in the Workflow Studio's FloatingChatPanel and chat's\n * ChatPanel): a pointer move paints the CSS `translate` property inside ONE\n * requestAnimationFrame and React state is committed EXACTLY ONCE, on release.\n * Writing state per pointer event re-renders whatever streams inside the dock\n * sixty times a second. `translate` composes with the host's own `transform`\n * and with any `left/right/top/bottom` it computes, so a host that builds its\n * own placement (CSS variables, lifts) still drags without re-rendering.\n *\n * What else it carries:\n * - a drag is never a click: below `threshold` px the gesture stays a click;\n * past it the trailing click is swallowed once;\n * - the position is an offset from a corner, clamped to the viewport on every\n * render (a position saved on a wide monitor never strands the dock), and\n * can snap to the nearest corner on release;\n * - it remembers its place per dock id (the store chain in ./store) or reports\n * it to a controlled host (`position` + `onPositionChange`, e.g. per-user sync);\n * - it joins the window manager's z-order lane on press (raised above every\n * window, below modals) when a SummonProvider is present;\n * - THE CURSOR LAW: the drag holds `grabbing` with design-system `holdDragCursor`;\n * - zero cost until mounted: no listener exists outside an active press.\n */\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type PointerEvent as ReactPointerEvent,\n} from \"react\";\nimport { useDragMove, type DragRect } from \"@ai-matrx/design-system\";\nimport { raiseTransientWindow, releaseTransientWindow } from \"@ai-matrx/summon\";\nimport { useOptionalSummonStore } from \"@ai-matrx/summon/react\";\nimport {\n avoidExclusions,\n clampOffset,\n DEFAULT_DOCK_MARGIN,\n DEFAULT_DRAG_THRESHOLD,\n fromScreen,\n nearestCorner,\n samePosition,\n toScreen,\n type DockClamp,\n type DockCorner,\n type DockOffset,\n type DockPosition,\n type DockSize,\n type ScreenPoint,\n type ScreenRect,\n} from \"./geometry\";\nimport { memoryOnlyDockStore, useDockStore, type DockPositionStore } from \"./store\";\n\n/** CSS placement used while the dock has no position (never moved, no `defaultPosition`). */\nexport interface DockAnchor {\n top?: string;\n bottom?: string;\n left?: string;\n right?: string;\n /** Centre horizontally (`left: 50%` + translateX(-50%)). */\n centerX?: boolean;\n}\n\n/** Controls inside the handle that stay controls — a press there never starts a drag. */\nexport const DEFAULT_DOCK_IGNORE = \"button,a,input,select,textarea,[contenteditable='true'],[data-no-drag]\";\n\nexport interface UseDockOptions {\n /** Identity: the remembered position, the z-order lane entry. Unique per dock. */\n id: string;\n /** The corner positions are measured from. Default \"bottom-right\". */\n corner?: DockCorner;\n /** Resting offset from `corner` before the person moves it. Omit to rest on `anchor`. */\n defaultPosition?: DockOffset | null;\n /** CSS resting place when there is no position at all. */\n anchor?: DockAnchor;\n /** Controlled position (the host owns storage, e.g. a per-user preference). `null` = resting. */\n position?: DockPosition | null;\n /** Told once per drag, on release, and on `reset()` (with `null`). */\n onPositionChange?: (position: DockPosition | null) => void;\n /** Remember the position across reloads (uncontrolled only). Default true. */\n persist?: boolean;\n /** This dock's own store; otherwise the nearest `<DockProvider>`, else localStorage. */\n store?: DockPositionStore;\n /** Default true. Pass false on surfaces that must not move (a phone sheet). */\n draggable?: boolean;\n /** Allow touch drags. Default true; a dock that fights page scroll passes false. */\n touch?: boolean;\n /** Px a pointer must travel before a press becomes a drag. Default 4. */\n threshold?: number;\n clamp?: DockClamp;\n /** Fixed controls the dock must stay off (nearest clear spot wins). */\n avoid?: { selector: string; gap?: number };\n /** On release, re-anchor to the nearest corner. Default false. */\n snapToCorner?: boolean;\n /** z-index before the first press raises it into the window lane. */\n zIndex?: number;\n /** Raise into the window manager's z-order lane on press. Default true. */\n raiseOnPress?: boolean;\n /** Selector of controls inside the handle that never start a drag. `null` = every press may drag. */\n ignore?: string | null;\n}\n\nexport interface DockHandleBindings {\n onPointerDown(event: ReactPointerEvent<HTMLElement>): void;\n style: CSSProperties;\n \"data-dock-handle\": \"\";\n}\n\nexport interface DockApi {\n /** Attach to the element that MOVES. */\n ref: (element: HTMLElement | null) => void;\n element: HTMLElement | null;\n /** Spread onto the moving element: fixed placement (placed or anchored) + z-index. */\n style: CSSProperties;\n /** Spread onto the element you drag BY (or the whole dock). */\n handleProps: DockHandleBindings;\n /** The clamped offset from `corner`; null while resting on `anchor`. */\n offset: DockOffset | null;\n /** The corner `offset` is measured from. */\n corner: DockCorner;\n /** True from the moment a press passes the threshold until release. */\n dragging: boolean;\n /** The person has moved it (a remembered or controlled position exists). */\n moved: boolean;\n /** Current z-index: the lane's once raised, else the `zIndex` option. */\n zIndex: number | undefined;\n /** Forget the position; the dock returns to its resting place. */\n reset(): void;\n}\n\nconst SSR_VIEWPORT: DockSize = { width: 1280, height: 800 };\nconst noopSubscribe = () => () => undefined;\n\ninterface Geometry {\n viewport: DockSize;\n size: DockSize;\n}\n\nfunction readViewport(): DockSize {\n return typeof window === \"undefined\" ? SSR_VIEWPORT : { width: window.innerWidth, height: window.innerHeight };\n}\n\nfunction measure(element: HTMLElement | null): DockSize {\n return { width: element?.offsetWidth ?? 0, height: element?.offsetHeight ?? 0 };\n}\n\nfunction excludedRects(avoid: UseDockOptions[\"avoid\"], self: HTMLElement | null): ScreenRect[] {\n if (!avoid || typeof document === \"undefined\") return [];\n return [...document.querySelectorAll<HTMLElement>(avoid.selector)]\n .filter((target) => target !== self && !self?.contains(target))\n .map((target) => target.getBoundingClientRect())\n .filter((rect) => rect.width > 0 && rect.height > 0);\n}\n\nexport function useDock(options: UseDockOptions): DockApi {\n const {\n id,\n corner = \"bottom-right\",\n defaultPosition = null,\n anchor,\n position: controlled,\n onPositionChange,\n persist = true,\n draggable = true,\n touch = true,\n threshold = DEFAULT_DRAG_THRESHOLD,\n clamp,\n avoid,\n snapToCorner = false,\n zIndex: baseZ,\n raiseOnPress = true,\n ignore = DEFAULT_DOCK_IGNORE,\n } = options;\n const isControlled = controlled !== undefined;\n const providedStore = useDockStore(options.store);\n const store = persist && !isControlled ? providedStore : memoryOnlyDockStore;\n\n const [element, setElement] = useState<HTMLElement | null>(null);\n const [local, setLocal] = useState<DockPosition | null>(null);\n /** A controlled commit, shown until the host's `position` changes (it may update async). */\n const [pending, setPending] = useState<{ value: DockPosition | null; forProp: DockPosition | null } | null>(null);\n const [geometry, setGeometry] = useState<Geometry>({ viewport: SSR_VIEWPORT, size: { width: 0, height: 0 } });\n const [nudge, setNudge] = useState<ScreenPoint | null>(null);\n const restoredRef = useRef<string | null>(null);\n\n // ── The position, resolved ───────────────────────────────────────────────\n const chosen: DockPosition | null = isControlled\n ? pending && samePosition(pending.forProp, controlled) ? pending.value : controlled\n : local;\n const raw: DockPosition | null = chosen ?? defaultPosition;\n const rawCorner = raw?.corner ?? corner;\n const offset = raw ? clampOffset(raw, rawCorner, geometry.size, geometry.viewport, clamp) : null;\n\n // Live refs for the press listeners (written in effects, never during render).\n const live = useRef({ offset, rawCorner, clamp, avoid, threshold, snapToCorner, onPositionChange, isControlled, controlled, store, id });\n useEffect(() => {\n live.current = { offset, rawCorner, clamp, avoid, threshold, snapToCorner, onPositionChange, isControlled, controlled, store, id };\n });\n\n // Restore the remembered position BEFORE paint, once per id, as soon as the element exists.\n useLayoutEffect(() => {\n if (!element || restoredRef.current === id) return;\n restoredRef.current = id;\n if (isControlled) return;\n setLocal(store.read(id));\n }, [element, id, isControlled, store]);\n\n // Track the viewport and the dock's own size (expanded/collapsed content), re-clamping on change.\n useLayoutEffect(() => {\n if (!element) return undefined;\n let frame: number | null = null;\n const read = () => {\n const next: Geometry = { viewport: readViewport(), size: measure(element) };\n setGeometry((current) =>\n current.viewport.width === next.viewport.width &&\n current.viewport.height === next.viewport.height &&\n current.size.width === next.size.width &&\n current.size.height === next.size.height\n ? current\n : next,\n );\n };\n const schedule = () => {\n if (frame !== null) return;\n frame = window.requestAnimationFrame(() => {\n frame = null;\n read();\n });\n };\n read();\n const observer = typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(schedule);\n observer?.observe(element);\n window.addEventListener(\"resize\", schedule);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", schedule);\n if (frame !== null) window.cancelAnimationFrame(frame);\n };\n }, [element]);\n\n // Exclusions: a placed dock is moved to the nearest clear spot at render; a resting (anchored)\n // dock gets a measured nudge, re-read whenever the page under it changes.\n const avoidSelector = avoid?.selector;\n const avoidGap = avoid?.gap;\n useLayoutEffect(() => {\n if (!element || !avoidSelector || offset) {\n setNudge((current) => (current === null ? current : null));\n return undefined;\n }\n let frame: number | null = null;\n let applied: ScreenPoint | null = null;\n const reconcile = () => {\n const rect = element.getBoundingClientRect();\n const size = measure(element);\n const viewport = readViewport();\n const base = { left: rect.left - (applied?.left ?? 0), top: rect.top - (applied?.top ?? 0) };\n const keep = (p: ScreenPoint) => {\n const o = clampOffset(fromScreen(p, \"top-left\", size, viewport), \"top-left\", size, viewport, live.current.clamp);\n return toScreen(o, \"top-left\", size, viewport);\n };\n const clear = avoidExclusions(base, size, excludedRects({ selector: avoidSelector }, element), avoidGap ?? DEFAULT_DOCK_MARGIN, keep);\n const next = { left: Math.round(clear.left - base.left), top: Math.round(clear.top - base.top) };\n applied = next.left === 0 && next.top === 0 ? null : next;\n setNudge((current) =>\n (current === null && applied === null) || (current && applied && current.left === applied.left && current.top === applied.top)\n ? current\n : applied,\n );\n };\n const schedule = () => {\n if (frame !== null) return;\n frame = window.requestAnimationFrame(() => {\n frame = null;\n reconcile();\n });\n };\n reconcile();\n const mutations = typeof MutationObserver === \"undefined\" ? null : new MutationObserver(schedule);\n mutations?.observe(document.body, { childList: true, subtree: true });\n window.addEventListener(\"resize\", schedule);\n window.addEventListener(\"scroll\", schedule, true);\n return () => {\n mutations?.disconnect();\n window.removeEventListener(\"resize\", schedule);\n window.removeEventListener(\"scroll\", schedule, true);\n if (frame !== null) window.cancelAnimationFrame(frame);\n };\n }, [element, avoidSelector, avoidGap, offset === null]); // eslint-disable-line react-hooks/exhaustive-deps\n\n /** A placed dock that would cover an excluded control renders at the nearest clear spot. */\n let placed: ScreenPoint | null = null;\n if (offset) {\n placed = toScreen(offset, rawCorner, geometry.size, geometry.viewport);\n if (avoid && element) {\n const size = geometry.size;\n const viewport = geometry.viewport;\n const keep = (p: ScreenPoint) =>\n toScreen(clampOffset(fromScreen(p, \"top-left\", size, viewport), \"top-left\", size, viewport, clamp), \"top-left\", size, viewport);\n placed = avoidExclusions(placed, size, excludedRects(avoid, element), avoid.gap ?? DEFAULT_DOCK_MARGIN, keep);\n }\n }\n const renderedOffset = placed && offset ? fromScreen(placed, rawCorner, geometry.size, geometry.viewport) : offset;\n\n // ── The z-order lane ────────────────────────────────────────────────────\n const summonStore = useOptionalSummonStore();\n const laneZ = useSyncExternalStore(\n summonStore ? summonStore.subscribe : noopSubscribe,\n () => (summonStore ? (summonStore.getState() as { windowManager?: { transientZ?: Record<string, number> } }).windowManager?.transientZ?.[id] : undefined),\n () => undefined,\n );\n useEffect(() => {\n if (!summonStore) return undefined;\n return () => {\n const state = summonStore.getState() as { windowManager?: { transientZ?: Record<string, number> } };\n if (state.windowManager?.transientZ?.[id] !== undefined) summonStore.dispatch(releaseTransientWindow(id));\n };\n }, [summonStore, id]);\n const zIndex = laneZ ?? baseZ;\n\n // ── The drag: THE WINDOW DRAG (design-system window-drag.ts) ─────────────\n // The pointer mechanics (threshold, rAF paint through `translate`, the cursor hold, the\n // swallowed trailing click, cleanup) are design-system's; the dock adds its corner-relative\n // geometry: the press starts from the committed offset, every frame is clamped (and kept off\n // exclusions) in corner space, and the release re-anchors (snap) and commits once.\n const commit = useCallback((next: DockPosition | null) => {\n const current = live.current;\n if (current.isControlled) setPending({ value: next, forProp: current.controlled ?? null });\n else {\n setLocal(next);\n current.store.write(current.id, next);\n }\n current.onPositionChange?.(next);\n }, []);\n\n /** The corner and viewport of the press in flight. */\n const pressRef = useRef<{ corner: DockCorner; viewport: DockSize }>({ corner, viewport: SSR_VIEWPORT });\n const keepScreen = (point: ScreenPoint, size: DockSize, viewport: DockSize): ScreenPoint =>\n toScreen(clampOffset(fromScreen(point, \"top-left\", size, viewport), \"top-left\", size, viewport, live.current.clamp), \"top-left\", size, viewport);\n\n const drag = useDragMove({\n element,\n enabled: draggable,\n touch,\n threshold,\n ignore,\n onPress: () => {\n if (raiseOnPress && summonStore) summonStore.dispatch(raiseTransientWindow(id));\n },\n // A placed dock starts from its committed offset (the host may add lifts on top of it);\n // a resting dock starts from where it is on screen.\n getRect: () => {\n const viewport = readViewport();\n const size = measure(element);\n const startCorner = live.current.rawCorner;\n const rect = element?.getBoundingClientRect();\n const startOffset: DockOffset =\n live.current.offset ?? fromScreen({ left: rect?.left ?? 0, top: rect?.top ?? 0 }, startCorner, size, viewport);\n pressRef.current = { corner: startCorner, viewport };\n const start = toScreen(startOffset, startCorner, size, viewport);\n return { x: start.left, y: start.top, ...size };\n },\n constrain: (rect: DragRect) => {\n const { corner: startCorner, viewport } = pressRef.current;\n const size = { width: rect.width, height: rect.height };\n const opts = live.current;\n const desired = fromScreen({ left: rect.x, top: rect.y }, startCorner, size, viewport);\n let next = toScreen(clampOffset(desired, startCorner, size, viewport, opts.clamp), startCorner, size, viewport);\n if (opts.avoid)\n next = avoidExclusions(next, size, excludedRects(opts.avoid, element), opts.avoid.gap ?? DEFAULT_DOCK_MARGIN, (p) => keepScreen(p, size, viewport));\n return { ...rect, x: next.left, y: next.top };\n },\n onCommit: (rect: DragRect) => {\n const { corner: startCorner, viewport } = pressRef.current;\n const size = { width: rect.width, height: rect.height };\n const latest = { left: rect.x, top: rect.y };\n const opts = live.current;\n const finalCorner = opts.snapToCorner ? nearestCorner(latest, size, viewport) : startCorner;\n const finalOffset = clampOffset(fromScreen(latest, finalCorner, size, viewport), finalCorner, size, viewport, opts.clamp);\n commit(finalCorner === corner ? { x: finalOffset.x, y: finalOffset.y } : { ...finalOffset, corner: finalCorner });\n },\n });\n const dragging = drag.dragging;\n const onPointerDown = drag.onPointerDown;\n\n const reset = useCallback(() => commit(null), [commit]);\n\n // ── Placement ───────────────────────────────────────────────────────────\n let style: CSSProperties;\n if (renderedOffset) {\n const left = rawCorner.endsWith(\"left\");\n const top = rawCorner.startsWith(\"top\");\n style = {\n position: \"fixed\",\n left: left ? renderedOffset.x : \"auto\",\n right: left ? \"auto\" : renderedOffset.x,\n top: top ? renderedOffset.y : \"auto\",\n bottom: top ? \"auto\" : renderedOffset.y,\n };\n } else {\n const transform = [anchor?.centerX ? \"translateX(-50%)\" : null, nudge ? `translate(${nudge.left}px, ${nudge.top}px)` : null]\n .filter(Boolean)\n .join(\" \");\n style = {\n position: \"fixed\",\n top: anchor?.top,\n bottom: anchor?.bottom,\n left: anchor?.centerX ? \"50%\" : anchor?.left,\n right: anchor?.right,\n ...(transform ? { transform } : {}),\n };\n }\n if (zIndex !== undefined) style.zIndex = zIndex;\n\n return {\n ref: setElement,\n element,\n style,\n handleProps: {\n onPointerDown,\n style: draggable ? { cursor: \"grab\", touchAction: touch ? \"none\" : \"auto\" } : {},\n \"data-dock-handle\": \"\",\n },\n offset: renderedOffset,\n corner: rawCorner,\n dragging,\n moved: chosen !== null,\n zIndex,\n reset,\n };\n}\n","/**\n * Dock geometry — the pure half of a dock: corner-relative offsets, the clamp\n * that keeps a dock reachable, the nearest-corner snap and the exclusion\n * avoidance. No DOM reads except `avoidExclusions`, which is handed the\n * rectangles it must stay off.\n *\n * A dock's position is stored as an OFFSET FROM A CORNER, not as left/top: a\n * dock resting 12px from the bottom-right keeps that relationship when the\n * window is resized, and a position saved on a wide monitor comes back next to\n * the same corner on a laptop.\n */\n\nexport type DockCorner = \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n\n/** Distance in px from the corner's horizontal (`x`) and vertical (`y`) edges. */\nexport interface DockOffset {\n x: number;\n y: number;\n}\n\n/** A remembered position: an offset and the corner it is measured from (the dock's corner when omitted). */\nexport interface DockPosition extends DockOffset {\n corner?: DockCorner;\n}\n\nexport interface DockSize {\n width: number;\n height: number;\n}\n\nexport interface DockClamp {\n /** Smallest gap kept between the dock and the viewport edge. Default 8. */\n margin?: number;\n /**\n * How much of the dock must stay on screen. Default: all of it. A dock that\n * may hang off an edge (a pill you can park half-hidden) passes its grab area.\n */\n minVisible?: DockSize;\n}\n\nexport interface ScreenPoint {\n left: number;\n top: number;\n}\n\nexport interface ScreenRect extends ScreenPoint {\n right: number;\n bottom: number;\n}\n\nexport const DEFAULT_DOCK_MARGIN = 8;\n\n/** A pointer that moved less than this is a click, not a drag. */\nexport const DEFAULT_DRAG_THRESHOLD = 4;\n\nconst fromLeft = (corner: DockCorner): boolean => corner.endsWith(\"left\");\nconst fromTop = (corner: DockCorner): boolean => corner.startsWith(\"top\");\n\n/** The offset → the dock's top-left in viewport px. */\nexport function toScreen(offset: DockOffset, corner: DockCorner, size: DockSize, viewport: DockSize): ScreenPoint {\n return {\n left: fromLeft(corner) ? offset.x : viewport.width - size.width - offset.x,\n top: fromTop(corner) ? offset.y : viewport.height - size.height - offset.y,\n };\n}\n\n/** A top-left in viewport px → the offset from `corner`. */\nexport function fromScreen(point: ScreenPoint, corner: DockCorner, size: DockSize, viewport: DockSize): DockOffset {\n return {\n x: fromLeft(corner) ? point.left : viewport.width - size.width - point.left,\n y: fromTop(corner) ? point.top : viewport.height - size.height - point.top,\n };\n}\n\n/**\n * Keep the offset inside the viewport. When the dock is larger than the room\n * there is, its TOP and LEFT stay visible — that is where a header, a grip\n * and a close button live, and an unreachable close button is how a movable\n * notice turns into an unclosable one.\n */\nexport function clampOffset(\n offset: DockOffset,\n corner: DockCorner,\n size: DockSize,\n viewport: DockSize,\n clamp: DockClamp = {},\n): DockOffset {\n const margin = clamp.margin ?? DEFAULT_DOCK_MARGIN;\n const visible = clamp.minVisible ?? size;\n const axis = (value: number, room: number, extent: number, nearIsStart: boolean): number => {\n const min = margin;\n const max = room - extent - margin;\n if (max < min) return Math.round(nearIsStart ? min : max);\n return Math.round(Math.min(Math.max(value, min), max));\n };\n return {\n x: axis(offset.x, viewport.width, visible.width, fromLeft(corner)),\n y: axis(offset.y, viewport.height, visible.height, fromTop(corner)),\n };\n}\n\n/** The corner nearest the dock's centre — where a dropped dock re-anchors when snapping is on. */\nexport function nearestCorner(point: ScreenPoint, size: DockSize, viewport: DockSize): DockCorner {\n const top = point.top + size.height / 2 < viewport.height / 2;\n const left = point.left + size.width / 2 < viewport.width / 2;\n return `${top ? \"top\" : \"bottom\"}-${left ? \"left\" : \"right\"}`;\n}\n\nfunction overlaps(a: ScreenRect, b: ScreenRect): boolean {\n return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top;\n}\n\n/**\n * Keep the dock off fixed controls it must never cover (a table's pager, a\n * composer) without reserving page space. A clear position is returned\n * untouched; a covering one moves to the NEAREST clear candidate — never\n * further than it has to.\n */\nexport function avoidExclusions(\n point: ScreenPoint,\n size: DockSize,\n excluded: readonly ScreenRect[],\n gap: number,\n keepOnScreen: (point: ScreenPoint) => ScreenPoint,\n): ScreenPoint {\n const bounded = keepOnScreen(point);\n if (excluded.length === 0) return bounded;\n const rectFor = (candidate: ScreenPoint): ScreenRect => ({\n left: candidate.left,\n top: candidate.top,\n right: candidate.left + size.width,\n bottom: candidate.top + size.height,\n });\n const isClear = (candidate: ScreenPoint) => !excluded.some((target) => overlaps(rectFor(candidate), target));\n if (isClear(bounded)) return bounded;\n const clear = excluded\n .flatMap((target) => [\n { left: bounded.left, top: target.top - size.height - gap },\n { left: bounded.left, top: target.bottom + gap },\n { left: target.left - size.width - gap, top: bounded.top },\n { left: target.right + gap, top: bounded.top },\n ])\n .map(keepOnScreen)\n .filter(isClear);\n if (clear.length === 0) return bounded;\n const distance = (candidate: ScreenPoint) => Math.hypot(candidate.left - bounded.left, candidate.top - bounded.top);\n return clear.reduce((nearest, candidate) => (distance(candidate) < distance(nearest) ? candidate : nearest));\n}\n\nexport function samePosition(a: DockPosition | null | undefined, b: DockPosition | null | undefined): boolean {\n if (!a || !b) return a === b || (!a && !b);\n return a.x === b.x && a.y === b.y && a.corner === b.corner;\n}\n","/**\n * Where a dock remembers its position.\n *\n * The package never decides WHICH storage a host uses: a host that keeps\n * browser state behind its own declared keys (or syncs it per user) hands a\n * `DockPositionStore` to `<DockProvider>`, or to one dock through its `store`\n * option. With neither, positions live in this browser's localStorage under\n * `matrx:summon:dock:<id>` — every read and write is SSR-safe and never throws\n * (private mode, quota and disabled storage degrade to \"nothing stored\").\n */\n\nimport { createContext, createElement, useContext, type ReactNode } from \"react\";\nimport type { DockCorner, DockPosition } from \"./geometry\";\n\nexport interface DockPositionStore {\n read(id: string): DockPosition | null;\n /** `null` forgets the position (the dock goes back to its resting place). */\n write(id: string, position: DockPosition | null): void;\n}\n\nexport const DOCK_STORAGE_PREFIX = \"matrx:summon:dock:\";\n\nconst CORNERS: readonly DockCorner[] = [\"top-left\", \"top-right\", \"bottom-left\", \"bottom-right\"];\n\n/** Accept only a well-formed position; anything else reads as \"nothing stored\". */\nexport function parseDockPosition(value: unknown): DockPosition | null {\n if (!value || typeof value !== \"object\") return null;\n const { x, y, corner } = value as Record<string, unknown>;\n if (typeof x !== \"number\" || typeof y !== \"number\" || !Number.isFinite(x) || !Number.isFinite(y)) return null;\n if (corner !== undefined && !CORNERS.includes(corner as DockCorner)) return null;\n return corner === undefined ? { x, y } : { x, y, corner: corner as DockCorner };\n}\n\nfunction storage(): Storage | null {\n try {\n return typeof window === \"undefined\" ? null : window.localStorage;\n } catch {\n return null;\n }\n}\n\n/** The default store: one localStorage key per dock id. */\nexport function createLocalDockStore(prefix: string = DOCK_STORAGE_PREFIX): DockPositionStore {\n return {\n read(id) {\n try {\n const raw = storage()?.getItem(prefix + id);\n return raw ? parseDockPosition(JSON.parse(raw)) : null;\n } catch {\n return null;\n }\n },\n write(id, position) {\n try {\n const area = storage();\n if (!area) return;\n if (position) area.setItem(prefix + id, JSON.stringify(position));\n else area.removeItem(prefix + id);\n } catch {\n // Storage refused: the in-memory position still applies for this page.\n }\n },\n };\n}\n\n/** A store that remembers nothing — for docks whose place is per-moment (a toast, a stacked widget). */\nexport const memoryOnlyDockStore: DockPositionStore = { read: () => null, write: () => undefined };\n\nconst defaultStore = createLocalDockStore();\n\nconst DockStoreContext = createContext<DockPositionStore | null>(null);\n\nexport interface DockProviderProps {\n store: DockPositionStore;\n children?: ReactNode;\n}\n\n/** Hand every dock below one position store (the host's declared keys, a per-user sync). */\nexport function DockProvider({ store, children }: DockProviderProps): ReactNode {\n return createElement(DockStoreContext.Provider, { value: store }, children);\n}\n\n/** The store a dock uses: its own option, else the nearest provider, else localStorage. */\nexport function useDockStore(own?: DockPositionStore): DockPositionStore {\n const provided = useContext(DockStoreContext);\n return own ?? provided ?? defaultStore;\n}\n"],"mappings":";;;AAOA,SAAS,iBAAAA,gBAAe,cAAAC,mBAA2E;;;ACkBnG;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,mBAAkC;AAC3C,SAAS,sBAAsB,8BAA8B;AAC7D,SAAS,8BAA8B;;;ACahC,IAAM,sBAAsB;AAG5B,IAAM,yBAAyB;AAEtC,IAAM,WAAW,CAAC,WAAgC,OAAO,SAAS,MAAM;AACxE,IAAM,UAAU,CAAC,WAAgC,OAAO,WAAW,KAAK;AAGjE,SAAS,SAAS,QAAoB,QAAoB,MAAgB,UAAiC;AAChH,SAAO;AAAA,IACL,MAAM,SAAS,MAAM,IAAI,OAAO,IAAI,SAAS,QAAQ,KAAK,QAAQ,OAAO;AAAA,IACzE,KAAK,QAAQ,MAAM,IAAI,OAAO,IAAI,SAAS,SAAS,KAAK,SAAS,OAAO;AAAA,EAC3E;AACF;AAGO,SAAS,WAAW,OAAoB,QAAoB,MAAgB,UAAgC;AACjH,SAAO;AAAA,IACL,GAAG,SAAS,MAAM,IAAI,MAAM,OAAO,SAAS,QAAQ,KAAK,QAAQ,MAAM;AAAA,IACvE,GAAG,QAAQ,MAAM,IAAI,MAAM,MAAM,SAAS,SAAS,KAAK,SAAS,MAAM;AAAA,EACzE;AACF;AAQO,SAAS,YACd,QACA,QACA,MACA,UACA,QAAmB,CAAC,GACR;AACZ,QAAM,SAAS,MAAM,UAAU;AAC/B,QAAM,UAAU,MAAM,cAAc;AACpC,QAAM,OAAO,CAAC,OAAe,MAAc,QAAgB,gBAAiC;AAC1F,UAAM,MAAM;AACZ,UAAM,MAAM,OAAO,SAAS;AAC5B,QAAI,MAAM,IAAK,QAAO,KAAK,MAAM,cAAc,MAAM,GAAG;AACxD,WAAO,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG,CAAC;AAAA,EACvD;AACA,SAAO;AAAA,IACL,GAAG,KAAK,OAAO,GAAG,SAAS,OAAO,QAAQ,OAAO,SAAS,MAAM,CAAC;AAAA,IACjE,GAAG,KAAK,OAAO,GAAG,SAAS,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,CAAC;AAAA,EACpE;AACF;AAGO,SAAS,cAAc,OAAoB,MAAgB,UAAgC;AAChG,QAAM,MAAM,MAAM,MAAM,KAAK,SAAS,IAAI,SAAS,SAAS;AAC5D,QAAM,OAAO,MAAM,OAAO,KAAK,QAAQ,IAAI,SAAS,QAAQ;AAC5D,SAAO,GAAG,MAAM,QAAQ,QAAQ,IAAI,OAAO,SAAS,OAAO;AAC7D;AAEA,SAAS,SAAS,GAAe,GAAwB;AACvD,SAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE;AAClF;AAQO,SAAS,gBACd,OACA,MACA,UACA,KACA,cACa;AACb,QAAM,UAAU,aAAa,KAAK;AAClC,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAM,UAAU,CAAC,eAAwC;AAAA,IACvD,MAAM,UAAU;AAAA,IAChB,KAAK,UAAU;AAAA,IACf,OAAO,UAAU,OAAO,KAAK;AAAA,IAC7B,QAAQ,UAAU,MAAM,KAAK;AAAA,EAC/B;AACA,QAAM,UAAU,CAAC,cAA2B,CAAC,SAAS,KAAK,CAAC,WAAW,SAAS,QAAQ,SAAS,GAAG,MAAM,CAAC;AAC3G,MAAI,QAAQ,OAAO,EAAG,QAAO;AAC7B,QAAM,QAAQ,SACX,QAAQ,CAAC,WAAW;AAAA,IACnB,EAAE,MAAM,QAAQ,MAAM,KAAK,OAAO,MAAM,KAAK,SAAS,IAAI;AAAA,IAC1D,EAAE,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,IAAI;AAAA,IAC/C,EAAE,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,KAAK,QAAQ,IAAI;AAAA,IACzD,EAAE,MAAM,OAAO,QAAQ,KAAK,KAAK,QAAQ,IAAI;AAAA,EAC/C,CAAC,EACA,IAAI,YAAY,EAChB,OAAO,OAAO;AACjB,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,WAAW,CAAC,cAA2B,KAAK,MAAM,UAAU,OAAO,QAAQ,MAAM,UAAU,MAAM,QAAQ,GAAG;AAClH,SAAO,MAAM,OAAO,CAAC,SAAS,cAAe,SAAS,SAAS,IAAI,SAAS,OAAO,IAAI,YAAY,OAAQ;AAC7G;AAEO,SAAS,aAAa,GAAoC,GAA6C;AAC5G,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO,MAAM,KAAM,CAAC,KAAK,CAAC;AACxC,SAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE;AACtD;;;AC7IA,SAAS,eAAe,eAAe,kBAAkC;AASlE,IAAM,sBAAsB;AAEnC,IAAM,UAAiC,CAAC,YAAY,aAAa,eAAe,cAAc;AAGvF,SAAS,kBAAkB,OAAqC;AACrE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,EAAE,GAAG,GAAG,OAAO,IAAI;AACzB,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AACzG,MAAI,WAAW,UAAa,CAAC,QAAQ,SAAS,MAAoB,EAAG,QAAO;AAC5E,SAAO,WAAW,SAAY,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAA6B;AAChF;AAEA,SAAS,UAA0B;AACjC,MAAI;AACF,WAAO,OAAO,WAAW,cAAc,OAAO,OAAO;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,qBAAqB,SAAiB,qBAAwC;AAC5F,SAAO;AAAA,IACL,KAAK,IAAI;AACP,UAAI;AACF,cAAM,MAAM,QAAQ,GAAG,QAAQ,SAAS,EAAE;AAC1C,eAAO,MAAM,kBAAkB,KAAK,MAAM,GAAG,CAAC,IAAI;AAAA,MACpD,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM,IAAI,UAAU;AAClB,UAAI;AACF,cAAM,OAAO,QAAQ;AACrB,YAAI,CAAC,KAAM;AACX,YAAI,SAAU,MAAK,QAAQ,SAAS,IAAI,KAAK,UAAU,QAAQ,CAAC;AAAA,YAC3D,MAAK,WAAW,SAAS,EAAE;AAAA,MAClC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,sBAAyC,EAAE,MAAM,MAAM,MAAM,OAAO,MAAM,OAAU;AAEjG,IAAM,eAAe,qBAAqB;AAE1C,IAAM,mBAAmB,cAAwC,IAAI;AAQ9D,SAAS,aAAa,EAAE,OAAO,SAAS,GAAiC;AAC9E,SAAO,cAAc,iBAAiB,UAAU,EAAE,OAAO,MAAM,GAAG,QAAQ;AAC5E;AAGO,SAAS,aAAa,KAA4C;AACvE,QAAM,WAAW,WAAW,gBAAgB;AAC5C,SAAO,OAAO,YAAY;AAC5B;;;AFlBO,IAAM,sBAAsB;AAkEnC,IAAM,eAAyB,EAAE,OAAO,MAAM,QAAQ,IAAI;AAC1D,IAAM,gBAAgB,MAAM,MAAM;AAOlC,SAAS,eAAyB;AAChC,SAAO,OAAO,WAAW,cAAc,eAAe,EAAE,OAAO,OAAO,YAAY,QAAQ,OAAO,YAAY;AAC/G;AAEA,SAAS,QAAQ,SAAuC;AACtD,SAAO,EAAE,OAAO,SAAS,eAAe,GAAG,QAAQ,SAAS,gBAAgB,EAAE;AAChF;AAEA,SAAS,cAAc,OAAgC,MAAwC;AAC7F,MAAI,CAAC,SAAS,OAAO,aAAa,YAAa,QAAO,CAAC;AACvD,SAAO,CAAC,GAAG,SAAS,iBAA8B,MAAM,QAAQ,CAAC,EAC9D,OAAO,CAAC,WAAW,WAAW,QAAQ,CAAC,MAAM,SAAS,MAAM,CAAC,EAC7D,IAAI,CAAC,WAAW,OAAO,sBAAsB,CAAC,EAC9C,OAAO,CAAC,SAAS,KAAK,QAAQ,KAAK,KAAK,SAAS,CAAC;AACvD;AAEO,SAAS,QAAQ,SAAkC;AACxD,QAAM;AAAA,IACJ;AAAA,IACA,SAAS;AAAA,IACT,kBAAkB;AAAA,IAClB;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,SAAS;AAAA,EACX,IAAI;AACJ,QAAM,eAAe,eAAe;AACpC,QAAM,gBAAgB,aAAa,QAAQ,KAAK;AAChD,QAAM,QAAQ,WAAW,CAAC,eAAe,gBAAgB;AAEzD,QAAM,CAAC,SAAS,UAAU,IAAI,SAA6B,IAAI;AAC/D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA8B,IAAI;AAE5D,QAAM,CAAC,SAAS,UAAU,IAAI,SAA8E,IAAI;AAChH,QAAM,CAAC,UAAU,WAAW,IAAI,SAAmB,EAAE,UAAU,cAAc,MAAM,EAAE,OAAO,GAAG,QAAQ,EAAE,EAAE,CAAC;AAC5G,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA6B,IAAI;AAC3D,QAAM,cAAc,OAAsB,IAAI;AAG9C,QAAM,SAA8B,eAChC,WAAW,aAAa,QAAQ,SAAS,UAAU,IAAI,QAAQ,QAAQ,aACvE;AACJ,QAAM,MAA2B,UAAU;AAC3C,QAAM,YAAY,KAAK,UAAU;AACjC,QAAM,SAAS,MAAM,YAAY,KAAK,WAAW,SAAS,MAAM,SAAS,UAAU,KAAK,IAAI;AAG5F,QAAM,OAAO,OAAO,EAAE,QAAQ,WAAW,OAAO,OAAO,WAAW,cAAc,kBAAkB,cAAc,YAAY,OAAO,GAAG,CAAC;AACvI,YAAU,MAAM;AACd,SAAK,UAAU,EAAE,QAAQ,WAAW,OAAO,OAAO,WAAW,cAAc,kBAAkB,cAAc,YAAY,OAAO,GAAG;AAAA,EACnI,CAAC;AAGD,kBAAgB,MAAM;AACpB,QAAI,CAAC,WAAW,YAAY,YAAY,GAAI;AAC5C,gBAAY,UAAU;AACtB,QAAI,aAAc;AAClB,aAAS,MAAM,KAAK,EAAE,CAAC;AAAA,EACzB,GAAG,CAAC,SAAS,IAAI,cAAc,KAAK,CAAC;AAGrC,kBAAgB,MAAM;AACpB,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,QAAuB;AAC3B,UAAM,OAAO,MAAM;AACjB,YAAM,OAAiB,EAAE,UAAU,aAAa,GAAG,MAAM,QAAQ,OAAO,EAAE;AAC1E;AAAA,QAAY,CAAC,YACX,QAAQ,SAAS,UAAU,KAAK,SAAS,SACzC,QAAQ,SAAS,WAAW,KAAK,SAAS,UAC1C,QAAQ,KAAK,UAAU,KAAK,KAAK,SACjC,QAAQ,KAAK,WAAW,KAAK,KAAK,SAC9B,UACA;AAAA,MACN;AAAA,IACF;AACA,UAAM,WAAW,MAAM;AACrB,UAAI,UAAU,KAAM;AACpB,cAAQ,OAAO,sBAAsB,MAAM;AACzC,gBAAQ;AACR,aAAK;AAAA,MACP,CAAC;AAAA,IACH;AACA,SAAK;AACL,UAAM,WAAW,OAAO,mBAAmB,cAAc,OAAO,IAAI,eAAe,QAAQ;AAC3F,cAAU,QAAQ,OAAO;AACzB,WAAO,iBAAiB,UAAU,QAAQ;AAC1C,WAAO,MAAM;AACX,gBAAU,WAAW;AACrB,aAAO,oBAAoB,UAAU,QAAQ;AAC7C,UAAI,UAAU,KAAM,QAAO,qBAAqB,KAAK;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAIZ,QAAM,gBAAgB,OAAO;AAC7B,QAAM,WAAW,OAAO;AACxB,kBAAgB,MAAM;AACpB,QAAI,CAAC,WAAW,CAAC,iBAAiB,QAAQ;AACxC,eAAS,CAAC,YAAa,YAAY,OAAO,UAAU,IAAK;AACzD,aAAO;AAAA,IACT;AACA,QAAI,QAAuB;AAC3B,QAAI,UAA8B;AAClC,UAAM,YAAY,MAAM;AACtB,YAAM,OAAO,QAAQ,sBAAsB;AAC3C,YAAM,OAAO,QAAQ,OAAO;AAC5B,YAAM,WAAW,aAAa;AAC9B,YAAM,OAAO,EAAE,MAAM,KAAK,QAAQ,SAAS,QAAQ,IAAI,KAAK,KAAK,OAAO,SAAS,OAAO,GAAG;AAC3F,YAAM,OAAO,CAAC,MAAmB;AAC/B,cAAM,IAAI,YAAY,WAAW,GAAG,YAAY,MAAM,QAAQ,GAAG,YAAY,MAAM,UAAU,KAAK,QAAQ,KAAK;AAC/G,eAAO,SAAS,GAAG,YAAY,MAAM,QAAQ;AAAA,MAC/C;AACA,YAAM,QAAQ,gBAAgB,MAAM,MAAM,cAAc,EAAE,UAAU,cAAc,GAAG,OAAO,GAAG,YAAY,qBAAqB,IAAI;AACpI,YAAM,OAAO,EAAE,MAAM,KAAK,MAAM,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,MAAM,MAAM,KAAK,GAAG,EAAE;AAC/F,gBAAU,KAAK,SAAS,KAAK,KAAK,QAAQ,IAAI,OAAO;AACrD;AAAA,QAAS,CAAC,YACP,YAAY,QAAQ,YAAY,QAAU,WAAW,WAAW,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,MACtH,UACA;AAAA,MACN;AAAA,IACF;AACA,UAAM,WAAW,MAAM;AACrB,UAAI,UAAU,KAAM;AACpB,cAAQ,OAAO,sBAAsB,MAAM;AACzC,gBAAQ;AACR,kBAAU;AAAA,MACZ,CAAC;AAAA,IACH;AACA,cAAU;AACV,UAAM,YAAY,OAAO,qBAAqB,cAAc,OAAO,IAAI,iBAAiB,QAAQ;AAChG,eAAW,QAAQ,SAAS,MAAM,EAAE,WAAW,MAAM,SAAS,KAAK,CAAC;AACpE,WAAO,iBAAiB,UAAU,QAAQ;AAC1C,WAAO,iBAAiB,UAAU,UAAU,IAAI;AAChD,WAAO,MAAM;AACX,iBAAW,WAAW;AACtB,aAAO,oBAAoB,UAAU,QAAQ;AAC7C,aAAO,oBAAoB,UAAU,UAAU,IAAI;AACnD,UAAI,UAAU,KAAM,QAAO,qBAAqB,KAAK;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,SAAS,eAAe,UAAU,WAAW,IAAI,CAAC;AAGtD,MAAI,SAA6B;AACjC,MAAI,QAAQ;AACV,aAAS,SAAS,QAAQ,WAAW,SAAS,MAAM,SAAS,QAAQ;AACrE,QAAI,SAAS,SAAS;AACpB,YAAM,OAAO,SAAS;AACtB,YAAM,WAAW,SAAS;AAC1B,YAAM,OAAO,CAAC,MACZ,SAAS,YAAY,WAAW,GAAG,YAAY,MAAM,QAAQ,GAAG,YAAY,MAAM,UAAU,KAAK,GAAG,YAAY,MAAM,QAAQ;AAChI,eAAS,gBAAgB,QAAQ,MAAM,cAAc,OAAO,OAAO,GAAG,MAAM,OAAO,qBAAqB,IAAI;AAAA,IAC9G;AAAA,EACF;AACA,QAAM,iBAAiB,UAAU,SAAS,WAAW,QAAQ,WAAW,SAAS,MAAM,SAAS,QAAQ,IAAI;AAG5G,QAAM,cAAc,uBAAuB;AAC3C,QAAM,QAAQ;AAAA,IACZ,cAAc,YAAY,YAAY;AAAA,IACtC,MAAO,cAAe,YAAY,SAAS,EAAkE,eAAe,aAAa,EAAE,IAAI;AAAA,IAC/I,MAAM;AAAA,EACR;AACA,YAAU,MAAM;AACd,QAAI,CAAC,YAAa,QAAO;AACzB,WAAO,MAAM;AACX,YAAM,QAAQ,YAAY,SAAS;AACnC,UAAI,MAAM,eAAe,aAAa,EAAE,MAAM,OAAW,aAAY,SAAS,uBAAuB,EAAE,CAAC;AAAA,IAC1G;AAAA,EACF,GAAG,CAAC,aAAa,EAAE,CAAC;AACpB,QAAM,SAAS,SAAS;AAOxB,QAAM,SAAS,YAAY,CAAC,SAA8B;AACxD,UAAM,UAAU,KAAK;AACrB,QAAI,QAAQ,aAAc,YAAW,EAAE,OAAO,MAAM,SAAS,QAAQ,cAAc,KAAK,CAAC;AAAA,SACpF;AACH,eAAS,IAAI;AACb,cAAQ,MAAM,MAAM,QAAQ,IAAI,IAAI;AAAA,IACtC;AACA,YAAQ,mBAAmB,IAAI;AAAA,EACjC,GAAG,CAAC,CAAC;AAGL,QAAM,WAAW,OAAmD,EAAE,QAAQ,UAAU,aAAa,CAAC;AACtG,QAAM,aAAa,CAAC,OAAoB,MAAgB,aACtD,SAAS,YAAY,WAAW,OAAO,YAAY,MAAM,QAAQ,GAAG,YAAY,MAAM,UAAU,KAAK,QAAQ,KAAK,GAAG,YAAY,MAAM,QAAQ;AAEjJ,QAAM,OAAO,YAAY;AAAA,IACvB;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,MAAM;AACb,UAAI,gBAAgB,YAAa,aAAY,SAAS,qBAAqB,EAAE,CAAC;AAAA,IAChF;AAAA;AAAA;AAAA,IAGA,SAAS,MAAM;AACb,YAAM,WAAW,aAAa;AAC9B,YAAM,OAAO,QAAQ,OAAO;AAC5B,YAAM,cAAc,KAAK,QAAQ;AACjC,YAAM,OAAO,SAAS,sBAAsB;AAC5C,YAAM,cACJ,KAAK,QAAQ,UAAU,WAAW,EAAE,MAAM,MAAM,QAAQ,GAAG,KAAK,MAAM,OAAO,EAAE,GAAG,aAAa,MAAM,QAAQ;AAC/G,eAAS,UAAU,EAAE,QAAQ,aAAa,SAAS;AACnD,YAAM,QAAQ,SAAS,aAAa,aAAa,MAAM,QAAQ;AAC/D,aAAO,EAAE,GAAG,MAAM,MAAM,GAAG,MAAM,KAAK,GAAG,KAAK;AAAA,IAChD;AAAA,IACA,WAAW,CAAC,SAAmB;AAC7B,YAAM,EAAE,QAAQ,aAAa,SAAS,IAAI,SAAS;AACnD,YAAM,OAAO,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AACtD,YAAM,OAAO,KAAK;AAClB,YAAM,UAAU,WAAW,EAAE,MAAM,KAAK,GAAG,KAAK,KAAK,EAAE,GAAG,aAAa,MAAM,QAAQ;AACrF,UAAI,OAAO,SAAS,YAAY,SAAS,aAAa,MAAM,UAAU,KAAK,KAAK,GAAG,aAAa,MAAM,QAAQ;AAC9G,UAAI,KAAK;AACP,eAAO,gBAAgB,MAAM,MAAM,cAAc,KAAK,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,qBAAqB,CAAC,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC;AACpJ,aAAO,EAAE,GAAG,MAAM,GAAG,KAAK,MAAM,GAAG,KAAK,IAAI;AAAA,IAC9C;AAAA,IACA,UAAU,CAAC,SAAmB;AAC5B,YAAM,EAAE,QAAQ,aAAa,SAAS,IAAI,SAAS;AACnD,YAAM,OAAO,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AACtD,YAAM,SAAS,EAAE,MAAM,KAAK,GAAG,KAAK,KAAK,EAAE;AAC3C,YAAM,OAAO,KAAK;AAClB,YAAM,cAAc,KAAK,eAAe,cAAc,QAAQ,MAAM,QAAQ,IAAI;AAChF,YAAM,cAAc,YAAY,WAAW,QAAQ,aAAa,MAAM,QAAQ,GAAG,aAAa,MAAM,UAAU,KAAK,KAAK;AACxH,aAAO,gBAAgB,SAAS,EAAE,GAAG,YAAY,GAAG,GAAG,YAAY,EAAE,IAAI,EAAE,GAAG,aAAa,QAAQ,YAAY,CAAC;AAAA,IAClH;AAAA,EACF,CAAC;AACD,QAAM,WAAW,KAAK;AACtB,QAAM,gBAAgB,KAAK;AAE3B,QAAM,QAAQ,YAAY,MAAM,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC;AAGtD,MAAI;AACJ,MAAI,gBAAgB;AAClB,UAAM,OAAO,UAAU,SAAS,MAAM;AACtC,UAAM,MAAM,UAAU,WAAW,KAAK;AACtC,YAAQ;AAAA,MACN,UAAU;AAAA,MACV,MAAM,OAAO,eAAe,IAAI;AAAA,MAChC,OAAO,OAAO,SAAS,eAAe;AAAA,MACtC,KAAK,MAAM,eAAe,IAAI;AAAA,MAC9B,QAAQ,MAAM,SAAS,eAAe;AAAA,IACxC;AAAA,EACF,OAAO;AACL,UAAM,YAAY,CAAC,QAAQ,UAAU,qBAAqB,MAAM,QAAQ,aAAa,MAAM,IAAI,OAAO,MAAM,GAAG,QAAQ,IAAI,EACxH,OAAO,OAAO,EACd,KAAK,GAAG;AACX,YAAQ;AAAA,MACN,UAAU;AAAA,MACV,KAAK,QAAQ;AAAA,MACb,QAAQ,QAAQ;AAAA,MAChB,MAAM,QAAQ,UAAU,QAAQ,QAAQ;AAAA,MACxC,OAAO,QAAQ;AAAA,MACf,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,IACnC;AAAA,EACF;AACA,MAAI,WAAW,OAAW,OAAM,SAAS;AAEzC,SAAO;AAAA,IACL,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa;AAAA,MACX;AAAA,MACA,OAAO,YAAY,EAAE,QAAQ,QAAQ,aAAa,QAAQ,SAAS,OAAO,IAAI,CAAC;AAAA,MAC/E,oBAAoB;AAAA,IACtB;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA,OAAO,WAAW;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AACF;;;ADvYM;AAhCN,IAAM,cAAcC,eAA8B,IAAI;AAG/C,SAAS,iBAA0B;AACxC,QAAM,OAAOC,YAAW,WAAW;AACnC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,sDAAsD;AACjF,SAAO;AACT;AAQA,IAAM,cAAc;AAAA,EAClB;AAAA,EAAM;AAAA,EAAU;AAAA,EAAmB;AAAA,EAAU;AAAA,EAAY;AAAA,EAAoB;AAAA,EAAW;AAAA,EAAS;AAAA,EACjG;AAAA,EAAS;AAAA,EAAa;AAAA,EAAS;AAAA,EAAS;AAAA,EAAgB;AAAA,EAAU;AAAA,EAAgB;AACpF;AAEO,SAAS,KAAK,OAA6B;AAChD,QAAM,UAAU,CAAC;AACjB,QAAM,OAAgC,CAAC;AACvC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAK,YAAkC,SAAS,GAAG,EAAG,CAAC,QAA+C,GAAG,IAAI;AAAA,aACpG,QAAQ,YAAY,QAAQ,cAAc,QAAQ,QAAS,MAAK,GAAG,IAAI;AAAA,EAClF;AACA,QAAM,OAAO,QAAQ,OAAO;AAC5B,QAAM,OAAO,MAAM,WAAW;AAC9B,QAAM,QAAuB,EAAE,GAAG,MAAM,OAAO,GAAG,KAAK,OAAO,GAAI,OAAO,KAAK,YAAY,QAAQ,CAAC,EAAG;AACtG,SACE,oBAAC,YAAY,UAAZ,EAAqB,OAAO,MAC3B;AAAA,IAAC;AAAA;AAAA,MACE,GAAI;AAAA,MACL,KAAK,KAAK;AAAA,MACV,aAAW,MAAM;AAAA,MACjB,iBAAe,KAAK,WAAW,KAAK;AAAA,MACpC;AAAA,MACC,GAAI,OAAO,EAAE,eAAe,KAAK,YAAY,eAAe,oBAAoB,GAAG,IAAI,CAAC;AAAA,MAExF,iBAAO,MAAM,aAAa,aAAa,MAAM,SAAS,IAAI,IAAI,MAAM;AAAA;AAAA,EACvE,GACF;AAEJ;AAOO,SAAS,WAAW,EAAE,OAAO,eAAe,GAAG,KAAK,GAA+B;AACxF,QAAM,OAAO,eAAe;AAC5B,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACH,GAAG,KAAK;AAAA,MACT,OAAO,EAAE,GAAG,OAAO,GAAG,KAAK,YAAY,MAAM;AAAA,MAC7C,eAAe,CAAC,UAAU;AACxB,wBAAgB,KAAK;AACrB,aAAK,YAAY,cAAc,KAAK;AAAA,MACtC;AAAA;AAAA,EACF;AAEJ;","names":["createContext","useContext","createContext","useContext"]}
package/dist/handles.js CHANGED
@@ -1,275 +1,12 @@
1
- // src/open/ids.ts
2
- function newHandleId() {
3
- const c = typeof globalThis.crypto === "undefined" ? void 0 : globalThis.crypto;
4
- if (c && typeof c.randomUUID === "function") return c.randomUUID();
5
- const bytes = new Uint8Array(16);
6
- if (c && typeof c.getRandomValues === "function") {
7
- c.getRandomValues(bytes);
8
- } else {
9
- for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);
10
- }
11
- bytes[6] = (bytes[6] ?? 0) & 15 | 64;
12
- bytes[8] = (bytes[8] ?? 0) & 63 | 128;
13
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
14
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
15
- }
16
-
17
- // src/handles/slot.ts
18
- var HANDLES_SLOT = "ai-matrx.summon.handles";
19
-
20
- // src/open/handleEntries.ts
21
- import { globalSlot } from "@ai-matrx/kit/global-slot";
22
- var handlesState = () => globalSlot(HANDLES_SLOT, () => ({
23
- callbacks: /* @__PURE__ */ new Map(),
24
- groups: /* @__PURE__ */ new Map(),
25
- owners: /* @__PURE__ */ new Map(),
26
- registryListeners: /* @__PURE__ */ new Set()
27
- }));
28
- function notifyHandlesRegistry() {
29
- for (const listener of [...handlesState().registryListeners]) listener();
30
- }
31
- function deleteHandleEntry(id, notify = true) {
32
- const s = handlesState();
33
- const entry = s.callbacks.get(id);
34
- if (!entry) return;
35
- s.callbacks.delete(id);
36
- if (entry.groupId !== void 0) {
37
- const group = s.groups.get(entry.groupId);
38
- group?.delete(id);
39
- if (group?.size === 0) s.groups.delete(entry.groupId);
40
- }
41
- if (entry.owner !== void 0) {
42
- const owned = s.owners.get(entry.owner);
43
- owned?.delete(id);
44
- if (owned?.size === 0) s.owners.delete(entry.owner);
45
- }
46
- if (notify) notifyHandlesRegistry();
47
- entry.onRemove?.();
48
- }
49
- function registerHandleEntry(slot, options = {}) {
50
- const s = handlesState();
51
- const id = options.id ?? newHandleId();
52
- if (s.callbacks.has(id)) deleteHandleEntry(id, false);
53
- const entry = { callback: slot };
54
- if (options.context !== void 0) entry.context = options.context;
55
- if (options.groupId !== void 0) entry.groupId = options.groupId;
56
- if (options.owner !== void 0) entry.owner = options.owner;
57
- if (options.onRemove !== void 0) entry.onRemove = options.onRemove;
58
- s.callbacks.set(id, entry);
59
- if (options.groupId !== void 0) {
60
- const group = s.groups.get(options.groupId) ?? /* @__PURE__ */ new Set();
61
- group.add(id);
62
- s.groups.set(options.groupId, group);
63
- }
64
- if (options.owner !== void 0) {
65
- const owned = s.owners.get(options.owner) ?? /* @__PURE__ */ new Set();
66
- owned.add(id);
67
- s.owners.set(options.owner, owned);
68
- }
69
- notifyHandlesRegistry();
70
- return id;
71
- }
72
- function removeHandleGroup(groupId) {
73
- const s = handlesState();
74
- const group = s.groups.get(groupId);
75
- if (!group) return;
76
- s.groups.delete(groupId);
77
- for (const id of [...group]) deleteHandleEntry(id, false);
78
- notifyHandlesRegistry();
79
- }
80
-
81
- // src/handles/callbackManager.ts
82
- var handlesState2 = () => handlesState();
83
- function invoke(slot, data, context) {
84
- if (typeof slot !== "function") return void 0;
85
- return slot(data, context);
86
- }
87
- function invokeListener(listener, data, context) {
88
- return listener(data, context);
89
- }
90
- var CallbackManager = class {
91
- get s() {
92
- return handlesState2();
93
- }
94
- /**
95
- * Observe the registry itself: `listener` runs after any entry is added or removed. This lets a
96
- * view that resolves ids with `get` re-render when an id goes away (`useSyncExternalStore`).
97
- */
98
- subscribeToRegistry(listener) {
99
- const listeners = this.s.registryListeners;
100
- listeners.add(listener);
101
- return () => {
102
- listeners.delete(listener);
103
- };
104
- }
105
- notifyRegistry() {
106
- notifyHandlesRegistry();
107
- }
108
- /** Register a callback; returns its id. */
109
- register(callback) {
110
- return this.registerEntry(callback, {});
111
- }
112
- /** Register with optional context, group and owner. */
113
- registerWithContext(callback, options = {}) {
114
- return this.registerEntry(callback, options);
115
- }
116
- /** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */
117
- registerEntry(slot, options) {
118
- return registerHandleEntry(slot, options);
119
- }
120
- /** Subscribe to results for a callback id. */
121
- subscribe(callbackId, listener) {
122
- const entry = this.s.callbacks.get(callbackId);
123
- if (!entry) return false;
124
- (entry.listeners ??= []).push(listener);
125
- return true;
126
- }
127
- /** Unsubscribe a listener from a callback id. */
128
- unsubscribe(callbackId, listener) {
129
- const entry = this.s.callbacks.get(callbackId);
130
- if (!entry || !entry.listeners) return false;
131
- const index = entry.listeners.indexOf(listener);
132
- if (index === -1) return false;
133
- entry.listeners.splice(index, 1);
134
- return true;
135
- }
136
- /** Run a callback once, notify its listeners, then remove it. */
137
- trigger(callbackId, data) {
138
- const entry = this.s.callbacks.get(callbackId);
139
- if (!entry) return;
140
- void invoke(entry.callback, data);
141
- if (entry.listeners && entry.listeners.length > 0) {
142
- for (const listener of [...entry.listeners]) void invokeListener(listener, data);
143
- }
144
- this.deleteEntry(callbackId, true);
145
- }
146
- /** Trigger with merged context and progress; removes unless `removeAfterTrigger: false`. */
147
- triggerWithContext(callbackId, data, options) {
148
- const entry = this.s.callbacks.get(callbackId);
149
- if (!entry) return;
150
- const mergedContext = {
151
- ...entry.context,
152
- ...options?.context,
153
- ...options?.progress && { progress: options.progress }
154
- };
155
- void invoke(entry.callback, data, mergedContext);
156
- if (entry.listeners && entry.listeners.length > 0) {
157
- for (const listener of [...entry.listeners]) void invokeListener(listener, data, mergedContext);
158
- }
159
- if (options?.removeAfterTrigger !== false) this.deleteEntry(callbackId, true);
160
- }
161
- /** Trigger every callback in a group. */
162
- triggerGroup(groupId, data, options) {
163
- const group = this.s.groups.get(groupId);
164
- if (!group) return;
165
- for (const callbackId of [...group]) this.triggerWithContext(callbackId, data, options);
166
- if (options?.removeAfterTrigger !== false) this.s.groups.delete(groupId);
167
- }
168
- /**
169
- * Run the one durable command registered for a group. A command is retained on rejection so its
170
- * caller can retry, and is removed only after its promise settles successfully.
171
- */
172
- async triggerGroupCommand(groupId, data, options) {
173
- const group = this.s.groups.get(groupId);
174
- const callbackIds = group ? [...group] : [];
175
- const callbackId = callbackIds[0];
176
- if (callbackIds.length !== 1 || callbackId === void 0) {
177
- throw new Error(`Expected exactly one command callback for group "${groupId}", found ${callbackIds.length}`);
178
- }
179
- const entry = this.s.callbacks.get(callbackId);
180
- if (!entry) throw new Error(`Command callback "${callbackId}" is no longer available`);
181
- const mergedContext = { ...entry.context, ...options?.context };
182
- await invoke(entry.callback, data, mergedContext);
183
- for (const listener of entry.listeners ?? []) {
184
- try {
185
- await invokeListener(listener, data, mergedContext);
186
- } catch (error) {
187
- console.error("[overlays/handles] command observer failed", error);
188
- }
189
- }
190
- if (options?.removeAfterSuccess !== false) {
191
- this.deleteEntry(callbackId, true);
192
- this.s.groups.delete(groupId);
193
- }
194
- }
195
- /** Report progress to a callback (or every callback of a group) without removing it. */
196
- updateProgress(id, progress, options) {
197
- const progressInfo = { progress, status: options?.status || "running" };
198
- if (options?.error !== void 0) progressInfo.error = options.error;
199
- if (options?.groupId) {
200
- this.triggerGroup(id, null, { progress: progressInfo, removeAfterTrigger: false });
201
- } else {
202
- this.triggerWithContext(id, null, { progress: progressInfo, removeAfterTrigger: false });
203
- }
204
- }
205
- /** Create a new, empty group and return its id. */
206
- createGroup() {
207
- const groupId = newHandleId();
208
- this.s.groups.set(groupId, /* @__PURE__ */ new Set());
209
- return groupId;
210
- }
211
- /** Remove one entry (and its group/owner membership). */
212
- remove(callbackId) {
213
- this.deleteEntry(callbackId, true);
214
- }
215
- /** Alias of `remove` — the preferred name when unregistering a handle on unmount. */
216
- unregister(callbackId) {
217
- this.remove(callbackId);
218
- }
219
- /** The stored callback or object handle for an id, without invoking it. */
220
- get(callbackId) {
221
- return this.s.callbacks.get(callbackId)?.callback;
222
- }
223
- /** True when the id is registered. */
224
- has(callbackId) {
225
- return this.s.callbacks.has(callbackId);
226
- }
227
- /**
228
- * Register an object handle (a widget handle, an editor API, …). The object occupies the slot;
229
- * it is never invoked by `trigger`, only retrieved with `get`.
230
- */
231
- registerWidgetHandle(handle, options = {}) {
232
- return this.registerEntry(handle, options.owner === void 0 ? {} : { owner: options.owner });
233
- }
234
- /** Remove a group and all its callbacks. */
235
- removeGroup(groupId) {
236
- removeHandleGroup(groupId);
237
- }
238
- /** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */
239
- disposeOwner(owner) {
240
- const s = this.s;
241
- const owned = s.owners.get(owner);
242
- if (!owned) return 0;
243
- s.owners.delete(owner);
244
- const ids = [...owned];
245
- for (const callbackId of ids) this.deleteEntry(callbackId, false);
246
- if (ids.length > 0) this.notifyRegistry();
247
- return ids.length;
248
- }
249
- /** Ids currently owned by `owner`. */
250
- ownedBy(owner) {
251
- return [...this.s.owners.get(owner) ?? []];
252
- }
253
- /** The member ids of a group (empty when unknown). */
254
- groupMembers(groupId) {
255
- return [...this.s.groups.get(groupId) ?? []];
256
- }
257
- /** Total registered entries (diagnostics, leak tests). */
258
- size() {
259
- return this.s.callbacks.size;
260
- }
261
- /**
262
- * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink
263
- * (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.
264
- */
265
- deleteEntry(callbackId, notify) {
266
- deleteHandleEntry(callbackId, notify);
267
- }
268
- };
269
- var callbackManager = new CallbackManager();
270
- function disposeOwner(owner) {
271
- return callbackManager.disposeOwner(owner);
272
- }
1
+ import {
2
+ CallbackManager,
3
+ callbackManager,
4
+ disposeOwner
5
+ } from "./chunk-server-WP7NOTZQ.js";
6
+ import {
7
+ HANDLES_SLOT,
8
+ newHandleId
9
+ } from "./chunk-server-H7NUWPEM.js";
273
10
 
274
11
  // src/handles/helpers.ts
275
12
  var HandleDisposedError = class extends Error {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/open/ids.ts","../src/handles/slot.ts","../src/open/handleEntries.ts","../src/handles/callbackManager.ts","../src/handles/helpers.ts"],"sourcesContent":["/** A fresh handle id: `crypto.randomUUID` when present, else a v4 UUID from `getRandomValues`. */\nexport function newHandleId(): string {\n const c: Crypto | undefined = typeof globalThis.crypto === \"undefined\" ? undefined : globalThis.crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const bytes = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n }\n bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/** The global slot that holds the handles registry (one registry per realm, whatever the bundling). */\nexport const HANDLES_SLOT = \"ai-matrx.summon.handles\";\n","// The handles registry's STATE and its insert/remove path, without the registry's verbs (trigger,\n// subscribe, requests, channels). A boot-time opener that only needs an id NOW — register a\n// callback, a payload group, an object handle; drop it on close — writes through here and never\n// puts `@ai-matrx/summon/handles` in first load; whoever later triggers or reads loads the full\n// registry, which works on the same global slot through these same functions (one removal path).\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { HANDLES_SLOT } from \"../handles/slot\";\nimport { newHandleId } from \"./ids\";\n\n/** A registry slot: a callback (invoked by triggers) or an object handle (only ever read). */\nexport type HandleEntrySlot = ((...args: never[]) => unknown) | object;\n\n/** @internal A registry entry (the handles section adds `listeners`). */\nexport interface HandleEntry {\n callback: HandleEntrySlot;\n context?: Record<string, unknown>;\n groupId?: string;\n owner?: string;\n listeners?: Array<(...args: never[]) => unknown>;\n /** Runs once when the entry leaves the registry for any reason (requests reject here). */\n onRemove?: () => void;\n}\n\n/** @internal The registry state in its global slot (one per realm, whatever the bundling). */\nexport interface HandlesState {\n readonly callbacks: Map<string, HandleEntry>;\n readonly groups: Map<string, Set<string>>;\n readonly owners: Map<string, Set<string>>;\n readonly registryListeners: Set<() => void>;\n}\n\nexport interface HandleEntryOptions {\n /** A fixed id (default: a fresh `newHandleId()`); an existing entry with it is replaced. */\n id?: string;\n context?: Record<string, unknown>;\n /** Join (or start) this group; `removeHandleGroup` drops every member. */\n groupId?: string;\n /** The InstanceKey (`overlayId:instanceId`) that owns this entry; disposed when it closes. */\n owner?: string;\n onRemove?: () => void;\n}\n\n/** @internal */\nexport const handlesState = (): HandlesState =>\n globalSlot<HandlesState>(HANDLES_SLOT, () => ({\n callbacks: new Map(),\n groups: new Map(),\n owners: new Map(),\n registryListeners: new Set(),\n }));\n\n/** @internal */\nexport function notifyHandlesRegistry(): void {\n for (const listener of [...handlesState().registryListeners]) listener();\n}\n\n/**\n * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink,\n * then runs the entry's removal hook.\n */\nexport function deleteHandleEntry(id: string, notify = true): void {\n const s = handlesState();\n const entry = s.callbacks.get(id);\n if (!entry) return;\n s.callbacks.delete(id);\n if (entry.groupId !== undefined) {\n const group = s.groups.get(entry.groupId);\n group?.delete(id);\n if (group?.size === 0) s.groups.delete(entry.groupId);\n }\n if (entry.owner !== undefined) {\n const owned = s.owners.get(entry.owner);\n owned?.delete(id);\n if (owned?.size === 0) s.owners.delete(entry.owner);\n }\n if (notify) notifyHandlesRegistry();\n entry.onRemove?.();\n}\n\n/** Register a callback or object handle NOW (no registry load); returns its id. */\nexport function registerHandleEntry(slot: HandleEntrySlot, options: HandleEntryOptions = {}): string {\n const s = handlesState();\n const id = options.id ?? newHandleId();\n if (s.callbacks.has(id)) deleteHandleEntry(id, false);\n const entry: HandleEntry = { callback: slot };\n if (options.context !== undefined) entry.context = options.context;\n if (options.groupId !== undefined) entry.groupId = options.groupId;\n if (options.owner !== undefined) entry.owner = options.owner;\n if (options.onRemove !== undefined) entry.onRemove = options.onRemove;\n s.callbacks.set(id, entry);\n if (options.groupId !== undefined) {\n const group = s.groups.get(options.groupId) ?? new Set<string>();\n group.add(id);\n s.groups.set(options.groupId, group);\n }\n if (options.owner !== undefined) {\n const owned = s.owners.get(options.owner) ?? new Set<string>();\n owned.add(id);\n s.owners.set(options.owner, owned);\n }\n notifyHandlesRegistry();\n return id;\n}\n\n/** `callbackManager.unregister(id)` without loading the registry. */\nexport function unregisterHandleEntry(id: string): void {\n deleteHandleEntry(id, true);\n}\n\n/** `callbackManager.removeGroup(groupId)` without loading the registry. */\nexport function removeHandleGroup(groupId: string): void {\n const s = handlesState();\n const group = s.groups.get(groupId);\n if (!group) return;\n s.groups.delete(groupId);\n for (const id of [...group]) deleteHandleEntry(id, false);\n notifyHandlesRegistry();\n}\n","/**\n * The handles registry — everything that cannot travel through Redux (callbacks, event channels,\n * one-shot requests, imperative object handles) lives here under a string id; only the id goes\n * into overlay open data.\n *\n * Successor of `@ai-matrx/chat` `utils/callbackManager.ts` with the SAME public verbs and types, so\n * moving a caller is an import-path swap. Changes (CHANGELOG 0.1.0):\n * 1. State lives in the global slot \"ai-matrx.summon.handles\", never in module scope: a\n * duplicated module graph (ESM + CJS, unsplit bundles, two packages) sees one registry.\n * 2. Removal bug fixed: `trigger` / `triggerWithContext` / `remove` deleted the entry BEFORE\n * `removeFromGroups` read it, so group membership never shrank and emptied groups were never\n * dropped. Removal now reads the entry first, in one place (`deleteEntry`).\n * 3. Owner scoping: `registerWithContext(cb, { owner })` (an InstanceKey `overlayId:instanceId`)\n * ties the entry to an open overlay instance; `disposeOwner(owner)` removes everything it\n * owns. The overlays close middleware calls it, so no opener disposes by hand.\n * 4. The object slot is handle-agnostic: `registerWidgetHandle<H extends object>(handle)` stores\n * any object (no chat `WidgetHandle` import).\n * 5. Ids come from `crypto.randomUUID` (no `uuid` dependency), with a fallback for insecure\n * contexts where `randomUUID` is absent.\n *\n * Framework-free: no React, no Redux.\n */\nimport { newHandleId } from \"./ids\";\nimport { HANDLES_SLOT } from \"./slot\";\nimport {\n deleteHandleEntry,\n handlesState as sharedHandlesState,\n notifyHandlesRegistry,\n registerHandleEntry,\n removeHandleGroup,\n type HandlesState as SharedHandlesState,\n} from \"../open/handleEntries\";\n\nexport type CallbackContext = Record<string, unknown>;\n\nexport interface ProgressInfo {\n progress?: number;\n status?: \"pending\" | \"running\" | \"completed\" | \"error\";\n error?: Error;\n}\n\nexport type Callback<T = unknown, C extends CallbackContext = CallbackContext> = (\n data: T,\n context?: C,\n) => void | Promise<void>;\n\n/** What an entry's slot holds: a callback, or an object handle retrieved with `get`. */\nexport type HandleSlot = Callback<never, never> | object;\n\nexport interface RegisterOptions<C extends CallbackContext = CallbackContext> {\n context?: C;\n groupId?: string;\n /** The InstanceKey (`overlayId:instanceId`) that owns this entry; disposed when it closes. */\n owner?: string;\n}\n\nexport interface TriggerOptions<C extends CallbackContext = CallbackContext> {\n context?: C;\n progress?: ProgressInfo;\n removeAfterTrigger?: boolean;\n}\n\n/** A registry entry. `callback` is the heterogeneous slot (function or object handle). */\ninterface CallbackEntry {\n callback: HandleSlot;\n context?: CallbackContext;\n groupId?: string;\n owner?: string;\n listeners?: Array<Callback<never, never>>;\n /** Runs once when the entry leaves the registry for any reason (requests reject here). */\n onRemove?: () => void;\n}\n\ninterface HandlesState {\n readonly callbacks: Map<string, CallbackEntry>;\n readonly groups: Map<string, Set<string>>;\n readonly owners: Map<string, Set<string>>;\n readonly registryListeners: Set<() => void>;\n}\n\nexport { HANDLES_SLOT };\n\n// State, insertion and removal are the light path on `./open` (`open/handleEntries.ts`), so a\n// boot-time registrant and this registry share one slot and one removal path.\nconst handlesState = (): HandlesState => sharedHandlesState() as SharedHandlesState as HandlesState;\n\n/** Invoke a slot as a callback (a function slot only; object handles are never invoked). */\nfunction invoke(slot: HandleSlot, data: unknown, context?: CallbackContext): void | Promise<void> {\n if (typeof slot !== \"function\") return undefined;\n return (slot as Callback<unknown, CallbackContext>)(data, context);\n}\n\nfunction invokeListener(listener: Callback<never, never>, data: unknown, context?: CallbackContext): void | Promise<void> {\n return (listener as Callback<unknown, CallbackContext>)(data, context);\n}\n\n/** Internal registration options beyond the public ones (used by the typed helpers). */\nexport interface InternalRegisterOptions extends RegisterOptions {\n id?: string;\n onRemove?: () => void;\n}\n\nexport class CallbackManager {\n private get s(): HandlesState {\n return handlesState();\n }\n\n /**\n * Observe the registry itself: `listener` runs after any entry is added or removed. This lets a\n * view that resolves ids with `get` re-render when an id goes away (`useSyncExternalStore`).\n */\n subscribeToRegistry(listener: () => void): () => void {\n const listeners = this.s.registryListeners;\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n }\n\n private notifyRegistry(): void {\n notifyHandlesRegistry();\n }\n\n /** Register a callback; returns its id. */\n register<T>(callback: Callback<T>): string {\n return this.registerEntry(callback as HandleSlot, {});\n }\n\n /** Register with optional context, group and owner. */\n registerWithContext<T, C extends CallbackContext = CallbackContext>(\n callback: Callback<T, C>,\n options: RegisterOptions<C> = {},\n ): string {\n return this.registerEntry(callback as HandleSlot, options);\n }\n\n /** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */\n registerEntry(slot: HandleSlot, options: InternalRegisterOptions): string {\n return registerHandleEntry(slot, options);\n }\n\n /** Subscribe to results for a callback id. */\n subscribe<T, C extends CallbackContext = CallbackContext>(callbackId: string, listener: Callback<T, C>): boolean {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return false;\n (entry.listeners ??= []).push(listener as Callback<never, never>);\n return true;\n }\n\n /** Unsubscribe a listener from a callback id. */\n unsubscribe<T, C extends CallbackContext = CallbackContext>(callbackId: string, listener: Callback<T, C>): boolean {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry || !entry.listeners) return false;\n const index = entry.listeners.indexOf(listener as Callback<never, never>);\n if (index === -1) return false;\n entry.listeners.splice(index, 1);\n return true;\n }\n\n /** Run a callback once, notify its listeners, then remove it. */\n trigger<T>(callbackId: string, data: T): void {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return;\n void invoke(entry.callback, data);\n if (entry.listeners && entry.listeners.length > 0) {\n for (const listener of [...entry.listeners]) void invokeListener(listener, data);\n }\n this.deleteEntry(callbackId, true);\n }\n\n /** Trigger with merged context and progress; removes unless `removeAfterTrigger: false`. */\n triggerWithContext<T, C extends CallbackContext = CallbackContext>(\n callbackId: string,\n data: T,\n options?: TriggerOptions<C>,\n ): void {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return;\n const mergedContext: CallbackContext = {\n ...entry.context,\n ...options?.context,\n ...(options?.progress && { progress: options.progress }),\n };\n void invoke(entry.callback, data, mergedContext);\n if (entry.listeners && entry.listeners.length > 0) {\n for (const listener of [...entry.listeners]) void invokeListener(listener, data, mergedContext);\n }\n if (options?.removeAfterTrigger !== false) this.deleteEntry(callbackId, true);\n }\n\n /** Trigger every callback in a group. */\n triggerGroup<T, C extends CallbackContext = CallbackContext>(groupId: string, data: T, options?: TriggerOptions<C>): void {\n const group = this.s.groups.get(groupId);\n if (!group) return;\n for (const callbackId of [...group]) this.triggerWithContext(callbackId, data, options);\n if (options?.removeAfterTrigger !== false) this.s.groups.delete(groupId);\n }\n\n /**\n * Run the one durable command registered for a group. A command is retained on rejection so its\n * caller can retry, and is removed only after its promise settles successfully.\n */\n async triggerGroupCommand<T, C extends CallbackContext = CallbackContext>(\n groupId: string,\n data: T,\n options?: { context?: C; removeAfterSuccess?: boolean },\n ): Promise<void> {\n const group = this.s.groups.get(groupId);\n const callbackIds = group ? [...group] : [];\n const callbackId = callbackIds[0];\n if (callbackIds.length !== 1 || callbackId === undefined) {\n throw new Error(`Expected exactly one command callback for group \"${groupId}\", found ${callbackIds.length}`);\n }\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) throw new Error(`Command callback \"${callbackId}\" is no longer available`);\n\n const mergedContext: CallbackContext = { ...entry.context, ...options?.context };\n await invoke(entry.callback, data, mergedContext);\n\n // Observers never control command settlement: the command already acknowledged.\n for (const listener of entry.listeners ?? []) {\n try {\n await invokeListener(listener, data, mergedContext);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"[overlays/handles] command observer failed\", error);\n }\n }\n\n if (options?.removeAfterSuccess !== false) {\n this.deleteEntry(callbackId, true);\n this.s.groups.delete(groupId);\n }\n }\n\n /** Report progress to a callback (or every callback of a group) without removing it. */\n updateProgress(\n id: string,\n progress: number,\n options?: { status?: ProgressInfo[\"status\"]; error?: Error; groupId?: boolean },\n ): void {\n const progressInfo: ProgressInfo = { progress, status: options?.status || \"running\" };\n if (options?.error !== undefined) progressInfo.error = options.error;\n if (options?.groupId) {\n this.triggerGroup(id, null, { progress: progressInfo, removeAfterTrigger: false });\n } else {\n this.triggerWithContext(id, null, { progress: progressInfo, removeAfterTrigger: false });\n }\n }\n\n /** Create a new, empty group and return its id. */\n createGroup(): string {\n const groupId = newHandleId();\n this.s.groups.set(groupId, new Set());\n return groupId;\n }\n\n /** Remove one entry (and its group/owner membership). */\n remove(callbackId: string): void {\n this.deleteEntry(callbackId, true);\n }\n\n /** Alias of `remove` — the preferred name when unregistering a handle on unmount. */\n unregister(callbackId: string): void {\n this.remove(callbackId);\n }\n\n /** The stored callback or object handle for an id, without invoking it. */\n get<T = unknown>(callbackId: string): T | undefined {\n return this.s.callbacks.get(callbackId)?.callback as T | undefined;\n }\n\n /** True when the id is registered. */\n has(callbackId: string): boolean {\n return this.s.callbacks.has(callbackId);\n }\n\n /**\n * Register an object handle (a widget handle, an editor API, …). The object occupies the slot;\n * it is never invoked by `trigger`, only retrieved with `get`.\n */\n registerWidgetHandle<H extends object>(handle: H, options: { owner?: string } = {}): string {\n return this.registerEntry(handle, options.owner === undefined ? {} : { owner: options.owner });\n }\n\n /** Remove a group and all its callbacks. */\n removeGroup(groupId: string): void {\n removeHandleGroup(groupId);\n }\n\n /** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */\n disposeOwner(owner: string): number {\n const s = this.s;\n const owned = s.owners.get(owner);\n if (!owned) return 0;\n s.owners.delete(owner);\n const ids = [...owned];\n for (const callbackId of ids) this.deleteEntry(callbackId, false);\n if (ids.length > 0) this.notifyRegistry();\n return ids.length;\n }\n\n /** Ids currently owned by `owner`. */\n ownedBy(owner: string): readonly string[] {\n return [...(this.s.owners.get(owner) ?? [])];\n }\n\n /** The member ids of a group (empty when unknown). */\n groupMembers(groupId: string): readonly string[] {\n return [...(this.s.groups.get(groupId) ?? [])];\n }\n\n /** Total registered entries (diagnostics, leak tests). */\n size(): number {\n return this.s.callbacks.size;\n }\n\n /**\n * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink\n * (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.\n */\n private deleteEntry(callbackId: string, notify: boolean): void {\n deleteHandleEntry(callbackId, notify);\n }\n}\n\n/** The process-wide handles registry (state in the global slot; every instance shares it). */\nexport const callbackManager = new CallbackManager();\n\n/** Remove everything owned by an open instance (`overlayId:instanceId`). */\nexport function disposeOwner(owner: string): number {\n return callbackManager.disposeOwner(owner);\n}\n","/**\n * Typed helpers over the handles registry: imperative object handles, event channels and one-shot\n * requests. Each is stored in the same registry (so its id can ride overlay open data and `get`\n * still resolves it) and accepts an `owner` InstanceKey so closing the owning overlay disposes it.\n */\nimport { callbackManager } from \"./callbackManager\";\n\nexport interface OwnerOption {\n /** The InstanceKey (`overlayId:instanceId`) that owns this handle; disposed when it closes. */\n owner?: string;\n}\n\n/** Thrown into a request's promise when it is disposed before being answered. */\nexport class HandleDisposedError extends Error {\n readonly handleId: string;\n constructor(handleId: string, message = `[overlays/handles] \"${handleId}\" was disposed before it settled`) {\n super(message);\n this.name = \"HandleDisposedError\";\n this.handleId = handleId;\n }\n}\n\nfunction ownerOptions(options: OwnerOption | undefined): { owner?: string } {\n return options?.owner === undefined ? {} : { owner: options.owner };\n}\n\n// ── Imperative object handles ────────────────────────────────────────────────────────────────\n\n/**\n * Register an imperative object handle under a caller-chosen id (an editor API, a widget handle,\n * a ref's methods). Re-registering an id replaces the previous handle. Returns a disposer that\n * removes the handle only if it is still the one registered (safe for React effect cleanup).\n */\nexport function registerHandle<T extends object>(id: string, handle: T, options?: OwnerOption): () => void {\n callbackManager.registerEntry(handle, { id, ...ownerOptions(options) });\n return () => {\n if (callbackManager.get<T>(id) === handle) callbackManager.remove(id);\n };\n}\n\n/** The object handle registered under `id`, or undefined. */\nexport function getHandle<T extends object>(id: string): T | undefined {\n const value = callbackManager.get<unknown>(id);\n return typeof value === \"object\" && value !== null ? (value as T) : undefined;\n}\n\n/** Remove the handle registered under `id`. */\nexport function removeHandle(id: string): void {\n callbackManager.remove(id);\n}\n\n// ── Event channels ───────────────────────────────────────────────────────────────────────────\n\nexport interface EventChannel<Events> {\n readonly id: string;\n /** Deliver an event to every subscriber (no-op after dispose). */\n emit(event: Events): void;\n /** Listen; returns the unsubscribe. */\n subscribe(listener: (event: Events) => void): () => void;\n /** Remove the channel and drop every subscriber. */\n dispose(): void;\n readonly disposed: boolean;\n}\n\n/**\n * A many-shot event stream between an opener and an overlay. Pass `channel.id` in open data; the\n * overlay resolves it with `getEventChannel(id)`.\n */\nexport function createEventChannel<Events>(options?: OwnerOption & { readonly id?: string }): EventChannel<Events> {\n const listeners = new Set<(event: Events) => void>();\n let disposed = false;\n let id = \"\";\n const channel: EventChannel<Events> = {\n get id() {\n return id;\n },\n get disposed() {\n return disposed;\n },\n emit(event) {\n if (disposed) return;\n for (const listener of [...listeners]) listener(event);\n },\n subscribe(listener) {\n if (disposed) return () => {};\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n if (disposed) return;\n callbackManager.remove(id);\n },\n };\n id = callbackManager.registerEntry(channel, {\n ...(options?.id === undefined ? {} : { id: options.id }),\n ...ownerOptions(options),\n onRemove: () => {\n disposed = true;\n listeners.clear();\n },\n });\n return channel;\n}\n\n/** The channel registered under `id`, or undefined once disposed. */\nexport function getEventChannel<Events>(id: string): EventChannel<Events> | undefined {\n return getHandle<EventChannel<Events>>(id);\n}\n\n// ── One-shot requests ────────────────────────────────────────────────────────────────────────\n\nexport interface PendingRequest<Req, Res> {\n readonly id: string;\n /** What the asker sent. */\n readonly request: Req;\n /** Answer the request (first answer wins; later calls are ignored). */\n resolve(response: Res): void;\n /** Fail the request. */\n reject(reason: unknown): void;\n readonly settled: boolean;\n}\n\nexport interface RequestHandle<Req, Res> extends PendingRequest<Req, Res> {\n /** Settles with the answer; rejects with `HandleDisposedError` when disposed unanswered. */\n readonly promise: Promise<Res>;\n /** Remove the request; an unanswered one rejects. */\n dispose(): void;\n}\n\n/**\n * A one-shot promise bridge: the asker awaits `promise`, the overlay answers through\n * `getRequest(id).resolve(…)`. Disposing it unanswered (or closing its owner) rejects.\n */\nexport function createRequest<Req, Res>(request: Req, options?: OwnerOption): RequestHandle<Req, Res> {\n let settle!: (response: Res) => void;\n let fail!: (reason: unknown) => void;\n const promise = new Promise<Res>((resolve, reject) => {\n settle = resolve;\n fail = reject;\n });\n // A disposed request nobody awaited must not surface as an unhandled rejection.\n promise.catch(() => {});\n let settled = false;\n let id = \"\";\n const handle: RequestHandle<Req, Res> = {\n get id() {\n return id;\n },\n request,\n promise,\n get settled() {\n return settled;\n },\n resolve(response) {\n if (settled) return;\n settled = true;\n settle(response);\n callbackManager.remove(id);\n },\n reject(reason) {\n if (settled) return;\n settled = true;\n fail(reason);\n callbackManager.remove(id);\n },\n dispose() {\n callbackManager.remove(id);\n },\n };\n id = callbackManager.registerEntry(handle, {\n ...ownerOptions(options),\n onRemove: () => {\n if (settled) return;\n settled = true;\n fail(new HandleDisposedError(id));\n },\n });\n return handle;\n}\n\n/** The pending request registered under `id`, or undefined once answered or disposed. */\nexport function getRequest<Req, Res>(id: string): PendingRequest<Req, Res> | undefined {\n return getHandle<RequestHandle<Req, Res>>(id);\n}\n"],"mappings":"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","names":["handlesState"]}
1
+ {"version":3,"sources":["../src/handles/helpers.ts"],"sourcesContent":["/**\n * Typed helpers over the handles registry: imperative object handles, event channels and one-shot\n * requests. Each is stored in the same registry (so its id can ride overlay open data and `get`\n * still resolves it) and accepts an `owner` InstanceKey so closing the owning overlay disposes it.\n */\nimport { callbackManager } from \"./callbackManager\";\n\nexport interface OwnerOption {\n /** The InstanceKey (`overlayId:instanceId`) that owns this handle; disposed when it closes. */\n owner?: string;\n}\n\n/** Thrown into a request's promise when it is disposed before being answered. */\nexport class HandleDisposedError extends Error {\n readonly handleId: string;\n constructor(handleId: string, message = `[overlays/handles] \"${handleId}\" was disposed before it settled`) {\n super(message);\n this.name = \"HandleDisposedError\";\n this.handleId = handleId;\n }\n}\n\nfunction ownerOptions(options: OwnerOption | undefined): { owner?: string } {\n return options?.owner === undefined ? {} : { owner: options.owner };\n}\n\n// ── Imperative object handles ────────────────────────────────────────────────────────────────\n\n/**\n * Register an imperative object handle under a caller-chosen id (an editor API, a widget handle,\n * a ref's methods). Re-registering an id replaces the previous handle. Returns a disposer that\n * removes the handle only if it is still the one registered (safe for React effect cleanup).\n */\nexport function registerHandle<T extends object>(id: string, handle: T, options?: OwnerOption): () => void {\n callbackManager.registerEntry(handle, { id, ...ownerOptions(options) });\n return () => {\n if (callbackManager.get<T>(id) === handle) callbackManager.remove(id);\n };\n}\n\n/** The object handle registered under `id`, or undefined. */\nexport function getHandle<T extends object>(id: string): T | undefined {\n const value = callbackManager.get<unknown>(id);\n return typeof value === \"object\" && value !== null ? (value as T) : undefined;\n}\n\n/** Remove the handle registered under `id`. */\nexport function removeHandle(id: string): void {\n callbackManager.remove(id);\n}\n\n// ── Event channels ───────────────────────────────────────────────────────────────────────────\n\nexport interface EventChannel<Events> {\n readonly id: string;\n /** Deliver an event to every subscriber (no-op after dispose). */\n emit(event: Events): void;\n /** Listen; returns the unsubscribe. */\n subscribe(listener: (event: Events) => void): () => void;\n /** Remove the channel and drop every subscriber. */\n dispose(): void;\n readonly disposed: boolean;\n}\n\n/**\n * A many-shot event stream between an opener and an overlay. Pass `channel.id` in open data; the\n * overlay resolves it with `getEventChannel(id)`.\n */\nexport function createEventChannel<Events>(options?: OwnerOption & { readonly id?: string }): EventChannel<Events> {\n const listeners = new Set<(event: Events) => void>();\n let disposed = false;\n let id = \"\";\n const channel: EventChannel<Events> = {\n get id() {\n return id;\n },\n get disposed() {\n return disposed;\n },\n emit(event) {\n if (disposed) return;\n for (const listener of [...listeners]) listener(event);\n },\n subscribe(listener) {\n if (disposed) return () => {};\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n if (disposed) return;\n callbackManager.remove(id);\n },\n };\n id = callbackManager.registerEntry(channel, {\n ...(options?.id === undefined ? {} : { id: options.id }),\n ...ownerOptions(options),\n onRemove: () => {\n disposed = true;\n listeners.clear();\n },\n });\n return channel;\n}\n\n/** The channel registered under `id`, or undefined once disposed. */\nexport function getEventChannel<Events>(id: string): EventChannel<Events> | undefined {\n return getHandle<EventChannel<Events>>(id);\n}\n\n// ── One-shot requests ────────────────────────────────────────────────────────────────────────\n\nexport interface PendingRequest<Req, Res> {\n readonly id: string;\n /** What the asker sent. */\n readonly request: Req;\n /** Answer the request (first answer wins; later calls are ignored). */\n resolve(response: Res): void;\n /** Fail the request. */\n reject(reason: unknown): void;\n readonly settled: boolean;\n}\n\nexport interface RequestHandle<Req, Res> extends PendingRequest<Req, Res> {\n /** Settles with the answer; rejects with `HandleDisposedError` when disposed unanswered. */\n readonly promise: Promise<Res>;\n /** Remove the request; an unanswered one rejects. */\n dispose(): void;\n}\n\n/**\n * A one-shot promise bridge: the asker awaits `promise`, the overlay answers through\n * `getRequest(id).resolve(…)`. Disposing it unanswered (or closing its owner) rejects.\n */\nexport function createRequest<Req, Res>(request: Req, options?: OwnerOption): RequestHandle<Req, Res> {\n let settle!: (response: Res) => void;\n let fail!: (reason: unknown) => void;\n const promise = new Promise<Res>((resolve, reject) => {\n settle = resolve;\n fail = reject;\n });\n // A disposed request nobody awaited must not surface as an unhandled rejection.\n promise.catch(() => {});\n let settled = false;\n let id = \"\";\n const handle: RequestHandle<Req, Res> = {\n get id() {\n return id;\n },\n request,\n promise,\n get settled() {\n return settled;\n },\n resolve(response) {\n if (settled) return;\n settled = true;\n settle(response);\n callbackManager.remove(id);\n },\n reject(reason) {\n if (settled) return;\n settled = true;\n fail(reason);\n callbackManager.remove(id);\n },\n dispose() {\n callbackManager.remove(id);\n },\n };\n id = callbackManager.registerEntry(handle, {\n ...ownerOptions(options),\n onRemove: () => {\n if (settled) return;\n settled = true;\n fail(new HandleDisposedError(id));\n },\n });\n return handle;\n}\n\n/** The pending request registered under `id`, or undefined once answered or disposed. */\nexport function getRequest<Req, Res>(id: string): PendingRequest<Req, Res> | undefined {\n return getHandle<RequestHandle<Req, Res>>(id);\n}\n"],"mappings":"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","names":[]}