@ai-matrx/summon 0.1.14 → 0.1.16
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/CHANGELOG.md +33 -0
- package/dist/define.cjs +123 -0
- package/dist/define.cjs.map +1 -0
- package/dist/define.d.cts +79 -0
- package/dist/define.d.ts +79 -0
- package/dist/define.js +114 -0
- package/dist/define.js.map +1 -0
- package/dist/{gate-1c08vnzx.d.cts → gate-BalZq3nu.d.cts} +1 -1
- package/dist/{gate-1c08vnzx.d.ts → gate-BalZq3nu.d.ts} +1 -1
- package/dist/handles.cjs +3 -1
- package/dist/handles.cjs.map +1 -1
- package/dist/handles.d.cts +4 -1
- package/dist/handles.d.ts +4 -1
- package/dist/handles.js +3 -1
- package/dist/handles.js.map +1 -1
- package/dist/index.cjs +3 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/open.cjs +83 -64
- package/dist/open.cjs.map +1 -1
- package/dist/open.d.cts +28 -13
- package/dist/open.d.ts +28 -13
- package/dist/open.js +83 -64
- package/dist/open.js.map +1 -1
- package/dist/react.cjs +8 -8
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -1
- package/dist/react.d.ts +2 -1
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/root.cjs +27 -125
- package/dist/root.cjs.map +1 -1
- package/dist/root.d.cts +4 -76
- package/dist/root.d.ts +4 -76
- package/dist/root.js +18 -121
- package/dist/root.js.map +1 -1
- package/dist/url.cjs +75 -16
- package/dist/url.cjs.map +1 -1
- package/dist/url.d.cts +13 -2
- package/dist/url.d.ts +13 -2
- package/dist/url.js +67 -17
- package/dist/url.js.map +1 -1
- package/package.json +14 -4
package/dist/root.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/root/SummonRoot.tsx","../src/root/context.ts","../src/root/host.ts","../src/root/storeHooks.ts","../src/root/defineOverlay.tsx","../src/root/dispatchers.ts","../src/root/emit.ts","../src/root/instance.ts","../src/root/PopoutContext.tsx","../src/root/usePopoutContainer.ts","../src/root/useOverlaySurfaceRenderAck.ts","../src/root/useOverlayLink.ts"],"sourcesContent":["// `<SummonRoot>` — the only summon component a host mounts at its root, and it does next to\n// nothing: it hands the host's store to the summon context (so every opener works at once) and\n// records the host's settings. Nothing heavy loads here. The host's runtime component (tray sync,\n// router bridge — whatever only matters once windows exist) is fetched and mounted the moment the\n// summon core installs: on the first open, a page's own WindowPanel, a link, or saved windows.\n\nimport { lazy, Suspense, useEffect, useState, useSyncExternalStore, type ComponentType, type ReactNode } from \"react\";\nimport { Provider } from \"react-redux\";\nimport type { Store } from \"@reduxjs/toolkit\";\nimport { isSummonInstalled, subscribeSummonInstalled } from \"@ai-matrx/summon/open\";\nimport { SummonStoreContext } from \"./context\";\nimport { configureSummonHost, type SummonHostConfig } from \"./host\";\n\nexport interface SummonRootProps extends SummonHostConfig {\n /** The host store, with the gate (`createSummonGate`) in its middleware chain. */\n readonly store: Store<unknown>;\n /** Mounted once the summon core is installed; one import() of the host's whole runtime set. */\n readonly runtime?: () => Promise<{ default: ComponentType }>;\n readonly children?: ReactNode;\n}\n\nconst serverSnapshot = () => false;\n\nexport function SummonRoot({ store, runtime, children, ports, renderWrapper, errorReportExtras, windows }: SummonRootProps) {\n // Settings are recorded before the first child renders (a page's WindowPanel may read them).\n const [config] = useState<SummonHostConfig>(() => {\n const initial = { ports, renderWrapper, errorReportExtras, windows };\n configureSummonHost(initial);\n return initial;\n });\n useEffect(() => {\n if (config.ports === ports && config.renderWrapper === renderWrapper && config.errorReportExtras === errorReportExtras && config.windows === windows) return;\n configureSummonHost({ ports, renderWrapper, errorReportExtras, windows });\n }, [config, ports, renderWrapper, errorReportExtras, windows]);\n\n const installed = useSyncExternalStore(subscribeSummonInstalled, isSummonInstalled, serverSnapshot);\n const [Runtime] = useState(() => (runtime ? lazy(runtime) : null));\n\n return (\n <Provider store={store} context={SummonStoreContext}>\n {children}\n {installed && Runtime ? (\n <Suspense fallback={null}>\n <Runtime />\n </Suspense>\n ) : null}\n </Provider>\n );\n}\n","// The contexts every summon section reads, light enough for the root: the overlays store rides\n// its OWN react-redux context (chat-package pattern), so a private overlays store never collides\n// with a host's store and a host that hands its one store to `<SummonRoot>` simply passes it in.\nimport { createContext } from \"react\";\nimport type { ReactReduxContextValue } from \"react-redux\";\nimport type { SummonPorts } from \"@ai-matrx/summon/open\";\n\nexport const SummonStoreContext = createContext<ReactReduxContextValue | null>(null);\nSummonStoreContext.displayName = \"SummonStoreContext\";\n\nexport const SummonPortsContext = createContext<SummonPorts | null>(null);\nSummonPortsContext.displayName = \"SummonPortsContext\";\n","// The host's settings, configured ONCE at the root (`<SummonRoot>`) and read wherever a provider\n// is absent: a WindowPanel rendered by a page, the lazily loaded host, links and persistence. A\n// heavy `<SummonProvider>` still overrides them for its subtree (a package's own bare host).\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { resolvePorts, type SummonPorts } from \"@ai-matrx/summon/open\";\nimport type { ComponentType, ReactNode } from \"react\";\n\n/** One rendered overlay instance, as the host wrapper and error extras see it. */\nexport interface OverlayRenderContext {\n readonly overlayId: string;\n readonly instanceId: string;\n readonly instanceKey: string;\n readonly label: string;\n}\n\nexport interface OverlayErrorExtrasContext extends OverlayRenderContext {\n readonly error: unknown;\n readonly componentStack: string | null;\n}\n\nexport interface SummonHostWindows {\n readonly escapeYieldSelector?: string | undefined;\n readonly mobileTopOffset?: string | undefined;\n readonly selectionSurface?: ComponentType<{ readonly children: ReactNode }> | undefined;\n}\n\nexport interface SummonHostConfig {\n readonly ports?: Partial<SummonPorts> | undefined;\n /** Wrap every rendered overlay (AI Matrx: its surface layer boundary). */\n readonly renderWrapper?: ((node: ReactNode, context: OverlayRenderContext) => ReactNode) | undefined;\n /** Extra controls in the default error card's footer (AI Matrx: \"Copy for AI\"). */\n readonly errorReportExtras?: ((context: OverlayErrorExtrasContext) => ReactNode) | undefined;\n /** Window host options (the window section reads them when no WindowsProvider is above). */\n readonly windows?: SummonHostWindows | undefined;\n}\n\ninterface HostSlot {\n config: SummonHostConfig;\n ports: SummonPorts | null;\n version: number;\n}\n\nconst slot = (): HostSlot => globalSlot<HostSlot>(\"ai-matrx.summon.host\", () => ({ config: {}, ports: null, version: 0 }));\n\nexport function configureSummonHost(config: SummonHostConfig): void {\n const s = slot();\n if (s.config === config) return;\n s.config = config;\n s.ports = null;\n s.version += 1;\n}\n\nexport function getSummonHostConfig(): SummonHostConfig {\n return slot().config;\n}\n\n/** The configured ports, resolved over the working defaults (stable until reconfigured). */\nexport function getSummonHostPorts(): SummonPorts {\n const s = slot();\n return (s.ports ??= resolvePorts(s.config.ports));\n}\n\n/** Bumps on every reconfigure; memo keys for values derived from the host config. */\nexport function getSummonHostVersion(): number {\n return slot().version;\n}\n","// Typed hooks over the package's own store context. Every package component reads the overlays\n// store through these, never react-redux's default context, so a host's own <Provider> and the\n// overlays store never collide.\n\nimport { useContext } from \"react\";\nimport {\n createDispatchHook,\n createSelectorHook,\n createStoreHook,\n type UseDispatch,\n type UseSelector,\n type UseStore,\n} from \"react-redux\";\nimport type { Dispatch, Store, UnknownAction } from \"@reduxjs/toolkit\";\nimport type { SummonRootState } from \"@ai-matrx/summon\";\nimport type { SummonPorts } from \"@ai-matrx/summon/open\";\nimport { SummonPortsContext, SummonStoreContext } from \"./context\";\nimport { getSummonHostPorts } from \"./host\";\n\n/** Dispatch as the React section uses it: plain actions (thunks are the host's business). */\nexport type SummonReactDispatch = Dispatch<UnknownAction>;\n\n/** The store as the React section sees it: the overlays slices at their root keys (a host store has more). */\nexport type SummonReactStore = Store<SummonRootState>;\n\n/** Select from the overlays store. */\nexport const useSummonSelector: UseSelector<SummonRootState> = createSelectorHook(SummonStoreContext).withTypes<SummonRootState>();\n\n/** The overlays store's dispatch. */\nexport const useSummonDispatch: UseDispatch<SummonReactDispatch> = createDispatchHook(SummonStoreContext).withTypes<SummonReactDispatch>();\n\n/** The overlays store itself (for `getState()` inside callbacks). */\nexport const useSummonStore: UseStore<SummonReactStore> = createStoreHook(SummonStoreContext).withTypes<SummonReactStore>();\n\n/** The nearest provider's ports, else the ports configured at the root, else the working defaults. */\nexport function useSummonPorts(): SummonPorts {\n return useContext(SummonPortsContext) ?? getSummonHostPorts();\n}\n\n/** Same as `useSummonPorts` (kept for components written to work page-local). */\nexport function useOptionalSummonPorts(): SummonPorts {\n return useContext(SummonPortsContext) ?? getSummonHostPorts();\n}\n\n/**\n * The nearest provider's store, or `null` when nothing above provides one — for packages that\n * mount their own provider only when the host has none (chat's windows on a bare host).\n */\nexport function useOptionalSummonStore(): SummonReactStore | null {\n const context = useContext(SummonStoreContext);\n return context ? (context.store as unknown as SummonReactStore) : null;\n}\n","// `defineOverlay` — the declare-once API (C38 R2). One light definition per overlay carries its\n// metadata and (optionally) a lazy `render` import; the definition generates the opener hook, the declarative\n// Controller, the open/close hooks and a typed event emitter. The host renders definitions; the\n// render module is the old controller block, moved verbatim, and is fetched only when opened.\n//\n// Light by construction (the root section): no slice, no host, no handles registry. Opening goes\n// through the light creators (a gated host store loads the core on the first one); a callback-aware\n// open reserves its event channel and the handles section arrives with that first open.\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n DEFAULT_INSTANCE_ID,\n instanceKey as toInstanceKey,\n openPendingEventChannel,\n selectIsOverlayOpen,\n type InstanceKey,\n type OverlayDefinitionMeta,\n type OverlayRawData,\n type AnyOverlayMeta,\n} from \"@ai-matrx/summon/open\";\nimport type { OverlayRenderProps } from \"../react/instanceBody\";\nimport type { RenderModule } from \"../react/loaderCache\";\nimport { dispatchClose, dispatchOpen, newInstanceId } from \"./dispatchers\";\nimport { emitOverlayEventLight } from \"./emit\";\nimport { useOptionalOverlayInstance } from \"./instance\";\nimport { useSummonDispatch, useSummonSelector, type SummonReactDispatch } from \"./storeHooks\";\n\n/** Event name → payload, declared once per overlay: `defineOverlay<Data, { uploaded: Result }>()`. */\nexport type OverlayEventMap = Record<string, unknown>;\n\nexport type OverlayHandlers<Events extends OverlayEventMap> = {\n readonly [K in keyof Events]?: (payload: Events[K]) => void;\n};\n\nfunction openOverlayEventChannel<Events extends OverlayEventMap>(\n owner: InstanceKey,\n getHandlers: () => OverlayHandlers<Events> | undefined,\n): string {\n return openPendingEventChannel(owner, getHandlers as () => Readonly<Record<string, ((payload: unknown) => void) | undefined>> | undefined);\n}\n\n/** The open-data key that carries an overlay's event channel id (the old callback-group key). */\nexport const CALLBACK_GROUP_KEY = \"callbackGroupId\";\n\nexport interface OverlayHandle {\n readonly overlayId: string;\n readonly instanceId: string;\n close(): void;\n}\n\nexport interface OverlayOpenOptions<Data, Events extends OverlayEventMap> {\n readonly data?: Data;\n /** Single-instance overlays ignore it unless given; multi-instance generate one when omitted. */\n readonly instanceId?: string;\n /** Plain functions; they reach the caller through an event channel owned by the instance. */\n readonly handlers?: OverlayHandlers<Events>;\n}\n\nexport type OverlayOpener<Data, Events extends OverlayEventMap> = (options?: OverlayOpenOptions<Data, Events>) => OverlayHandle;\n\nexport type OverlayControllerProps<Data, Events extends OverlayEventMap> = Data & {\n readonly instanceId?: string;\n readonly handlers?: OverlayHandlers<Events>;\n};\n\nexport type OverlayEmitter<Events extends OverlayEventMap> = <K extends keyof Events & string>(event: K, payload: Events[K]) => void;\n\n/** What a definition file passes to `defineOverlay<Data>()(…)`. */\nexport interface OverlaySpec<Id extends string, Data, Parsed> extends OverlayDefinitionMeta<Id, Data> {\n /**\n * The lazily loaded render module. Its default export receives `OverlayRenderProps<Parsed>`.\n * Optional: a large app leaves it out and hands `<SummonHost renders={…}>` one id → import map,\n * so files that import a definition for its opener never gain an import() edge to the window\n * (the host-build law). When present it wins over the host map.\n */\n readonly render?: () => Promise<RenderModule<OverlayRenderProps<NoInfer<Parsed>>>>;\n /** Narrow raw open data once. `null` = refuse: the instance is closed with a warning. */\n readonly parse?: (raw: OverlayRawData) => Parsed | null;\n}\n\n/** The type-erased half the host renders; no data types cross this line. */\nexport interface HostableOverlay {\n readonly id: string;\n readonly label: string;\n readonly instance: \"single\" | \"multi\";\n readonly keepAlive: \"none\" | \"hidden\" | \"restore\";\n /** The metadata the core registry stores (links, window options, defaults…). */\n readonly meta: AnyOverlayMeta;\n /** The definition's own render loader (the host's `renders` map is used when absent). */\n readonly render?: (() => Promise<unknown>) | undefined;\n /** Narrows raw open data; the host builds each instance body from it on first render. */\n readonly parse?: ((raw: OverlayRawData) => unknown) | undefined;\n}\n\nexport interface OverlayDefinition<Id extends string, Data, Parsed, Events extends OverlayEventMap>\n extends Omit<OverlayDefinitionMeta<Id, Data>, \"id\" | \"instance\" | \"keepAlive\">,\n HostableOverlay {\n readonly id: Id;\n readonly render?: () => Promise<RenderModule<OverlayRenderProps<Parsed>>>;\n /** Imperative opener: `const open = def.useSummon(); open({ data })` → handle. */\n useSummon(): OverlayOpener<Data, Events>;\n /** Declarative form: opens on mount, closes on unmount, re-opens when its data changes. */\n readonly Controller: (props: OverlayControllerProps<Data, Events>) => null;\n useIsOpen(instanceId?: string): boolean;\n /** `const close = def.useClose(); close(instanceId?)`. */\n useClose(): (instanceId?: string) => void;\n /** Inside the render tree: emit a declared event to the opener's handlers. */\n useEmit(): OverlayEmitter<Events>;\n}\n\nfunction openWith<Data, Events extends OverlayEventMap>(\n dispatch: SummonReactDispatch,\n id: string,\n multi: boolean,\n options: OverlayOpenOptions<Data, Events> | undefined,\n): OverlayHandle {\n const instanceId = options?.instanceId ?? (multi ? newInstanceId(id) : DEFAULT_INSTANCE_ID);\n const key = toInstanceKey(id, instanceId);\n const data: Record<string, unknown> = {};\n const given: unknown = options?.data;\n if (typeof given === \"object\" && given !== null) Object.assign(data, given);\n const handlers = options?.handlers;\n if (handlers) data[CALLBACK_GROUP_KEY] = openOverlayEventChannel<Events>(key, () => handlers);\n dispatchOpen(dispatch, id, instanceId, data);\n return { overlayId: id, instanceId, close: () => dispatchClose(dispatch, id, instanceId) };\n}\n\n/** Stable identity for the serialisable part of Controller props (functions never reopen). */\nfunction dataSignature(data: Readonly<Record<string, unknown>>): string {\n try {\n return JSON.stringify(data, (_key, value: unknown) => (typeof value === \"function\" ? undefined : value));\n } catch {\n return String(Date.now());\n }\n}\n\n/**\n * Declare an overlay once. Curried so `Data` (and `Events`) are named while the id and the\n * parsed-data type are inferred:\n *\n * export const notesWindow = defineOverlay<NotesWindowData>()({\n * id: \"notesWindow\", label: \"Notes\", render: () => import(\"./notesWindow.render\"),\n * });\n */\nexport function defineOverlay<Data extends object = Record<string, never>, Events extends OverlayEventMap = Record<never, never>>() {\n return function define<const Id extends string, Parsed = OverlayRawData>(\n spec: OverlaySpec<Id, Data, Parsed>,\n ): OverlayDefinition<Id, Data, Parsed, Events> {\n const id = spec.id;\n const multi = spec.instance === \"multi\";\n // The registry stores every definition's metadata under one erased type (AnyOverlayMeta).\n const meta: AnyOverlayMeta = spec;\n\n function useSummon(): OverlayOpener<Data, Events> {\n const dispatch = useSummonDispatch();\n return useCallback((options) => openWith(dispatch, id, multi, options), [dispatch]);\n }\n\n function useIsOpen(instanceId: string = DEFAULT_INSTANCE_ID): boolean {\n return useSummonSelector((state) => selectIsOverlayOpen(state, id, instanceId));\n }\n\n function useClose(): (instanceId?: string) => void {\n const dispatch = useSummonDispatch();\n return useCallback((instanceId?: string) => dispatchClose(dispatch, id, instanceId ?? DEFAULT_INSTANCE_ID), [dispatch]);\n }\n\n function useEmit(): OverlayEmitter<Events> {\n const instance = useOptionalOverlayInstance();\n const channelId = instance?.overlayId === id ? instance.rawData?.[CALLBACK_GROUP_KEY] : undefined;\n return useCallback<OverlayEmitter<Events>>(\n (event, payload) => {\n if (typeof channelId === \"string\") emitOverlayEventLight(channelId, event, payload);\n },\n [channelId],\n );\n }\n\n function Controller(props: OverlayControllerProps<Data, Events>): null {\n const dispatch = useSummonDispatch();\n const [ownInstanceId] = useState(() => (multi ? newInstanceId(id) : DEFAULT_INSTANCE_ID));\n const instanceId = props.instanceId ?? ownInstanceId;\n // Latest handlers, read at emit time: a new function identity never reopens the overlay.\n const handlersRef = useRef(props.handlers);\n handlersRef.current = props.handlers;\n const hasHandlers = props.handlers !== undefined;\n\n const data = useMemo(() => {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(props)) {\n if (key !== \"instanceId\" && key !== \"handlers\") out[key] = value;\n }\n return out;\n }, [props]);\n const signature = dataSignature(data);\n const dataRef = useRef(data);\n dataRef.current = data;\n\n useEffect(() => {\n const key: InstanceKey = toInstanceKey(id, instanceId);\n const open: Record<string, unknown> = { ...dataRef.current };\n if (hasHandlers) open[CALLBACK_GROUP_KEY] = openOverlayEventChannel<Events>(key, () => handlersRef.current);\n dispatchOpen(dispatch, id, instanceId, open);\n return () => dispatchClose(dispatch, id, instanceId);\n }, [dispatch, instanceId, signature, hasHandlers]);\n\n return null;\n }\n Controller.displayName = `${id}Controller`;\n\n return {\n ...spec,\n id,\n label: spec.label,\n instance: multi ? \"multi\" : \"single\",\n keepAlive: spec.keepAlive ?? \"none\",\n meta,\n useSummon,\n Controller,\n useIsOpen,\n useClose,\n useEmit,\n };\n };\n}\n","// The one place a definition turns an untyped (string) overlay id into the typed action creators.\n// Inside the package `OverlayId` is `string`; a host that augments the registry narrows it to its\n// ids, and these keep compiling there because definitions carry their own id.\n\nimport { closeOverlay, openOverlay, DEFAULT_INSTANCE_ID, type OverlayDataOf, type OverlayId } from \"@ai-matrx/summon/open\";\nimport type { SummonReactDispatch } from \"./storeHooks\";\n\nexport function newInstanceId(overlayId: string): string {\n return `${overlayId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function dispatchOpen(dispatch: SummonReactDispatch, overlayId: string, instanceId: string, data: unknown): void {\n dispatch(\n openOverlay({\n overlayId: overlayId as OverlayId,\n instanceId,\n data: (data ?? null) as OverlayDataOf<OverlayId> | null,\n }),\n );\n}\n\nexport function dispatchClose(dispatch: SummonReactDispatch, overlayId: string, instanceId: string = DEFAULT_INSTANCE_ID): void {\n dispatch(closeOverlay({ overlayId: overlayId as OverlayId, instanceId }));\n}\n","// Emitting an overlay event from inside its render tree. The render tree has the handles registry\n// loaded (the react section registers its synchronous emitter on evaluation); before that, the\n// emit waits for the one handles import.\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { materializePendingChannels } from \"@ai-matrx/summon/open\";\n\ntype Emitter = (channelId: string, type: string, payload: unknown) => void;\n\nconst slot = (): { emit: Emitter | null } => globalSlot(\"ai-matrx.summon.emitter\", () => ({ emit: null as Emitter | null }));\n\n/** The react section installs the synchronous emitter (it holds handles statically). */\nexport function registerOverlayEmitter(emit: Emitter): void {\n slot().emit = emit;\n}\n\nexport function emitOverlayEventLight(channelId: string, type: string, payload: unknown): void {\n const emit = slot().emit;\n if (emit) {\n emit(channelId, type, payload);\n return;\n }\n void import(\"@ai-matrx/summon/handles\").then(({ createEventChannel, getEventChannel }) => {\n materializePendingChannels(createEventChannel);\n getEventChannel<{ type: string; payload: unknown }>(channelId)?.emit({ type, payload });\n });\n}\n","// The context every rendered overlay instance provides to its subtree, and the hooks that read who\n// it is. Light: a definition's `useEmit` reads it; the saved-state hook stays in `./react`.\nimport { createContext, useContext } from \"react\";\nimport type { InstanceKey, OverlayRawData } from \"@ai-matrx/summon/open\";\n\nexport interface OverlayInstanceContextValue {\n readonly overlayId: string;\n readonly instanceId: string;\n readonly instanceKey: InstanceKey;\n readonly close: () => void;\n readonly hidden: boolean;\n /** The open data as it arrived (pre-parse); event emitters read their channel id from it. */\n readonly rawData: OverlayRawData;\n}\n\nexport const OverlayInstanceContext = createContext<OverlayInstanceContextValue | null>(null);\nOverlayInstanceContext.displayName = \"OverlayInstanceContext\";\n\n/** The open instance this component renders inside. Throws outside an overlay. */\nexport function useOverlayInstance(): OverlayInstanceContextValue {\n const value = useContext(OverlayInstanceContext);\n if (!value) throw new Error(\"[summon] useOverlayInstance() was called outside a rendered overlay.\");\n return value;\n}\n\n/** Same as `useOverlayInstance`, but `null` outside an overlay. */\nexport function useOptionalOverlayInstance(): OverlayInstanceContextValue | null {\n return useContext(OverlayInstanceContext);\n}\n","\"use client\";\n\n/**\n * PopoutContext — exposes the popout `Document` / `Window` / portal target\n * to descendants of a popped-out subtree.\n *\n * The default value (`popoutDocument: null`, etc.) means \"I'm rendered in\n * the parent document.\" Descendants that need to behave differently inside\n * a popout (e.g. Radix portals retargeting their content) call\n * `usePopout()` and branch on the values.\n *\n * **Architectural note:** because `createPortal` preserves the parent\n * React tree, every ancestor `Provider` cascades into the popout — Redux,\n * Theme, Tooltip, Router, all work without re-wrapping. This context is\n * for the *opposite* direction: telling popped-out content \"you're inside\n * a popout window, here's the document/window/container you should use\n * instead of the global `document`/`window`.\"\n */\nimport { createContext, useContext } from \"react\";\n\nexport type PopoutMode = \"pip\" | \"popup\";\n\nexport interface PopoutContextValue {\n /**\n * The popout `Document`. `null` when this subtree is rendered in the\n * parent (the default).\n */\n popoutDocument: Document | null;\n /**\n * The popout `Window`. `null` when this subtree is rendered in the parent.\n * Use for `addEventListener`, `matchMedia`, `innerWidth`, etc., when the\n * popout-scoped answer is what you need.\n */\n popoutWindow: Window | null;\n /**\n * Element to use as the Radix `Portal.container` prop so floating UI\n * (tooltips, popovers, dropdowns) renders inside the popout instead of\n * the parent `document.body`. Returns `null` outside a popout —\n * `usePopoutContainer` coerces this to `undefined` so Radix falls back\n * to its default container automatically.\n */\n popoutContainer: HTMLElement | null;\n /**\n * Which popout mechanism is in use. `null` outside a popout.\n * Currently only used for diagnostics; downstream components may branch\n * on it later (e.g. show different chrome for `popup` vs `pip`).\n */\n mode: PopoutMode | null;\n}\n\nconst DEFAULT_VALUE: PopoutContextValue = {\n popoutDocument: null,\n popoutWindow: null,\n popoutContainer: null,\n mode: null,\n};\n\nexport const PopoutContext =\n createContext<PopoutContextValue>(DEFAULT_VALUE);\n\n/** Read the current popout context. Always defined — never throws. */\nexport function usePopout(): PopoutContextValue {\n return useContext(PopoutContext);\n}\n","\"use client\";\n\n/**\n * usePopoutContainer — return the element Radix portals (Tooltip, Popover,\n * Dropdown, Dialog, Select, AlertDialog, HoverCard) should mount into.\n *\n * **The problem this solves:** by default every Radix `Portal` mounts to\n * `document.body`. When a tooltip-trigger lives inside a popped-out window\n * but the popover content portals to the *parent* document, the tooltip\n * appears in the wrong window — typically invisible to the user.\n *\n * **The fix:** wrappers around Radix primitives call this hook and pass\n * the result as `<Primitive.Portal container={...} />`. Inside a popout,\n * the result is the popout's `<body>`. Outside a popout, the result is\n * `undefined`, which Radix interprets as \"use the default\" — meaning no\n * behavior change for any of the thousands of existing usages.\n *\n * **Why `undefined` instead of `null`?** Radix's `container` prop accepts\n * `HTMLElement | null | undefined`. Passing `null` actively tries to\n * portal into a `null` target and crashes. Passing `undefined` falls\n * through to the default (`document.body`). The `?? undefined` coerces\n * the `null` we get from `PopoutContext` outside a popout into the\n * Radix-friendly default-fallthrough value.\n */\nimport { usePopout } from \"./PopoutContext\";\n\nexport function usePopoutContainer(): HTMLElement | undefined {\n return usePopout().popoutContainer ?? undefined;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { ackOverlaySurfaceRender, clearOverlaySurfaceRender } from \"@ai-matrx/summon/open\";\n\n/**\n * Acknowledge a visible presentation that intentionally replaces WindowPanel.\n *\n * Registered singleton window overlays normally prove they are on screen by mounting a\n * WindowPanel and registering geometry. Mobile drawers, sheets, and full-screen\n * viewers have no window-manager geometry, so their composition root must use\n * this hook while that alternate surface is active.\n */\nexport function useOverlaySurfaceRenderAck(\n overlayId: string,\n active: boolean,\n): void {\n useEffect(() => {\n if (!active) return undefined;\n ackOverlaySurfaceRender(overlayId);\n return () => clearOverlaySurfaceRender(overlayId);\n }, [active, overlayId]);\n}\n","import { useEffect, useMemo } from \"react\";\nimport { registerSyncEntry, unregisterSyncEntry } from \"@ai-matrx/summon/open\";\nimport { useSummonDispatch } from \"./storeHooks\";\n\n/**\n * Publish an open instance's address into `?panels=` while it is mounted. `linkKey` undefined →\n * nothing is published (a window with no link); so is an undefined or empty `id`. The token is `linkKey:id[:args]`; args are\n * compared by value, so a new object with equal contents never re-registers.\n */\nexport function useOverlayLink(linkKey: string | undefined, id: string | undefined, args?: Readonly<Record<string, string>>): void {\n const dispatch = useSummonDispatch();\n const argsKey = args\n ? JSON.stringify(\n Object.keys(args)\n .sort()\n .map((k) => [k, args[k]]),\n )\n : \"\";\n // Rebuilt only when the VALUES change (argsKey), never on a fresh object with equal contents.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stableArgs = useMemo(() => (args ? { ...args } : undefined), [argsKey]);\n\n useEffect(() => {\n if (!linkKey || !id) return undefined;\n dispatch(registerSyncEntry(stableArgs ? { typeKey: linkKey, instanceId: id, args: stableArgs } : { typeKey: linkKey, instanceId: id }));\n return () => {\n dispatch(unregisterSyncEntry({ typeKey: linkKey, instanceId: id }));\n };\n }, [dispatch, linkKey, id, stableArgs]);\n}\n"],"mappings":"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","names":["useEffect","useState","DEFAULT_INSTANCE_ID","globalSlot","slot","createContext","useContext","DEFAULT_INSTANCE_ID","useState","useEffect","createContext","useContext","useEffect","useEffect","useMemo","useMemo","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/root/SummonRoot.tsx","../src/root/context.ts","../src/root/host.ts","../src/root/storeHooks.ts","../src/root/dispatchers.ts","../src/root/instance.ts","../src/root/emit.ts","../src/root/PopoutContext.tsx","../src/root/usePopoutContainer.ts","../src/root/useOverlaySurfaceRenderAck.ts","../src/root/useOverlayLink.ts"],"sourcesContent":["// `<SummonRoot>` — the only summon component a host mounts at its root, and it does next to\n// nothing: it hands the host's store to the summon context (so every opener works at once) and\n// records the host's settings. Nothing heavy loads here. The host's runtime component (tray sync,\n// router bridge — whatever only matters once windows exist) is fetched and mounted the moment the\n// summon core installs: on the first open, a page's own WindowPanel, a link, or saved windows.\n\nimport { lazy, Suspense, useEffect, useState, useSyncExternalStore, type ComponentType, type ReactNode } from \"react\";\nimport { Provider } from \"react-redux\";\nimport type { Store } from \"@reduxjs/toolkit\";\nimport { isSummonInstalled, subscribeSummonInstalled } from \"@ai-matrx/summon/open\";\nimport { SummonStoreContext } from \"./context\";\nimport { configureSummonHost, type SummonHostConfig } from \"./host\";\n\nexport interface SummonRootProps extends SummonHostConfig {\n /** The host store, with the gate (`createSummonGate`) in its middleware chain. */\n readonly store: Store<unknown>;\n /** Mounted once the summon core is installed; one import() of the host's whole runtime set. */\n readonly runtime?: () => Promise<{ default: ComponentType }>;\n readonly children?: ReactNode;\n}\n\nconst serverSnapshot = () => false;\n\nexport function SummonRoot({ store, runtime, children, ports, renderWrapper, errorReportExtras, windows }: SummonRootProps) {\n // Settings are recorded before the first child renders (a page's WindowPanel may read them).\n const [config] = useState<SummonHostConfig>(() => {\n const initial = { ports, renderWrapper, errorReportExtras, windows };\n configureSummonHost(initial);\n return initial;\n });\n useEffect(() => {\n if (config.ports === ports && config.renderWrapper === renderWrapper && config.errorReportExtras === errorReportExtras && config.windows === windows) return;\n configureSummonHost({ ports, renderWrapper, errorReportExtras, windows });\n }, [config, ports, renderWrapper, errorReportExtras, windows]);\n\n const installed = useSyncExternalStore(subscribeSummonInstalled, isSummonInstalled, serverSnapshot);\n const [Runtime] = useState(() => (runtime ? lazy(runtime) : null));\n\n return (\n <Provider store={store} context={SummonStoreContext}>\n {children}\n {installed && Runtime ? (\n <Suspense fallback={null}>\n <Runtime />\n </Suspense>\n ) : null}\n </Provider>\n );\n}\n","// The contexts every summon section reads, light enough for the root: the overlays store rides\n// its OWN react-redux context (chat-package pattern), so a private overlays store never collides\n// with a host's store and a host that hands its one store to `<SummonRoot>` simply passes it in.\nimport { createContext } from \"react\";\nimport type { ReactReduxContextValue } from \"react-redux\";\nimport type { SummonPorts } from \"@ai-matrx/summon/open\";\n\nexport const SummonStoreContext = createContext<ReactReduxContextValue | null>(null);\nSummonStoreContext.displayName = \"SummonStoreContext\";\n\nexport const SummonPortsContext = createContext<SummonPorts | null>(null);\nSummonPortsContext.displayName = \"SummonPortsContext\";\n","// The host's settings, configured ONCE at the root (`<SummonRoot>`) and read wherever a provider\n// is absent: a WindowPanel rendered by a page, the lazily loaded host, links and persistence. A\n// heavy `<SummonProvider>` still overrides them for its subtree (a package's own bare host).\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { resolvePorts, type SummonPorts } from \"@ai-matrx/summon/open\";\nimport type { ComponentType, ReactNode } from \"react\";\n\n/** One rendered overlay instance, as the host wrapper and error extras see it. */\nexport interface OverlayRenderContext {\n readonly overlayId: string;\n readonly instanceId: string;\n readonly instanceKey: string;\n readonly label: string;\n}\n\nexport interface OverlayErrorExtrasContext extends OverlayRenderContext {\n readonly error: unknown;\n readonly componentStack: string | null;\n}\n\nexport interface SummonHostWindows {\n readonly escapeYieldSelector?: string | undefined;\n readonly mobileTopOffset?: string | undefined;\n readonly selectionSurface?: ComponentType<{ readonly children: ReactNode }> | undefined;\n}\n\nexport interface SummonHostConfig {\n readonly ports?: Partial<SummonPorts> | undefined;\n /** Wrap every rendered overlay (AI Matrx: its surface layer boundary). */\n readonly renderWrapper?: ((node: ReactNode, context: OverlayRenderContext) => ReactNode) | undefined;\n /** Extra controls in the default error card's footer (AI Matrx: \"Copy for AI\"). */\n readonly errorReportExtras?: ((context: OverlayErrorExtrasContext) => ReactNode) | undefined;\n /** Window host options (the window section reads them when no WindowsProvider is above). */\n readonly windows?: SummonHostWindows | undefined;\n}\n\ninterface HostSlot {\n config: SummonHostConfig;\n ports: SummonPorts | null;\n version: number;\n}\n\nconst slot = (): HostSlot => globalSlot<HostSlot>(\"ai-matrx.summon.host\", () => ({ config: {}, ports: null, version: 0 }));\n\nexport function configureSummonHost(config: SummonHostConfig): void {\n const s = slot();\n if (s.config === config) return;\n s.config = config;\n s.ports = null;\n s.version += 1;\n}\n\nexport function getSummonHostConfig(): SummonHostConfig {\n return slot().config;\n}\n\n/** The configured ports, resolved over the working defaults (stable until reconfigured). */\nexport function getSummonHostPorts(): SummonPorts {\n const s = slot();\n return (s.ports ??= resolvePorts(s.config.ports));\n}\n\n/** Bumps on every reconfigure; memo keys for values derived from the host config. */\nexport function getSummonHostVersion(): number {\n return slot().version;\n}\n","// Typed hooks over the package's own store context. Every package component reads the overlays\n// store through these, never react-redux's default context, so a host's own <Provider> and the\n// overlays store never collide.\n\nimport { useContext } from \"react\";\nimport {\n createDispatchHook,\n createSelectorHook,\n createStoreHook,\n type UseDispatch,\n type UseSelector,\n type UseStore,\n} from \"react-redux\";\nimport type { Dispatch, Store, UnknownAction } from \"@reduxjs/toolkit\";\nimport type { SummonRootState } from \"@ai-matrx/summon\";\nimport type { SummonPorts } from \"@ai-matrx/summon/open\";\nimport { SummonPortsContext, SummonStoreContext } from \"./context\";\nimport { getSummonHostPorts } from \"./host\";\n\n/** Dispatch as the React section uses it: plain actions (thunks are the host's business). */\nexport type SummonReactDispatch = Dispatch<UnknownAction>;\n\n/** The store as the React section sees it: the overlays slices at their root keys (a host store has more). */\nexport type SummonReactStore = Store<SummonRootState>;\n\n/** Select from the overlays store. */\nexport const useSummonSelector: UseSelector<SummonRootState> = createSelectorHook(SummonStoreContext).withTypes<SummonRootState>();\n\n/** The overlays store's dispatch. */\nexport const useSummonDispatch: UseDispatch<SummonReactDispatch> = createDispatchHook(SummonStoreContext).withTypes<SummonReactDispatch>();\n\n/** The overlays store itself (for `getState()` inside callbacks). */\nexport const useSummonStore: UseStore<SummonReactStore> = createStoreHook(SummonStoreContext).withTypes<SummonReactStore>();\n\n/** The nearest provider's ports, else the ports configured at the root, else the working defaults. */\nexport function useSummonPorts(): SummonPorts {\n return useContext(SummonPortsContext) ?? getSummonHostPorts();\n}\n\n/** Same as `useSummonPorts` (kept for components written to work page-local). */\nexport function useOptionalSummonPorts(): SummonPorts {\n return useContext(SummonPortsContext) ?? getSummonHostPorts();\n}\n\n/**\n * The nearest provider's store, or `null` when nothing above provides one — for packages that\n * mount their own provider only when the host has none (chat's windows on a bare host).\n */\nexport function useOptionalSummonStore(): SummonReactStore | null {\n const context = useContext(SummonStoreContext);\n return context ? (context.store as unknown as SummonReactStore) : null;\n}\n","// The one place a definition turns an untyped (string) overlay id into the typed action creators.\n// Inside the package `OverlayId` is `string`; a host that augments the registry narrows it to its\n// ids, and these keep compiling there because definitions carry their own id.\n\nimport { closeOverlay, openOverlay, DEFAULT_INSTANCE_ID, type OverlayDataOf, type OverlayId } from \"@ai-matrx/summon/open\";\nimport type { SummonReactDispatch } from \"./storeHooks\";\n\nexport function newInstanceId(overlayId: string): string {\n return `${overlayId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function dispatchOpen(dispatch: SummonReactDispatch, overlayId: string, instanceId: string, data: unknown): void {\n dispatch(\n openOverlay({\n overlayId: overlayId as OverlayId,\n instanceId,\n data: (data ?? null) as OverlayDataOf<OverlayId> | null,\n }),\n );\n}\n\nexport function dispatchClose(dispatch: SummonReactDispatch, overlayId: string, instanceId: string = DEFAULT_INSTANCE_ID): void {\n dispatch(closeOverlay({ overlayId: overlayId as OverlayId, instanceId }));\n}\n","// The context every rendered overlay instance provides to its subtree, and the hooks that read who\n// it is. Light: a definition's `useEmit` reads it; the saved-state hook stays in `./react`.\nimport { createContext, useContext } from \"react\";\nimport type { InstanceKey, OverlayRawData } from \"@ai-matrx/summon/open\";\n\nexport interface OverlayInstanceContextValue {\n readonly overlayId: string;\n readonly instanceId: string;\n readonly instanceKey: InstanceKey;\n readonly close: () => void;\n readonly hidden: boolean;\n /** The open data as it arrived (pre-parse); event emitters read their channel id from it. */\n readonly rawData: OverlayRawData;\n}\n\nexport const OverlayInstanceContext = createContext<OverlayInstanceContextValue | null>(null);\nOverlayInstanceContext.displayName = \"OverlayInstanceContext\";\n\n/** The open instance this component renders inside. Throws outside an overlay. */\nexport function useOverlayInstance(): OverlayInstanceContextValue {\n const value = useContext(OverlayInstanceContext);\n if (!value) throw new Error(\"[summon] useOverlayInstance() was called outside a rendered overlay.\");\n return value;\n}\n\n/** Same as `useOverlayInstance`, but `null` outside an overlay. */\nexport function useOptionalOverlayInstance(): OverlayInstanceContextValue | null {\n return useContext(OverlayInstanceContext);\n}\n","// Emitting an overlay event from inside its render tree. The render tree has the handles registry\n// loaded (the react section registers its synchronous emitter on evaluation); before that, the\n// emit waits for the one handles import.\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { materializePendingChannels } from \"@ai-matrx/summon/open\";\n\ntype Emitter = (channelId: string, type: string, payload: unknown) => void;\n\nconst slot = (): { emit: Emitter | null } => globalSlot(\"ai-matrx.summon.emitter\", () => ({ emit: null as Emitter | null }));\n\n/** The react section installs the synchronous emitter (it holds handles statically). */\nexport function registerOverlayEmitter(emit: Emitter): void {\n slot().emit = emit;\n}\n\nexport function emitOverlayEventLight(channelId: string, type: string, payload: unknown): void {\n const emit = slot().emit;\n if (emit) {\n emit(channelId, type, payload);\n return;\n }\n void import(\"@ai-matrx/summon/handles\").then(({ createEventChannel, getEventChannel }) => {\n materializePendingChannels(createEventChannel);\n getEventChannel<{ type: string; payload: unknown }>(channelId)?.emit({ type, payload });\n });\n}\n","\"use client\";\n\n/**\n * PopoutContext — exposes the popout `Document` / `Window` / portal target\n * to descendants of a popped-out subtree.\n *\n * The default value (`popoutDocument: null`, etc.) means \"I'm rendered in\n * the parent document.\" Descendants that need to behave differently inside\n * a popout (e.g. Radix portals retargeting their content) call\n * `usePopout()` and branch on the values.\n *\n * **Architectural note:** because `createPortal` preserves the parent\n * React tree, every ancestor `Provider` cascades into the popout — Redux,\n * Theme, Tooltip, Router, all work without re-wrapping. This context is\n * for the *opposite* direction: telling popped-out content \"you're inside\n * a popout window, here's the document/window/container you should use\n * instead of the global `document`/`window`.\"\n */\nimport { createContext, useContext } from \"react\";\n\nexport type PopoutMode = \"pip\" | \"popup\";\n\nexport interface PopoutContextValue {\n /**\n * The popout `Document`. `null` when this subtree is rendered in the\n * parent (the default).\n */\n popoutDocument: Document | null;\n /**\n * The popout `Window`. `null` when this subtree is rendered in the parent.\n * Use for `addEventListener`, `matchMedia`, `innerWidth`, etc., when the\n * popout-scoped answer is what you need.\n */\n popoutWindow: Window | null;\n /**\n * Element to use as the Radix `Portal.container` prop so floating UI\n * (tooltips, popovers, dropdowns) renders inside the popout instead of\n * the parent `document.body`. Returns `null` outside a popout —\n * `usePopoutContainer` coerces this to `undefined` so Radix falls back\n * to its default container automatically.\n */\n popoutContainer: HTMLElement | null;\n /**\n * Which popout mechanism is in use. `null` outside a popout.\n * Currently only used for diagnostics; downstream components may branch\n * on it later (e.g. show different chrome for `popup` vs `pip`).\n */\n mode: PopoutMode | null;\n}\n\nconst DEFAULT_VALUE: PopoutContextValue = {\n popoutDocument: null,\n popoutWindow: null,\n popoutContainer: null,\n mode: null,\n};\n\nexport const PopoutContext =\n createContext<PopoutContextValue>(DEFAULT_VALUE);\n\n/** Read the current popout context. Always defined — never throws. */\nexport function usePopout(): PopoutContextValue {\n return useContext(PopoutContext);\n}\n","\"use client\";\n\n/**\n * usePopoutContainer — return the element Radix portals (Tooltip, Popover,\n * Dropdown, Dialog, Select, AlertDialog, HoverCard) should mount into.\n *\n * **The problem this solves:** by default every Radix `Portal` mounts to\n * `document.body`. When a tooltip-trigger lives inside a popped-out window\n * but the popover content portals to the *parent* document, the tooltip\n * appears in the wrong window — typically invisible to the user.\n *\n * **The fix:** wrappers around Radix primitives call this hook and pass\n * the result as `<Primitive.Portal container={...} />`. Inside a popout,\n * the result is the popout's `<body>`. Outside a popout, the result is\n * `undefined`, which Radix interprets as \"use the default\" — meaning no\n * behavior change for any of the thousands of existing usages.\n *\n * **Why `undefined` instead of `null`?** Radix's `container` prop accepts\n * `HTMLElement | null | undefined`. Passing `null` actively tries to\n * portal into a `null` target and crashes. Passing `undefined` falls\n * through to the default (`document.body`). The `?? undefined` coerces\n * the `null` we get from `PopoutContext` outside a popout into the\n * Radix-friendly default-fallthrough value.\n */\nimport { usePopout } from \"./PopoutContext\";\n\nexport function usePopoutContainer(): HTMLElement | undefined {\n return usePopout().popoutContainer ?? undefined;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { ackOverlaySurfaceRender, clearOverlaySurfaceRender } from \"@ai-matrx/summon/open\";\n\n/**\n * Acknowledge a visible presentation that intentionally replaces WindowPanel.\n *\n * Registered singleton window overlays normally prove they are on screen by mounting a\n * WindowPanel and registering geometry. Mobile drawers, sheets, and full-screen\n * viewers have no window-manager geometry, so their composition root must use\n * this hook while that alternate surface is active.\n */\nexport function useOverlaySurfaceRenderAck(\n overlayId: string,\n active: boolean,\n): void {\n useEffect(() => {\n if (!active) return undefined;\n ackOverlaySurfaceRender(overlayId);\n return () => clearOverlaySurfaceRender(overlayId);\n }, [active, overlayId]);\n}\n","import { useEffect, useMemo } from \"react\";\nimport { registerSyncEntry, unregisterSyncEntry } from \"@ai-matrx/summon/open\";\nimport { useSummonDispatch } from \"./storeHooks\";\n\n/**\n * Publish an open instance's address into `?panels=` while it is mounted. `linkKey` undefined →\n * nothing is published (a window with no link); so is an undefined or empty `id`. The token is `linkKey:id[:args]`; args are\n * compared by value, so a new object with equal contents never re-registers.\n */\nexport function useOverlayLink(linkKey: string | undefined, id: string | undefined, args?: Readonly<Record<string, string>>): void {\n const dispatch = useSummonDispatch();\n const argsKey = args\n ? JSON.stringify(\n Object.keys(args)\n .sort()\n .map((k) => [k, args[k]]),\n )\n : \"\";\n // Rebuilt only when the VALUES change (argsKey), never on a fresh object with equal contents.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stableArgs = useMemo(() => (args ? { ...args } : undefined), [argsKey]);\n\n useEffect(() => {\n if (!linkKey || !id) return undefined;\n dispatch(registerSyncEntry(stableArgs ? { typeKey: linkKey, instanceId: id, args: stableArgs } : { typeKey: linkKey, instanceId: id }));\n return () => {\n dispatch(unregisterSyncEntry({ typeKey: linkKey, instanceId: id }));\n };\n }, [dispatch, linkKey, id, stableArgs]);\n}\n"],"mappings":"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","names":["createContext","useContext","globalSlot","slot","createContext","useContext","useEffect","useEffect","useEffect"]}
|
package/dist/url.cjs
CHANGED
|
@@ -24,27 +24,87 @@ __export(url_exports, {
|
|
|
24
24
|
OverlayLinkManager: () => OverlayLinkManager,
|
|
25
25
|
canonicalLinkKey: () => canonicalLinkKey,
|
|
26
26
|
canonicalizeToken: () => canonicalizeToken,
|
|
27
|
-
decodeLinkPart: () =>
|
|
28
|
-
encodeLinkArg: () =>
|
|
29
|
-
encodeLinkId: () =>
|
|
30
|
-
formatLinkToken: () =>
|
|
27
|
+
decodeLinkPart: () => decodeLinkPart,
|
|
28
|
+
encodeLinkArg: () => import_open2.encodeLinkArg,
|
|
29
|
+
encodeLinkId: () => import_open2.encodeLinkId,
|
|
30
|
+
formatLinkToken: () => import_open2.formatLinkToken,
|
|
31
31
|
hydrateLinkToken: () => hydrateLinkToken,
|
|
32
32
|
mergeManagedLinkParam: () => mergeManagedLinkParam,
|
|
33
33
|
openFromLink: () => openFromLink,
|
|
34
34
|
openPanelRequest: () => openPanelRequest,
|
|
35
35
|
openRegisteredOverlay: () => openRegisteredOverlay,
|
|
36
|
-
parseLinkParam: () =>
|
|
37
|
-
parseLinkToken: () =>
|
|
38
|
-
readPanelsParam: () =>
|
|
36
|
+
parseLinkParam: () => parseLinkParam,
|
|
37
|
+
parseLinkToken: () => parseLinkToken,
|
|
38
|
+
readPanelsParam: () => readPanelsParam,
|
|
39
39
|
serializeLinkEntries: () => serializeLinkEntries,
|
|
40
40
|
unopenedWindowNotice: () => unopenedWindowNotice,
|
|
41
41
|
useOverlayLink: () => import_react3.useOverlayLink,
|
|
42
|
-
withPanelsParam: () =>
|
|
42
|
+
withPanelsParam: () => withPanelsParam,
|
|
43
43
|
withUnresolvedTokens: () => withUnresolvedTokens
|
|
44
44
|
});
|
|
45
45
|
module.exports = __toCommonJS(url_exports);
|
|
46
46
|
var import_react3 = require("@ai-matrx/summon/react");
|
|
47
|
-
var
|
|
47
|
+
var import_open2 = require("@ai-matrx/summon/open");
|
|
48
|
+
|
|
49
|
+
// src/url/linkParse.ts
|
|
50
|
+
function decodeLinkPart(value) {
|
|
51
|
+
if (!value.includes("%")) return value;
|
|
52
|
+
return value.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {
|
|
53
|
+
try {
|
|
54
|
+
return decodeURIComponent(run);
|
|
55
|
+
} catch {
|
|
56
|
+
return run;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function parseLinkToken(raw) {
|
|
61
|
+
if (!raw) return null;
|
|
62
|
+
const first = raw.indexOf(":");
|
|
63
|
+
const key = first === -1 ? raw : raw.slice(0, first);
|
|
64
|
+
if (!key) return null;
|
|
65
|
+
if (first === -1) return { raw, key, id: void 0, args: void 0 };
|
|
66
|
+
const rest = raw.slice(first + 1);
|
|
67
|
+
const second = rest.indexOf(":");
|
|
68
|
+
const idPart = second === -1 ? rest : rest.slice(0, second);
|
|
69
|
+
const argsPart = second === -1 ? void 0 : rest.slice(second + 1);
|
|
70
|
+
let args;
|
|
71
|
+
if (argsPart) {
|
|
72
|
+
args = {};
|
|
73
|
+
for (const pair of argsPart.split("_")) {
|
|
74
|
+
const at = pair.indexOf("-");
|
|
75
|
+
if (at <= 0 || at === pair.length - 1) continue;
|
|
76
|
+
args[decodeLinkPart(pair.slice(0, at))] = decodeLinkPart(pair.slice(at + 1));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { raw, key: decodeLinkPart(key), id: decodeLinkPart(idPart), args };
|
|
80
|
+
}
|
|
81
|
+
function parseLinkParam(param) {
|
|
82
|
+
if (!param) return [];
|
|
83
|
+
const out = [];
|
|
84
|
+
for (const raw of param.split(",")) {
|
|
85
|
+
const token = parseLinkToken(raw);
|
|
86
|
+
if (token) out.push(token);
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
function readPanelsParam(searchOrUrl) {
|
|
91
|
+
const q = searchOrUrl.indexOf("?");
|
|
92
|
+
const hash = searchOrUrl.indexOf("#");
|
|
93
|
+
let search;
|
|
94
|
+
if (q === -1) {
|
|
95
|
+
search = searchOrUrl.includes("=") && !searchOrUrl.includes("/") ? searchOrUrl : "";
|
|
96
|
+
} else {
|
|
97
|
+
search = searchOrUrl.slice(q + 1, hash > q ? hash : void 0);
|
|
98
|
+
}
|
|
99
|
+
return new URLSearchParams(search).get("panels");
|
|
100
|
+
}
|
|
101
|
+
function withPanelsParam(search, param) {
|
|
102
|
+
const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
|
|
103
|
+
if (param) params.set("panels", param);
|
|
104
|
+
else params.delete("panels");
|
|
105
|
+
const qs = params.toString();
|
|
106
|
+
return qs ? `?${qs}` : "";
|
|
107
|
+
}
|
|
48
108
|
|
|
49
109
|
// src/url/OverlayLinkManager.tsx
|
|
50
110
|
var import_react = require("react");
|
|
@@ -53,7 +113,6 @@ var import_react2 = require("@ai-matrx/summon/react");
|
|
|
53
113
|
|
|
54
114
|
// src/url/door.ts
|
|
55
115
|
var import_summon = require("@ai-matrx/summon");
|
|
56
|
-
var import_open = require("@ai-matrx/summon/open");
|
|
57
116
|
function openRegisteredOverlay(dispatch, overlayId, instanceId = import_summon.DEFAULT_INSTANCE_ID, data) {
|
|
58
117
|
const defaults = (0, import_summon.getOverlayMeta)(overlayId)?.defaults;
|
|
59
118
|
dispatch(
|
|
@@ -106,7 +165,7 @@ function hydrateLinkToken(token, dispatch, onError) {
|
|
|
106
165
|
}
|
|
107
166
|
}
|
|
108
167
|
async function openFromLink(searchOrUrl, options) {
|
|
109
|
-
const tokens =
|
|
168
|
+
const tokens = parseLinkParam(readPanelsParam(searchOrUrl));
|
|
110
169
|
const result = { opened: [], refused: [], unknown: [], failed: [] };
|
|
111
170
|
const unknown = tokens.filter((token) => !(0, import_summon.getOverlayMetaByLinkKey)(token.key)?.link);
|
|
112
171
|
if (options.requireAll && unknown.length > 0) {
|
|
@@ -150,10 +209,10 @@ function openPanelRequest(rawPayload, options) {
|
|
|
150
209
|
}
|
|
151
210
|
|
|
152
211
|
// src/url/OverlayLinkManager.tsx
|
|
153
|
-
var
|
|
212
|
+
var import_open = require("@ai-matrx/summon/open");
|
|
154
213
|
function serializeLinkEntries(entries, managedKeys) {
|
|
155
214
|
const managed = managedKeys ? new Set(managedKeys) : null;
|
|
156
|
-
return Object.values(entries).filter((entry) => !managed || managed.has(entry.typeKey)).map((entry) => (0,
|
|
215
|
+
return Object.values(entries).filter((entry) => !managed || managed.has(entry.typeKey)).map((entry) => (0, import_open.formatLinkToken)({ key: entry.typeKey, id: entry.instanceId, args: entry.args })).join(",");
|
|
157
216
|
}
|
|
158
217
|
function mergeManagedLinkParam(currentParam, nextManaged, managedKeys) {
|
|
159
218
|
if (!managedKeys) return nextManaged;
|
|
@@ -209,7 +268,7 @@ function OverlayLinkManager({ managedKeys, noticeDeadlineMs }) {
|
|
|
209
268
|
if (initialLoadDone.current) return;
|
|
210
269
|
initialLoadDone.current = true;
|
|
211
270
|
const managed = managedKeys ? new Set(managedKeys) : null;
|
|
212
|
-
const tokens =
|
|
271
|
+
const tokens = parseLinkParam(readPanelsParam(router.search()));
|
|
213
272
|
const noHydrator = [];
|
|
214
273
|
for (const token of tokens) {
|
|
215
274
|
if (managed && !managed.has(token.key)) continue;
|
|
@@ -256,13 +315,13 @@ function OverlayLinkManager({ managedKeys, noticeDeadlineMs }) {
|
|
|
256
315
|
);
|
|
257
316
|
}
|
|
258
317
|
const liveSearch = router.search();
|
|
259
|
-
const currentParam =
|
|
318
|
+
const currentParam = readPanelsParam(liveSearch) ?? "";
|
|
260
319
|
const nextManaged = withUnresolvedTokens(
|
|
261
320
|
serializeLinkEntries(entries, managedKeys),
|
|
262
321
|
stillPending.map((pending) => pending.token)
|
|
263
322
|
);
|
|
264
323
|
const nextParam = mergeManagedLinkParam(currentParam, nextManaged, managedKeys);
|
|
265
|
-
if (currentParam !== nextParam) router.replaceSearch(
|
|
324
|
+
if (currentParam !== nextParam) router.replaceSearch(withPanelsParam(liveSearch, nextParam));
|
|
266
325
|
}, [entries, isHydrated, managedKeys, router, search, noticeDue, asyncTick]);
|
|
267
326
|
return null;
|
|
268
327
|
}
|
package/dist/url.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/url.ts","../src/url/OverlayLinkManager.tsx","../src/url/door.ts"],"sourcesContent":["// @ai-matrx/summon/url — address-bar links: the `?panels=` grammar, the link manager, the\n// `useOverlayLink` publisher and the one door (`openFromLink`) every link source goes through.\nexport { useOverlayLink } from \"@ai-matrx/summon/react\";\nexport {\n parseLinkToken,\n parseLinkParam,\n formatLinkToken,\n encodeLinkId,\n encodeLinkArg,\n decodeLinkPart,\n readPanelsParam,\n withPanelsParam,\n type LinkToken,\n type LinkEntry,\n} from \"@ai-matrx/summon/open\";\nexport {\n OverlayLinkManager,\n serializeLinkEntries,\n mergeManagedLinkParam,\n withUnresolvedTokens,\n unopenedWindowNotice,\n type OverlayLinkManagerProps,\n} from \"./url/OverlayLinkManager\";\nexport {\n openFromLink,\n openPanelRequest,\n hydrateLinkToken,\n canonicalLinkKey,\n canonicalizeToken,\n openRegisteredOverlay,\n type OpenFromLinkResult,\n type OpenFromLinkOptions,\n type OpenPanelRequest,\n type OpenPanelResult,\n type TokenOutcome,\n type LinkDispatch,\n} from \"./url/door\";\n","/**\n * OverlayLinkManager — the address bar's `?panels=` channel (successor of matrx-frontend's\n * UrlPanelManager). Mount ONCE inside <SummonProvider>. Driven entirely by `ports.router`.\n *\n * 1. On mount: read `?panels=`, hydrate each token through the one door (`hydrateLinkToken`).\n * 2. After that: write back the union of registered link entries (`useOverlayLink`) plus every\n * pasted token that has not been settled yet, via `router.replaceSearch` — never navigation.\n *\n * THE ADDRESS IS NEVER DESTROYED BY A CLOCK (matrx-frontend V-28 NEW-5). A token whose window has\n * not registered yet is kept verbatim in the address however long its lazy chunk takes. The\n * deadline (12 s prod / 45 s dev) decides one thing only: when to stop waiting silently and tell\n * the person, through `ports.notify` and `ports.errors`.\n *\n * A token is SETTLED when ANY registered entry carries its canonical key — the window a hydrator\n * opens may publish a different id (a vault link names an item, the vault window publishes its\n * singleton id) and may even be a different overlay than the one owning the key (key `agent`\n * opens display-mode shells). An alias resolves to its definition's own `link.key`.\n */\nimport { useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport {\n LAZY_WINDOW_MOUNT_DEADLINE_MS,\n getOverlayMetaByLinkKey,\n selectIsUrlHydrated,\n selectUrlSyncEntries,\n setHydrated,\n type UrlSyncEntry,\n} from \"@ai-matrx/summon\";\nimport { useSummonDispatch, useSummonPorts, useSummonSelector } from \"@ai-matrx/summon/react\";\nimport { canonicalLinkKey, canonicalizeToken, hydrateLinkToken } from \"./door\";\nimport { formatLinkToken, parseLinkParam, readPanelsParam, withPanelsParam } from \"@ai-matrx/summon/open\";\n\nexport interface OverlayLinkManagerProps {\n /**\n * Restrict hydration and write-back to these link keys (a route-scoped manager). Tokens of\n * other keys are preserved verbatim and never hydrated. Omit for the app-wide manager.\n */\n readonly managedKeys?: readonly string[];\n /** Override the notice deadline (ms). Default: 12 s in production, 45 s in development. */\n readonly noticeDeadlineMs?: number;\n}\n\n/** Serialize registered entries as v2 tokens (only managed keys when given). */\nexport function serializeLinkEntries(entries: Readonly<Record<string, UrlSyncEntry>>, managedKeys?: readonly string[]): string {\n const managed = managedKeys ? new Set(managedKeys) : null;\n return Object.values(entries)\n .filter((entry) => !managed || managed.has(entry.typeKey))\n .map((entry) => formatLinkToken({ key: entry.typeKey, id: entry.instanceId, args: entry.args }))\n .join(\",\");\n}\n\n/** Keep the current param's unmanaged tokens; replace the managed ones with `nextManaged`. */\nexport function mergeManagedLinkParam(currentParam: string, nextManaged: string, managedKeys?: readonly string[]): string {\n if (!managedKeys) return nextManaged;\n const managed = new Set(managedKeys);\n const unmanaged = currentParam\n .split(\",\")\n .filter(Boolean)\n .filter((token) => !managed.has(token.split(\":\")[0] ?? \"\"));\n return [...unmanaged, ...nextManaged.split(\",\").filter(Boolean)].join(\",\");\n}\n\n/**\n * Re-attach pasted tokens no window has settled. A token is skipped when its CANONICAL key is\n * already represented by a live entry, so a late registration replaces the placeholder.\n */\nexport function withUnresolvedTokens(managedParam: string, unresolvedTokens: readonly string[]): string {\n if (unresolvedTokens.length === 0) return managedParam;\n const managed = managedParam.split(\",\").filter(Boolean);\n const represented = new Set(managed.map((token) => canonicalLinkKey(token.split(\":\")[0] ?? \"\")));\n const kept = unresolvedTokens.filter((token) => Boolean(token) && !represented.has(canonicalLinkKey(token.split(\":\")[0] ?? \"\")));\n return [...managed, ...kept].join(\",\");\n}\n\n/** The honest sentence for a link naming a window that did not open. */\nexport function unopenedWindowNotice(keys: readonly string[]): string {\n return (\n `This link names a window this build could not open: ${keys.join(\", \")}. ` +\n `The link is unchanged in your address bar — reload to try again, or report it if it keeps failing.`\n );\n}\n\ninterface PendingToken {\n /** What is written back while unsettled (canonical key, pasted id and args). */\n readonly token: string;\n /** The key the person pasted (named in the notice). */\n readonly tokenKey: string;\n readonly canonicalKey: string;\n}\n\nconst noopSubscribe = () => () => {};\n\nexport function OverlayLinkManager({ managedKeys, noticeDeadlineMs }: OverlayLinkManagerProps): null {\n const ports = useSummonPorts();\n const dispatch = useSummonDispatch();\n const entries = useSummonSelector(selectUrlSyncEntries);\n const isHydrated = useSummonSelector(selectIsUrlHydrated);\n const router = ports.router;\n // Re-run the write-back after ANY address change the router reports.\n const search = useSyncExternalStore(router.subscribe ?? noopSubscribe, router.search, () => \"\");\n\n const initialLoadDone = useRef(false);\n // Keyed by canonical key + id so two tokens that canonicalise together never overwrite each\n // other (V-30 NEW-5); the settle sweep still runs on canonical key.\n const unresolved = useRef(new Map<string, PendingToken>());\n const announced = useRef(new Set<string>());\n const [noticeDue, setNoticeDue] = useState(false);\n const [asyncTick, setAsyncTick] = useState(0);\n const deadline = noticeDeadlineMs ?? LAZY_WINDOW_MOUNT_DEADLINE_MS;\n\n const announce = (keys: readonly string[], reason: \"no-hydrator\" | \"never-registered\" | \"hydrator-threw\"): void => {\n const fresh = keys.filter((key) => !announced.current.has(key));\n if (fresh.length === 0) return;\n for (const key of fresh) announced.current.add(key);\n const joined = fresh.join(\", \");\n const why =\n reason === \"no-hydrator\"\n ? `no registered definition owns ${fresh.length > 1 ? \"these keys\" : \"this key\"}`\n : reason === \"hydrator-threw\"\n ? \"its hydrator threw\"\n : `hydrated, but no window registered its link within ${deadline}ms`;\n const message =\n `[overlays/url] ?panels= token(s) [${joined}]: ${why}. The token is KEPT in the address. ` +\n `Give the overlay definition a matching \\`link\\` (key or alias), or stop publishing the link.`;\n ports.errors.capture(new Error(message), {\n source: \"url-panel-unopened\",\n relation: `?panels=${joined}`,\n code: reason,\n typeKeys: fresh,\n deadlineMs: deadline,\n userMessage: unopenedWindowNotice(fresh),\n });\n ports.notify.error(unopenedWindowNotice(fresh), { duration: 12_000 });\n };\n const announceRef = useRef(announce);\n announceRef.current = announce;\n\n // 1. HYDRATION (address → store), once.\n useEffect(() => {\n if (initialLoadDone.current) return;\n initialLoadDone.current = true;\n const managed = managedKeys ? new Set(managedKeys) : null;\n const tokens = parseLinkParam(readPanelsParam(router.search()));\n const noHydrator: string[] = [];\n for (const token of tokens) {\n if (managed && !managed.has(token.key)) continue; // preserved verbatim by the write-back\n const canonicalKey = canonicalLinkKey(token.key);\n const pendingKey = `${canonicalKey}\\u0000${token.id ?? \"\"}`;\n const pending: PendingToken = { token: canonicalizeToken(token.raw), tokenKey: token.key, canonicalKey };\n if (!getOverlayMetaByLinkKey(token.key)?.link) {\n unresolved.current.set(pendingKey, { ...pending, token: token.raw });\n noHydrator.push(token.key);\n continue;\n }\n unresolved.current.set(pendingKey, pending);\n const outcome = hydrateLinkToken(token, dispatch, (error) => {\n ports.errors.capture(error, { source: \"url-panel-hydrator\", token: token.raw });\n });\n const apply = (result: string): void => {\n // A refused link is dropped from the address; it is not \"still to come\".\n if (result === \"refused\" && unresolved.current.get(pendingKey) === pending) {\n unresolved.current.delete(pendingKey);\n setAsyncTick((n) => n + 1);\n }\n };\n if (typeof outcome === \"string\") apply(outcome);\n else void outcome.then(apply);\n }\n if (noHydrator.length > 0) announceRef.current(noHydrator, \"no-hydrator\");\n dispatch(setHydrated());\n }, [dispatch, managedKeys, router, ports]);\n\n // The notice deadline: never gates a write; only turns a silent wait into a visible notice.\n useEffect(() => {\n if (!isHydrated || unresolved.current.size === 0) return undefined;\n const timer = setTimeout(() => setNoticeDue(true), deadline);\n return () => clearTimeout(timer);\n }, [isHydrated, deadline]);\n\n // 2. WRITE-BACK (store → address).\n useEffect(() => {\n if (!isHydrated) return;\n const observed = new Set(Object.values(entries).map((entry) => canonicalLinkKey(entry.typeKey)));\n for (const [key, pending] of [...unresolved.current.entries()]) {\n if (observed.has(pending.canonicalKey)) unresolved.current.delete(key);\n }\n const stillPending = [...unresolved.current.values()];\n if (noticeDue && stillPending.length > 0) {\n announceRef.current(\n stillPending.map((pending) => pending.tokenKey),\n \"never-registered\",\n );\n }\n // The LIVE address, never a snapshot: the page may have written `?view=` a moment ago.\n const liveSearch = router.search();\n const currentParam = readPanelsParam(liveSearch) ?? \"\";\n const nextManaged = withUnresolvedTokens(\n serializeLinkEntries(entries, managedKeys),\n stillPending.map((pending) => pending.token),\n );\n const nextParam = mergeManagedLinkParam(currentParam, nextManaged, managedKeys);\n if (currentParam !== nextParam) router.replaceSearch(withPanelsParam(liveSearch, nextParam));\n }, [entries, isHydrated, managedKeys, router, search, noticeDue, asyncTick]);\n\n return null;\n}\n","/**\n * The ONE door every link source goes through: the address-bar manager on load, a notification\n * that opens its link in place, the extension bridge. A token is resolved through the registry —\n * `getOverlayMetaByLinkKey(key).link.hydrate({ id, args, open, dispatch })` — never a side table.\n */\nimport type { Dispatch, UnknownAction } from \"@reduxjs/toolkit\";\nimport {\n DEFAULT_INSTANCE_ID,\n getOverlayMeta,\n getOverlayMetaByLinkKey,\n isRegisteredOverlay,\n overlaysRawActions,\n type AnyOverlayMeta,\n type OverlayHostDispatch,\n} from \"@ai-matrx/summon\";\nimport { parseLinkParam, readPanelsParam, type LinkToken } from \"@ai-matrx/summon/open\";\n\n/** Any Redux dispatch that takes plain actions (a host's thunk-aware dispatch qualifies). */\nexport type LinkDispatch = Dispatch<UnknownAction>;\n\n/**\n * Open an overlay by its registry id from inside the package. Uses the raw action so internal\n * code passes plain strings (a host's augmented `OverlayId` union never needs a cast here); the\n * definition's `defaults` are merged underneath exactly as the typed creator does.\n */\nexport function openRegisteredOverlay(\n dispatch: LinkDispatch,\n overlayId: string,\n instanceId: string = DEFAULT_INSTANCE_ID,\n data?: unknown,\n): void {\n const defaults = getOverlayMeta(overlayId)?.defaults;\n dispatch(\n overlaysRawActions.openOverlay({\n overlayId,\n instanceId,\n ...(data === undefined ? {} : { data }),\n ...(isRecord(defaults) ? { defaults } : {}),\n }),\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** The key a token is judged by: the owning definition's own `link.key` (aliases resolve to it). */\nexport function canonicalLinkKey(key: string): string {\n return getOverlayMetaByLinkKey(key)?.link?.key ?? key;\n}\n\n/** The token with its key replaced by the canonical key; id and args carry across untouched. */\nexport function canonicalizeToken(raw: string): string {\n const at = raw.indexOf(\":\");\n const key = at === -1 ? raw : raw.slice(0, at);\n const canonical = canonicalLinkKey(key);\n if (canonical === key) return raw;\n return at === -1 ? canonical : `${canonical}${raw.slice(at)}`;\n}\n\nexport type TokenOutcome = \"opened\" | \"refused\" | \"unknown\" | \"failed\";\n\n/**\n * Run one token's hydrator. Synchronous hydrators dispatch before this returns; an async one\n * yields a promise. A throw (sync or async) is `\"failed\"` and is reported to `onError`.\n */\nexport function hydrateLinkToken(\n token: LinkToken,\n dispatch: LinkDispatch,\n onError?: (error: unknown, token: LinkToken) => void,\n): TokenOutcome | Promise<TokenOutcome> {\n const meta: AnyOverlayMeta | undefined = getOverlayMetaByLinkKey(token.key);\n const link = meta?.link;\n if (!meta || !link) return \"unknown\";\n const ownerId = meta.id;\n const settle = (result: void | \"refused\"): TokenOutcome => (result === \"refused\" ? \"refused\" : \"opened\");\n const fail = (error: unknown): TokenOutcome => {\n onError?.(error, token);\n return \"failed\";\n };\n try {\n const result = link.hydrate({\n id: token.id ?? \"\",\n args: token.args ?? {},\n open: (options) => openRegisteredOverlay(dispatch, ownerId, options?.instanceId ?? DEFAULT_INSTANCE_ID, options?.data),\n // The host's store dispatch IS the host dispatch at runtime; its thunk-aware type is the\n // host's augmentation (`OverlayHostTypes`), which the package cannot see.\n dispatch: dispatch as unknown as OverlayHostDispatch,\n });\n if (result && typeof (result as PromiseLike<unknown>).then === \"function\") {\n return Promise.resolve(result).then(settle, fail);\n }\n return settle(result as void | \"refused\");\n } catch (error) {\n return fail(error);\n }\n}\n\nexport interface OpenFromLinkResult {\n /** Tokens (verbatim) whose hydrator ran and did not refuse. */\n readonly opened: string[];\n /** Tokens whose hydrator declined the link on purpose. */\n readonly refused: string[];\n /** Tokens whose key no registered definition owns. */\n readonly unknown: string[];\n /** Tokens whose hydrator threw. */\n readonly failed: string[];\n}\n\nexport interface OpenFromLinkOptions {\n readonly dispatch: LinkDispatch;\n /**\n * All or nothing: when any token's key is unknown, open NOTHING and report the unknown tokens\n * (a notification then opens the link in a new tab instead of half of it in place).\n */\n readonly requireAll?: boolean;\n readonly onError?: (error: unknown, token: LinkToken) => void;\n}\n\n/**\n * Open every `?panels=` token of a search string or URL through its definition's hydrator.\n * Synchronous hydrators have dispatched by the time this returns; the promise settles once\n * async hydrators finish.\n */\nexport async function openFromLink(searchOrUrl: string, options: OpenFromLinkOptions): Promise<OpenFromLinkResult> {\n const tokens = parseLinkParam(readPanelsParam(searchOrUrl));\n const result: OpenFromLinkResult = { opened: [], refused: [], unknown: [], failed: [] };\n const unknown = tokens.filter((token) => !getOverlayMetaByLinkKey(token.key)?.link);\n if (options.requireAll && unknown.length > 0) {\n result.unknown.push(...unknown.map((token) => token.raw));\n return result;\n }\n const outcomes = tokens.map((token) => hydrateLinkToken(token, options.dispatch, options.onError));\n const settled = await Promise.all(outcomes.map((outcome) => Promise.resolve(outcome)));\n settled.forEach((outcome, index) => {\n const raw = tokens[index]?.raw ?? \"\";\n result[outcome === \"opened\" ? \"opened\" : outcome].push(raw);\n });\n return result;\n}\n\n// ── The extension door ──────────────────────────────────────────────────────────────────────\n\nexport interface OpenPanelRequest {\n readonly panelId: string;\n readonly instanceId?: string;\n readonly data?: Readonly<Record<string, unknown>>;\n}\n\nexport type OpenPanelResult =\n | { readonly ok: true; readonly opened: true; readonly panelId: string; readonly instanceId: string | null }\n | { readonly ok: false; readonly error: \"invalid_payload\" | \"unknown_panel\"; readonly details?: unknown };\n\nfunction readOpenPanelRequest(raw: unknown): OpenPanelRequest | string {\n if (!isRecord(raw)) return \"payload must be an object\";\n const { panelId, instanceId, data } = raw;\n if (typeof panelId !== \"string\" || panelId.length === 0) return \"panelId must be a non-empty string\";\n if (instanceId !== undefined && instanceId !== null && (typeof instanceId !== \"string\" || instanceId.length === 0)) {\n return \"instanceId must be a non-empty string when given\";\n }\n if (data !== undefined && data !== null && !isRecord(data)) return \"data must be an object when given\";\n return {\n panelId,\n ...(typeof instanceId === \"string\" ? { instanceId } : {}),\n ...(isRecord(data) ? { data } : {}),\n };\n}\n\n/**\n * Open a registered overlay by id for an outside caller (the browser extension's `openPanel`\n * envelope). Validates the payload and the id; failures come back as values, never throws, so\n * the bridge can reply verbatim. `instanceId: null` in the reply = the caller used the singleton.\n */\nexport function openPanelRequest(rawPayload: unknown, options: { readonly dispatch: LinkDispatch }): OpenPanelResult {\n const request = readOpenPanelRequest(rawPayload);\n if (typeof request === \"string\") return { ok: false, error: \"invalid_payload\", details: request };\n if (!isRegisteredOverlay(request.panelId)) {\n return {\n ok: false,\n error: \"unknown_panel\",\n details: { panelId: request.panelId, hint: \"panelId must be the id of a registered overlay definition.\" },\n };\n }\n openRegisteredOverlay(options.dispatch, request.panelId, request.instanceId ?? DEFAULT_INSTANCE_ID, request.data);\n return { ok: true, opened: true, panelId: request.panelId, instanceId: request.instanceId ?? null };\n}\n"],"mappings":"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","names":["import_react","import_open","import_summon","import_react","import_open"]}
|
|
1
|
+
{"version":3,"sources":["../src/url.ts","../src/url/linkParse.ts","../src/url/OverlayLinkManager.tsx","../src/url/door.ts"],"sourcesContent":["// @ai-matrx/summon/url — address-bar links: the `?panels=` grammar, the link manager, the\n// `useOverlayLink` publisher and the one door (`openFromLink`) every link source goes through.\nexport { useOverlayLink } from \"@ai-matrx/summon/react\";\nexport { formatLinkToken, encodeLinkId, encodeLinkArg, type LinkToken, type LinkEntry } from \"@ai-matrx/summon/open\";\nexport { parseLinkToken, parseLinkParam, decodeLinkPart, readPanelsParam, withPanelsParam } from \"./url/linkParse\";\nexport {\n OverlayLinkManager,\n serializeLinkEntries,\n mergeManagedLinkParam,\n withUnresolvedTokens,\n unopenedWindowNotice,\n type OverlayLinkManagerProps,\n} from \"./url/OverlayLinkManager\";\nexport {\n openFromLink,\n openPanelRequest,\n hydrateLinkToken,\n canonicalLinkKey,\n canonicalizeToken,\n openRegisteredOverlay,\n type OpenFromLinkResult,\n type OpenFromLinkOptions,\n type OpenPanelRequest,\n type OpenPanelResult,\n type TokenOutcome,\n type LinkDispatch,\n} from \"./url/door\";\n","// The READ side of the `?panels=` grammar (see ../open/grammar.ts for the grammar and the WRITE\n// side). Ships on `./url` only: parsing a link happens in the lazy link runtime, never at boot.\nimport type { LinkToken } from \"@ai-matrx/summon/open\";\n\n/** Decode well-formed `%XX` escapes; anything else (a v1 literal `%`) is kept as written. */\nexport function decodeLinkPart(value: string): string {\n if (!value.includes(\"%\")) return value;\n return value.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {\n try {\n return decodeURIComponent(run);\n } catch {\n return run;\n }\n });\n}\n\n/** Parse one token (v1 or v2). Returns `null` for an empty token. */\nexport function parseLinkToken(raw: string): LinkToken | null {\n if (!raw) return null;\n const first = raw.indexOf(\":\");\n const key = first === -1 ? raw : raw.slice(0, first);\n if (!key) return null;\n if (first === -1) return { raw, key, id: undefined, args: undefined };\n const rest = raw.slice(first + 1);\n const second = rest.indexOf(\":\");\n const idPart = second === -1 ? rest : rest.slice(0, second);\n const argsPart = second === -1 ? undefined : rest.slice(second + 1);\n let args: Record<string, string> | undefined;\n if (argsPart) {\n args = {};\n for (const pair of argsPart.split(\"_\")) {\n // Split on the FIRST hyphen only: a v1 value may hold hyphens (uuid, date).\n const at = pair.indexOf(\"-\");\n if (at <= 0 || at === pair.length - 1) continue;\n args[decodeLinkPart(pair.slice(0, at))] = decodeLinkPart(pair.slice(at + 1));\n }\n }\n return { raw, key: decodeLinkPart(key), id: decodeLinkPart(idPart), args };\n}\n\n/** Parse a whole `panels` value. Empty segments are skipped. */\nexport function parseLinkParam(param: string | null | undefined): LinkToken[] {\n if (!param) return [];\n const out: LinkToken[] = [];\n for (const raw of param.split(\",\")) {\n const token = parseLinkToken(raw);\n if (token) out.push(token);\n }\n return out;\n}\n\n/** The `panels` value from a search string or a full URL (`null` when absent). */\nexport function readPanelsParam(searchOrUrl: string): string | null {\n const q = searchOrUrl.indexOf(\"?\");\n const hash = searchOrUrl.indexOf(\"#\");\n let search: string;\n if (q === -1) {\n // A bare query string without \"?\" (`panels=…`) or a URL with no query.\n search = searchOrUrl.includes(\"=\") && !searchOrUrl.includes(\"/\") ? searchOrUrl : \"\";\n } else {\n search = searchOrUrl.slice(q + 1, hash > q ? hash : undefined);\n }\n return new URLSearchParams(search).get(\"panels\");\n}\n\n/** `search` with `panels` set to `param` (removed when empty). Returns \"\" or \"?…\". */\nexport function withPanelsParam(search: string, param: string): string {\n const params = new URLSearchParams(search.startsWith(\"?\") ? search.slice(1) : search);\n if (param) params.set(\"panels\", param);\n else params.delete(\"panels\");\n const qs = params.toString();\n return qs ? `?${qs}` : \"\";\n}\n","/**\n * OverlayLinkManager — the address bar's `?panels=` channel (successor of matrx-frontend's\n * UrlPanelManager). Mount ONCE inside <SummonProvider>. Driven entirely by `ports.router`.\n *\n * 1. On mount: read `?panels=`, hydrate each token through the one door (`hydrateLinkToken`).\n * 2. After that: write back the union of registered link entries (`useOverlayLink`) plus every\n * pasted token that has not been settled yet, via `router.replaceSearch` — never navigation.\n *\n * THE ADDRESS IS NEVER DESTROYED BY A CLOCK (matrx-frontend V-28 NEW-5). A token whose window has\n * not registered yet is kept verbatim in the address however long its lazy chunk takes. The\n * deadline (12 s prod / 45 s dev) decides one thing only: when to stop waiting silently and tell\n * the person, through `ports.notify` and `ports.errors`.\n *\n * A token is SETTLED when ANY registered entry carries its canonical key — the window a hydrator\n * opens may publish a different id (a vault link names an item, the vault window publishes its\n * singleton id) and may even be a different overlay than the one owning the key (key `agent`\n * opens display-mode shells). An alias resolves to its definition's own `link.key`.\n */\nimport { useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport {\n LAZY_WINDOW_MOUNT_DEADLINE_MS,\n getOverlayMetaByLinkKey,\n selectIsUrlHydrated,\n selectUrlSyncEntries,\n setHydrated,\n type UrlSyncEntry,\n} from \"@ai-matrx/summon\";\nimport { useSummonDispatch, useSummonPorts, useSummonSelector } from \"@ai-matrx/summon/react\";\nimport { canonicalLinkKey, canonicalizeToken, hydrateLinkToken } from \"./door\";\nimport { formatLinkToken } from \"@ai-matrx/summon/open\";\nimport { parseLinkParam, readPanelsParam, withPanelsParam } from \"./linkParse\";\n\nexport interface OverlayLinkManagerProps {\n /**\n * Restrict hydration and write-back to these link keys (a route-scoped manager). Tokens of\n * other keys are preserved verbatim and never hydrated. Omit for the app-wide manager.\n */\n readonly managedKeys?: readonly string[];\n /** Override the notice deadline (ms). Default: 12 s in production, 45 s in development. */\n readonly noticeDeadlineMs?: number;\n}\n\n/** Serialize registered entries as v2 tokens (only managed keys when given). */\nexport function serializeLinkEntries(entries: Readonly<Record<string, UrlSyncEntry>>, managedKeys?: readonly string[]): string {\n const managed = managedKeys ? new Set(managedKeys) : null;\n return Object.values(entries)\n .filter((entry) => !managed || managed.has(entry.typeKey))\n .map((entry) => formatLinkToken({ key: entry.typeKey, id: entry.instanceId, args: entry.args }))\n .join(\",\");\n}\n\n/** Keep the current param's unmanaged tokens; replace the managed ones with `nextManaged`. */\nexport function mergeManagedLinkParam(currentParam: string, nextManaged: string, managedKeys?: readonly string[]): string {\n if (!managedKeys) return nextManaged;\n const managed = new Set(managedKeys);\n const unmanaged = currentParam\n .split(\",\")\n .filter(Boolean)\n .filter((token) => !managed.has(token.split(\":\")[0] ?? \"\"));\n return [...unmanaged, ...nextManaged.split(\",\").filter(Boolean)].join(\",\");\n}\n\n/**\n * Re-attach pasted tokens no window has settled. A token is skipped when its CANONICAL key is\n * already represented by a live entry, so a late registration replaces the placeholder.\n */\nexport function withUnresolvedTokens(managedParam: string, unresolvedTokens: readonly string[]): string {\n if (unresolvedTokens.length === 0) return managedParam;\n const managed = managedParam.split(\",\").filter(Boolean);\n const represented = new Set(managed.map((token) => canonicalLinkKey(token.split(\":\")[0] ?? \"\")));\n const kept = unresolvedTokens.filter((token) => Boolean(token) && !represented.has(canonicalLinkKey(token.split(\":\")[0] ?? \"\")));\n return [...managed, ...kept].join(\",\");\n}\n\n/** The honest sentence for a link naming a window that did not open. */\nexport function unopenedWindowNotice(keys: readonly string[]): string {\n return (\n `This link names a window this build could not open: ${keys.join(\", \")}. ` +\n `The link is unchanged in your address bar — reload to try again, or report it if it keeps failing.`\n );\n}\n\ninterface PendingToken {\n /** What is written back while unsettled (canonical key, pasted id and args). */\n readonly token: string;\n /** The key the person pasted (named in the notice). */\n readonly tokenKey: string;\n readonly canonicalKey: string;\n}\n\nconst noopSubscribe = () => () => {};\n\nexport function OverlayLinkManager({ managedKeys, noticeDeadlineMs }: OverlayLinkManagerProps): null {\n const ports = useSummonPorts();\n const dispatch = useSummonDispatch();\n const entries = useSummonSelector(selectUrlSyncEntries);\n const isHydrated = useSummonSelector(selectIsUrlHydrated);\n const router = ports.router;\n // Re-run the write-back after ANY address change the router reports.\n const search = useSyncExternalStore(router.subscribe ?? noopSubscribe, router.search, () => \"\");\n\n const initialLoadDone = useRef(false);\n // Keyed by canonical key + id so two tokens that canonicalise together never overwrite each\n // other (V-30 NEW-5); the settle sweep still runs on canonical key.\n const unresolved = useRef(new Map<string, PendingToken>());\n const announced = useRef(new Set<string>());\n const [noticeDue, setNoticeDue] = useState(false);\n const [asyncTick, setAsyncTick] = useState(0);\n const deadline = noticeDeadlineMs ?? LAZY_WINDOW_MOUNT_DEADLINE_MS;\n\n const announce = (keys: readonly string[], reason: \"no-hydrator\" | \"never-registered\" | \"hydrator-threw\"): void => {\n const fresh = keys.filter((key) => !announced.current.has(key));\n if (fresh.length === 0) return;\n for (const key of fresh) announced.current.add(key);\n const joined = fresh.join(\", \");\n const why =\n reason === \"no-hydrator\"\n ? `no registered definition owns ${fresh.length > 1 ? \"these keys\" : \"this key\"}`\n : reason === \"hydrator-threw\"\n ? \"its hydrator threw\"\n : `hydrated, but no window registered its link within ${deadline}ms`;\n const message =\n `[overlays/url] ?panels= token(s) [${joined}]: ${why}. The token is KEPT in the address. ` +\n `Give the overlay definition a matching \\`link\\` (key or alias), or stop publishing the link.`;\n ports.errors.capture(new Error(message), {\n source: \"url-panel-unopened\",\n relation: `?panels=${joined}`,\n code: reason,\n typeKeys: fresh,\n deadlineMs: deadline,\n userMessage: unopenedWindowNotice(fresh),\n });\n ports.notify.error(unopenedWindowNotice(fresh), { duration: 12_000 });\n };\n const announceRef = useRef(announce);\n announceRef.current = announce;\n\n // 1. HYDRATION (address → store), once.\n useEffect(() => {\n if (initialLoadDone.current) return;\n initialLoadDone.current = true;\n const managed = managedKeys ? new Set(managedKeys) : null;\n const tokens = parseLinkParam(readPanelsParam(router.search()));\n const noHydrator: string[] = [];\n for (const token of tokens) {\n if (managed && !managed.has(token.key)) continue; // preserved verbatim by the write-back\n const canonicalKey = canonicalLinkKey(token.key);\n const pendingKey = `${canonicalKey}\\u0000${token.id ?? \"\"}`;\n const pending: PendingToken = { token: canonicalizeToken(token.raw), tokenKey: token.key, canonicalKey };\n if (!getOverlayMetaByLinkKey(token.key)?.link) {\n unresolved.current.set(pendingKey, { ...pending, token: token.raw });\n noHydrator.push(token.key);\n continue;\n }\n unresolved.current.set(pendingKey, pending);\n const outcome = hydrateLinkToken(token, dispatch, (error) => {\n ports.errors.capture(error, { source: \"url-panel-hydrator\", token: token.raw });\n });\n const apply = (result: string): void => {\n // A refused link is dropped from the address; it is not \"still to come\".\n if (result === \"refused\" && unresolved.current.get(pendingKey) === pending) {\n unresolved.current.delete(pendingKey);\n setAsyncTick((n) => n + 1);\n }\n };\n if (typeof outcome === \"string\") apply(outcome);\n else void outcome.then(apply);\n }\n if (noHydrator.length > 0) announceRef.current(noHydrator, \"no-hydrator\");\n dispatch(setHydrated());\n }, [dispatch, managedKeys, router, ports]);\n\n // The notice deadline: never gates a write; only turns a silent wait into a visible notice.\n useEffect(() => {\n if (!isHydrated || unresolved.current.size === 0) return undefined;\n const timer = setTimeout(() => setNoticeDue(true), deadline);\n return () => clearTimeout(timer);\n }, [isHydrated, deadline]);\n\n // 2. WRITE-BACK (store → address).\n useEffect(() => {\n if (!isHydrated) return;\n const observed = new Set(Object.values(entries).map((entry) => canonicalLinkKey(entry.typeKey)));\n for (const [key, pending] of [...unresolved.current.entries()]) {\n if (observed.has(pending.canonicalKey)) unresolved.current.delete(key);\n }\n const stillPending = [...unresolved.current.values()];\n if (noticeDue && stillPending.length > 0) {\n announceRef.current(\n stillPending.map((pending) => pending.tokenKey),\n \"never-registered\",\n );\n }\n // The LIVE address, never a snapshot: the page may have written `?view=` a moment ago.\n const liveSearch = router.search();\n const currentParam = readPanelsParam(liveSearch) ?? \"\";\n const nextManaged = withUnresolvedTokens(\n serializeLinkEntries(entries, managedKeys),\n stillPending.map((pending) => pending.token),\n );\n const nextParam = mergeManagedLinkParam(currentParam, nextManaged, managedKeys);\n if (currentParam !== nextParam) router.replaceSearch(withPanelsParam(liveSearch, nextParam));\n }, [entries, isHydrated, managedKeys, router, search, noticeDue, asyncTick]);\n\n return null;\n}\n","/**\n * The ONE door every link source goes through: the address-bar manager on load, a notification\n * that opens its link in place, the extension bridge. A token is resolved through the registry —\n * `getOverlayMetaByLinkKey(key).link.hydrate({ id, args, open, dispatch })` — never a side table.\n */\nimport type { Dispatch, UnknownAction } from \"@reduxjs/toolkit\";\nimport {\n DEFAULT_INSTANCE_ID,\n getOverlayMeta,\n getOverlayMetaByLinkKey,\n isRegisteredOverlay,\n overlaysRawActions,\n type AnyOverlayMeta,\n type OverlayHostDispatch,\n} from \"@ai-matrx/summon\";\nimport type { LinkToken } from \"@ai-matrx/summon/open\";\nimport { parseLinkParam, readPanelsParam } from \"./linkParse\";\n\n/** Any Redux dispatch that takes plain actions (a host's thunk-aware dispatch qualifies). */\nexport type LinkDispatch = Dispatch<UnknownAction>;\n\n/**\n * Open an overlay by its registry id from inside the package. Uses the raw action so internal\n * code passes plain strings (a host's augmented `OverlayId` union never needs a cast here); the\n * definition's `defaults` are merged underneath exactly as the typed creator does.\n */\nexport function openRegisteredOverlay(\n dispatch: LinkDispatch,\n overlayId: string,\n instanceId: string = DEFAULT_INSTANCE_ID,\n data?: unknown,\n): void {\n const defaults = getOverlayMeta(overlayId)?.defaults;\n dispatch(\n overlaysRawActions.openOverlay({\n overlayId,\n instanceId,\n ...(data === undefined ? {} : { data }),\n ...(isRecord(defaults) ? { defaults } : {}),\n }),\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** The key a token is judged by: the owning definition's own `link.key` (aliases resolve to it). */\nexport function canonicalLinkKey(key: string): string {\n return getOverlayMetaByLinkKey(key)?.link?.key ?? key;\n}\n\n/** The token with its key replaced by the canonical key; id and args carry across untouched. */\nexport function canonicalizeToken(raw: string): string {\n const at = raw.indexOf(\":\");\n const key = at === -1 ? raw : raw.slice(0, at);\n const canonical = canonicalLinkKey(key);\n if (canonical === key) return raw;\n return at === -1 ? canonical : `${canonical}${raw.slice(at)}`;\n}\n\nexport type TokenOutcome = \"opened\" | \"refused\" | \"unknown\" | \"failed\";\n\n/**\n * Run one token's hydrator. Synchronous hydrators dispatch before this returns; an async one\n * yields a promise. A throw (sync or async) is `\"failed\"` and is reported to `onError`.\n */\nexport function hydrateLinkToken(\n token: LinkToken,\n dispatch: LinkDispatch,\n onError?: (error: unknown, token: LinkToken) => void,\n): TokenOutcome | Promise<TokenOutcome> {\n const meta: AnyOverlayMeta | undefined = getOverlayMetaByLinkKey(token.key);\n const link = meta?.link;\n if (!meta || !link) return \"unknown\";\n const ownerId = meta.id;\n const settle = (result: void | \"refused\"): TokenOutcome => (result === \"refused\" ? \"refused\" : \"opened\");\n const fail = (error: unknown): TokenOutcome => {\n onError?.(error, token);\n return \"failed\";\n };\n try {\n const result = link.hydrate({\n id: token.id ?? \"\",\n args: token.args ?? {},\n open: (options) => openRegisteredOverlay(dispatch, ownerId, options?.instanceId ?? DEFAULT_INSTANCE_ID, options?.data),\n // The host's store dispatch IS the host dispatch at runtime; its thunk-aware type is the\n // host's augmentation (`OverlayHostTypes`), which the package cannot see.\n dispatch: dispatch as unknown as OverlayHostDispatch,\n });\n if (result && typeof (result as PromiseLike<unknown>).then === \"function\") {\n return Promise.resolve(result).then(settle, fail);\n }\n return settle(result as void | \"refused\");\n } catch (error) {\n return fail(error);\n }\n}\n\nexport interface OpenFromLinkResult {\n /** Tokens (verbatim) whose hydrator ran and did not refuse. */\n readonly opened: string[];\n /** Tokens whose hydrator declined the link on purpose. */\n readonly refused: string[];\n /** Tokens whose key no registered definition owns. */\n readonly unknown: string[];\n /** Tokens whose hydrator threw. */\n readonly failed: string[];\n}\n\nexport interface OpenFromLinkOptions {\n readonly dispatch: LinkDispatch;\n /**\n * All or nothing: when any token's key is unknown, open NOTHING and report the unknown tokens\n * (a notification then opens the link in a new tab instead of half of it in place).\n */\n readonly requireAll?: boolean;\n readonly onError?: (error: unknown, token: LinkToken) => void;\n}\n\n/**\n * Open every `?panels=` token of a search string or URL through its definition's hydrator.\n * Synchronous hydrators have dispatched by the time this returns; the promise settles once\n * async hydrators finish.\n */\nexport async function openFromLink(searchOrUrl: string, options: OpenFromLinkOptions): Promise<OpenFromLinkResult> {\n const tokens = parseLinkParam(readPanelsParam(searchOrUrl));\n const result: OpenFromLinkResult = { opened: [], refused: [], unknown: [], failed: [] };\n const unknown = tokens.filter((token) => !getOverlayMetaByLinkKey(token.key)?.link);\n if (options.requireAll && unknown.length > 0) {\n result.unknown.push(...unknown.map((token) => token.raw));\n return result;\n }\n const outcomes = tokens.map((token) => hydrateLinkToken(token, options.dispatch, options.onError));\n const settled = await Promise.all(outcomes.map((outcome) => Promise.resolve(outcome)));\n settled.forEach((outcome, index) => {\n const raw = tokens[index]?.raw ?? \"\";\n result[outcome === \"opened\" ? \"opened\" : outcome].push(raw);\n });\n return result;\n}\n\n// ── The extension door ──────────────────────────────────────────────────────────────────────\n\nexport interface OpenPanelRequest {\n readonly panelId: string;\n readonly instanceId?: string;\n readonly data?: Readonly<Record<string, unknown>>;\n}\n\nexport type OpenPanelResult =\n | { readonly ok: true; readonly opened: true; readonly panelId: string; readonly instanceId: string | null }\n | { readonly ok: false; readonly error: \"invalid_payload\" | \"unknown_panel\"; readonly details?: unknown };\n\nfunction readOpenPanelRequest(raw: unknown): OpenPanelRequest | string {\n if (!isRecord(raw)) return \"payload must be an object\";\n const { panelId, instanceId, data } = raw;\n if (typeof panelId !== \"string\" || panelId.length === 0) return \"panelId must be a non-empty string\";\n if (instanceId !== undefined && instanceId !== null && (typeof instanceId !== \"string\" || instanceId.length === 0)) {\n return \"instanceId must be a non-empty string when given\";\n }\n if (data !== undefined && data !== null && !isRecord(data)) return \"data must be an object when given\";\n return {\n panelId,\n ...(typeof instanceId === \"string\" ? { instanceId } : {}),\n ...(isRecord(data) ? { data } : {}),\n };\n}\n\n/**\n * Open a registered overlay by id for an outside caller (the browser extension's `openPanel`\n * envelope). Validates the payload and the id; failures come back as values, never throws, so\n * the bridge can reply verbatim. `instanceId: null` in the reply = the caller used the singleton.\n */\nexport function openPanelRequest(rawPayload: unknown, options: { readonly dispatch: LinkDispatch }): OpenPanelResult {\n const request = readOpenPanelRequest(rawPayload);\n if (typeof request === \"string\") return { ok: false, error: \"invalid_payload\", details: request };\n if (!isRegisteredOverlay(request.panelId)) {\n return {\n ok: false,\n error: \"unknown_panel\",\n details: { panelId: request.panelId, hint: \"panelId must be the id of a registered overlay definition.\" },\n };\n }\n openRegisteredOverlay(options.dispatch, request.panelId, request.instanceId ?? DEFAULT_INSTANCE_ID, request.data);\n return { ok: true, opened: true, panelId: request.panelId, instanceId: request.instanceId ?? null };\n}\n"],"mappings":"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","names":["import_react","import_open","import_summon","import_react"]}
|
package/dist/url.d.cts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
export { useOverlayLink } from '@ai-matrx/summon/react';
|
|
2
2
|
import { LinkToken } from '@ai-matrx/summon/open';
|
|
3
|
-
export { LinkEntry, LinkToken,
|
|
3
|
+
export { LinkEntry, LinkToken, encodeLinkArg, encodeLinkId, formatLinkToken } from '@ai-matrx/summon/open';
|
|
4
4
|
import { UrlSyncEntry } from '@ai-matrx/summon';
|
|
5
5
|
import { Dispatch, UnknownAction } from '@reduxjs/toolkit';
|
|
6
6
|
|
|
7
|
+
/** Decode well-formed `%XX` escapes; anything else (a v1 literal `%`) is kept as written. */
|
|
8
|
+
declare function decodeLinkPart(value: string): string;
|
|
9
|
+
/** Parse one token (v1 or v2). Returns `null` for an empty token. */
|
|
10
|
+
declare function parseLinkToken(raw: string): LinkToken | null;
|
|
11
|
+
/** Parse a whole `panels` value. Empty segments are skipped. */
|
|
12
|
+
declare function parseLinkParam(param: string | null | undefined): LinkToken[];
|
|
13
|
+
/** The `panels` value from a search string or a full URL (`null` when absent). */
|
|
14
|
+
declare function readPanelsParam(searchOrUrl: string): string | null;
|
|
15
|
+
/** `search` with `panels` set to `param` (removed when empty). Returns "" or "?…". */
|
|
16
|
+
declare function withPanelsParam(search: string, param: string): string;
|
|
17
|
+
|
|
7
18
|
interface OverlayLinkManagerProps {
|
|
8
19
|
/**
|
|
9
20
|
* Restrict hydration and write-back to these link keys (a route-scoped manager). Tokens of
|
|
@@ -99,4 +110,4 @@ declare function openPanelRequest(rawPayload: unknown, options: {
|
|
|
99
110
|
readonly dispatch: LinkDispatch;
|
|
100
111
|
}): OpenPanelResult;
|
|
101
112
|
|
|
102
|
-
export { type LinkDispatch, type OpenFromLinkOptions, type OpenFromLinkResult, type OpenPanelRequest, type OpenPanelResult, OverlayLinkManager, type OverlayLinkManagerProps, type TokenOutcome, canonicalLinkKey, canonicalizeToken, hydrateLinkToken, mergeManagedLinkParam, openFromLink, openPanelRequest, openRegisteredOverlay, serializeLinkEntries, unopenedWindowNotice, withUnresolvedTokens };
|
|
113
|
+
export { type LinkDispatch, type OpenFromLinkOptions, type OpenFromLinkResult, type OpenPanelRequest, type OpenPanelResult, OverlayLinkManager, type OverlayLinkManagerProps, type TokenOutcome, canonicalLinkKey, canonicalizeToken, decodeLinkPart, hydrateLinkToken, mergeManagedLinkParam, openFromLink, openPanelRequest, openRegisteredOverlay, parseLinkParam, parseLinkToken, readPanelsParam, serializeLinkEntries, unopenedWindowNotice, withPanelsParam, withUnresolvedTokens };
|
package/dist/url.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
export { useOverlayLink } from '@ai-matrx/summon/react';
|
|
2
2
|
import { LinkToken } from '@ai-matrx/summon/open';
|
|
3
|
-
export { LinkEntry, LinkToken,
|
|
3
|
+
export { LinkEntry, LinkToken, encodeLinkArg, encodeLinkId, formatLinkToken } from '@ai-matrx/summon/open';
|
|
4
4
|
import { UrlSyncEntry } from '@ai-matrx/summon';
|
|
5
5
|
import { Dispatch, UnknownAction } from '@reduxjs/toolkit';
|
|
6
6
|
|
|
7
|
+
/** Decode well-formed `%XX` escapes; anything else (a v1 literal `%`) is kept as written. */
|
|
8
|
+
declare function decodeLinkPart(value: string): string;
|
|
9
|
+
/** Parse one token (v1 or v2). Returns `null` for an empty token. */
|
|
10
|
+
declare function parseLinkToken(raw: string): LinkToken | null;
|
|
11
|
+
/** Parse a whole `panels` value. Empty segments are skipped. */
|
|
12
|
+
declare function parseLinkParam(param: string | null | undefined): LinkToken[];
|
|
13
|
+
/** The `panels` value from a search string or a full URL (`null` when absent). */
|
|
14
|
+
declare function readPanelsParam(searchOrUrl: string): string | null;
|
|
15
|
+
/** `search` with `panels` set to `param` (removed when empty). Returns "" or "?…". */
|
|
16
|
+
declare function withPanelsParam(search: string, param: string): string;
|
|
17
|
+
|
|
7
18
|
interface OverlayLinkManagerProps {
|
|
8
19
|
/**
|
|
9
20
|
* Restrict hydration and write-back to these link keys (a route-scoped manager). Tokens of
|
|
@@ -99,4 +110,4 @@ declare function openPanelRequest(rawPayload: unknown, options: {
|
|
|
99
110
|
readonly dispatch: LinkDispatch;
|
|
100
111
|
}): OpenPanelResult;
|
|
101
112
|
|
|
102
|
-
export { type LinkDispatch, type OpenFromLinkOptions, type OpenFromLinkResult, type OpenPanelRequest, type OpenPanelResult, OverlayLinkManager, type OverlayLinkManagerProps, type TokenOutcome, canonicalLinkKey, canonicalizeToken, hydrateLinkToken, mergeManagedLinkParam, openFromLink, openPanelRequest, openRegisteredOverlay, serializeLinkEntries, unopenedWindowNotice, withUnresolvedTokens };
|
|
113
|
+
export { type LinkDispatch, type OpenFromLinkOptions, type OpenFromLinkResult, type OpenPanelRequest, type OpenPanelResult, OverlayLinkManager, type OverlayLinkManagerProps, type TokenOutcome, canonicalLinkKey, canonicalizeToken, decodeLinkPart, hydrateLinkToken, mergeManagedLinkParam, openFromLink, openPanelRequest, openRegisteredOverlay, parseLinkParam, parseLinkToken, readPanelsParam, serializeLinkEntries, unopenedWindowNotice, withPanelsParam, withUnresolvedTokens };
|
package/dist/url.js
CHANGED
|
@@ -2,16 +2,67 @@
|
|
|
2
2
|
|
|
3
3
|
// src/url.ts
|
|
4
4
|
import { useOverlayLink } from "@ai-matrx/summon/react";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
import { formatLinkToken as formatLinkToken2, encodeLinkId, encodeLinkArg } from "@ai-matrx/summon/open";
|
|
6
|
+
|
|
7
|
+
// src/url/linkParse.ts
|
|
8
|
+
function decodeLinkPart(value) {
|
|
9
|
+
if (!value.includes("%")) return value;
|
|
10
|
+
return value.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {
|
|
11
|
+
try {
|
|
12
|
+
return decodeURIComponent(run);
|
|
13
|
+
} catch {
|
|
14
|
+
return run;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function parseLinkToken(raw) {
|
|
19
|
+
if (!raw) return null;
|
|
20
|
+
const first = raw.indexOf(":");
|
|
21
|
+
const key = first === -1 ? raw : raw.slice(0, first);
|
|
22
|
+
if (!key) return null;
|
|
23
|
+
if (first === -1) return { raw, key, id: void 0, args: void 0 };
|
|
24
|
+
const rest = raw.slice(first + 1);
|
|
25
|
+
const second = rest.indexOf(":");
|
|
26
|
+
const idPart = second === -1 ? rest : rest.slice(0, second);
|
|
27
|
+
const argsPart = second === -1 ? void 0 : rest.slice(second + 1);
|
|
28
|
+
let args;
|
|
29
|
+
if (argsPart) {
|
|
30
|
+
args = {};
|
|
31
|
+
for (const pair of argsPart.split("_")) {
|
|
32
|
+
const at = pair.indexOf("-");
|
|
33
|
+
if (at <= 0 || at === pair.length - 1) continue;
|
|
34
|
+
args[decodeLinkPart(pair.slice(0, at))] = decodeLinkPart(pair.slice(at + 1));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { raw, key: decodeLinkPart(key), id: decodeLinkPart(idPart), args };
|
|
38
|
+
}
|
|
39
|
+
function parseLinkParam(param) {
|
|
40
|
+
if (!param) return [];
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const raw of param.split(",")) {
|
|
43
|
+
const token = parseLinkToken(raw);
|
|
44
|
+
if (token) out.push(token);
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
function readPanelsParam(searchOrUrl) {
|
|
49
|
+
const q = searchOrUrl.indexOf("?");
|
|
50
|
+
const hash = searchOrUrl.indexOf("#");
|
|
51
|
+
let search;
|
|
52
|
+
if (q === -1) {
|
|
53
|
+
search = searchOrUrl.includes("=") && !searchOrUrl.includes("/") ? searchOrUrl : "";
|
|
54
|
+
} else {
|
|
55
|
+
search = searchOrUrl.slice(q + 1, hash > q ? hash : void 0);
|
|
56
|
+
}
|
|
57
|
+
return new URLSearchParams(search).get("panels");
|
|
58
|
+
}
|
|
59
|
+
function withPanelsParam(search, param) {
|
|
60
|
+
const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
|
|
61
|
+
if (param) params.set("panels", param);
|
|
62
|
+
else params.delete("panels");
|
|
63
|
+
const qs = params.toString();
|
|
64
|
+
return qs ? `?${qs}` : "";
|
|
65
|
+
}
|
|
15
66
|
|
|
16
67
|
// src/url/OverlayLinkManager.tsx
|
|
17
68
|
import { useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
@@ -32,7 +83,6 @@ import {
|
|
|
32
83
|
isRegisteredOverlay,
|
|
33
84
|
overlaysRawActions
|
|
34
85
|
} from "@ai-matrx/summon";
|
|
35
|
-
import { parseLinkParam, readPanelsParam } from "@ai-matrx/summon/open";
|
|
36
86
|
function openRegisteredOverlay(dispatch, overlayId, instanceId = DEFAULT_INSTANCE_ID, data) {
|
|
37
87
|
const defaults = getOverlayMeta(overlayId)?.defaults;
|
|
38
88
|
dispatch(
|
|
@@ -129,7 +179,7 @@ function openPanelRequest(rawPayload, options) {
|
|
|
129
179
|
}
|
|
130
180
|
|
|
131
181
|
// src/url/OverlayLinkManager.tsx
|
|
132
|
-
import { formatLinkToken
|
|
182
|
+
import { formatLinkToken } from "@ai-matrx/summon/open";
|
|
133
183
|
function serializeLinkEntries(entries, managedKeys) {
|
|
134
184
|
const managed = managedKeys ? new Set(managedKeys) : null;
|
|
135
185
|
return Object.values(entries).filter((entry) => !managed || managed.has(entry.typeKey)).map((entry) => formatLinkToken({ key: entry.typeKey, id: entry.instanceId, args: entry.args })).join(",");
|
|
@@ -188,7 +238,7 @@ function OverlayLinkManager({ managedKeys, noticeDeadlineMs }) {
|
|
|
188
238
|
if (initialLoadDone.current) return;
|
|
189
239
|
initialLoadDone.current = true;
|
|
190
240
|
const managed = managedKeys ? new Set(managedKeys) : null;
|
|
191
|
-
const tokens =
|
|
241
|
+
const tokens = parseLinkParam(readPanelsParam(router.search()));
|
|
192
242
|
const noHydrator = [];
|
|
193
243
|
for (const token of tokens) {
|
|
194
244
|
if (managed && !managed.has(token.key)) continue;
|
|
@@ -235,7 +285,7 @@ function OverlayLinkManager({ managedKeys, noticeDeadlineMs }) {
|
|
|
235
285
|
);
|
|
236
286
|
}
|
|
237
287
|
const liveSearch = router.search();
|
|
238
|
-
const currentParam =
|
|
288
|
+
const currentParam = readPanelsParam(liveSearch) ?? "";
|
|
239
289
|
const nextManaged = withUnresolvedTokens(
|
|
240
290
|
serializeLinkEntries(entries, managedKeys),
|
|
241
291
|
stillPending.map((pending) => pending.token)
|
|
@@ -258,13 +308,13 @@ export {
|
|
|
258
308
|
openFromLink,
|
|
259
309
|
openPanelRequest,
|
|
260
310
|
openRegisteredOverlay,
|
|
261
|
-
|
|
311
|
+
parseLinkParam,
|
|
262
312
|
parseLinkToken,
|
|
263
|
-
|
|
313
|
+
readPanelsParam,
|
|
264
314
|
serializeLinkEntries,
|
|
265
315
|
unopenedWindowNotice,
|
|
266
316
|
useOverlayLink,
|
|
267
|
-
|
|
317
|
+
withPanelsParam,
|
|
268
318
|
withUnresolvedTokens
|
|
269
319
|
};
|
|
270
320
|
//# sourceMappingURL=url.js.map
|