@proteos/component-sdk 0.59.2 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-3VUWWDED.cjs → chunk-HD5ADY7F.cjs} +2 -2
- package/dist/{chunk-3VUWWDED.cjs.map → chunk-HD5ADY7F.cjs.map} +1 -1
- package/dist/{chunk-HODSL6ER.js → chunk-NOCSMXCW.js} +2 -2
- package/dist/{chunk-HODSL6ER.js.map → chunk-NOCSMXCW.js.map} +1 -1
- package/dist/index.cjs +34 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +25 -4
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs +5 -5
- package/dist/protocol.d.cts +22 -2
- package/dist/protocol.d.ts +22 -2
- package/dist/protocol.js +1 -1
- package/package.json +2 -2
- package/src/context.tsx +52 -1
- package/src/hooks.test.tsx +35 -0
- package/src/hooks.ts +21 -0
- package/src/index.ts +5 -0
- package/src/protocol.ts +24 -0
|
@@ -14,5 +14,5 @@ exports.COMPONENT_OVERLAY_Z_INDEX = COMPONENT_OVERLAY_Z_INDEX;
|
|
|
14
14
|
exports.MESSAGE_SOURCE = MESSAGE_SOURCE;
|
|
15
15
|
exports.isHostMessage = isHostMessage;
|
|
16
16
|
exports.isRuntimeMessage = isRuntimeMessage;
|
|
17
|
-
//# sourceMappingURL=chunk-
|
|
18
|
-
//# sourceMappingURL=chunk-
|
|
17
|
+
//# sourceMappingURL=chunk-HD5ADY7F.cjs.map
|
|
18
|
+
//# sourceMappingURL=chunk-HD5ADY7F.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/protocol.ts"],"names":[],"mappings":";;;AAUO,IAAM,cAAA,GAAiB;AAuBvB,IAAM,yBAAA,GAA4B;AAmNlC,SAAS,cAAc,IAAA,EAA8C;AAC1E,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACR,KAA8B,MAAA,KAAW,cAAA,IAC1C,OAAQ,IAAA,CAA4B,IAAA,KAAS,QAAA;AAEjD;AAEO,SAAS,iBAAiB,IAAA,EAAiD;AAChF,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACR,KAA8B,MAAA,KAAW,cAAA,IAC1C,OAAQ,IAAA,CAA4B,IAAA,KAAS,QAAA;AAEjD","file":"chunk-3VUWWDED.cjs","sourcesContent":["// postMessage protocol between the host page and the component-runtime iframe.\n// This is the single source of truth for the wire contract: the host renderer\n// (clients/web .../component-frame.tsx), the runtime shell\n// (clients/web/component-runtime/shell.tsx), and the author-facing hooks\n// (this package's RuntimeProvider / useNavigate) all import these same types so\n// every end stays in lockstep.\n//\n// Every message carries `source: MESSAGE_SOURCE` so we can ignore the unrelated\n// postMessage traffic that shares the window (OIDC silent-renew, Vite HMR, …).\n\nexport const MESSAGE_SOURCE = 'proteos-component-runtime' as const\n\n/**\n * z-index the component iframe is promoted to while one of its own overlays\n * (modal, popover, menu, select, tooltip) is open. The iframe goes\n * `position: fixed; inset: 0` at this z so the overlay — which portals to the\n * IFRAME's body and would otherwise clip at the frame's box — can paint over\n * the rest of the page.\n *\n * Host z ladder this slots into:\n * 50 @proteos/ui Modal / Tooltip / ImageLightbox (default tier)\n * 1000+ full-page panel stack (scrim 1000, panels 1001+n)\n * 1090 peek scrim 1100 peek panel, ask-proteos window\n * 1150 ← THIS\n * 1200 menu popover, command palette, action dialog, component modal\n * 1300 select popover\n *\n * Above the peek/full-page surfaces a component can live on, below the global\n * host chrome. Note the cap: inside a peek (a `fixed` + z-index ancestor) the\n * promoted iframe paints within that 1100 stacking context — it covers the\n * peek's own content and everything under it, but host chrome at 1200+ still\n * wins. That is the intended layering, not a bug.\n */\nexport const COMPONENT_OVERLAY_Z_INDEX = 1150\n\n/** Viewport-relative box of the iframe's in-flow placeholder, in CSS px. */\nexport interface OverlayRect {\n top: number\n left: number\n width: number\n height: number\n}\n\nexport interface PageContext {\n record?: Record<string, unknown>\n entity?: { slug: string; [key: string]: unknown }\n params?: Record<string, unknown>\n /** The signed-in viewer (absent on public pages and offline previews). The\n * host resolves `{{ current_user.* }}` and the bare `current_user` prop\n * sentinel against it before posting props. */\n current_user?: { id: string; email: string; given_name: string; family_name: string }\n [key: string]: unknown\n}\n\n// ── Host → runtime ──────────────────────────────────────────────────────────\n\nexport interface InitMessage {\n type: 'INIT'\n /** Raw bearer token (the OIDC `id_token`). The runtime's ProteosClient\n * prepends `Bearer ` when building the Authorization header, so the host\n * sends the token verbatim — NOT a full `Bearer …` header value.\n * Empty string in public mode (`isPublic: true`). */\n authToken: string\n /** API origin the bundle endpoint + SDK calls resolve against. */\n apiBaseUrl: string\n /** Resolved props for the component (Liquid templating happens host-side). */\n props: Record<string, unknown>\n pageCtx: PageContext\n /** PUBLIC mode: the component renders on an unauthenticated public page.\n * The shell fetches the bundle from the public endpoint (no bearer) and\n * injects a restricted sdk — only `functions.actions.invokePublic` reaches\n * the platform. Requires `orgId`. */\n isPublic?: boolean\n /** Org id for public mode — public endpoints carry the org in the path\n * because there is no token to scope from. */\n orgId?: string\n /** Set when this frame is the CONTENT of a host component modal (opened by\n * another component via OPEN_MODAL). The component reads it through\n * `useModalHost()` and closes with a result by posting MODAL_RESULT with\n * this id. Absent on ordinary page-hosted frames. */\n modalRequestId?: string\n}\n\nexport interface AuthRefreshMessage {\n type: 'AUTH_REFRESH'\n token: string\n}\n\nexport interface DataInvalidateMessage {\n type: 'DATA_INVALIDATE'\n queryKey?: unknown[]\n}\n\n/** Where the iframe's in-flow box sits in the host viewport. Sent while the\n * frame is promoted (OVERLAY_STATE open) so the runtime can keep its content\n * parked at the original rect while the iframe itself covers the viewport —\n * and re-sent when the host window resizes under an open overlay. */\nexport interface OverlayRectMessage {\n type: 'OVERLAY_RECT'\n rect: OverlayRect\n}\n\n/** Resolution of an OPEN_MODAL request: the host modal closed. `result` is the\n * value the modal's component passed to MODAL_RESULT, and is absent when the\n * modal was dismissed (backdrop / Esc / ×). */\nexport interface ModalClosedMessage {\n type: 'MODAL_CLOSED'\n requestId: string\n result?: unknown\n}\n\nexport type HostMessage =\n | InitMessage\n | AuthRefreshMessage\n | DataInvalidateMessage\n | OverlayRectMessage\n | ModalClosedMessage\n\n// ── Runtime → host ──────────────────────────────────────────────────────────\n\nexport interface ReadyMessage {\n type: 'READY'\n}\n\nexport interface ResizeMessage {\n type: 'RESIZE'\n height: number\n}\n\nexport interface NavigateMessage {\n type: 'NAVIGATE'\n to: string\n}\n\n/** Open an in-app path in a peek (slide-over) panel instead of full-page\n * navigation. Same `/…` in-app path grammar as NAVIGATE; the host decides\n * what can peek (today: platform pages) and falls back to NAVIGATE otherwise. */\nexport interface OpenPeekMessage {\n type: 'OPEN_PEEK'\n to: string\n}\n\n/** Ask the host to open a prepared call: the host shows the conversation brief's\n * preview (its softphone owns the dial) and lets the user place the call.\n * Ignored on public pages and on hosts without a softphone. */\nexport interface OpenCallMessage {\n type: 'OPEN_CALL'\n /** The conversation brief to preview and dial from. */\n conversationBriefId: string\n}\n\n/** The runtime has (or no longer has) an overlay open and needs the host to\n * promote (or restore) the iframe. See {@link COMPONENT_OVERLAY_Z_INDEX}.\n *\n * Same-origin today, so the runtime applies the promotion styles to its own\n * `window.frameElement` synchronously (pre-paint, no flicker) and this message\n * is the host's cue to MIRROR that state into React — gate RESIZE, hold the\n * placeholder height, start posting OVERLAY_RECT. When components move to a\n * dedicated origin the runtime can no longer touch the frame and this message\n * becomes the actuation instead; the runtime hides its overlay until the\n * answering OVERLAY_RECT arrives. */\nexport interface OverlayStateMessage {\n type: 'OVERLAY_STATE'\n open: boolean\n}\n\n/** Width of a host component modal's panel: `sm` 384px, `md` 512px, `lg` 672px,\n * `xl` 896px. Below 640px the modal is a full-screen sheet whatever the\n * width. */\nexport type ModalWidth = 'sm' | 'md' | 'lg' | 'xl'\n\n/** Ask the host to open ANOTHER component in a central modal, above everything\n * this frame could reach on its own. The host answers with MODAL_CLOSED\n * carrying the same `requestId`.\n *\n * One modal per opener at a time: while a request from this frame is still\n * open, the host answers any further OPEN_MODAL from it as dismissed (a\n * double click must not stack two dialogs). The modal's own component may\n * open another — that request comes from a different frame. */\nexport interface OpenModalMessage {\n type: 'OPEN_MODAL'\n /** Correlates the eventual MODAL_CLOSED. Generated by the opener. */\n requestId: string\n /** Slug of the component to mount inside the modal. */\n componentSlug: string\n /** Props for that component. The host renders Liquid in every string value\n * against the OPENER's page scope (`record`, `entity`, `params`,\n * `current_user`) before handing them over — exactly as it does for a\n * page-placed component. So `{{ … }}` / `{% … %}` in a value is evaluated,\n * not passed through. */\n props?: Record<string, unknown>\n /** Optional modal heading. */\n title?: string\n /** Panel width. Defaults to `lg`. */\n width?: ModalWidth\n /** Height of the component's frame, in px. Set: the frame is that tall from\n * the first paint (capped at the viewport), scrolls its own content, and\n * sits edge to edge in the panel — the component owns its padding. Unset:\n * the frame grows with its content inside the host's padded body. */\n height?: number\n}\n\n/** Escape was pressed with focus inside the iframe, and nothing in the runtime\n * consumed it.\n *\n * Keyboard events don't cross the iframe boundary, and react-aria binds its\n * Escape handling to the overlay element in its own React tree — so a\n * host-rendered dialog CANNOT see Escape once the user has clicked into a\n * component. The runtime forwards it so the host can dismiss on the user's\n * behalf. Not sent while the runtime has its own overlay open: that overlay\n * owns Escape. */\nexport interface EscapePressedMessage {\n type: 'ESCAPE_PRESSED'\n}\n\n/** Posted by the component INSIDE a host modal to close it with a value. The\n * `requestId` is the one INIT carried as `modalRequestId`. */\nexport interface ModalResultMessage {\n type: 'MODAL_RESULT'\n requestId: string\n result?: unknown\n}\n\nexport interface ErrorMessage {\n type: 'ERROR'\n message: string\n stack?: string\n}\n\nexport type RuntimeMessage =\n | ReadyMessage\n | ResizeMessage\n | NavigateMessage\n | OpenPeekMessage\n | OpenCallMessage\n | OverlayStateMessage\n | OpenModalMessage\n | ModalResultMessage\n | EscapePressedMessage\n | ErrorMessage\n\n// Wire envelope: the message plus the source discriminator.\nexport type Envelope<T> = T & { source: typeof MESSAGE_SOURCE }\n\nexport function isHostMessage(data: unknown): data is Envelope<HostMessage> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { source?: unknown }).source === MESSAGE_SOURCE &&\n typeof (data as { type?: unknown }).type === 'string'\n )\n}\n\nexport function isRuntimeMessage(data: unknown): data is Envelope<RuntimeMessage> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { source?: unknown }).source === MESSAGE_SOURCE &&\n typeof (data as { type?: unknown }).type === 'string'\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/protocol.ts"],"names":[],"mappings":";;;AAUO,IAAM,cAAA,GAAiB;AAuBvB,IAAM,yBAAA,GAA4B;AA2OlC,SAAS,cAAc,IAAA,EAA8C;AAC1E,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACR,KAA8B,MAAA,KAAW,cAAA,IAC1C,OAAQ,IAAA,CAA4B,IAAA,KAAS,QAAA;AAEjD;AAEO,SAAS,iBAAiB,IAAA,EAAiD;AAChF,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACR,KAA8B,MAAA,KAAW,cAAA,IAC1C,OAAQ,IAAA,CAA4B,IAAA,KAAS,QAAA;AAEjD","file":"chunk-HD5ADY7F.cjs","sourcesContent":["// postMessage protocol between the host page and the component-runtime iframe.\n// This is the single source of truth for the wire contract: the host renderer\n// (clients/web .../component-frame.tsx), the runtime shell\n// (clients/web/component-runtime/shell.tsx), and the author-facing hooks\n// (this package's RuntimeProvider / useNavigate) all import these same types so\n// every end stays in lockstep.\n//\n// Every message carries `source: MESSAGE_SOURCE` so we can ignore the unrelated\n// postMessage traffic that shares the window (OIDC silent-renew, Vite HMR, …).\n\nexport const MESSAGE_SOURCE = 'proteos-component-runtime' as const\n\n/**\n * z-index the component iframe is promoted to while one of its own overlays\n * (modal, popover, menu, select, tooltip) is open. The iframe goes\n * `position: fixed; inset: 0` at this z so the overlay — which portals to the\n * IFRAME's body and would otherwise clip at the frame's box — can paint over\n * the rest of the page.\n *\n * Host z ladder this slots into:\n * 50 @proteos/ui Modal / Tooltip / ImageLightbox (default tier)\n * 1000+ full-page panel stack (scrim 1000, panels 1001+n)\n * 1090 peek scrim 1100 peek panel, ask-proteos window\n * 1150 ← THIS\n * 1200 menu popover, command palette, action dialog, component modal\n * 1300 select popover\n *\n * Above the peek/full-page surfaces a component can live on, below the global\n * host chrome. Note the cap: inside a peek (a `fixed` + z-index ancestor) the\n * promoted iframe paints within that 1100 stacking context — it covers the\n * peek's own content and everything under it, but host chrome at 1200+ still\n * wins. That is the intended layering, not a bug.\n */\nexport const COMPONENT_OVERLAY_Z_INDEX = 1150\n\n/** Viewport-relative box of the iframe's in-flow placeholder, in CSS px. */\nexport interface OverlayRect {\n top: number\n left: number\n width: number\n height: number\n}\n\nexport interface PageContext {\n record?: Record<string, unknown>\n entity?: { slug: string; [key: string]: unknown }\n params?: Record<string, unknown>\n /** The signed-in viewer (absent on public pages and offline previews). The\n * host resolves `{{ current_user.* }}` and the bare `current_user` prop\n * sentinel against it before posting props. */\n current_user?: { id: string; email: string; given_name: string; family_name: string }\n [key: string]: unknown\n}\n\n// ── Host → runtime ──────────────────────────────────────────────────────────\n\nexport interface InitMessage {\n type: 'INIT'\n /** Raw bearer token (the OIDC `id_token`). The runtime's ProteosClient\n * prepends `Bearer ` when building the Authorization header, so the host\n * sends the token verbatim — NOT a full `Bearer …` header value.\n * Empty string in public mode (`isPublic: true`). */\n authToken: string\n /** API origin the bundle endpoint + SDK calls resolve against. */\n apiBaseUrl: string\n /** Resolved props for the component (Liquid templating happens host-side). */\n props: Record<string, unknown>\n pageCtx: PageContext\n /** PUBLIC mode: the component renders on an unauthenticated public page.\n * The shell fetches the bundle from the public endpoint (no bearer) and\n * injects a restricted sdk — only `functions.actions.invokePublic` reaches\n * the platform. Requires `orgId`. */\n isPublic?: boolean\n /** Org id for public mode — public endpoints carry the org in the path\n * because there is no token to scope from. */\n orgId?: string\n /** Set when this frame is the CONTENT of a host component modal (opened by\n * another component via OPEN_MODAL). The component reads it through\n * `useModalHost()` and closes with a result by posting MODAL_RESULT with\n * this id. Absent on ordinary page-hosted frames. */\n modalRequestId?: string\n}\n\nexport interface AuthRefreshMessage {\n type: 'AUTH_REFRESH'\n token: string\n}\n\nexport interface DataInvalidateMessage {\n type: 'DATA_INVALIDATE'\n queryKey?: unknown[]\n}\n\n/** Where the iframe's in-flow box sits in the host viewport. Sent while the\n * frame is promoted (OVERLAY_STATE open) so the runtime can keep its content\n * parked at the original rect while the iframe itself covers the viewport —\n * and re-sent when the host window resizes under an open overlay. */\nexport interface OverlayRectMessage {\n type: 'OVERLAY_RECT'\n rect: OverlayRect\n}\n\n/** Resolution of an OPEN_MODAL request: the host modal closed. `result` is the\n * value the modal's component passed to MODAL_RESULT, and is absent when the\n * modal was dismissed (backdrop / Esc / ×). */\nexport interface ModalClosedMessage {\n type: 'MODAL_CLOSED'\n requestId: string\n result?: unknown\n}\n\nexport type HostMessage =\n | InitMessage\n | AuthRefreshMessage\n | DataInvalidateMessage\n | OverlayRectMessage\n | ModalClosedMessage\n\n// ── Runtime → host ──────────────────────────────────────────────────────────\n\nexport interface ReadyMessage {\n type: 'READY'\n}\n\nexport interface ResizeMessage {\n type: 'RESIZE'\n height: number\n}\n\nexport interface NavigateMessage {\n type: 'NAVIGATE'\n to: string\n}\n\n/** Open an in-app path in a peek (slide-over) panel instead of full-page\n * navigation. Same `/…` in-app path grammar as NAVIGATE; the host decides\n * what can peek (today: platform pages) and falls back to NAVIGATE otherwise. */\nexport interface OpenPeekMessage {\n type: 'OPEN_PEEK'\n to: string\n}\n\n/** Ask the host to open a prepared call: the host shows the conversation brief's\n * preview (its softphone owns the dial) and lets the user place the call.\n * Ignored on public pages and on hosts without a softphone. */\nexport interface OpenCallMessage {\n type: 'OPEN_CALL'\n /** The conversation brief to preview and dial from. */\n conversationBriefId: string\n}\n\n/** The runtime has (or no longer has) an overlay open and needs the host to\n * promote (or restore) the iframe. See {@link COMPONENT_OVERLAY_Z_INDEX}.\n *\n * Same-origin today, so the runtime applies the promotion styles to its own\n * `window.frameElement` synchronously (pre-paint, no flicker) and this message\n * is the host's cue to MIRROR that state into React — gate RESIZE, hold the\n * placeholder height, start posting OVERLAY_RECT. When components move to a\n * dedicated origin the runtime can no longer touch the frame and this message\n * becomes the actuation instead; the runtime hides its overlay until the\n * answering OVERLAY_RECT arrives. */\nexport interface OverlayStateMessage {\n type: 'OVERLAY_STATE'\n open: boolean\n}\n\n/** Width of a host component modal's panel: `sm` 384px, `md` 512px, `lg` 672px,\n * `xl` 896px. Below 640px the modal is a full-screen sheet whatever the\n * width. */\nexport type ModalWidth = 'sm' | 'md' | 'lg' | 'xl'\n\n/** Ask the host to open ANOTHER component in a central modal, above everything\n * this frame could reach on its own. The host answers with MODAL_CLOSED\n * carrying the same `requestId`.\n *\n * One modal per opener at a time: while a request from this frame is still\n * open, the host answers any further OPEN_MODAL from it as dismissed (a\n * double click must not stack two dialogs). The modal's own component may\n * open another — that request comes from a different frame. */\nexport interface OpenModalMessage {\n type: 'OPEN_MODAL'\n /** Correlates the eventual MODAL_CLOSED. Generated by the opener. */\n requestId: string\n /** Slug of the component to mount inside the modal. */\n componentSlug: string\n /** Props for that component. The host renders Liquid in every string value\n * against the OPENER's page scope (`record`, `entity`, `params`,\n * `current_user`) before handing them over — exactly as it does for a\n * page-placed component. So `{{ … }}` / `{% … %}` in a value is evaluated,\n * not passed through. */\n props?: Record<string, unknown>\n /** Optional modal heading. */\n title?: string\n /** Panel width. Defaults to `lg`. */\n width?: ModalWidth\n /** Height of the component's frame, in px. Set: the frame is that tall from\n * the first paint (capped at the viewport), scrolls its own content, and\n * sits edge to edge in the panel — the component owns its padding. Unset:\n * the frame grows with its content inside the host's padded body. */\n height?: number\n}\n\n/** Escape was pressed with focus inside the iframe, and nothing in the runtime\n * consumed it.\n *\n * Keyboard events don't cross the iframe boundary, and react-aria binds its\n * Escape handling to the overlay element in its own React tree — so a\n * host-rendered dialog CANNOT see Escape once the user has clicked into a\n * component. The runtime forwards it so the host can dismiss on the user's\n * behalf. Not sent while the runtime has its own overlay open: that overlay\n * owns Escape. */\nexport interface EscapePressedMessage {\n type: 'ESCAPE_PRESSED'\n}\n\n/** Posted by the component INSIDE a host modal to close it with a value. The\n * `requestId` is the one INIT carried as `modalRequestId`. */\nexport interface ModalResultMessage {\n type: 'MODAL_RESULT'\n requestId: string\n result?: unknown\n}\n\n/** Ask the host to open its agent session window (the floating assistant).\n *\n * - `sessionId` set: the window resumes THAT session (its agent is read from\n * the session). A `prompt` is sent into it.\n * - no `sessionId`: a fresh session with `agentKey` (or the agent currently\n * selected in the window); a `prompt` is sent as its first message. With\n * neither field the window just opens as it is.\n *\n * Ignored on public pages (there is no signed-in user to run an agent as). */\nexport interface OpenAgentWindowMessage {\n type: 'OPEN_AGENT_WINDOW'\n sessionId?: string\n agentKey?: string\n prompt?: string\n}\n\n/** Ask the host to minimize its agent session window. The active session and\n * its stream stay alive — reopening resumes it. */\nexport interface CloseAgentWindowMessage {\n type: 'CLOSE_AGENT_WINDOW'\n}\n\nexport interface ErrorMessage {\n type: 'ERROR'\n message: string\n stack?: string\n}\n\nexport type RuntimeMessage =\n | ReadyMessage\n | ResizeMessage\n | NavigateMessage\n | OpenPeekMessage\n | OpenCallMessage\n | OverlayStateMessage\n | OpenModalMessage\n | ModalResultMessage\n | EscapePressedMessage\n | OpenAgentWindowMessage\n | CloseAgentWindowMessage\n | ErrorMessage\n\n// Wire envelope: the message plus the source discriminator.\nexport type Envelope<T> = T & { source: typeof MESSAGE_SOURCE }\n\nexport function isHostMessage(data: unknown): data is Envelope<HostMessage> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { source?: unknown }).source === MESSAGE_SOURCE &&\n typeof (data as { type?: unknown }).type === 'string'\n )\n}\n\nexport function isRuntimeMessage(data: unknown): data is Envelope<RuntimeMessage> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { source?: unknown }).source === MESSAGE_SOURCE &&\n typeof (data as { type?: unknown }).type === 'string'\n )\n}\n"]}
|
|
@@ -9,5 +9,5 @@ function isRuntimeMessage(data) {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export { COMPONENT_OVERLAY_Z_INDEX, MESSAGE_SOURCE, isHostMessage, isRuntimeMessage };
|
|
12
|
-
//# sourceMappingURL=chunk-
|
|
13
|
-
//# sourceMappingURL=chunk-
|
|
12
|
+
//# sourceMappingURL=chunk-NOCSMXCW.js.map
|
|
13
|
+
//# sourceMappingURL=chunk-NOCSMXCW.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/protocol.ts"],"names":[],"mappings":";AAUO,IAAM,cAAA,GAAiB;AAuBvB,IAAM,yBAAA,GAA4B;AAmNlC,SAAS,cAAc,IAAA,EAA8C;AAC1E,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACR,KAA8B,MAAA,KAAW,cAAA,IAC1C,OAAQ,IAAA,CAA4B,IAAA,KAAS,QAAA;AAEjD;AAEO,SAAS,iBAAiB,IAAA,EAAiD;AAChF,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACR,KAA8B,MAAA,KAAW,cAAA,IAC1C,OAAQ,IAAA,CAA4B,IAAA,KAAS,QAAA;AAEjD","file":"chunk-HODSL6ER.js","sourcesContent":["// postMessage protocol between the host page and the component-runtime iframe.\n// This is the single source of truth for the wire contract: the host renderer\n// (clients/web .../component-frame.tsx), the runtime shell\n// (clients/web/component-runtime/shell.tsx), and the author-facing hooks\n// (this package's RuntimeProvider / useNavigate) all import these same types so\n// every end stays in lockstep.\n//\n// Every message carries `source: MESSAGE_SOURCE` so we can ignore the unrelated\n// postMessage traffic that shares the window (OIDC silent-renew, Vite HMR, …).\n\nexport const MESSAGE_SOURCE = 'proteos-component-runtime' as const\n\n/**\n * z-index the component iframe is promoted to while one of its own overlays\n * (modal, popover, menu, select, tooltip) is open. The iframe goes\n * `position: fixed; inset: 0` at this z so the overlay — which portals to the\n * IFRAME's body and would otherwise clip at the frame's box — can paint over\n * the rest of the page.\n *\n * Host z ladder this slots into:\n * 50 @proteos/ui Modal / Tooltip / ImageLightbox (default tier)\n * 1000+ full-page panel stack (scrim 1000, panels 1001+n)\n * 1090 peek scrim 1100 peek panel, ask-proteos window\n * 1150 ← THIS\n * 1200 menu popover, command palette, action dialog, component modal\n * 1300 select popover\n *\n * Above the peek/full-page surfaces a component can live on, below the global\n * host chrome. Note the cap: inside a peek (a `fixed` + z-index ancestor) the\n * promoted iframe paints within that 1100 stacking context — it covers the\n * peek's own content and everything under it, but host chrome at 1200+ still\n * wins. That is the intended layering, not a bug.\n */\nexport const COMPONENT_OVERLAY_Z_INDEX = 1150\n\n/** Viewport-relative box of the iframe's in-flow placeholder, in CSS px. */\nexport interface OverlayRect {\n top: number\n left: number\n width: number\n height: number\n}\n\nexport interface PageContext {\n record?: Record<string, unknown>\n entity?: { slug: string; [key: string]: unknown }\n params?: Record<string, unknown>\n /** The signed-in viewer (absent on public pages and offline previews). The\n * host resolves `{{ current_user.* }}` and the bare `current_user` prop\n * sentinel against it before posting props. */\n current_user?: { id: string; email: string; given_name: string; family_name: string }\n [key: string]: unknown\n}\n\n// ── Host → runtime ──────────────────────────────────────────────────────────\n\nexport interface InitMessage {\n type: 'INIT'\n /** Raw bearer token (the OIDC `id_token`). The runtime's ProteosClient\n * prepends `Bearer ` when building the Authorization header, so the host\n * sends the token verbatim — NOT a full `Bearer …` header value.\n * Empty string in public mode (`isPublic: true`). */\n authToken: string\n /** API origin the bundle endpoint + SDK calls resolve against. */\n apiBaseUrl: string\n /** Resolved props for the component (Liquid templating happens host-side). */\n props: Record<string, unknown>\n pageCtx: PageContext\n /** PUBLIC mode: the component renders on an unauthenticated public page.\n * The shell fetches the bundle from the public endpoint (no bearer) and\n * injects a restricted sdk — only `functions.actions.invokePublic` reaches\n * the platform. Requires `orgId`. */\n isPublic?: boolean\n /** Org id for public mode — public endpoints carry the org in the path\n * because there is no token to scope from. */\n orgId?: string\n /** Set when this frame is the CONTENT of a host component modal (opened by\n * another component via OPEN_MODAL). The component reads it through\n * `useModalHost()` and closes with a result by posting MODAL_RESULT with\n * this id. Absent on ordinary page-hosted frames. */\n modalRequestId?: string\n}\n\nexport interface AuthRefreshMessage {\n type: 'AUTH_REFRESH'\n token: string\n}\n\nexport interface DataInvalidateMessage {\n type: 'DATA_INVALIDATE'\n queryKey?: unknown[]\n}\n\n/** Where the iframe's in-flow box sits in the host viewport. Sent while the\n * frame is promoted (OVERLAY_STATE open) so the runtime can keep its content\n * parked at the original rect while the iframe itself covers the viewport —\n * and re-sent when the host window resizes under an open overlay. */\nexport interface OverlayRectMessage {\n type: 'OVERLAY_RECT'\n rect: OverlayRect\n}\n\n/** Resolution of an OPEN_MODAL request: the host modal closed. `result` is the\n * value the modal's component passed to MODAL_RESULT, and is absent when the\n * modal was dismissed (backdrop / Esc / ×). */\nexport interface ModalClosedMessage {\n type: 'MODAL_CLOSED'\n requestId: string\n result?: unknown\n}\n\nexport type HostMessage =\n | InitMessage\n | AuthRefreshMessage\n | DataInvalidateMessage\n | OverlayRectMessage\n | ModalClosedMessage\n\n// ── Runtime → host ──────────────────────────────────────────────────────────\n\nexport interface ReadyMessage {\n type: 'READY'\n}\n\nexport interface ResizeMessage {\n type: 'RESIZE'\n height: number\n}\n\nexport interface NavigateMessage {\n type: 'NAVIGATE'\n to: string\n}\n\n/** Open an in-app path in a peek (slide-over) panel instead of full-page\n * navigation. Same `/…` in-app path grammar as NAVIGATE; the host decides\n * what can peek (today: platform pages) and falls back to NAVIGATE otherwise. */\nexport interface OpenPeekMessage {\n type: 'OPEN_PEEK'\n to: string\n}\n\n/** Ask the host to open a prepared call: the host shows the conversation brief's\n * preview (its softphone owns the dial) and lets the user place the call.\n * Ignored on public pages and on hosts without a softphone. */\nexport interface OpenCallMessage {\n type: 'OPEN_CALL'\n /** The conversation brief to preview and dial from. */\n conversationBriefId: string\n}\n\n/** The runtime has (or no longer has) an overlay open and needs the host to\n * promote (or restore) the iframe. See {@link COMPONENT_OVERLAY_Z_INDEX}.\n *\n * Same-origin today, so the runtime applies the promotion styles to its own\n * `window.frameElement` synchronously (pre-paint, no flicker) and this message\n * is the host's cue to MIRROR that state into React — gate RESIZE, hold the\n * placeholder height, start posting OVERLAY_RECT. When components move to a\n * dedicated origin the runtime can no longer touch the frame and this message\n * becomes the actuation instead; the runtime hides its overlay until the\n * answering OVERLAY_RECT arrives. */\nexport interface OverlayStateMessage {\n type: 'OVERLAY_STATE'\n open: boolean\n}\n\n/** Width of a host component modal's panel: `sm` 384px, `md` 512px, `lg` 672px,\n * `xl` 896px. Below 640px the modal is a full-screen sheet whatever the\n * width. */\nexport type ModalWidth = 'sm' | 'md' | 'lg' | 'xl'\n\n/** Ask the host to open ANOTHER component in a central modal, above everything\n * this frame could reach on its own. The host answers with MODAL_CLOSED\n * carrying the same `requestId`.\n *\n * One modal per opener at a time: while a request from this frame is still\n * open, the host answers any further OPEN_MODAL from it as dismissed (a\n * double click must not stack two dialogs). The modal's own component may\n * open another — that request comes from a different frame. */\nexport interface OpenModalMessage {\n type: 'OPEN_MODAL'\n /** Correlates the eventual MODAL_CLOSED. Generated by the opener. */\n requestId: string\n /** Slug of the component to mount inside the modal. */\n componentSlug: string\n /** Props for that component. The host renders Liquid in every string value\n * against the OPENER's page scope (`record`, `entity`, `params`,\n * `current_user`) before handing them over — exactly as it does for a\n * page-placed component. So `{{ … }}` / `{% … %}` in a value is evaluated,\n * not passed through. */\n props?: Record<string, unknown>\n /** Optional modal heading. */\n title?: string\n /** Panel width. Defaults to `lg`. */\n width?: ModalWidth\n /** Height of the component's frame, in px. Set: the frame is that tall from\n * the first paint (capped at the viewport), scrolls its own content, and\n * sits edge to edge in the panel — the component owns its padding. Unset:\n * the frame grows with its content inside the host's padded body. */\n height?: number\n}\n\n/** Escape was pressed with focus inside the iframe, and nothing in the runtime\n * consumed it.\n *\n * Keyboard events don't cross the iframe boundary, and react-aria binds its\n * Escape handling to the overlay element in its own React tree — so a\n * host-rendered dialog CANNOT see Escape once the user has clicked into a\n * component. The runtime forwards it so the host can dismiss on the user's\n * behalf. Not sent while the runtime has its own overlay open: that overlay\n * owns Escape. */\nexport interface EscapePressedMessage {\n type: 'ESCAPE_PRESSED'\n}\n\n/** Posted by the component INSIDE a host modal to close it with a value. The\n * `requestId` is the one INIT carried as `modalRequestId`. */\nexport interface ModalResultMessage {\n type: 'MODAL_RESULT'\n requestId: string\n result?: unknown\n}\n\nexport interface ErrorMessage {\n type: 'ERROR'\n message: string\n stack?: string\n}\n\nexport type RuntimeMessage =\n | ReadyMessage\n | ResizeMessage\n | NavigateMessage\n | OpenPeekMessage\n | OpenCallMessage\n | OverlayStateMessage\n | OpenModalMessage\n | ModalResultMessage\n | EscapePressedMessage\n | ErrorMessage\n\n// Wire envelope: the message plus the source discriminator.\nexport type Envelope<T> = T & { source: typeof MESSAGE_SOURCE }\n\nexport function isHostMessage(data: unknown): data is Envelope<HostMessage> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { source?: unknown }).source === MESSAGE_SOURCE &&\n typeof (data as { type?: unknown }).type === 'string'\n )\n}\n\nexport function isRuntimeMessage(data: unknown): data is Envelope<RuntimeMessage> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { source?: unknown }).source === MESSAGE_SOURCE &&\n typeof (data as { type?: unknown }).type === 'string'\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/protocol.ts"],"names":[],"mappings":";AAUO,IAAM,cAAA,GAAiB;AAuBvB,IAAM,yBAAA,GAA4B;AA2OlC,SAAS,cAAc,IAAA,EAA8C;AAC1E,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACR,KAA8B,MAAA,KAAW,cAAA,IAC1C,OAAQ,IAAA,CAA4B,IAAA,KAAS,QAAA;AAEjD;AAEO,SAAS,iBAAiB,IAAA,EAAiD;AAChF,EAAA,OACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACR,KAA8B,MAAA,KAAW,cAAA,IAC1C,OAAQ,IAAA,CAA4B,IAAA,KAAS,QAAA;AAEjD","file":"chunk-NOCSMXCW.js","sourcesContent":["// postMessage protocol between the host page and the component-runtime iframe.\n// This is the single source of truth for the wire contract: the host renderer\n// (clients/web .../component-frame.tsx), the runtime shell\n// (clients/web/component-runtime/shell.tsx), and the author-facing hooks\n// (this package's RuntimeProvider / useNavigate) all import these same types so\n// every end stays in lockstep.\n//\n// Every message carries `source: MESSAGE_SOURCE` so we can ignore the unrelated\n// postMessage traffic that shares the window (OIDC silent-renew, Vite HMR, …).\n\nexport const MESSAGE_SOURCE = 'proteos-component-runtime' as const\n\n/**\n * z-index the component iframe is promoted to while one of its own overlays\n * (modal, popover, menu, select, tooltip) is open. The iframe goes\n * `position: fixed; inset: 0` at this z so the overlay — which portals to the\n * IFRAME's body and would otherwise clip at the frame's box — can paint over\n * the rest of the page.\n *\n * Host z ladder this slots into:\n * 50 @proteos/ui Modal / Tooltip / ImageLightbox (default tier)\n * 1000+ full-page panel stack (scrim 1000, panels 1001+n)\n * 1090 peek scrim 1100 peek panel, ask-proteos window\n * 1150 ← THIS\n * 1200 menu popover, command palette, action dialog, component modal\n * 1300 select popover\n *\n * Above the peek/full-page surfaces a component can live on, below the global\n * host chrome. Note the cap: inside a peek (a `fixed` + z-index ancestor) the\n * promoted iframe paints within that 1100 stacking context — it covers the\n * peek's own content and everything under it, but host chrome at 1200+ still\n * wins. That is the intended layering, not a bug.\n */\nexport const COMPONENT_OVERLAY_Z_INDEX = 1150\n\n/** Viewport-relative box of the iframe's in-flow placeholder, in CSS px. */\nexport interface OverlayRect {\n top: number\n left: number\n width: number\n height: number\n}\n\nexport interface PageContext {\n record?: Record<string, unknown>\n entity?: { slug: string; [key: string]: unknown }\n params?: Record<string, unknown>\n /** The signed-in viewer (absent on public pages and offline previews). The\n * host resolves `{{ current_user.* }}` and the bare `current_user` prop\n * sentinel against it before posting props. */\n current_user?: { id: string; email: string; given_name: string; family_name: string }\n [key: string]: unknown\n}\n\n// ── Host → runtime ──────────────────────────────────────────────────────────\n\nexport interface InitMessage {\n type: 'INIT'\n /** Raw bearer token (the OIDC `id_token`). The runtime's ProteosClient\n * prepends `Bearer ` when building the Authorization header, so the host\n * sends the token verbatim — NOT a full `Bearer …` header value.\n * Empty string in public mode (`isPublic: true`). */\n authToken: string\n /** API origin the bundle endpoint + SDK calls resolve against. */\n apiBaseUrl: string\n /** Resolved props for the component (Liquid templating happens host-side). */\n props: Record<string, unknown>\n pageCtx: PageContext\n /** PUBLIC mode: the component renders on an unauthenticated public page.\n * The shell fetches the bundle from the public endpoint (no bearer) and\n * injects a restricted sdk — only `functions.actions.invokePublic` reaches\n * the platform. Requires `orgId`. */\n isPublic?: boolean\n /** Org id for public mode — public endpoints carry the org in the path\n * because there is no token to scope from. */\n orgId?: string\n /** Set when this frame is the CONTENT of a host component modal (opened by\n * another component via OPEN_MODAL). The component reads it through\n * `useModalHost()` and closes with a result by posting MODAL_RESULT with\n * this id. Absent on ordinary page-hosted frames. */\n modalRequestId?: string\n}\n\nexport interface AuthRefreshMessage {\n type: 'AUTH_REFRESH'\n token: string\n}\n\nexport interface DataInvalidateMessage {\n type: 'DATA_INVALIDATE'\n queryKey?: unknown[]\n}\n\n/** Where the iframe's in-flow box sits in the host viewport. Sent while the\n * frame is promoted (OVERLAY_STATE open) so the runtime can keep its content\n * parked at the original rect while the iframe itself covers the viewport —\n * and re-sent when the host window resizes under an open overlay. */\nexport interface OverlayRectMessage {\n type: 'OVERLAY_RECT'\n rect: OverlayRect\n}\n\n/** Resolution of an OPEN_MODAL request: the host modal closed. `result` is the\n * value the modal's component passed to MODAL_RESULT, and is absent when the\n * modal was dismissed (backdrop / Esc / ×). */\nexport interface ModalClosedMessage {\n type: 'MODAL_CLOSED'\n requestId: string\n result?: unknown\n}\n\nexport type HostMessage =\n | InitMessage\n | AuthRefreshMessage\n | DataInvalidateMessage\n | OverlayRectMessage\n | ModalClosedMessage\n\n// ── Runtime → host ──────────────────────────────────────────────────────────\n\nexport interface ReadyMessage {\n type: 'READY'\n}\n\nexport interface ResizeMessage {\n type: 'RESIZE'\n height: number\n}\n\nexport interface NavigateMessage {\n type: 'NAVIGATE'\n to: string\n}\n\n/** Open an in-app path in a peek (slide-over) panel instead of full-page\n * navigation. Same `/…` in-app path grammar as NAVIGATE; the host decides\n * what can peek (today: platform pages) and falls back to NAVIGATE otherwise. */\nexport interface OpenPeekMessage {\n type: 'OPEN_PEEK'\n to: string\n}\n\n/** Ask the host to open a prepared call: the host shows the conversation brief's\n * preview (its softphone owns the dial) and lets the user place the call.\n * Ignored on public pages and on hosts without a softphone. */\nexport interface OpenCallMessage {\n type: 'OPEN_CALL'\n /** The conversation brief to preview and dial from. */\n conversationBriefId: string\n}\n\n/** The runtime has (or no longer has) an overlay open and needs the host to\n * promote (or restore) the iframe. See {@link COMPONENT_OVERLAY_Z_INDEX}.\n *\n * Same-origin today, so the runtime applies the promotion styles to its own\n * `window.frameElement` synchronously (pre-paint, no flicker) and this message\n * is the host's cue to MIRROR that state into React — gate RESIZE, hold the\n * placeholder height, start posting OVERLAY_RECT. When components move to a\n * dedicated origin the runtime can no longer touch the frame and this message\n * becomes the actuation instead; the runtime hides its overlay until the\n * answering OVERLAY_RECT arrives. */\nexport interface OverlayStateMessage {\n type: 'OVERLAY_STATE'\n open: boolean\n}\n\n/** Width of a host component modal's panel: `sm` 384px, `md` 512px, `lg` 672px,\n * `xl` 896px. Below 640px the modal is a full-screen sheet whatever the\n * width. */\nexport type ModalWidth = 'sm' | 'md' | 'lg' | 'xl'\n\n/** Ask the host to open ANOTHER component in a central modal, above everything\n * this frame could reach on its own. The host answers with MODAL_CLOSED\n * carrying the same `requestId`.\n *\n * One modal per opener at a time: while a request from this frame is still\n * open, the host answers any further OPEN_MODAL from it as dismissed (a\n * double click must not stack two dialogs). The modal's own component may\n * open another — that request comes from a different frame. */\nexport interface OpenModalMessage {\n type: 'OPEN_MODAL'\n /** Correlates the eventual MODAL_CLOSED. Generated by the opener. */\n requestId: string\n /** Slug of the component to mount inside the modal. */\n componentSlug: string\n /** Props for that component. The host renders Liquid in every string value\n * against the OPENER's page scope (`record`, `entity`, `params`,\n * `current_user`) before handing them over — exactly as it does for a\n * page-placed component. So `{{ … }}` / `{% … %}` in a value is evaluated,\n * not passed through. */\n props?: Record<string, unknown>\n /** Optional modal heading. */\n title?: string\n /** Panel width. Defaults to `lg`. */\n width?: ModalWidth\n /** Height of the component's frame, in px. Set: the frame is that tall from\n * the first paint (capped at the viewport), scrolls its own content, and\n * sits edge to edge in the panel — the component owns its padding. Unset:\n * the frame grows with its content inside the host's padded body. */\n height?: number\n}\n\n/** Escape was pressed with focus inside the iframe, and nothing in the runtime\n * consumed it.\n *\n * Keyboard events don't cross the iframe boundary, and react-aria binds its\n * Escape handling to the overlay element in its own React tree — so a\n * host-rendered dialog CANNOT see Escape once the user has clicked into a\n * component. The runtime forwards it so the host can dismiss on the user's\n * behalf. Not sent while the runtime has its own overlay open: that overlay\n * owns Escape. */\nexport interface EscapePressedMessage {\n type: 'ESCAPE_PRESSED'\n}\n\n/** Posted by the component INSIDE a host modal to close it with a value. The\n * `requestId` is the one INIT carried as `modalRequestId`. */\nexport interface ModalResultMessage {\n type: 'MODAL_RESULT'\n requestId: string\n result?: unknown\n}\n\n/** Ask the host to open its agent session window (the floating assistant).\n *\n * - `sessionId` set: the window resumes THAT session (its agent is read from\n * the session). A `prompt` is sent into it.\n * - no `sessionId`: a fresh session with `agentKey` (or the agent currently\n * selected in the window); a `prompt` is sent as its first message. With\n * neither field the window just opens as it is.\n *\n * Ignored on public pages (there is no signed-in user to run an agent as). */\nexport interface OpenAgentWindowMessage {\n type: 'OPEN_AGENT_WINDOW'\n sessionId?: string\n agentKey?: string\n prompt?: string\n}\n\n/** Ask the host to minimize its agent session window. The active session and\n * its stream stay alive — reopening resumes it. */\nexport interface CloseAgentWindowMessage {\n type: 'CLOSE_AGENT_WINDOW'\n}\n\nexport interface ErrorMessage {\n type: 'ERROR'\n message: string\n stack?: string\n}\n\nexport type RuntimeMessage =\n | ReadyMessage\n | ResizeMessage\n | NavigateMessage\n | OpenPeekMessage\n | OpenCallMessage\n | OverlayStateMessage\n | OpenModalMessage\n | ModalResultMessage\n | EscapePressedMessage\n | OpenAgentWindowMessage\n | CloseAgentWindowMessage\n | ErrorMessage\n\n// Wire envelope: the message plus the source discriminator.\nexport type Envelope<T> = T & { source: typeof MESSAGE_SOURCE }\n\nexport function isHostMessage(data: unknown): data is Envelope<HostMessage> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { source?: unknown }).source === MESSAGE_SOURCE &&\n typeof (data as { type?: unknown }).type === 'string'\n )\n}\n\nexport function isRuntimeMessage(data: unknown): data is Envelope<RuntimeMessage> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { source?: unknown }).source === MESSAGE_SOURCE &&\n typeof (data as { type?: unknown }).type === 'string'\n )\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1,26 +1,44 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkHD5ADY7F_cjs = require('./chunk-HD5ADY7F.cjs');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
|
|
7
7
|
var RuntimeContext = react.createContext(null);
|
|
8
8
|
function postNavigate(to) {
|
|
9
|
-
const envelope = { source:
|
|
9
|
+
const envelope = { source: chunkHD5ADY7F_cjs.MESSAGE_SOURCE, type: "NAVIGATE", to };
|
|
10
10
|
window.parent?.postMessage(envelope, "*");
|
|
11
11
|
}
|
|
12
12
|
function postOpenPeek(to) {
|
|
13
|
-
const envelope = { source:
|
|
13
|
+
const envelope = { source: chunkHD5ADY7F_cjs.MESSAGE_SOURCE, type: "OPEN_PEEK", to };
|
|
14
14
|
window.parent?.postMessage(envelope, "*");
|
|
15
15
|
}
|
|
16
16
|
function openCall(conversationBriefId) {
|
|
17
17
|
const envelope = {
|
|
18
|
-
source:
|
|
18
|
+
source: chunkHD5ADY7F_cjs.MESSAGE_SOURCE,
|
|
19
19
|
type: "OPEN_CALL",
|
|
20
20
|
conversationBriefId
|
|
21
21
|
};
|
|
22
22
|
window.parent?.postMessage(envelope, "*");
|
|
23
23
|
}
|
|
24
|
+
function openAgentWindow(options) {
|
|
25
|
+
const envelope = {
|
|
26
|
+
source: chunkHD5ADY7F_cjs.MESSAGE_SOURCE,
|
|
27
|
+
type: "OPEN_AGENT_WINDOW",
|
|
28
|
+
...options?.sessionId !== void 0 ? { sessionId: options.sessionId } : {},
|
|
29
|
+
...options?.agentKey !== void 0 ? { agentKey: options.agentKey } : {},
|
|
30
|
+
...options?.prompt !== void 0 ? { prompt: options.prompt } : {}
|
|
31
|
+
};
|
|
32
|
+
window.parent?.postMessage(envelope, "*");
|
|
33
|
+
}
|
|
34
|
+
function closeAgentWindow() {
|
|
35
|
+
const envelope = {
|
|
36
|
+
source: chunkHD5ADY7F_cjs.MESSAGE_SOURCE,
|
|
37
|
+
type: "CLOSE_AGENT_WINDOW"
|
|
38
|
+
};
|
|
39
|
+
window.parent?.postMessage(envelope, "*");
|
|
40
|
+
}
|
|
41
|
+
var agentWindow = { open: openAgentWindow, close: closeAgentWindow };
|
|
24
42
|
function navigate(to, options) {
|
|
25
43
|
if (options?.peek) postOpenPeek(to);
|
|
26
44
|
else postNavigate(to);
|
|
@@ -32,7 +50,7 @@ function ensureModalListener() {
|
|
|
32
50
|
modalListenerAttached = true;
|
|
33
51
|
window.addEventListener("message", (event) => {
|
|
34
52
|
const data = event.data;
|
|
35
|
-
if (!
|
|
53
|
+
if (!chunkHD5ADY7F_cjs.isHostMessage(data) || data.type !== "MODAL_CLOSED") return;
|
|
36
54
|
const resolve = pendingModals.get(data.requestId);
|
|
37
55
|
if (!resolve) return;
|
|
38
56
|
pendingModals.delete(data.requestId);
|
|
@@ -68,7 +86,7 @@ function openModal(componentSlug, props, options) {
|
|
|
68
86
|
ensureModalListener();
|
|
69
87
|
const requestId = newRequestId();
|
|
70
88
|
const envelope = {
|
|
71
|
-
source:
|
|
89
|
+
source: chunkHD5ADY7F_cjs.MESSAGE_SOURCE,
|
|
72
90
|
type: "OPEN_MODAL",
|
|
73
91
|
requestId,
|
|
74
92
|
componentSlug,
|
|
@@ -88,7 +106,7 @@ function openModal(componentSlug, props, options) {
|
|
|
88
106
|
}
|
|
89
107
|
function postModalResult(requestId, result) {
|
|
90
108
|
const envelope = {
|
|
91
|
-
source:
|
|
109
|
+
source: chunkHD5ADY7F_cjs.MESSAGE_SOURCE,
|
|
92
110
|
type: "MODAL_RESULT",
|
|
93
111
|
requestId,
|
|
94
112
|
...result !== void 0 ? { result } : {}
|
|
@@ -97,7 +115,7 @@ function postModalResult(requestId, result) {
|
|
|
97
115
|
}
|
|
98
116
|
function RuntimeProvider({ value, children }) {
|
|
99
117
|
const ctx = react.useMemo(
|
|
100
|
-
() => ({ ...value, navigate, openCall, openModal }),
|
|
118
|
+
() => ({ ...value, navigate, openCall, openModal, agentWindow }),
|
|
101
119
|
[value]
|
|
102
120
|
);
|
|
103
121
|
return /* @__PURE__ */ jsxRuntime.jsx(RuntimeContext.Provider, { value: ctx, children });
|
|
@@ -126,6 +144,9 @@ function useNavigate() {
|
|
|
126
144
|
function useOpenCall() {
|
|
127
145
|
return useRuntime().openCall;
|
|
128
146
|
}
|
|
147
|
+
function useAgentWindow() {
|
|
148
|
+
return useRuntime().agentWindow;
|
|
149
|
+
}
|
|
129
150
|
function useApiBaseUrl() {
|
|
130
151
|
return useRuntime().apiBaseUrl;
|
|
131
152
|
}
|
|
@@ -208,21 +229,22 @@ function useFillViewportHeight(options = {}) {
|
|
|
208
229
|
|
|
209
230
|
Object.defineProperty(exports, "COMPONENT_OVERLAY_Z_INDEX", {
|
|
210
231
|
enumerable: true,
|
|
211
|
-
get: function () { return
|
|
232
|
+
get: function () { return chunkHD5ADY7F_cjs.COMPONENT_OVERLAY_Z_INDEX; }
|
|
212
233
|
});
|
|
213
234
|
Object.defineProperty(exports, "MESSAGE_SOURCE", {
|
|
214
235
|
enumerable: true,
|
|
215
|
-
get: function () { return
|
|
236
|
+
get: function () { return chunkHD5ADY7F_cjs.MESSAGE_SOURCE; }
|
|
216
237
|
});
|
|
217
238
|
Object.defineProperty(exports, "isHostMessage", {
|
|
218
239
|
enumerable: true,
|
|
219
|
-
get: function () { return
|
|
240
|
+
get: function () { return chunkHD5ADY7F_cjs.isHostMessage; }
|
|
220
241
|
});
|
|
221
242
|
Object.defineProperty(exports, "isRuntimeMessage", {
|
|
222
243
|
enumerable: true,
|
|
223
|
-
get: function () { return
|
|
244
|
+
get: function () { return chunkHD5ADY7F_cjs.isRuntimeMessage; }
|
|
224
245
|
});
|
|
225
246
|
exports.RuntimeProvider = RuntimeProvider;
|
|
247
|
+
exports.useAgentWindow = useAgentWindow;
|
|
226
248
|
exports.useApiBaseUrl = useApiBaseUrl;
|
|
227
249
|
exports.useAuthToken = useAuthToken;
|
|
228
250
|
exports.useComponentModal = useComponentModal;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.tsx","../src/hooks.ts"],"names":["createContext","MESSAGE_SOURCE","isHostMessage","useMemo","useContext","useCallback","useState","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n AccountClient,\n AgentClient,\n ConversationClient,\n DataClient,\n FunctionsClient,\n KnowledgeClient,\n MetaClient,\n StorageClient,\n} from '@proteos/sdk'\nimport { createContext, type ReactNode, useMemo } from 'react'\n\nimport {\n type Envelope,\n isHostMessage,\n MESSAGE_SOURCE,\n type ModalResultMessage,\n type ModalWidth,\n type NavigateMessage,\n type OpenCallMessage,\n type OpenModalMessage,\n type OpenPeekMessage,\n type PageContext,\n} from './protocol'\n\n/** Options for {@link RuntimeContextValue.navigate}. */\nexport interface NavigateOptions {\n /** Open the target in a peek (slide-over) panel instead of full-page. */\n peek?: boolean\n}\n\n/** Options for {@link RuntimeContextValue.openModal}. */\nexport interface OpenModalOptions {\n /** Heading rendered in the modal chrome. */\n title?: string\n /** Panel width: `sm` 384px, `md` 512px, `lg` 672px (default), `xl` 896px. */\n width?: ModalWidth\n /** Height of the component's frame, in px. Pass it whenever you know it: the\n * modal then opens at its final size instead of growing once the component\n * has loaded. The frame is capped at the viewport, scrolls its own content,\n * and sits edge to edge in the panel — so the component owns its padding\n * (`ModalBody` / `ModalFooter` from `@proteos/ui` carry the standard one).\n * Leave it out and the frame grows with its content inside the host's\n * padded body. */\n height?: number\n}\n\n/**\n * Manual control over iframe overlay promotion. The runtime already detects\n * overlays automatically (anything portaled to the iframe's `document.body` —\n * every `@proteos/ui` overlay qualifies), so components need this only for\n * custom portal targets, or to promote a frame *before* opening an overlay.\n * Calls are reference-counted: every `expand()` needs its `collapse()`.\n */\nexport interface OverlayController {\n expand: () => void\n collapse: () => void\n isExpanded: () => boolean\n}\n\n/**\n * The authenticated `@proteos/sdk` clients the runtime injects. Same instances\n * the host app uses — pre-wired with a refreshing bearer token, so a component\n * never touches auth headers.\n */\nexport interface ComponentSdk {\n account: AccountClient\n agents: AgentClient\n /**\n * Conversations, messages, contacts, connections (conversation-service) —\n * e.g. the DraftMessageComposer review surface. Plural per the platform's\n * resource-naming rule; the web app's own `useSdk().conversation` predates\n * it (documented asymmetry).\n */\n conversations: ConversationClient\n data: DataClient\n functions: FunctionsClient\n knowledge: KnowledgeClient\n meta: MetaClient\n storage: StorageClient\n}\n\n/**\n * The raw runtime payload the shell hands a component at INIT. The shell builds\n * this; component authors read it through the hooks.\n */\nexport interface RuntimeValue {\n /**\n * The injected sdk clients. On an authenticated page these are the full,\n * bearer-wired clients. On a PUBLIC page (`isPublic: true`) only\n * `functions.actions.invokePublic` reaches the platform — every other\n * client throws on use (there is no token, and the server rejects\n * unauthenticated calls anyway).\n */\n sdk: ComponentSdk\n /** Liquid-resolved props (a snapshot — not a live binding). */\n props: Record<string, unknown>\n /** Page context snapshot: the host record, entity, and URL params. */\n pageCtx: PageContext\n /** API origin SDK calls + asset URLs resolve against. */\n apiBaseUrl: string\n /** Current bearer token getter (escape hatch for raw fetches). Returns ''\n * on public pages. */\n getToken: () => string\n /** True when rendering on an unauthenticated public page. */\n isPublic?: boolean\n /** Org id, set in public mode (public API routes carry it in the path). */\n orgId?: string\n /** Set when this component is the content of a host component modal — the\n * correlation id it closes with. See `useModalHost()`. */\n modalRequestId?: string\n /** Overlay promotion controller, provided by the shell. Absent on older\n * shells / non-promoting hosts, in which case `useOverlayExpansion()`\n * no-ops. */\n overlay?: OverlayController\n}\n\n/** Runtime value plus the derived helpers the hooks expose. */\nexport interface RuntimeContextValue extends RuntimeValue {\n /** Navigate the host SPA to an in-app path (posts a NAVIGATE message), or\n * open it in a peek panel with `{ peek: true }` (posts OPEN_PEEK). */\n navigate: (to: string, options?: NavigateOptions) => void\n /** Ask the host to open a prepared call from a conversation brief (posts\n * OPEN_CALL): the host previews the brief and dials through its softphone. */\n openCall: (conversationBriefId: string) => void\n /** Open another component in a host-rendered modal; resolves with the value\n * that component closed with (`undefined` if dismissed). */\n openModal: (\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n ) => Promise<unknown>\n}\n\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null)\n\n/** Post a NAVIGATE envelope to the host. The host only honours relative,\n * in-app targets (it ignores absolute/external URLs). */\nfunction postNavigate(to: string): void {\n const envelope: Envelope<NavigateMessage> = { source: MESSAGE_SOURCE, type: 'NAVIGATE', to }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post an OPEN_PEEK envelope to the host — open the in-app path in a peek\n * panel. Same relative-path contract as {@link postNavigate}. */\nfunction postOpenPeek(to: string): void {\n const envelope: Envelope<OpenPeekMessage> = { source: MESSAGE_SOURCE, type: 'OPEN_PEEK', to }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post OPEN_CALL — the host previews the conversation brief and owns the dial. */\nfunction openCall(conversationBriefId: string): void {\n const envelope: Envelope<OpenCallMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_CALL',\n conversationBriefId,\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Navigate full-page (default) or into a peek panel (`{ peek: true }`). */\nfunction navigate(to: string, options?: NavigateOptions): void {\n if (options?.peek) postOpenPeek(to)\n else postNavigate(to)\n}\n\n// ── Component modals ────────────────────────────────────────────────────────\n// One module-level listener + a pending map, rather than a listener per call:\n// the SDK is an import-mapped singleton, so this is per-iframe state.\n\nconst pendingModals = new Map<string, (result: unknown) => void>()\nlet modalListenerAttached = false\n\nfunction ensureModalListener(): void {\n if (modalListenerAttached) return\n modalListenerAttached = true\n window.addEventListener('message', (event: MessageEvent) => {\n const data: unknown = event.data\n if (!isHostMessage(data) || data.type !== 'MODAL_CLOSED') return\n const resolve = pendingModals.get(data.requestId)\n // An unknown requestId is someone else's modal (or a stale reply) — drop it.\n if (!resolve) return\n pendingModals.delete(data.requestId)\n resolve(data.result)\n })\n}\n\n// How long after a modal closed the opener still claims the focus the host\n// hands back. Past that, focus arriving on the frame is something else.\nconst FOCUS_RESTORE_WINDOW_MS = 1000\n\n/** Puts focus back on the control that opened a modal.\n *\n * The host returns focus to this FRAME when its dialog closes, but not to the\n * control inside it: the frame is inert while the dialog is up, which drops\n * the focus it held, so it comes back on `<body>`. Only this side knows which\n * control it was.\n *\n * The reply (MODAL_CLOSED) arrives before the dialog has finished closing —\n * the frame may still be inert then and refuse focus — so this waits for the\n * frame to actually receive it. */\nfunction restoreFocus(opener: HTMLElement | null): void {\n if (!opener?.isConnected) return\n const isUnplaced = () =>\n document.activeElement === null || document.activeElement === document.body\n if (document.hasFocus() && isUnplaced()) {\n opener.focus({ preventScroll: true })\n return\n }\n const onFocus = () => {\n stop()\n // Something in here took focus in the meantime — leave it be.\n if (opener.isConnected && isUnplaced()) opener.focus({ preventScroll: true })\n }\n const timer = window.setTimeout(stop, FOCUS_RESTORE_WINDOW_MS)\n function stop(): void {\n window.clearTimeout(timer)\n window.removeEventListener('focus', onFocus)\n }\n window.addEventListener('focus', onFocus)\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `modal_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`\n}\n\n/** Post OPEN_MODAL and resolve when the host answers MODAL_CLOSED. */\nfunction openModal(\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n): Promise<unknown> {\n ensureModalListener()\n const requestId = newRequestId()\n const envelope: Envelope<OpenModalMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_MODAL',\n requestId,\n componentSlug,\n ...(props !== undefined ? { props } : {}),\n ...(options?.title !== undefined ? { title: options.title } : {}),\n ...(options?.width !== undefined ? { width: options.width } : {}),\n ...(options?.height !== undefined ? { height: options.height } : {}),\n }\n const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null\n return new Promise<unknown>((resolve) => {\n pendingModals.set(requestId, (result) => {\n restoreFocus(opener)\n resolve(result)\n })\n window.parent?.postMessage(envelope, '*')\n })\n}\n\n/** Post MODAL_RESULT — the component inside a host modal closing with a value. */\nexport function postModalResult(requestId: string, result?: unknown): void {\n const envelope: Envelope<ModalResultMessage> = {\n source: MESSAGE_SOURCE,\n type: 'MODAL_RESULT',\n requestId,\n ...(result !== undefined ? { result } : {}),\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/**\n * Provides the runtime value + derived helpers to a component subtree. Mounted\n * by the component-runtime shell around the loaded component — authors never\n * render this themselves. Lives in `@proteos/component-sdk` (a shared,\n * import-mapped singleton) so the context the shell provides is the very same\n * context the component's hooks read, across the bundle boundary.\n */\nexport function RuntimeProvider({ value, children }: { value: RuntimeValue; children: ReactNode }) {\n // The shell builds `value` once at INIT (a stable state object — clients,\n // props and pageCtx are all set once), so memoizing on its identity recreates\n // the context only when the runtime genuinely re-inits.\n const ctx = useMemo<RuntimeContextValue>(\n () => ({ ...value, navigate, openCall, openModal }),\n [value],\n )\n return <RuntimeContext.Provider value={ctx}>{children}</RuntimeContext.Provider>\n}\n","import { useCallback, useContext, useEffect, useMemo, useState } from 'react'\n\nimport {\n type ComponentSdk,\n type NavigateOptions,\n type OpenModalOptions,\n type OverlayController,\n postModalResult,\n RuntimeContext,\n type RuntimeContextValue,\n} from './context'\nimport type { PageContext } from './protocol'\n\n/**\n * Low-level escape hatch: the whole runtime context (sdk, props, pageCtx,\n * apiBaseUrl, getToken, navigate). Prefer the focused hooks below. Throws a\n * clear error if used outside the component runtime.\n */\nexport function useRuntime(): RuntimeContextValue {\n const ctx = useContext(RuntimeContext)\n if (!ctx) {\n throw new Error(\n '@proteos/component-sdk: hooks must run inside the Proteos component runtime. ' +\n 'This usually means the component is rendered outside its iframe, or a ' +\n 'second copy of @proteos/component-sdk was bundled instead of using the ' +\n 'runtime-provided one (keep it external).',\n )\n }\n return ctx\n}\n\n/** The authenticated SDK clients (account / agents / conversations / data / functions / knowledge / meta / storage). */\nexport function useSdk(): ComponentSdk {\n return useRuntime().sdk\n}\n\n/** The component's Liquid-resolved props (a snapshot, not a live binding). */\nexport function useProps<T extends Record<string, unknown> = Record<string, unknown>>(): T {\n return useRuntime().props as T\n}\n\n/**\n * The full page context: host `record`, `entity`, and URL `params`. The\n * `record` is a snapshot taken at mount (`undefined` on record-less platform\n * pages) — fetch via `useSdk().data` for anything that must stay fresh.\n */\nexport function usePageContext(): PageContext {\n return useRuntime().pageCtx\n}\n\n/**\n * Navigate the host SPA to an in-app path, e.g.\n * `navigate('/org/acme/record/task/123')`. The host ignores absolute/external\n * URLs. Build org/app-prefixed paths with `useHostLocation().appBasePath`.\n *\n * Pass `{ peek: true }` to open the path in a peek (slide-over) panel instead\n * of full-page navigation — e.g. `navigate(path, { peek: true })`. The host\n * peeks what it can (today: platform pages) and falls back to full navigation.\n */\nexport function useNavigate(): (to: string, options?: NavigateOptions) => void {\n return useRuntime().navigate\n}\n\n/**\n * Open a prepared call from a conversation brief id: the HOST previews the brief and\n * places the call through its softphone (an iframe cannot dial). Pair it with\n * `ConversationBriefReview`'s `onOpenCall` from `@proteos/ui`.\n */\nexport function useOpenCall(): (conversationBriefId: string) => void {\n return useRuntime().openCall\n}\n\n/** The API origin SDK calls resolve against (for building raw asset URLs). */\nexport function useApiBaseUrl(): string {\n return useRuntime().apiBaseUrl\n}\n\nconst NOOP_OVERLAY: OverlayController = {\n expand: () => {\n console.warn(\n '@proteos/component-sdk: useOverlayExpansion() has no effect — this runtime ' +\n 'does not support overlay promotion (older shell or non-promoting host).',\n )\n },\n collapse: () => {},\n isExpanded: () => false,\n}\n\n/**\n * Manual overlay promotion — lifts the whole component iframe over the rest of\n * the page so an overlay can escape the frame's box.\n *\n * You rarely need this. Every `@proteos/ui` overlay (Modal, Popover, Menu,\n * Select, Tooltip, …) portals to the iframe document's `<body>`, which the\n * runtime watches — those promote automatically, with no code. Reach for this\n * hook only when you portal somewhere else yourself, or want the frame lifted\n * *before* the overlay mounts. Reference-counted: pair every `expand()` with a\n * `collapse()`.\n */\nexport function useOverlayExpansion(): OverlayController {\n return useRuntime().overlay ?? NOOP_OVERLAY\n}\n\n/**\n * Open ANOTHER component in a modal the host renders above everything — the\n * cross-component flow (a picker, a wizard step, a confirm dialog shared by\n * several components). Resolves with whatever that component closed with, or\n * `undefined` if the user dismissed it.\n *\n * ```ts\n * const openModal = useComponentModal()\n * const picked = await openModal(\n * 'contact-picker',\n * { entity: 'contact' },\n * { title: 'Pick a contact', width: 'md', height: 480 },\n * )\n * ```\n *\n * Pass `height` whenever you know it — the modal opens at its final size\n * instead of growing after the component loads (see {@link OpenModalOptions}).\n * One modal per component at a time: a second call while the first is still\n * open resolves `undefined` straight away.\n *\n * String props are Liquid-rendered by the host against this component's page\n * scope, like the props of a page-placed component.\n *\n * For an overlay that is part of THIS component, just render a `<Modal>` from\n * `@proteos/ui` — it promotes automatically.\n */\nexport function useComponentModal(): (\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n) => Promise<unknown> {\n return useRuntime().openModal\n}\n\nexport interface ModalHost {\n /** True when this component is rendered as the content of a host modal. */\n isInModal: boolean\n /** Close the host modal, resolving the opener's promise with `result`.\n * No-op outside a modal. */\n close: (result?: unknown) => void\n}\n\n/**\n * For a component that is being shown INSIDE a host modal (opened by another\n * component's `useComponentModal()`): close it and hand a value back.\n * `isInModal` is false on ordinary page-hosted mounts, so one component can\n * serve both roles.\n */\nexport function useModalHost(): ModalHost {\n const requestId = useRuntime().modalRequestId\n const close = useCallback(\n (result?: unknown) => {\n if (requestId === undefined) return\n postModalResult(requestId, result)\n },\n [requestId],\n )\n return useMemo(() => ({ isInModal: requestId !== undefined, close }), [requestId, close])\n}\n\n/**\n * A getter for the current bearer token. Returns a function (not the token)\n * because the token is refreshed in place — read it at call time for a raw\n * `fetch`. Most components never need this; prefer `useSdk()`.\n */\nexport function useAuthToken(): () => string {\n return useRuntime().getToken\n}\n\nexport interface HostLocation {\n /** The host tab's pathname (same-origin read), or null if unavailable. */\n pathname: string | null\n /** The org id parsed from `/org/<orgId>/…`, or null. */\n orgId: string | null\n /** The `/org/<orgId>/app/<appId>` prefix, or null when not on an app page. */\n appBasePath: string | null\n}\n\nfunction readHostLocation(): HostLocation {\n const empty: HostLocation = { pathname: null, orgId: null, appBasePath: null }\n try {\n const parentWindow = window.parent\n if (!parentWindow || parentWindow === window) return empty\n const pathname = parentWindow.location.pathname\n const appMatch = pathname.match(/^\\/org\\/[^/]+\\/app\\/[^/]+/)\n const orgMatch = pathname.match(/^\\/org\\/([^/]+)\\//)\n return {\n pathname,\n orgId: orgMatch ? (orgMatch[1] ?? null) : null,\n appBasePath: appMatch ? appMatch[0] : null,\n }\n } catch {\n // Cross-origin (untrusted-component future) or detached — fall back to null.\n return empty\n }\n}\n\n/**\n * Read the host tab's location (same-origin) — the org id and `/org/.../app/...`\n * prefix needed to build navigation targets on platform pages. Read once on\n * mount (the iframe re-inits when the host route changes).\n */\nexport function useHostLocation(): HostLocation {\n const [loc] = useState(readHostLocation)\n return loc\n}\n\nexport interface FillViewportOptions {\n /** Gap left below the component (px). Default 24. */\n bottomMargin?: number\n /** Floor for the computed height (px). Default 320. */\n minHeight?: number\n}\n\n/**\n * Compute a \"fill the rest of the viewport\" height for full-bleed layouts\n * (boards, canvases). The runtime auto-sizes the iframe to its content, so a\n * component can't use its own `vh` units (circular). The iframe is same-origin,\n * so this measures the parent viewport minus the iframe's offset instead.\n * Returns `null` when it can't measure (fall back to natural height).\n */\nexport function useFillViewportHeight(options: FillViewportOptions = {}): number | null {\n const { bottomMargin = 24, minHeight = 320 } = options\n const [height, setHeight] = useState<number | null>(null)\n\n useEffect(() => {\n function compute(): void {\n try {\n const frame = window.frameElement\n const parentWindow = window.parent\n if (!frame || !parentWindow || parentWindow === window) return\n const available =\n parentWindow.innerHeight - frame.getBoundingClientRect().top - bottomMargin\n setHeight(Math.max(available, minHeight))\n } catch {\n setHeight(null) // cross-origin or detached — fall back to natural height\n }\n }\n compute()\n try {\n window.parent.addEventListener('resize', compute)\n return () => window.parent.removeEventListener('resize', compute)\n } catch {\n return\n }\n }, [bottomMargin, minHeight])\n\n return height\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.tsx","../src/hooks.ts"],"names":["createContext","MESSAGE_SOURCE","isHostMessage","useMemo","useContext","useCallback","useState","useEffect"],"mappings":";;;;;;AAkKO,IAAM,cAAA,GAAiBA,oBAA0C,IAAI,CAAA;AAI5E,SAAS,aAAa,EAAA,EAAkB;AACtC,EAAA,MAAM,WAAsC,EAAE,MAAA,EAAQC,gCAAA,EAAgB,IAAA,EAAM,YAAY,EAAA,EAAG;AAC3F,EAAA,MAAA,CAAO,MAAA,EAAQ,WAAA,CAAY,QAAA,EAAU,GAAG,CAAA;AAC1C;AAIA,SAAS,aAAa,EAAA,EAAkB;AACtC,EAAA,MAAM,WAAsC,EAAE,MAAA,EAAQA,gCAAA,EAAgB,IAAA,EAAM,aAAa,EAAA,EAAG;AAC5F,EAAA,MAAA,CAAO,MAAA,EAAQ,WAAA,CAAY,QAAA,EAAU,GAAG,CAAA;AAC1C;AAGA,SAAS,SAAS,mBAAA,EAAmC;AACnD,EAAA,MAAM,QAAA,GAAsC;AAAA,IAC1C,MAAA,EAAQA,gCAAA;AAAA,IACR,IAAA,EAAM,WAAA;AAAA,IACN;AAAA,GACF;AACA,EAAA,MAAA,CAAO,MAAA,EAAQ,WAAA,CAAY,QAAA,EAAU,GAAG,CAAA;AAC1C;AAGA,SAAS,gBAAgB,OAAA,EAAwC;AAC/D,EAAA,MAAM,QAAA,GAA6C;AAAA,IACjD,MAAA,EAAQA,gCAAA;AAAA,IACR,IAAA,EAAM,mBAAA;AAAA,IACN,GAAI,SAAS,SAAA,KAAc,MAAA,GAAY,EAAE,SAAA,EAAW,OAAA,CAAQ,SAAA,EAAU,GAAI,EAAC;AAAA,IAC3E,GAAI,SAAS,QAAA,KAAa,MAAA,GAAY,EAAE,QAAA,EAAU,OAAA,CAAQ,QAAA,EAAS,GAAI,EAAC;AAAA,IACxE,GAAI,SAAS,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAO,GAAI;AAAC,GACpE;AACA,EAAA,MAAA,CAAO,MAAA,EAAQ,WAAA,CAAY,QAAA,EAAU,GAAG,CAAA;AAC1C;AAGA,SAAS,gBAAA,GAAyB;AAChC,EAAA,MAAM,QAAA,GAA8C;AAAA,IAClD,MAAA,EAAQA,gCAAA;AAAA,IACR,IAAA,EAAM;AAAA,GACR;AACA,EAAA,MAAA,CAAO,MAAA,EAAQ,WAAA,CAAY,QAAA,EAAU,GAAG,CAAA;AAC1C;AAEA,IAAM,WAAA,GAAqC,EAAE,IAAA,EAAM,eAAA,EAAiB,OAAO,gBAAA,EAAiB;AAG5F,SAAS,QAAA,CAAS,IAAY,OAAA,EAAiC;AAC7D,EAAA,IAAI,OAAA,EAAS,IAAA,EAAM,YAAA,CAAa,EAAE,CAAA;AAAA,oBAChB,EAAE,CAAA;AACtB;AAMA,IAAM,aAAA,uBAAoB,GAAA,EAAuC;AACjE,IAAI,qBAAA,GAAwB,KAAA;AAE5B,SAAS,mBAAA,GAA4B;AACnC,EAAA,IAAI,qBAAA,EAAuB;AAC3B,EAAA,qBAAA,GAAwB,IAAA;AACxB,EAAA,MAAA,CAAO,gBAAA,CAAiB,SAAA,EAAW,CAAC,KAAA,KAAwB;AAC1D,IAAA,MAAM,OAAgB,KAAA,CAAM,IAAA;AAC5B,IAAA,IAAI,CAACC,+BAAA,CAAc,IAAI,CAAA,IAAK,IAAA,CAAK,SAAS,cAAA,EAAgB;AAC1D,IAAA,MAAM,OAAA,GAAU,aAAA,CAAc,GAAA,CAAI,IAAA,CAAK,SAAS,CAAA;AAEhD,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,aAAA,CAAc,MAAA,CAAO,KAAK,SAAS,CAAA;AACnC,IAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA,EACrB,CAAC,CAAA;AACH;AAIA,IAAM,uBAAA,GAA0B,GAAA;AAYhC,SAAS,aAAa,MAAA,EAAkC;AACtD,EAAA,IAAI,CAAC,QAAQ,WAAA,EAAa;AAC1B,EAAA,MAAM,aAAa,MACjB,QAAA,CAAS,kBAAkB,IAAA,IAAQ,QAAA,CAAS,kBAAkB,QAAA,CAAS,IAAA;AACzE,EAAA,IAAI,QAAA,CAAS,QAAA,EAAS,IAAK,UAAA,EAAW,EAAG;AACvC,IAAA,MAAA,CAAO,KAAA,CAAM,EAAE,aAAA,EAAe,IAAA,EAAM,CAAA;AACpC,IAAA;AAAA,EACF;AACA,EAAA,MAAM,UAAU,MAAM;AACpB,IAAA,IAAA,EAAK;AAEL,IAAA,IAAI,MAAA,CAAO,eAAe,UAAA,EAAW,SAAU,KAAA,CAAM,EAAE,aAAA,EAAe,IAAA,EAAM,CAAA;AAAA,EAC9E,CAAA;AACA,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,UAAA,CAAW,IAAA,EAAM,uBAAuB,CAAA;AAC7D,EAAA,SAAS,IAAA,GAAa;AACpB,IAAA,MAAA,CAAO,aAAa,KAAK,CAAA;AACzB,IAAA,MAAA,CAAO,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAAA,EAC7C;AACA,EAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,OAAO,CAAA;AAC1C;AAEA,SAAS,YAAA,GAAuB;AAC9B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,MAAA,CAAO,eAAe,UAAA,EAAY;AAC5E,IAAA,OAAO,OAAO,UAAA,EAAW;AAAA,EAC3B;AACA,EAAA,OAAO,SAAS,IAAA,CAAK,GAAA,EAAI,CAAE,QAAA,CAAS,EAAE,CAAC,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,EAAE,EAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,CAAA;AACpF;AAGA,SAAS,SAAA,CACP,aAAA,EACA,KAAA,EACA,OAAA,EACkB;AAClB,EAAA,mBAAA,EAAoB;AACpB,EAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,EAAA,MAAM,QAAA,GAAuC;AAAA,IAC3C,MAAA,EAAQD,gCAAA;AAAA,IACR,IAAA,EAAM,YAAA;AAAA,IACN,SAAA;AAAA,IACA,aAAA;AAAA,IACA,GAAI,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,KAAU,EAAC;AAAA,IACvC,GAAI,SAAS,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAM,GAAI,EAAC;AAAA,IAC/D,GAAI,SAAS,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAM,GAAI,EAAC;AAAA,IAC/D,GAAI,SAAS,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAO,GAAI;AAAC,GACpE;AACA,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,YAAyB,WAAA,GAAc,SAAS,aAAA,GAAgB,IAAA;AACxF,EAAA,OAAO,IAAI,OAAA,CAAiB,CAAC,OAAA,KAAY;AACvC,IAAA,aAAA,CAAc,GAAA,CAAI,SAAA,EAAW,CAAC,MAAA,KAAW;AACvC,MAAA,YAAA,CAAa,MAAM,CAAA;AACnB,MAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,IAChB,CAAC,CAAA;AACD,IAAA,MAAA,CAAO,MAAA,EAAQ,WAAA,CAAY,QAAA,EAAU,GAAG,CAAA;AAAA,EAC1C,CAAC,CAAA;AACH;AAGO,SAAS,eAAA,CAAgB,WAAmB,MAAA,EAAwB;AACzE,EAAA,MAAM,QAAA,GAAyC;AAAA,IAC7C,MAAA,EAAQA,gCAAA;AAAA,IACR,IAAA,EAAM,cAAA;AAAA,IACN,SAAA;AAAA,IACA,GAAI,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,KAAW;AAAC,GAC3C;AACA,EAAA,MAAA,CAAO,MAAA,EAAQ,WAAA,CAAY,QAAA,EAAU,GAAG,CAAA;AAC1C;AASO,SAAS,eAAA,CAAgB,EAAE,KAAA,EAAO,QAAA,EAAS,EAAiD;AAIjG,EAAA,MAAM,GAAA,GAAME,aAAA;AAAA,IACV,OAAO,EAAE,GAAG,OAAO,QAAA,EAAU,QAAA,EAAU,WAAW,WAAA,EAAY,CAAA;AAAA,IAC9D,CAAC,KAAK;AAAA,GACR;AACA,EAAA,sCAAQ,cAAA,CAAe,QAAA,EAAf,EAAwB,KAAA,EAAO,KAAM,QAAA,EAAS,CAAA;AACxD;AC5TO,SAAS,UAAA,GAAkC;AAChD,EAAA,MAAM,GAAA,GAAMC,iBAAW,cAAc,CAAA;AACrC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAIF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,MAAA,GAAuB;AACrC,EAAA,OAAO,YAAW,CAAE,GAAA;AACtB;AAGO,SAAS,QAAA,GAA2E;AACzF,EAAA,OAAO,YAAW,CAAE,KAAA;AACtB;AAOO,SAAS,cAAA,GAA8B;AAC5C,EAAA,OAAO,YAAW,CAAE,OAAA;AACtB;AAWO,SAAS,WAAA,GAA+D;AAC7E,EAAA,OAAO,YAAW,CAAE,QAAA;AACtB;AAOO,SAAS,WAAA,GAAqD;AACnE,EAAA,OAAO,YAAW,CAAE,QAAA;AACtB;AAkBO,SAAS,cAAA,GAAwC;AACtD,EAAA,OAAO,YAAW,CAAE,WAAA;AACtB;AAGO,SAAS,aAAA,GAAwB;AACtC,EAAA,OAAO,YAAW,CAAE,UAAA;AACtB;AAEA,IAAM,YAAA,GAAkC;AAAA,EACtC,QAAQ,MAAM;AACZ,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KAEF;AAAA,EACF,CAAA;AAAA,EACA,UAAU,MAAM;AAAA,EAAC,CAAA;AAAA,EACjB,YAAY,MAAM;AACpB,CAAA;AAaO,SAAS,mBAAA,GAAyC;AACvD,EAAA,OAAO,UAAA,GAAa,OAAA,IAAW,YAAA;AACjC;AA4BO,SAAS,iBAAA,GAIM;AACpB,EAAA,OAAO,YAAW,CAAE,SAAA;AACtB;AAgBO,SAAS,YAAA,GAA0B;AACxC,EAAA,MAAM,SAAA,GAAY,YAAW,CAAE,cAAA;AAC/B,EAAA,MAAM,KAAA,GAAQC,iBAAA;AAAA,IACZ,CAAC,MAAA,KAAqB;AACpB,MAAA,IAAI,cAAc,MAAA,EAAW;AAC7B,MAAA,eAAA,CAAgB,WAAW,MAAM,CAAA;AAAA,IACnC,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AACA,EAAA,OAAOF,aAAAA,CAAQ,OAAO,EAAE,SAAA,EAAW,SAAA,KAAc,MAAA,EAAW,KAAA,EAAM,CAAA,EAAI,CAAC,SAAA,EAAW,KAAK,CAAC,CAAA;AAC1F;AAOO,SAAS,YAAA,GAA6B;AAC3C,EAAA,OAAO,YAAW,CAAE,QAAA;AACtB;AAWA,SAAS,gBAAA,GAAiC;AACxC,EAAA,MAAM,QAAsB,EAAE,QAAA,EAAU,MAAM,KAAA,EAAO,IAAA,EAAM,aAAa,IAAA,EAAK;AAC7E,EAAA,IAAI;AACF,IAAA,MAAM,eAAe,MAAA,CAAO,MAAA;AAC5B,IAAA,IAAI,CAAC,YAAA,IAAgB,YAAA,KAAiB,MAAA,EAAQ,OAAO,KAAA;AACrD,IAAA,MAAM,QAAA,GAAW,aAAa,QAAA,CAAS,QAAA;AACvC,IAAA,MAAM,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,2BAA2B,CAAA;AAC3D,IAAA,MAAM,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,mBAAmB,CAAA;AACnD,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,KAAA,EAAO,QAAA,GAAY,QAAA,CAAS,CAAC,KAAK,IAAA,GAAQ,IAAA;AAAA,MAC1C,WAAA,EAAa,QAAA,GAAW,QAAA,CAAS,CAAC,CAAA,GAAI;AAAA,KACxC;AAAA,EACF,CAAA,CAAA,MAAQ;AAEN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAOO,SAAS,eAAA,GAAgC;AAC9C,EAAA,MAAM,CAAC,GAAG,CAAA,GAAIG,cAAA,CAAS,gBAAgB,CAAA;AACvC,EAAA,OAAO,GAAA;AACT;AAgBO,SAAS,qBAAA,CAAsB,OAAA,GAA+B,EAAC,EAAkB;AACtF,EAAA,MAAM,EAAE,YAAA,GAAe,EAAA,EAAI,SAAA,GAAY,KAAI,GAAI,OAAA;AAC/C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIA,eAAwB,IAAI,CAAA;AAExD,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,SAAS,OAAA,GAAgB;AACvB,MAAA,IAAI;AACF,QAAA,MAAM,QAAQ,MAAA,CAAO,YAAA;AACrB,QAAA,MAAM,eAAe,MAAA,CAAO,MAAA;AAC5B,QAAA,IAAI,CAAC,KAAA,IAAS,CAAC,YAAA,IAAgB,iBAAiB,MAAA,EAAQ;AACxD,QAAA,MAAM,YACJ,YAAA,CAAa,WAAA,GAAc,KAAA,CAAM,qBAAA,GAAwB,GAAA,GAAM,YAAA;AACjE,QAAA,SAAA,CAAU,IAAA,CAAK,GAAA,CAAI,SAAA,EAAW,SAAS,CAAC,CAAA;AAAA,MAC1C,CAAA,CAAA,MAAQ;AACN,QAAA,SAAA,CAAU,IAAI,CAAA;AAAA,MAChB;AAAA,IACF;AACA,IAAA,OAAA,EAAQ;AACR,IAAA,IAAI;AACF,MAAA,MAAA,CAAO,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,OAAO,CAAA;AAChD,MAAA,OAAO,MAAM,MAAA,CAAO,MAAA,CAAO,mBAAA,CAAoB,UAAU,OAAO,CAAA;AAAA,IAClE,CAAA,CAAA,MAAQ;AACN,MAAA;AAAA,IACF;AAAA,EACF,CAAA,EAAG,CAAC,YAAA,EAAc,SAAS,CAAC,CAAA;AAE5B,EAAA,OAAO,MAAA;AACT","file":"index.cjs","sourcesContent":["import type {\n AccountClient,\n AgentClient,\n ConversationClient,\n DataClient,\n FunctionsClient,\n KnowledgeClient,\n MetaClient,\n StorageClient,\n} from '@proteos/sdk'\nimport { createContext, type ReactNode, useMemo } from 'react'\n\nimport {\n type CloseAgentWindowMessage,\n type Envelope,\n isHostMessage,\n MESSAGE_SOURCE,\n type ModalResultMessage,\n type ModalWidth,\n type NavigateMessage,\n type OpenAgentWindowMessage,\n type OpenCallMessage,\n type OpenModalMessage,\n type OpenPeekMessage,\n type PageContext,\n} from './protocol'\n\n/** Options for {@link RuntimeContextValue.navigate}. */\nexport interface NavigateOptions {\n /** Open the target in a peek (slide-over) panel instead of full-page. */\n peek?: boolean\n}\n\n/** Options for {@link RuntimeContextValue.openModal}. */\nexport interface OpenModalOptions {\n /** Heading rendered in the modal chrome. */\n title?: string\n /** Panel width: `sm` 384px, `md` 512px, `lg` 672px (default), `xl` 896px. */\n width?: ModalWidth\n /** Height of the component's frame, in px. Pass it whenever you know it: the\n * modal then opens at its final size instead of growing once the component\n * has loaded. The frame is capped at the viewport, scrolls its own content,\n * and sits edge to edge in the panel — so the component owns its padding\n * (`ModalBody` / `ModalFooter` from `@proteos/ui` carry the standard one).\n * Leave it out and the frame grows with its content inside the host's\n * padded body. */\n height?: number\n}\n\n/** What {@link AgentWindowController.open} opens the host's agent window to. */\nexport interface OpenAgentWindowOptions {\n /** Resume this session (its agent comes with it). */\n sessionId?: string\n /** Start a fresh session with this agent. Ignored when `sessionId` is set,\n * and when the host surface pins its agent. Without it (and without a\n * session) the window's currently selected agent is used. */\n agentKey?: string\n /** A message to send — into the resumed session, or as the first message\n * of the fresh one. */\n prompt?: string\n}\n\n/**\n * The host's agent session window (the floating assistant), driven from a\n * component. `open()` posts OPEN_AGENT_WINDOW, `close()` posts\n * CLOSE_AGENT_WINDOW — closing minimizes; the session and its stream live on.\n */\nexport interface AgentWindowController {\n open: (options?: OpenAgentWindowOptions) => void\n close: () => void\n}\n\n/**\n * Manual control over iframe overlay promotion. The runtime already detects\n * overlays automatically (anything portaled to the iframe's `document.body` —\n * every `@proteos/ui` overlay qualifies), so components need this only for\n * custom portal targets, or to promote a frame *before* opening an overlay.\n * Calls are reference-counted: every `expand()` needs its `collapse()`.\n */\nexport interface OverlayController {\n expand: () => void\n collapse: () => void\n isExpanded: () => boolean\n}\n\n/**\n * The authenticated `@proteos/sdk` clients the runtime injects. Same instances\n * the host app uses — pre-wired with a refreshing bearer token, so a component\n * never touches auth headers.\n */\nexport interface ComponentSdk {\n account: AccountClient\n agents: AgentClient\n /**\n * Conversations, messages, contacts, connections (conversation-service) —\n * e.g. the DraftMessageComposer review surface. Plural per the platform's\n * resource-naming rule; the web app's own `useSdk().conversation` predates\n * it (documented asymmetry).\n */\n conversations: ConversationClient\n data: DataClient\n functions: FunctionsClient\n knowledge: KnowledgeClient\n meta: MetaClient\n storage: StorageClient\n}\n\n/**\n * The raw runtime payload the shell hands a component at INIT. The shell builds\n * this; component authors read it through the hooks.\n */\nexport interface RuntimeValue {\n /**\n * The injected sdk clients. On an authenticated page these are the full,\n * bearer-wired clients. On a PUBLIC page (`isPublic: true`) only\n * `functions.actions.invokePublic` reaches the platform — every other\n * client throws on use (there is no token, and the server rejects\n * unauthenticated calls anyway).\n */\n sdk: ComponentSdk\n /** Liquid-resolved props (a snapshot — not a live binding). */\n props: Record<string, unknown>\n /** Page context snapshot: the host record, entity, and URL params. */\n pageCtx: PageContext\n /** API origin SDK calls + asset URLs resolve against. */\n apiBaseUrl: string\n /** Current bearer token getter (escape hatch for raw fetches). Returns ''\n * on public pages. */\n getToken: () => string\n /** True when rendering on an unauthenticated public page. */\n isPublic?: boolean\n /** Org id, set in public mode (public API routes carry it in the path). */\n orgId?: string\n /** Set when this component is the content of a host component modal — the\n * correlation id it closes with. See `useModalHost()`. */\n modalRequestId?: string\n /** Overlay promotion controller, provided by the shell. Absent on older\n * shells / non-promoting hosts, in which case `useOverlayExpansion()`\n * no-ops. */\n overlay?: OverlayController\n}\n\n/** Runtime value plus the derived helpers the hooks expose. */\nexport interface RuntimeContextValue extends RuntimeValue {\n /** Navigate the host SPA to an in-app path (posts a NAVIGATE message), or\n * open it in a peek panel with `{ peek: true }` (posts OPEN_PEEK). */\n navigate: (to: string, options?: NavigateOptions) => void\n /** Ask the host to open a prepared call from a conversation brief (posts\n * OPEN_CALL): the host previews the brief and dials through its softphone. */\n openCall: (conversationBriefId: string) => void\n /** Open another component in a host-rendered modal; resolves with the value\n * that component closed with (`undefined` if dismissed). */\n openModal: (\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n ) => Promise<unknown>\n /** Open / minimize the host's agent session window (posts OPEN_AGENT_WINDOW\n * / CLOSE_AGENT_WINDOW). */\n agentWindow: AgentWindowController\n}\n\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null)\n\n/** Post a NAVIGATE envelope to the host. The host only honours relative,\n * in-app targets (it ignores absolute/external URLs). */\nfunction postNavigate(to: string): void {\n const envelope: Envelope<NavigateMessage> = { source: MESSAGE_SOURCE, type: 'NAVIGATE', to }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post an OPEN_PEEK envelope to the host — open the in-app path in a peek\n * panel. Same relative-path contract as {@link postNavigate}. */\nfunction postOpenPeek(to: string): void {\n const envelope: Envelope<OpenPeekMessage> = { source: MESSAGE_SOURCE, type: 'OPEN_PEEK', to }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post OPEN_CALL — the host previews the conversation brief and owns the dial. */\nfunction openCall(conversationBriefId: string): void {\n const envelope: Envelope<OpenCallMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_CALL',\n conversationBriefId,\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post OPEN_AGENT_WINDOW — only the fields that were given ride the wire. */\nfunction openAgentWindow(options?: OpenAgentWindowOptions): void {\n const envelope: Envelope<OpenAgentWindowMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_AGENT_WINDOW',\n ...(options?.sessionId !== undefined ? { sessionId: options.sessionId } : {}),\n ...(options?.agentKey !== undefined ? { agentKey: options.agentKey } : {}),\n ...(options?.prompt !== undefined ? { prompt: options.prompt } : {}),\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post CLOSE_AGENT_WINDOW — minimize the host's agent window. */\nfunction closeAgentWindow(): void {\n const envelope: Envelope<CloseAgentWindowMessage> = {\n source: MESSAGE_SOURCE,\n type: 'CLOSE_AGENT_WINDOW',\n }\n window.parent?.postMessage(envelope, '*')\n}\n\nconst agentWindow: AgentWindowController = { open: openAgentWindow, close: closeAgentWindow }\n\n/** Navigate full-page (default) or into a peek panel (`{ peek: true }`). */\nfunction navigate(to: string, options?: NavigateOptions): void {\n if (options?.peek) postOpenPeek(to)\n else postNavigate(to)\n}\n\n// ── Component modals ────────────────────────────────────────────────────────\n// One module-level listener + a pending map, rather than a listener per call:\n// the SDK is an import-mapped singleton, so this is per-iframe state.\n\nconst pendingModals = new Map<string, (result: unknown) => void>()\nlet modalListenerAttached = false\n\nfunction ensureModalListener(): void {\n if (modalListenerAttached) return\n modalListenerAttached = true\n window.addEventListener('message', (event: MessageEvent) => {\n const data: unknown = event.data\n if (!isHostMessage(data) || data.type !== 'MODAL_CLOSED') return\n const resolve = pendingModals.get(data.requestId)\n // An unknown requestId is someone else's modal (or a stale reply) — drop it.\n if (!resolve) return\n pendingModals.delete(data.requestId)\n resolve(data.result)\n })\n}\n\n// How long after a modal closed the opener still claims the focus the host\n// hands back. Past that, focus arriving on the frame is something else.\nconst FOCUS_RESTORE_WINDOW_MS = 1000\n\n/** Puts focus back on the control that opened a modal.\n *\n * The host returns focus to this FRAME when its dialog closes, but not to the\n * control inside it: the frame is inert while the dialog is up, which drops\n * the focus it held, so it comes back on `<body>`. Only this side knows which\n * control it was.\n *\n * The reply (MODAL_CLOSED) arrives before the dialog has finished closing —\n * the frame may still be inert then and refuse focus — so this waits for the\n * frame to actually receive it. */\nfunction restoreFocus(opener: HTMLElement | null): void {\n if (!opener?.isConnected) return\n const isUnplaced = () =>\n document.activeElement === null || document.activeElement === document.body\n if (document.hasFocus() && isUnplaced()) {\n opener.focus({ preventScroll: true })\n return\n }\n const onFocus = () => {\n stop()\n // Something in here took focus in the meantime — leave it be.\n if (opener.isConnected && isUnplaced()) opener.focus({ preventScroll: true })\n }\n const timer = window.setTimeout(stop, FOCUS_RESTORE_WINDOW_MS)\n function stop(): void {\n window.clearTimeout(timer)\n window.removeEventListener('focus', onFocus)\n }\n window.addEventListener('focus', onFocus)\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `modal_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`\n}\n\n/** Post OPEN_MODAL and resolve when the host answers MODAL_CLOSED. */\nfunction openModal(\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n): Promise<unknown> {\n ensureModalListener()\n const requestId = newRequestId()\n const envelope: Envelope<OpenModalMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_MODAL',\n requestId,\n componentSlug,\n ...(props !== undefined ? { props } : {}),\n ...(options?.title !== undefined ? { title: options.title } : {}),\n ...(options?.width !== undefined ? { width: options.width } : {}),\n ...(options?.height !== undefined ? { height: options.height } : {}),\n }\n const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null\n return new Promise<unknown>((resolve) => {\n pendingModals.set(requestId, (result) => {\n restoreFocus(opener)\n resolve(result)\n })\n window.parent?.postMessage(envelope, '*')\n })\n}\n\n/** Post MODAL_RESULT — the component inside a host modal closing with a value. */\nexport function postModalResult(requestId: string, result?: unknown): void {\n const envelope: Envelope<ModalResultMessage> = {\n source: MESSAGE_SOURCE,\n type: 'MODAL_RESULT',\n requestId,\n ...(result !== undefined ? { result } : {}),\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/**\n * Provides the runtime value + derived helpers to a component subtree. Mounted\n * by the component-runtime shell around the loaded component — authors never\n * render this themselves. Lives in `@proteos/component-sdk` (a shared,\n * import-mapped singleton) so the context the shell provides is the very same\n * context the component's hooks read, across the bundle boundary.\n */\nexport function RuntimeProvider({ value, children }: { value: RuntimeValue; children: ReactNode }) {\n // The shell builds `value` once at INIT (a stable state object — clients,\n // props and pageCtx are all set once), so memoizing on its identity recreates\n // the context only when the runtime genuinely re-inits.\n const ctx = useMemo<RuntimeContextValue>(\n () => ({ ...value, navigate, openCall, openModal, agentWindow }),\n [value],\n )\n return <RuntimeContext.Provider value={ctx}>{children}</RuntimeContext.Provider>\n}\n","import { useCallback, useContext, useEffect, useMemo, useState } from 'react'\n\nimport {\n type AgentWindowController,\n type ComponentSdk,\n type NavigateOptions,\n type OpenModalOptions,\n type OverlayController,\n postModalResult,\n RuntimeContext,\n type RuntimeContextValue,\n} from './context'\nimport type { PageContext } from './protocol'\n\n/**\n * Low-level escape hatch: the whole runtime context (sdk, props, pageCtx,\n * apiBaseUrl, getToken, navigate). Prefer the focused hooks below. Throws a\n * clear error if used outside the component runtime.\n */\nexport function useRuntime(): RuntimeContextValue {\n const ctx = useContext(RuntimeContext)\n if (!ctx) {\n throw new Error(\n '@proteos/component-sdk: hooks must run inside the Proteos component runtime. ' +\n 'This usually means the component is rendered outside its iframe, or a ' +\n 'second copy of @proteos/component-sdk was bundled instead of using the ' +\n 'runtime-provided one (keep it external).',\n )\n }\n return ctx\n}\n\n/** The authenticated SDK clients (account / agents / conversations / data / functions / knowledge / meta / storage). */\nexport function useSdk(): ComponentSdk {\n return useRuntime().sdk\n}\n\n/** The component's Liquid-resolved props (a snapshot, not a live binding). */\nexport function useProps<T extends Record<string, unknown> = Record<string, unknown>>(): T {\n return useRuntime().props as T\n}\n\n/**\n * The full page context: host `record`, `entity`, and URL `params`. The\n * `record` is a snapshot taken at mount (`undefined` on record-less platform\n * pages) — fetch via `useSdk().data` for anything that must stay fresh.\n */\nexport function usePageContext(): PageContext {\n return useRuntime().pageCtx\n}\n\n/**\n * Navigate the host SPA to an in-app path, e.g.\n * `navigate('/org/acme/record/task/123')`. The host ignores absolute/external\n * URLs. Build org/app-prefixed paths with `useHostLocation().appBasePath`.\n *\n * Pass `{ peek: true }` to open the path in a peek (slide-over) panel instead\n * of full-page navigation — e.g. `navigate(path, { peek: true })`. The host\n * peeks what it can (today: platform pages) and falls back to full navigation.\n */\nexport function useNavigate(): (to: string, options?: NavigateOptions) => void {\n return useRuntime().navigate\n}\n\n/**\n * Open a prepared call from a conversation brief id: the HOST previews the brief and\n * places the call through its softphone (an iframe cannot dial). Pair it with\n * `ConversationBriefReview`'s `onOpenCall` from `@proteos/ui`.\n */\nexport function useOpenCall(): (conversationBriefId: string) => void {\n return useRuntime().openCall\n}\n\n/**\n * Open or minimize the HOST's agent session window (the floating assistant)\n * from a component — the iframe cannot render it itself.\n *\n * ```ts\n * const agentWindow = useAgentWindow()\n * agentWindow.open() // just open it\n * agentWindow.open({ sessionId }) // resume a session\n * agentWindow.open({ agentKey: 'sales-bot', prompt }) // fresh session, prompt sent\n * agentWindow.close() // minimize (session lives on)\n * ```\n *\n * `sessionId` wins over `agentKey` (the session already has an agent); a\n * `prompt` goes into whichever session the window lands on. Ignored on public\n * pages, and by preview hosts.\n */\nexport function useAgentWindow(): AgentWindowController {\n return useRuntime().agentWindow\n}\n\n/** The API origin SDK calls resolve against (for building raw asset URLs). */\nexport function useApiBaseUrl(): string {\n return useRuntime().apiBaseUrl\n}\n\nconst NOOP_OVERLAY: OverlayController = {\n expand: () => {\n console.warn(\n '@proteos/component-sdk: useOverlayExpansion() has no effect — this runtime ' +\n 'does not support overlay promotion (older shell or non-promoting host).',\n )\n },\n collapse: () => {},\n isExpanded: () => false,\n}\n\n/**\n * Manual overlay promotion — lifts the whole component iframe over the rest of\n * the page so an overlay can escape the frame's box.\n *\n * You rarely need this. Every `@proteos/ui` overlay (Modal, Popover, Menu,\n * Select, Tooltip, …) portals to the iframe document's `<body>`, which the\n * runtime watches — those promote automatically, with no code. Reach for this\n * hook only when you portal somewhere else yourself, or want the frame lifted\n * *before* the overlay mounts. Reference-counted: pair every `expand()` with a\n * `collapse()`.\n */\nexport function useOverlayExpansion(): OverlayController {\n return useRuntime().overlay ?? NOOP_OVERLAY\n}\n\n/**\n * Open ANOTHER component in a modal the host renders above everything — the\n * cross-component flow (a picker, a wizard step, a confirm dialog shared by\n * several components). Resolves with whatever that component closed with, or\n * `undefined` if the user dismissed it.\n *\n * ```ts\n * const openModal = useComponentModal()\n * const picked = await openModal(\n * 'contact-picker',\n * { entity: 'contact' },\n * { title: 'Pick a contact', width: 'md', height: 480 },\n * )\n * ```\n *\n * Pass `height` whenever you know it — the modal opens at its final size\n * instead of growing after the component loads (see {@link OpenModalOptions}).\n * One modal per component at a time: a second call while the first is still\n * open resolves `undefined` straight away.\n *\n * String props are Liquid-rendered by the host against this component's page\n * scope, like the props of a page-placed component.\n *\n * For an overlay that is part of THIS component, just render a `<Modal>` from\n * `@proteos/ui` — it promotes automatically.\n */\nexport function useComponentModal(): (\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n) => Promise<unknown> {\n return useRuntime().openModal\n}\n\nexport interface ModalHost {\n /** True when this component is rendered as the content of a host modal. */\n isInModal: boolean\n /** Close the host modal, resolving the opener's promise with `result`.\n * No-op outside a modal. */\n close: (result?: unknown) => void\n}\n\n/**\n * For a component that is being shown INSIDE a host modal (opened by another\n * component's `useComponentModal()`): close it and hand a value back.\n * `isInModal` is false on ordinary page-hosted mounts, so one component can\n * serve both roles.\n */\nexport function useModalHost(): ModalHost {\n const requestId = useRuntime().modalRequestId\n const close = useCallback(\n (result?: unknown) => {\n if (requestId === undefined) return\n postModalResult(requestId, result)\n },\n [requestId],\n )\n return useMemo(() => ({ isInModal: requestId !== undefined, close }), [requestId, close])\n}\n\n/**\n * A getter for the current bearer token. Returns a function (not the token)\n * because the token is refreshed in place — read it at call time for a raw\n * `fetch`. Most components never need this; prefer `useSdk()`.\n */\nexport function useAuthToken(): () => string {\n return useRuntime().getToken\n}\n\nexport interface HostLocation {\n /** The host tab's pathname (same-origin read), or null if unavailable. */\n pathname: string | null\n /** The org id parsed from `/org/<orgId>/…`, or null. */\n orgId: string | null\n /** The `/org/<orgId>/app/<appId>` prefix, or null when not on an app page. */\n appBasePath: string | null\n}\n\nfunction readHostLocation(): HostLocation {\n const empty: HostLocation = { pathname: null, orgId: null, appBasePath: null }\n try {\n const parentWindow = window.parent\n if (!parentWindow || parentWindow === window) return empty\n const pathname = parentWindow.location.pathname\n const appMatch = pathname.match(/^\\/org\\/[^/]+\\/app\\/[^/]+/)\n const orgMatch = pathname.match(/^\\/org\\/([^/]+)\\//)\n return {\n pathname,\n orgId: orgMatch ? (orgMatch[1] ?? null) : null,\n appBasePath: appMatch ? appMatch[0] : null,\n }\n } catch {\n // Cross-origin (untrusted-component future) or detached — fall back to null.\n return empty\n }\n}\n\n/**\n * Read the host tab's location (same-origin) — the org id and `/org/.../app/...`\n * prefix needed to build navigation targets on platform pages. Read once on\n * mount (the iframe re-inits when the host route changes).\n */\nexport function useHostLocation(): HostLocation {\n const [loc] = useState(readHostLocation)\n return loc\n}\n\nexport interface FillViewportOptions {\n /** Gap left below the component (px). Default 24. */\n bottomMargin?: number\n /** Floor for the computed height (px). Default 320. */\n minHeight?: number\n}\n\n/**\n * Compute a \"fill the rest of the viewport\" height for full-bleed layouts\n * (boards, canvases). The runtime auto-sizes the iframe to its content, so a\n * component can't use its own `vh` units (circular). The iframe is same-origin,\n * so this measures the parent viewport minus the iframe's offset instead.\n * Returns `null` when it can't measure (fall back to natural height).\n */\nexport function useFillViewportHeight(options: FillViewportOptions = {}): number | null {\n const { bottomMargin = 24, minHeight = 320 } = options\n const [height, setHeight] = useState<number | null>(null)\n\n useEffect(() => {\n function compute(): void {\n try {\n const frame = window.frameElement\n const parentWindow = window.parent\n if (!frame || !parentWindow || parentWindow === window) return\n const available =\n parentWindow.innerHeight - frame.getBoundingClientRect().top - bottomMargin\n setHeight(Math.max(available, minHeight))\n } catch {\n setHeight(null) // cross-origin or detached — fall back to natural height\n }\n }\n compute()\n try {\n window.parent.addEventListener('resize', compute)\n return () => window.parent.removeEventListener('resize', compute)\n } catch {\n return\n }\n }, [bottomMargin, minHeight])\n\n return height\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { AccountClient, AgentClient, ConversationClient, DataClient, FunctionsClient, KnowledgeClient, MetaClient, StorageClient } from '@proteos/sdk';
|
|
4
4
|
import { ModalWidth, PageContext } from './protocol.cjs';
|
|
5
|
-
export { AuthRefreshMessage, COMPONENT_OVERLAY_Z_INDEX, DataInvalidateMessage, Envelope, ErrorMessage, EscapePressedMessage, HostMessage, InitMessage, MESSAGE_SOURCE, ModalClosedMessage, ModalResultMessage, NavigateMessage, OpenCallMessage, OpenModalMessage, OpenPeekMessage, OverlayRect, OverlayRectMessage, OverlayStateMessage, ReadyMessage, ResizeMessage, RuntimeMessage, isHostMessage, isRuntimeMessage } from './protocol.cjs';
|
|
5
|
+
export { AuthRefreshMessage, COMPONENT_OVERLAY_Z_INDEX, CloseAgentWindowMessage, DataInvalidateMessage, Envelope, ErrorMessage, EscapePressedMessage, HostMessage, InitMessage, MESSAGE_SOURCE, ModalClosedMessage, ModalResultMessage, NavigateMessage, OpenAgentWindowMessage, OpenCallMessage, OpenModalMessage, OpenPeekMessage, OverlayRect, OverlayRectMessage, OverlayStateMessage, ReadyMessage, ResizeMessage, RuntimeMessage, isHostMessage, isRuntimeMessage } from './protocol.cjs';
|
|
6
6
|
|
|
7
7
|
/** Options for {@link RuntimeContextValue.navigate}. */
|
|
8
8
|
interface NavigateOptions {
|
|
@@ -24,6 +24,27 @@ interface OpenModalOptions {
|
|
|
24
24
|
* padded body. */
|
|
25
25
|
height?: number;
|
|
26
26
|
}
|
|
27
|
+
/** What {@link AgentWindowController.open} opens the host's agent window to. */
|
|
28
|
+
interface OpenAgentWindowOptions {
|
|
29
|
+
/** Resume this session (its agent comes with it). */
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
/** Start a fresh session with this agent. Ignored when `sessionId` is set,
|
|
32
|
+
* and when the host surface pins its agent. Without it (and without a
|
|
33
|
+
* session) the window's currently selected agent is used. */
|
|
34
|
+
agentKey?: string;
|
|
35
|
+
/** A message to send — into the resumed session, or as the first message
|
|
36
|
+
* of the fresh one. */
|
|
37
|
+
prompt?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The host's agent session window (the floating assistant), driven from a
|
|
41
|
+
* component. `open()` posts OPEN_AGENT_WINDOW, `close()` posts
|
|
42
|
+
* CLOSE_AGENT_WINDOW — closing minimizes; the session and its stream live on.
|
|
43
|
+
*/
|
|
44
|
+
interface AgentWindowController {
|
|
45
|
+
open: (options?: OpenAgentWindowOptions) => void;
|
|
46
|
+
close: () => void;
|
|
47
|
+
}
|
|
27
48
|
/**
|
|
28
49
|
* Manual control over iframe overlay promotion. The runtime already detects
|
|
29
50
|
* overlays automatically (anything portaled to the iframe's `document.body` —
|
|
@@ -102,6 +123,9 @@ interface RuntimeContextValue extends RuntimeValue {
|
|
|
102
123
|
/** Open another component in a host-rendered modal; resolves with the value
|
|
103
124
|
* that component closed with (`undefined` if dismissed). */
|
|
104
125
|
openModal: (componentSlug: string, props?: Record<string, unknown>, options?: OpenModalOptions) => Promise<unknown>;
|
|
126
|
+
/** Open / minimize the host's agent session window (posts OPEN_AGENT_WINDOW
|
|
127
|
+
* / CLOSE_AGENT_WINDOW). */
|
|
128
|
+
agentWindow: AgentWindowController;
|
|
105
129
|
}
|
|
106
130
|
/**
|
|
107
131
|
* Provides the runtime value + derived helpers to a component subtree. Mounted
|
|
@@ -159,6 +183,23 @@ declare function useNavigate(): (to: string, options?: NavigateOptions) => void;
|
|
|
159
183
|
* `ConversationBriefReview`'s `onOpenCall` from `@proteos/ui`.
|
|
160
184
|
*/
|
|
161
185
|
declare function useOpenCall(): (conversationBriefId: string) => void;
|
|
186
|
+
/**
|
|
187
|
+
* Open or minimize the HOST's agent session window (the floating assistant)
|
|
188
|
+
* from a component — the iframe cannot render it itself.
|
|
189
|
+
*
|
|
190
|
+
* ```ts
|
|
191
|
+
* const agentWindow = useAgentWindow()
|
|
192
|
+
* agentWindow.open() // just open it
|
|
193
|
+
* agentWindow.open({ sessionId }) // resume a session
|
|
194
|
+
* agentWindow.open({ agentKey: 'sales-bot', prompt }) // fresh session, prompt sent
|
|
195
|
+
* agentWindow.close() // minimize (session lives on)
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* `sessionId` wins over `agentKey` (the session already has an agent); a
|
|
199
|
+
* `prompt` goes into whichever session the window lands on. Ignored on public
|
|
200
|
+
* pages, and by preview hosts.
|
|
201
|
+
*/
|
|
202
|
+
declare function useAgentWindow(): AgentWindowController;
|
|
162
203
|
/** The API origin SDK calls resolve against (for building raw asset URLs). */
|
|
163
204
|
declare function useApiBaseUrl(): string;
|
|
164
205
|
/**
|
|
@@ -249,4 +290,4 @@ interface FillViewportOptions {
|
|
|
249
290
|
*/
|
|
250
291
|
declare function useFillViewportHeight(options?: FillViewportOptions): number | null;
|
|
251
292
|
|
|
252
|
-
export { type ComponentSdk, type FillViewportOptions, type HostLocation, type ModalHost, ModalWidth, type NavigateOptions, type OpenModalOptions, type OverlayController, PageContext, type RuntimeContextValue, RuntimeProvider, type RuntimeValue, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageContext, useProps, useRuntime, useSdk };
|
|
293
|
+
export { type AgentWindowController, type ComponentSdk, type FillViewportOptions, type HostLocation, type ModalHost, ModalWidth, type NavigateOptions, type OpenAgentWindowOptions, type OpenModalOptions, type OverlayController, PageContext, type RuntimeContextValue, RuntimeProvider, type RuntimeValue, useAgentWindow, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageContext, useProps, useRuntime, useSdk };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { AccountClient, AgentClient, ConversationClient, DataClient, FunctionsClient, KnowledgeClient, MetaClient, StorageClient } from '@proteos/sdk';
|
|
4
4
|
import { ModalWidth, PageContext } from './protocol.js';
|
|
5
|
-
export { AuthRefreshMessage, COMPONENT_OVERLAY_Z_INDEX, DataInvalidateMessage, Envelope, ErrorMessage, EscapePressedMessage, HostMessage, InitMessage, MESSAGE_SOURCE, ModalClosedMessage, ModalResultMessage, NavigateMessage, OpenCallMessage, OpenModalMessage, OpenPeekMessage, OverlayRect, OverlayRectMessage, OverlayStateMessage, ReadyMessage, ResizeMessage, RuntimeMessage, isHostMessage, isRuntimeMessage } from './protocol.js';
|
|
5
|
+
export { AuthRefreshMessage, COMPONENT_OVERLAY_Z_INDEX, CloseAgentWindowMessage, DataInvalidateMessage, Envelope, ErrorMessage, EscapePressedMessage, HostMessage, InitMessage, MESSAGE_SOURCE, ModalClosedMessage, ModalResultMessage, NavigateMessage, OpenAgentWindowMessage, OpenCallMessage, OpenModalMessage, OpenPeekMessage, OverlayRect, OverlayRectMessage, OverlayStateMessage, ReadyMessage, ResizeMessage, RuntimeMessage, isHostMessage, isRuntimeMessage } from './protocol.js';
|
|
6
6
|
|
|
7
7
|
/** Options for {@link RuntimeContextValue.navigate}. */
|
|
8
8
|
interface NavigateOptions {
|
|
@@ -24,6 +24,27 @@ interface OpenModalOptions {
|
|
|
24
24
|
* padded body. */
|
|
25
25
|
height?: number;
|
|
26
26
|
}
|
|
27
|
+
/** What {@link AgentWindowController.open} opens the host's agent window to. */
|
|
28
|
+
interface OpenAgentWindowOptions {
|
|
29
|
+
/** Resume this session (its agent comes with it). */
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
/** Start a fresh session with this agent. Ignored when `sessionId` is set,
|
|
32
|
+
* and when the host surface pins its agent. Without it (and without a
|
|
33
|
+
* session) the window's currently selected agent is used. */
|
|
34
|
+
agentKey?: string;
|
|
35
|
+
/** A message to send — into the resumed session, or as the first message
|
|
36
|
+
* of the fresh one. */
|
|
37
|
+
prompt?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The host's agent session window (the floating assistant), driven from a
|
|
41
|
+
* component. `open()` posts OPEN_AGENT_WINDOW, `close()` posts
|
|
42
|
+
* CLOSE_AGENT_WINDOW — closing minimizes; the session and its stream live on.
|
|
43
|
+
*/
|
|
44
|
+
interface AgentWindowController {
|
|
45
|
+
open: (options?: OpenAgentWindowOptions) => void;
|
|
46
|
+
close: () => void;
|
|
47
|
+
}
|
|
27
48
|
/**
|
|
28
49
|
* Manual control over iframe overlay promotion. The runtime already detects
|
|
29
50
|
* overlays automatically (anything portaled to the iframe's `document.body` —
|
|
@@ -102,6 +123,9 @@ interface RuntimeContextValue extends RuntimeValue {
|
|
|
102
123
|
/** Open another component in a host-rendered modal; resolves with the value
|
|
103
124
|
* that component closed with (`undefined` if dismissed). */
|
|
104
125
|
openModal: (componentSlug: string, props?: Record<string, unknown>, options?: OpenModalOptions) => Promise<unknown>;
|
|
126
|
+
/** Open / minimize the host's agent session window (posts OPEN_AGENT_WINDOW
|
|
127
|
+
* / CLOSE_AGENT_WINDOW). */
|
|
128
|
+
agentWindow: AgentWindowController;
|
|
105
129
|
}
|
|
106
130
|
/**
|
|
107
131
|
* Provides the runtime value + derived helpers to a component subtree. Mounted
|
|
@@ -159,6 +183,23 @@ declare function useNavigate(): (to: string, options?: NavigateOptions) => void;
|
|
|
159
183
|
* `ConversationBriefReview`'s `onOpenCall` from `@proteos/ui`.
|
|
160
184
|
*/
|
|
161
185
|
declare function useOpenCall(): (conversationBriefId: string) => void;
|
|
186
|
+
/**
|
|
187
|
+
* Open or minimize the HOST's agent session window (the floating assistant)
|
|
188
|
+
* from a component — the iframe cannot render it itself.
|
|
189
|
+
*
|
|
190
|
+
* ```ts
|
|
191
|
+
* const agentWindow = useAgentWindow()
|
|
192
|
+
* agentWindow.open() // just open it
|
|
193
|
+
* agentWindow.open({ sessionId }) // resume a session
|
|
194
|
+
* agentWindow.open({ agentKey: 'sales-bot', prompt }) // fresh session, prompt sent
|
|
195
|
+
* agentWindow.close() // minimize (session lives on)
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* `sessionId` wins over `agentKey` (the session already has an agent); a
|
|
199
|
+
* `prompt` goes into whichever session the window lands on. Ignored on public
|
|
200
|
+
* pages, and by preview hosts.
|
|
201
|
+
*/
|
|
202
|
+
declare function useAgentWindow(): AgentWindowController;
|
|
162
203
|
/** The API origin SDK calls resolve against (for building raw asset URLs). */
|
|
163
204
|
declare function useApiBaseUrl(): string;
|
|
164
205
|
/**
|
|
@@ -249,4 +290,4 @@ interface FillViewportOptions {
|
|
|
249
290
|
*/
|
|
250
291
|
declare function useFillViewportHeight(options?: FillViewportOptions): number | null;
|
|
251
292
|
|
|
252
|
-
export { type ComponentSdk, type FillViewportOptions, type HostLocation, type ModalHost, ModalWidth, type NavigateOptions, type OpenModalOptions, type OverlayController, PageContext, type RuntimeContextValue, RuntimeProvider, type RuntimeValue, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageContext, useProps, useRuntime, useSdk };
|
|
293
|
+
export { type AgentWindowController, type ComponentSdk, type FillViewportOptions, type HostLocation, type ModalHost, ModalWidth, type NavigateOptions, type OpenAgentWindowOptions, type OpenModalOptions, type OverlayController, PageContext, type RuntimeContextValue, RuntimeProvider, type RuntimeValue, useAgentWindow, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageContext, useProps, useRuntime, useSdk };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MESSAGE_SOURCE, isHostMessage } from './chunk-
|
|
2
|
-
export { COMPONENT_OVERLAY_Z_INDEX, MESSAGE_SOURCE, isHostMessage, isRuntimeMessage } from './chunk-
|
|
1
|
+
import { MESSAGE_SOURCE, isHostMessage } from './chunk-NOCSMXCW.js';
|
|
2
|
+
export { COMPONENT_OVERLAY_Z_INDEX, MESSAGE_SOURCE, isHostMessage, isRuntimeMessage } from './chunk-NOCSMXCW.js';
|
|
3
3
|
import { createContext, useMemo, useContext, useCallback, useState, useEffect } from 'react';
|
|
4
4
|
import { jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
@@ -20,6 +20,24 @@ function openCall(conversationBriefId) {
|
|
|
20
20
|
};
|
|
21
21
|
window.parent?.postMessage(envelope, "*");
|
|
22
22
|
}
|
|
23
|
+
function openAgentWindow(options) {
|
|
24
|
+
const envelope = {
|
|
25
|
+
source: MESSAGE_SOURCE,
|
|
26
|
+
type: "OPEN_AGENT_WINDOW",
|
|
27
|
+
...options?.sessionId !== void 0 ? { sessionId: options.sessionId } : {},
|
|
28
|
+
...options?.agentKey !== void 0 ? { agentKey: options.agentKey } : {},
|
|
29
|
+
...options?.prompt !== void 0 ? { prompt: options.prompt } : {}
|
|
30
|
+
};
|
|
31
|
+
window.parent?.postMessage(envelope, "*");
|
|
32
|
+
}
|
|
33
|
+
function closeAgentWindow() {
|
|
34
|
+
const envelope = {
|
|
35
|
+
source: MESSAGE_SOURCE,
|
|
36
|
+
type: "CLOSE_AGENT_WINDOW"
|
|
37
|
+
};
|
|
38
|
+
window.parent?.postMessage(envelope, "*");
|
|
39
|
+
}
|
|
40
|
+
var agentWindow = { open: openAgentWindow, close: closeAgentWindow };
|
|
23
41
|
function navigate(to, options) {
|
|
24
42
|
if (options?.peek) postOpenPeek(to);
|
|
25
43
|
else postNavigate(to);
|
|
@@ -96,7 +114,7 @@ function postModalResult(requestId, result) {
|
|
|
96
114
|
}
|
|
97
115
|
function RuntimeProvider({ value, children }) {
|
|
98
116
|
const ctx = useMemo(
|
|
99
|
-
() => ({ ...value, navigate, openCall, openModal }),
|
|
117
|
+
() => ({ ...value, navigate, openCall, openModal, agentWindow }),
|
|
100
118
|
[value]
|
|
101
119
|
);
|
|
102
120
|
return /* @__PURE__ */ jsx(RuntimeContext.Provider, { value: ctx, children });
|
|
@@ -125,6 +143,9 @@ function useNavigate() {
|
|
|
125
143
|
function useOpenCall() {
|
|
126
144
|
return useRuntime().openCall;
|
|
127
145
|
}
|
|
146
|
+
function useAgentWindow() {
|
|
147
|
+
return useRuntime().agentWindow;
|
|
148
|
+
}
|
|
128
149
|
function useApiBaseUrl() {
|
|
129
150
|
return useRuntime().apiBaseUrl;
|
|
130
151
|
}
|
|
@@ -205,6 +226,6 @@ function useFillViewportHeight(options = {}) {
|
|
|
205
226
|
return height;
|
|
206
227
|
}
|
|
207
228
|
|
|
208
|
-
export { RuntimeProvider, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageContext, useProps, useRuntime, useSdk };
|
|
229
|
+
export { RuntimeProvider, useAgentWindow, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageContext, useProps, useRuntime, useSdk };
|
|
209
230
|
//# sourceMappingURL=index.js.map
|
|
210
231
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.tsx","../src/hooks.ts"],"names":["useMemo"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n AccountClient,\n AgentClient,\n ConversationClient,\n DataClient,\n FunctionsClient,\n KnowledgeClient,\n MetaClient,\n StorageClient,\n} from '@proteos/sdk'\nimport { createContext, type ReactNode, useMemo } from 'react'\n\nimport {\n type Envelope,\n isHostMessage,\n MESSAGE_SOURCE,\n type ModalResultMessage,\n type ModalWidth,\n type NavigateMessage,\n type OpenCallMessage,\n type OpenModalMessage,\n type OpenPeekMessage,\n type PageContext,\n} from './protocol'\n\n/** Options for {@link RuntimeContextValue.navigate}. */\nexport interface NavigateOptions {\n /** Open the target in a peek (slide-over) panel instead of full-page. */\n peek?: boolean\n}\n\n/** Options for {@link RuntimeContextValue.openModal}. */\nexport interface OpenModalOptions {\n /** Heading rendered in the modal chrome. */\n title?: string\n /** Panel width: `sm` 384px, `md` 512px, `lg` 672px (default), `xl` 896px. */\n width?: ModalWidth\n /** Height of the component's frame, in px. Pass it whenever you know it: the\n * modal then opens at its final size instead of growing once the component\n * has loaded. The frame is capped at the viewport, scrolls its own content,\n * and sits edge to edge in the panel — so the component owns its padding\n * (`ModalBody` / `ModalFooter` from `@proteos/ui` carry the standard one).\n * Leave it out and the frame grows with its content inside the host's\n * padded body. */\n height?: number\n}\n\n/**\n * Manual control over iframe overlay promotion. The runtime already detects\n * overlays automatically (anything portaled to the iframe's `document.body` —\n * every `@proteos/ui` overlay qualifies), so components need this only for\n * custom portal targets, or to promote a frame *before* opening an overlay.\n * Calls are reference-counted: every `expand()` needs its `collapse()`.\n */\nexport interface OverlayController {\n expand: () => void\n collapse: () => void\n isExpanded: () => boolean\n}\n\n/**\n * The authenticated `@proteos/sdk` clients the runtime injects. Same instances\n * the host app uses — pre-wired with a refreshing bearer token, so a component\n * never touches auth headers.\n */\nexport interface ComponentSdk {\n account: AccountClient\n agents: AgentClient\n /**\n * Conversations, messages, contacts, connections (conversation-service) —\n * e.g. the DraftMessageComposer review surface. Plural per the platform's\n * resource-naming rule; the web app's own `useSdk().conversation` predates\n * it (documented asymmetry).\n */\n conversations: ConversationClient\n data: DataClient\n functions: FunctionsClient\n knowledge: KnowledgeClient\n meta: MetaClient\n storage: StorageClient\n}\n\n/**\n * The raw runtime payload the shell hands a component at INIT. The shell builds\n * this; component authors read it through the hooks.\n */\nexport interface RuntimeValue {\n /**\n * The injected sdk clients. On an authenticated page these are the full,\n * bearer-wired clients. On a PUBLIC page (`isPublic: true`) only\n * `functions.actions.invokePublic` reaches the platform — every other\n * client throws on use (there is no token, and the server rejects\n * unauthenticated calls anyway).\n */\n sdk: ComponentSdk\n /** Liquid-resolved props (a snapshot — not a live binding). */\n props: Record<string, unknown>\n /** Page context snapshot: the host record, entity, and URL params. */\n pageCtx: PageContext\n /** API origin SDK calls + asset URLs resolve against. */\n apiBaseUrl: string\n /** Current bearer token getter (escape hatch for raw fetches). Returns ''\n * on public pages. */\n getToken: () => string\n /** True when rendering on an unauthenticated public page. */\n isPublic?: boolean\n /** Org id, set in public mode (public API routes carry it in the path). */\n orgId?: string\n /** Set when this component is the content of a host component modal — the\n * correlation id it closes with. See `useModalHost()`. */\n modalRequestId?: string\n /** Overlay promotion controller, provided by the shell. Absent on older\n * shells / non-promoting hosts, in which case `useOverlayExpansion()`\n * no-ops. */\n overlay?: OverlayController\n}\n\n/** Runtime value plus the derived helpers the hooks expose. */\nexport interface RuntimeContextValue extends RuntimeValue {\n /** Navigate the host SPA to an in-app path (posts a NAVIGATE message), or\n * open it in a peek panel with `{ peek: true }` (posts OPEN_PEEK). */\n navigate: (to: string, options?: NavigateOptions) => void\n /** Ask the host to open a prepared call from a conversation brief (posts\n * OPEN_CALL): the host previews the brief and dials through its softphone. */\n openCall: (conversationBriefId: string) => void\n /** Open another component in a host-rendered modal; resolves with the value\n * that component closed with (`undefined` if dismissed). */\n openModal: (\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n ) => Promise<unknown>\n}\n\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null)\n\n/** Post a NAVIGATE envelope to the host. The host only honours relative,\n * in-app targets (it ignores absolute/external URLs). */\nfunction postNavigate(to: string): void {\n const envelope: Envelope<NavigateMessage> = { source: MESSAGE_SOURCE, type: 'NAVIGATE', to }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post an OPEN_PEEK envelope to the host — open the in-app path in a peek\n * panel. Same relative-path contract as {@link postNavigate}. */\nfunction postOpenPeek(to: string): void {\n const envelope: Envelope<OpenPeekMessage> = { source: MESSAGE_SOURCE, type: 'OPEN_PEEK', to }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post OPEN_CALL — the host previews the conversation brief and owns the dial. */\nfunction openCall(conversationBriefId: string): void {\n const envelope: Envelope<OpenCallMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_CALL',\n conversationBriefId,\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Navigate full-page (default) or into a peek panel (`{ peek: true }`). */\nfunction navigate(to: string, options?: NavigateOptions): void {\n if (options?.peek) postOpenPeek(to)\n else postNavigate(to)\n}\n\n// ── Component modals ────────────────────────────────────────────────────────\n// One module-level listener + a pending map, rather than a listener per call:\n// the SDK is an import-mapped singleton, so this is per-iframe state.\n\nconst pendingModals = new Map<string, (result: unknown) => void>()\nlet modalListenerAttached = false\n\nfunction ensureModalListener(): void {\n if (modalListenerAttached) return\n modalListenerAttached = true\n window.addEventListener('message', (event: MessageEvent) => {\n const data: unknown = event.data\n if (!isHostMessage(data) || data.type !== 'MODAL_CLOSED') return\n const resolve = pendingModals.get(data.requestId)\n // An unknown requestId is someone else's modal (or a stale reply) — drop it.\n if (!resolve) return\n pendingModals.delete(data.requestId)\n resolve(data.result)\n })\n}\n\n// How long after a modal closed the opener still claims the focus the host\n// hands back. Past that, focus arriving on the frame is something else.\nconst FOCUS_RESTORE_WINDOW_MS = 1000\n\n/** Puts focus back on the control that opened a modal.\n *\n * The host returns focus to this FRAME when its dialog closes, but not to the\n * control inside it: the frame is inert while the dialog is up, which drops\n * the focus it held, so it comes back on `<body>`. Only this side knows which\n * control it was.\n *\n * The reply (MODAL_CLOSED) arrives before the dialog has finished closing —\n * the frame may still be inert then and refuse focus — so this waits for the\n * frame to actually receive it. */\nfunction restoreFocus(opener: HTMLElement | null): void {\n if (!opener?.isConnected) return\n const isUnplaced = () =>\n document.activeElement === null || document.activeElement === document.body\n if (document.hasFocus() && isUnplaced()) {\n opener.focus({ preventScroll: true })\n return\n }\n const onFocus = () => {\n stop()\n // Something in here took focus in the meantime — leave it be.\n if (opener.isConnected && isUnplaced()) opener.focus({ preventScroll: true })\n }\n const timer = window.setTimeout(stop, FOCUS_RESTORE_WINDOW_MS)\n function stop(): void {\n window.clearTimeout(timer)\n window.removeEventListener('focus', onFocus)\n }\n window.addEventListener('focus', onFocus)\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `modal_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`\n}\n\n/** Post OPEN_MODAL and resolve when the host answers MODAL_CLOSED. */\nfunction openModal(\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n): Promise<unknown> {\n ensureModalListener()\n const requestId = newRequestId()\n const envelope: Envelope<OpenModalMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_MODAL',\n requestId,\n componentSlug,\n ...(props !== undefined ? { props } : {}),\n ...(options?.title !== undefined ? { title: options.title } : {}),\n ...(options?.width !== undefined ? { width: options.width } : {}),\n ...(options?.height !== undefined ? { height: options.height } : {}),\n }\n const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null\n return new Promise<unknown>((resolve) => {\n pendingModals.set(requestId, (result) => {\n restoreFocus(opener)\n resolve(result)\n })\n window.parent?.postMessage(envelope, '*')\n })\n}\n\n/** Post MODAL_RESULT — the component inside a host modal closing with a value. */\nexport function postModalResult(requestId: string, result?: unknown): void {\n const envelope: Envelope<ModalResultMessage> = {\n source: MESSAGE_SOURCE,\n type: 'MODAL_RESULT',\n requestId,\n ...(result !== undefined ? { result } : {}),\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/**\n * Provides the runtime value + derived helpers to a component subtree. Mounted\n * by the component-runtime shell around the loaded component — authors never\n * render this themselves. Lives in `@proteos/component-sdk` (a shared,\n * import-mapped singleton) so the context the shell provides is the very same\n * context the component's hooks read, across the bundle boundary.\n */\nexport function RuntimeProvider({ value, children }: { value: RuntimeValue; children: ReactNode }) {\n // The shell builds `value` once at INIT (a stable state object — clients,\n // props and pageCtx are all set once), so memoizing on its identity recreates\n // the context only when the runtime genuinely re-inits.\n const ctx = useMemo<RuntimeContextValue>(\n () => ({ ...value, navigate, openCall, openModal }),\n [value],\n )\n return <RuntimeContext.Provider value={ctx}>{children}</RuntimeContext.Provider>\n}\n","import { useCallback, useContext, useEffect, useMemo, useState } from 'react'\n\nimport {\n type ComponentSdk,\n type NavigateOptions,\n type OpenModalOptions,\n type OverlayController,\n postModalResult,\n RuntimeContext,\n type RuntimeContextValue,\n} from './context'\nimport type { PageContext } from './protocol'\n\n/**\n * Low-level escape hatch: the whole runtime context (sdk, props, pageCtx,\n * apiBaseUrl, getToken, navigate). Prefer the focused hooks below. Throws a\n * clear error if used outside the component runtime.\n */\nexport function useRuntime(): RuntimeContextValue {\n const ctx = useContext(RuntimeContext)\n if (!ctx) {\n throw new Error(\n '@proteos/component-sdk: hooks must run inside the Proteos component runtime. ' +\n 'This usually means the component is rendered outside its iframe, or a ' +\n 'second copy of @proteos/component-sdk was bundled instead of using the ' +\n 'runtime-provided one (keep it external).',\n )\n }\n return ctx\n}\n\n/** The authenticated SDK clients (account / agents / conversations / data / functions / knowledge / meta / storage). */\nexport function useSdk(): ComponentSdk {\n return useRuntime().sdk\n}\n\n/** The component's Liquid-resolved props (a snapshot, not a live binding). */\nexport function useProps<T extends Record<string, unknown> = Record<string, unknown>>(): T {\n return useRuntime().props as T\n}\n\n/**\n * The full page context: host `record`, `entity`, and URL `params`. The\n * `record` is a snapshot taken at mount (`undefined` on record-less platform\n * pages) — fetch via `useSdk().data` for anything that must stay fresh.\n */\nexport function usePageContext(): PageContext {\n return useRuntime().pageCtx\n}\n\n/**\n * Navigate the host SPA to an in-app path, e.g.\n * `navigate('/org/acme/record/task/123')`. The host ignores absolute/external\n * URLs. Build org/app-prefixed paths with `useHostLocation().appBasePath`.\n *\n * Pass `{ peek: true }` to open the path in a peek (slide-over) panel instead\n * of full-page navigation — e.g. `navigate(path, { peek: true })`. The host\n * peeks what it can (today: platform pages) and falls back to full navigation.\n */\nexport function useNavigate(): (to: string, options?: NavigateOptions) => void {\n return useRuntime().navigate\n}\n\n/**\n * Open a prepared call from a conversation brief id: the HOST previews the brief and\n * places the call through its softphone (an iframe cannot dial). Pair it with\n * `ConversationBriefReview`'s `onOpenCall` from `@proteos/ui`.\n */\nexport function useOpenCall(): (conversationBriefId: string) => void {\n return useRuntime().openCall\n}\n\n/** The API origin SDK calls resolve against (for building raw asset URLs). */\nexport function useApiBaseUrl(): string {\n return useRuntime().apiBaseUrl\n}\n\nconst NOOP_OVERLAY: OverlayController = {\n expand: () => {\n console.warn(\n '@proteos/component-sdk: useOverlayExpansion() has no effect — this runtime ' +\n 'does not support overlay promotion (older shell or non-promoting host).',\n )\n },\n collapse: () => {},\n isExpanded: () => false,\n}\n\n/**\n * Manual overlay promotion — lifts the whole component iframe over the rest of\n * the page so an overlay can escape the frame's box.\n *\n * You rarely need this. Every `@proteos/ui` overlay (Modal, Popover, Menu,\n * Select, Tooltip, …) portals to the iframe document's `<body>`, which the\n * runtime watches — those promote automatically, with no code. Reach for this\n * hook only when you portal somewhere else yourself, or want the frame lifted\n * *before* the overlay mounts. Reference-counted: pair every `expand()` with a\n * `collapse()`.\n */\nexport function useOverlayExpansion(): OverlayController {\n return useRuntime().overlay ?? NOOP_OVERLAY\n}\n\n/**\n * Open ANOTHER component in a modal the host renders above everything — the\n * cross-component flow (a picker, a wizard step, a confirm dialog shared by\n * several components). Resolves with whatever that component closed with, or\n * `undefined` if the user dismissed it.\n *\n * ```ts\n * const openModal = useComponentModal()\n * const picked = await openModal(\n * 'contact-picker',\n * { entity: 'contact' },\n * { title: 'Pick a contact', width: 'md', height: 480 },\n * )\n * ```\n *\n * Pass `height` whenever you know it — the modal opens at its final size\n * instead of growing after the component loads (see {@link OpenModalOptions}).\n * One modal per component at a time: a second call while the first is still\n * open resolves `undefined` straight away.\n *\n * String props are Liquid-rendered by the host against this component's page\n * scope, like the props of a page-placed component.\n *\n * For an overlay that is part of THIS component, just render a `<Modal>` from\n * `@proteos/ui` — it promotes automatically.\n */\nexport function useComponentModal(): (\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n) => Promise<unknown> {\n return useRuntime().openModal\n}\n\nexport interface ModalHost {\n /** True when this component is rendered as the content of a host modal. */\n isInModal: boolean\n /** Close the host modal, resolving the opener's promise with `result`.\n * No-op outside a modal. */\n close: (result?: unknown) => void\n}\n\n/**\n * For a component that is being shown INSIDE a host modal (opened by another\n * component's `useComponentModal()`): close it and hand a value back.\n * `isInModal` is false on ordinary page-hosted mounts, so one component can\n * serve both roles.\n */\nexport function useModalHost(): ModalHost {\n const requestId = useRuntime().modalRequestId\n const close = useCallback(\n (result?: unknown) => {\n if (requestId === undefined) return\n postModalResult(requestId, result)\n },\n [requestId],\n )\n return useMemo(() => ({ isInModal: requestId !== undefined, close }), [requestId, close])\n}\n\n/**\n * A getter for the current bearer token. Returns a function (not the token)\n * because the token is refreshed in place — read it at call time for a raw\n * `fetch`. Most components never need this; prefer `useSdk()`.\n */\nexport function useAuthToken(): () => string {\n return useRuntime().getToken\n}\n\nexport interface HostLocation {\n /** The host tab's pathname (same-origin read), or null if unavailable. */\n pathname: string | null\n /** The org id parsed from `/org/<orgId>/…`, or null. */\n orgId: string | null\n /** The `/org/<orgId>/app/<appId>` prefix, or null when not on an app page. */\n appBasePath: string | null\n}\n\nfunction readHostLocation(): HostLocation {\n const empty: HostLocation = { pathname: null, orgId: null, appBasePath: null }\n try {\n const parentWindow = window.parent\n if (!parentWindow || parentWindow === window) return empty\n const pathname = parentWindow.location.pathname\n const appMatch = pathname.match(/^\\/org\\/[^/]+\\/app\\/[^/]+/)\n const orgMatch = pathname.match(/^\\/org\\/([^/]+)\\//)\n return {\n pathname,\n orgId: orgMatch ? (orgMatch[1] ?? null) : null,\n appBasePath: appMatch ? appMatch[0] : null,\n }\n } catch {\n // Cross-origin (untrusted-component future) or detached — fall back to null.\n return empty\n }\n}\n\n/**\n * Read the host tab's location (same-origin) — the org id and `/org/.../app/...`\n * prefix needed to build navigation targets on platform pages. Read once on\n * mount (the iframe re-inits when the host route changes).\n */\nexport function useHostLocation(): HostLocation {\n const [loc] = useState(readHostLocation)\n return loc\n}\n\nexport interface FillViewportOptions {\n /** Gap left below the component (px). Default 24. */\n bottomMargin?: number\n /** Floor for the computed height (px). Default 320. */\n minHeight?: number\n}\n\n/**\n * Compute a \"fill the rest of the viewport\" height for full-bleed layouts\n * (boards, canvases). The runtime auto-sizes the iframe to its content, so a\n * component can't use its own `vh` units (circular). The iframe is same-origin,\n * so this measures the parent viewport minus the iframe's offset instead.\n * Returns `null` when it can't measure (fall back to natural height).\n */\nexport function useFillViewportHeight(options: FillViewportOptions = {}): number | null {\n const { bottomMargin = 24, minHeight = 320 } = options\n const [height, setHeight] = useState<number | null>(null)\n\n useEffect(() => {\n function compute(): void {\n try {\n const frame = window.frameElement\n const parentWindow = window.parent\n if (!frame || !parentWindow || parentWindow === window) return\n const available =\n parentWindow.innerHeight - frame.getBoundingClientRect().top - bottomMargin\n setHeight(Math.max(available, minHeight))\n } catch {\n setHeight(null) // cross-origin or detached — fall back to natural height\n }\n }\n compute()\n try {\n window.parent.addEventListener('resize', compute)\n return () => window.parent.removeEventListener('resize', compute)\n } catch {\n return\n }\n }, [bottomMargin, minHeight])\n\n return height\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.tsx","../src/hooks.ts"],"names":["useMemo"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n AccountClient,\n AgentClient,\n ConversationClient,\n DataClient,\n FunctionsClient,\n KnowledgeClient,\n MetaClient,\n StorageClient,\n} from '@proteos/sdk'\nimport { createContext, type ReactNode, useMemo } from 'react'\n\nimport {\n type CloseAgentWindowMessage,\n type Envelope,\n isHostMessage,\n MESSAGE_SOURCE,\n type ModalResultMessage,\n type ModalWidth,\n type NavigateMessage,\n type OpenAgentWindowMessage,\n type OpenCallMessage,\n type OpenModalMessage,\n type OpenPeekMessage,\n type PageContext,\n} from './protocol'\n\n/** Options for {@link RuntimeContextValue.navigate}. */\nexport interface NavigateOptions {\n /** Open the target in a peek (slide-over) panel instead of full-page. */\n peek?: boolean\n}\n\n/** Options for {@link RuntimeContextValue.openModal}. */\nexport interface OpenModalOptions {\n /** Heading rendered in the modal chrome. */\n title?: string\n /** Panel width: `sm` 384px, `md` 512px, `lg` 672px (default), `xl` 896px. */\n width?: ModalWidth\n /** Height of the component's frame, in px. Pass it whenever you know it: the\n * modal then opens at its final size instead of growing once the component\n * has loaded. The frame is capped at the viewport, scrolls its own content,\n * and sits edge to edge in the panel — so the component owns its padding\n * (`ModalBody` / `ModalFooter` from `@proteos/ui` carry the standard one).\n * Leave it out and the frame grows with its content inside the host's\n * padded body. */\n height?: number\n}\n\n/** What {@link AgentWindowController.open} opens the host's agent window to. */\nexport interface OpenAgentWindowOptions {\n /** Resume this session (its agent comes with it). */\n sessionId?: string\n /** Start a fresh session with this agent. Ignored when `sessionId` is set,\n * and when the host surface pins its agent. Without it (and without a\n * session) the window's currently selected agent is used. */\n agentKey?: string\n /** A message to send — into the resumed session, or as the first message\n * of the fresh one. */\n prompt?: string\n}\n\n/**\n * The host's agent session window (the floating assistant), driven from a\n * component. `open()` posts OPEN_AGENT_WINDOW, `close()` posts\n * CLOSE_AGENT_WINDOW — closing minimizes; the session and its stream live on.\n */\nexport interface AgentWindowController {\n open: (options?: OpenAgentWindowOptions) => void\n close: () => void\n}\n\n/**\n * Manual control over iframe overlay promotion. The runtime already detects\n * overlays automatically (anything portaled to the iframe's `document.body` —\n * every `@proteos/ui` overlay qualifies), so components need this only for\n * custom portal targets, or to promote a frame *before* opening an overlay.\n * Calls are reference-counted: every `expand()` needs its `collapse()`.\n */\nexport interface OverlayController {\n expand: () => void\n collapse: () => void\n isExpanded: () => boolean\n}\n\n/**\n * The authenticated `@proteos/sdk` clients the runtime injects. Same instances\n * the host app uses — pre-wired with a refreshing bearer token, so a component\n * never touches auth headers.\n */\nexport interface ComponentSdk {\n account: AccountClient\n agents: AgentClient\n /**\n * Conversations, messages, contacts, connections (conversation-service) —\n * e.g. the DraftMessageComposer review surface. Plural per the platform's\n * resource-naming rule; the web app's own `useSdk().conversation` predates\n * it (documented asymmetry).\n */\n conversations: ConversationClient\n data: DataClient\n functions: FunctionsClient\n knowledge: KnowledgeClient\n meta: MetaClient\n storage: StorageClient\n}\n\n/**\n * The raw runtime payload the shell hands a component at INIT. The shell builds\n * this; component authors read it through the hooks.\n */\nexport interface RuntimeValue {\n /**\n * The injected sdk clients. On an authenticated page these are the full,\n * bearer-wired clients. On a PUBLIC page (`isPublic: true`) only\n * `functions.actions.invokePublic` reaches the platform — every other\n * client throws on use (there is no token, and the server rejects\n * unauthenticated calls anyway).\n */\n sdk: ComponentSdk\n /** Liquid-resolved props (a snapshot — not a live binding). */\n props: Record<string, unknown>\n /** Page context snapshot: the host record, entity, and URL params. */\n pageCtx: PageContext\n /** API origin SDK calls + asset URLs resolve against. */\n apiBaseUrl: string\n /** Current bearer token getter (escape hatch for raw fetches). Returns ''\n * on public pages. */\n getToken: () => string\n /** True when rendering on an unauthenticated public page. */\n isPublic?: boolean\n /** Org id, set in public mode (public API routes carry it in the path). */\n orgId?: string\n /** Set when this component is the content of a host component modal — the\n * correlation id it closes with. See `useModalHost()`. */\n modalRequestId?: string\n /** Overlay promotion controller, provided by the shell. Absent on older\n * shells / non-promoting hosts, in which case `useOverlayExpansion()`\n * no-ops. */\n overlay?: OverlayController\n}\n\n/** Runtime value plus the derived helpers the hooks expose. */\nexport interface RuntimeContextValue extends RuntimeValue {\n /** Navigate the host SPA to an in-app path (posts a NAVIGATE message), or\n * open it in a peek panel with `{ peek: true }` (posts OPEN_PEEK). */\n navigate: (to: string, options?: NavigateOptions) => void\n /** Ask the host to open a prepared call from a conversation brief (posts\n * OPEN_CALL): the host previews the brief and dials through its softphone. */\n openCall: (conversationBriefId: string) => void\n /** Open another component in a host-rendered modal; resolves with the value\n * that component closed with (`undefined` if dismissed). */\n openModal: (\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n ) => Promise<unknown>\n /** Open / minimize the host's agent session window (posts OPEN_AGENT_WINDOW\n * / CLOSE_AGENT_WINDOW). */\n agentWindow: AgentWindowController\n}\n\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null)\n\n/** Post a NAVIGATE envelope to the host. The host only honours relative,\n * in-app targets (it ignores absolute/external URLs). */\nfunction postNavigate(to: string): void {\n const envelope: Envelope<NavigateMessage> = { source: MESSAGE_SOURCE, type: 'NAVIGATE', to }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post an OPEN_PEEK envelope to the host — open the in-app path in a peek\n * panel. Same relative-path contract as {@link postNavigate}. */\nfunction postOpenPeek(to: string): void {\n const envelope: Envelope<OpenPeekMessage> = { source: MESSAGE_SOURCE, type: 'OPEN_PEEK', to }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post OPEN_CALL — the host previews the conversation brief and owns the dial. */\nfunction openCall(conversationBriefId: string): void {\n const envelope: Envelope<OpenCallMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_CALL',\n conversationBriefId,\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post OPEN_AGENT_WINDOW — only the fields that were given ride the wire. */\nfunction openAgentWindow(options?: OpenAgentWindowOptions): void {\n const envelope: Envelope<OpenAgentWindowMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_AGENT_WINDOW',\n ...(options?.sessionId !== undefined ? { sessionId: options.sessionId } : {}),\n ...(options?.agentKey !== undefined ? { agentKey: options.agentKey } : {}),\n ...(options?.prompt !== undefined ? { prompt: options.prompt } : {}),\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/** Post CLOSE_AGENT_WINDOW — minimize the host's agent window. */\nfunction closeAgentWindow(): void {\n const envelope: Envelope<CloseAgentWindowMessage> = {\n source: MESSAGE_SOURCE,\n type: 'CLOSE_AGENT_WINDOW',\n }\n window.parent?.postMessage(envelope, '*')\n}\n\nconst agentWindow: AgentWindowController = { open: openAgentWindow, close: closeAgentWindow }\n\n/** Navigate full-page (default) or into a peek panel (`{ peek: true }`). */\nfunction navigate(to: string, options?: NavigateOptions): void {\n if (options?.peek) postOpenPeek(to)\n else postNavigate(to)\n}\n\n// ── Component modals ────────────────────────────────────────────────────────\n// One module-level listener + a pending map, rather than a listener per call:\n// the SDK is an import-mapped singleton, so this is per-iframe state.\n\nconst pendingModals = new Map<string, (result: unknown) => void>()\nlet modalListenerAttached = false\n\nfunction ensureModalListener(): void {\n if (modalListenerAttached) return\n modalListenerAttached = true\n window.addEventListener('message', (event: MessageEvent) => {\n const data: unknown = event.data\n if (!isHostMessage(data) || data.type !== 'MODAL_CLOSED') return\n const resolve = pendingModals.get(data.requestId)\n // An unknown requestId is someone else's modal (or a stale reply) — drop it.\n if (!resolve) return\n pendingModals.delete(data.requestId)\n resolve(data.result)\n })\n}\n\n// How long after a modal closed the opener still claims the focus the host\n// hands back. Past that, focus arriving on the frame is something else.\nconst FOCUS_RESTORE_WINDOW_MS = 1000\n\n/** Puts focus back on the control that opened a modal.\n *\n * The host returns focus to this FRAME when its dialog closes, but not to the\n * control inside it: the frame is inert while the dialog is up, which drops\n * the focus it held, so it comes back on `<body>`. Only this side knows which\n * control it was.\n *\n * The reply (MODAL_CLOSED) arrives before the dialog has finished closing —\n * the frame may still be inert then and refuse focus — so this waits for the\n * frame to actually receive it. */\nfunction restoreFocus(opener: HTMLElement | null): void {\n if (!opener?.isConnected) return\n const isUnplaced = () =>\n document.activeElement === null || document.activeElement === document.body\n if (document.hasFocus() && isUnplaced()) {\n opener.focus({ preventScroll: true })\n return\n }\n const onFocus = () => {\n stop()\n // Something in here took focus in the meantime — leave it be.\n if (opener.isConnected && isUnplaced()) opener.focus({ preventScroll: true })\n }\n const timer = window.setTimeout(stop, FOCUS_RESTORE_WINDOW_MS)\n function stop(): void {\n window.clearTimeout(timer)\n window.removeEventListener('focus', onFocus)\n }\n window.addEventListener('focus', onFocus)\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `modal_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`\n}\n\n/** Post OPEN_MODAL and resolve when the host answers MODAL_CLOSED. */\nfunction openModal(\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n): Promise<unknown> {\n ensureModalListener()\n const requestId = newRequestId()\n const envelope: Envelope<OpenModalMessage> = {\n source: MESSAGE_SOURCE,\n type: 'OPEN_MODAL',\n requestId,\n componentSlug,\n ...(props !== undefined ? { props } : {}),\n ...(options?.title !== undefined ? { title: options.title } : {}),\n ...(options?.width !== undefined ? { width: options.width } : {}),\n ...(options?.height !== undefined ? { height: options.height } : {}),\n }\n const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null\n return new Promise<unknown>((resolve) => {\n pendingModals.set(requestId, (result) => {\n restoreFocus(opener)\n resolve(result)\n })\n window.parent?.postMessage(envelope, '*')\n })\n}\n\n/** Post MODAL_RESULT — the component inside a host modal closing with a value. */\nexport function postModalResult(requestId: string, result?: unknown): void {\n const envelope: Envelope<ModalResultMessage> = {\n source: MESSAGE_SOURCE,\n type: 'MODAL_RESULT',\n requestId,\n ...(result !== undefined ? { result } : {}),\n }\n window.parent?.postMessage(envelope, '*')\n}\n\n/**\n * Provides the runtime value + derived helpers to a component subtree. Mounted\n * by the component-runtime shell around the loaded component — authors never\n * render this themselves. Lives in `@proteos/component-sdk` (a shared,\n * import-mapped singleton) so the context the shell provides is the very same\n * context the component's hooks read, across the bundle boundary.\n */\nexport function RuntimeProvider({ value, children }: { value: RuntimeValue; children: ReactNode }) {\n // The shell builds `value` once at INIT (a stable state object — clients,\n // props and pageCtx are all set once), so memoizing on its identity recreates\n // the context only when the runtime genuinely re-inits.\n const ctx = useMemo<RuntimeContextValue>(\n () => ({ ...value, navigate, openCall, openModal, agentWindow }),\n [value],\n )\n return <RuntimeContext.Provider value={ctx}>{children}</RuntimeContext.Provider>\n}\n","import { useCallback, useContext, useEffect, useMemo, useState } from 'react'\n\nimport {\n type AgentWindowController,\n type ComponentSdk,\n type NavigateOptions,\n type OpenModalOptions,\n type OverlayController,\n postModalResult,\n RuntimeContext,\n type RuntimeContextValue,\n} from './context'\nimport type { PageContext } from './protocol'\n\n/**\n * Low-level escape hatch: the whole runtime context (sdk, props, pageCtx,\n * apiBaseUrl, getToken, navigate). Prefer the focused hooks below. Throws a\n * clear error if used outside the component runtime.\n */\nexport function useRuntime(): RuntimeContextValue {\n const ctx = useContext(RuntimeContext)\n if (!ctx) {\n throw new Error(\n '@proteos/component-sdk: hooks must run inside the Proteos component runtime. ' +\n 'This usually means the component is rendered outside its iframe, or a ' +\n 'second copy of @proteos/component-sdk was bundled instead of using the ' +\n 'runtime-provided one (keep it external).',\n )\n }\n return ctx\n}\n\n/** The authenticated SDK clients (account / agents / conversations / data / functions / knowledge / meta / storage). */\nexport function useSdk(): ComponentSdk {\n return useRuntime().sdk\n}\n\n/** The component's Liquid-resolved props (a snapshot, not a live binding). */\nexport function useProps<T extends Record<string, unknown> = Record<string, unknown>>(): T {\n return useRuntime().props as T\n}\n\n/**\n * The full page context: host `record`, `entity`, and URL `params`. The\n * `record` is a snapshot taken at mount (`undefined` on record-less platform\n * pages) — fetch via `useSdk().data` for anything that must stay fresh.\n */\nexport function usePageContext(): PageContext {\n return useRuntime().pageCtx\n}\n\n/**\n * Navigate the host SPA to an in-app path, e.g.\n * `navigate('/org/acme/record/task/123')`. The host ignores absolute/external\n * URLs. Build org/app-prefixed paths with `useHostLocation().appBasePath`.\n *\n * Pass `{ peek: true }` to open the path in a peek (slide-over) panel instead\n * of full-page navigation — e.g. `navigate(path, { peek: true })`. The host\n * peeks what it can (today: platform pages) and falls back to full navigation.\n */\nexport function useNavigate(): (to: string, options?: NavigateOptions) => void {\n return useRuntime().navigate\n}\n\n/**\n * Open a prepared call from a conversation brief id: the HOST previews the brief and\n * places the call through its softphone (an iframe cannot dial). Pair it with\n * `ConversationBriefReview`'s `onOpenCall` from `@proteos/ui`.\n */\nexport function useOpenCall(): (conversationBriefId: string) => void {\n return useRuntime().openCall\n}\n\n/**\n * Open or minimize the HOST's agent session window (the floating assistant)\n * from a component — the iframe cannot render it itself.\n *\n * ```ts\n * const agentWindow = useAgentWindow()\n * agentWindow.open() // just open it\n * agentWindow.open({ sessionId }) // resume a session\n * agentWindow.open({ agentKey: 'sales-bot', prompt }) // fresh session, prompt sent\n * agentWindow.close() // minimize (session lives on)\n * ```\n *\n * `sessionId` wins over `agentKey` (the session already has an agent); a\n * `prompt` goes into whichever session the window lands on. Ignored on public\n * pages, and by preview hosts.\n */\nexport function useAgentWindow(): AgentWindowController {\n return useRuntime().agentWindow\n}\n\n/** The API origin SDK calls resolve against (for building raw asset URLs). */\nexport function useApiBaseUrl(): string {\n return useRuntime().apiBaseUrl\n}\n\nconst NOOP_OVERLAY: OverlayController = {\n expand: () => {\n console.warn(\n '@proteos/component-sdk: useOverlayExpansion() has no effect — this runtime ' +\n 'does not support overlay promotion (older shell or non-promoting host).',\n )\n },\n collapse: () => {},\n isExpanded: () => false,\n}\n\n/**\n * Manual overlay promotion — lifts the whole component iframe over the rest of\n * the page so an overlay can escape the frame's box.\n *\n * You rarely need this. Every `@proteos/ui` overlay (Modal, Popover, Menu,\n * Select, Tooltip, …) portals to the iframe document's `<body>`, which the\n * runtime watches — those promote automatically, with no code. Reach for this\n * hook only when you portal somewhere else yourself, or want the frame lifted\n * *before* the overlay mounts. Reference-counted: pair every `expand()` with a\n * `collapse()`.\n */\nexport function useOverlayExpansion(): OverlayController {\n return useRuntime().overlay ?? NOOP_OVERLAY\n}\n\n/**\n * Open ANOTHER component in a modal the host renders above everything — the\n * cross-component flow (a picker, a wizard step, a confirm dialog shared by\n * several components). Resolves with whatever that component closed with, or\n * `undefined` if the user dismissed it.\n *\n * ```ts\n * const openModal = useComponentModal()\n * const picked = await openModal(\n * 'contact-picker',\n * { entity: 'contact' },\n * { title: 'Pick a contact', width: 'md', height: 480 },\n * )\n * ```\n *\n * Pass `height` whenever you know it — the modal opens at its final size\n * instead of growing after the component loads (see {@link OpenModalOptions}).\n * One modal per component at a time: a second call while the first is still\n * open resolves `undefined` straight away.\n *\n * String props are Liquid-rendered by the host against this component's page\n * scope, like the props of a page-placed component.\n *\n * For an overlay that is part of THIS component, just render a `<Modal>` from\n * `@proteos/ui` — it promotes automatically.\n */\nexport function useComponentModal(): (\n componentSlug: string,\n props?: Record<string, unknown>,\n options?: OpenModalOptions,\n) => Promise<unknown> {\n return useRuntime().openModal\n}\n\nexport interface ModalHost {\n /** True when this component is rendered as the content of a host modal. */\n isInModal: boolean\n /** Close the host modal, resolving the opener's promise with `result`.\n * No-op outside a modal. */\n close: (result?: unknown) => void\n}\n\n/**\n * For a component that is being shown INSIDE a host modal (opened by another\n * component's `useComponentModal()`): close it and hand a value back.\n * `isInModal` is false on ordinary page-hosted mounts, so one component can\n * serve both roles.\n */\nexport function useModalHost(): ModalHost {\n const requestId = useRuntime().modalRequestId\n const close = useCallback(\n (result?: unknown) => {\n if (requestId === undefined) return\n postModalResult(requestId, result)\n },\n [requestId],\n )\n return useMemo(() => ({ isInModal: requestId !== undefined, close }), [requestId, close])\n}\n\n/**\n * A getter for the current bearer token. Returns a function (not the token)\n * because the token is refreshed in place — read it at call time for a raw\n * `fetch`. Most components never need this; prefer `useSdk()`.\n */\nexport function useAuthToken(): () => string {\n return useRuntime().getToken\n}\n\nexport interface HostLocation {\n /** The host tab's pathname (same-origin read), or null if unavailable. */\n pathname: string | null\n /** The org id parsed from `/org/<orgId>/…`, or null. */\n orgId: string | null\n /** The `/org/<orgId>/app/<appId>` prefix, or null when not on an app page. */\n appBasePath: string | null\n}\n\nfunction readHostLocation(): HostLocation {\n const empty: HostLocation = { pathname: null, orgId: null, appBasePath: null }\n try {\n const parentWindow = window.parent\n if (!parentWindow || parentWindow === window) return empty\n const pathname = parentWindow.location.pathname\n const appMatch = pathname.match(/^\\/org\\/[^/]+\\/app\\/[^/]+/)\n const orgMatch = pathname.match(/^\\/org\\/([^/]+)\\//)\n return {\n pathname,\n orgId: orgMatch ? (orgMatch[1] ?? null) : null,\n appBasePath: appMatch ? appMatch[0] : null,\n }\n } catch {\n // Cross-origin (untrusted-component future) or detached — fall back to null.\n return empty\n }\n}\n\n/**\n * Read the host tab's location (same-origin) — the org id and `/org/.../app/...`\n * prefix needed to build navigation targets on platform pages. Read once on\n * mount (the iframe re-inits when the host route changes).\n */\nexport function useHostLocation(): HostLocation {\n const [loc] = useState(readHostLocation)\n return loc\n}\n\nexport interface FillViewportOptions {\n /** Gap left below the component (px). Default 24. */\n bottomMargin?: number\n /** Floor for the computed height (px). Default 320. */\n minHeight?: number\n}\n\n/**\n * Compute a \"fill the rest of the viewport\" height for full-bleed layouts\n * (boards, canvases). The runtime auto-sizes the iframe to its content, so a\n * component can't use its own `vh` units (circular). The iframe is same-origin,\n * so this measures the parent viewport minus the iframe's offset instead.\n * Returns `null` when it can't measure (fall back to natural height).\n */\nexport function useFillViewportHeight(options: FillViewportOptions = {}): number | null {\n const { bottomMargin = 24, minHeight = 320 } = options\n const [height, setHeight] = useState<number | null>(null)\n\n useEffect(() => {\n function compute(): void {\n try {\n const frame = window.frameElement\n const parentWindow = window.parent\n if (!frame || !parentWindow || parentWindow === window) return\n const available =\n parentWindow.innerHeight - frame.getBoundingClientRect().top - bottomMargin\n setHeight(Math.max(available, minHeight))\n } catch {\n setHeight(null) // cross-origin or detached — fall back to natural height\n }\n }\n compute()\n try {\n window.parent.addEventListener('resize', compute)\n return () => window.parent.removeEventListener('resize', compute)\n } catch {\n return\n }\n }, [bottomMargin, minHeight])\n\n return height\n}\n"]}
|
package/dist/protocol.cjs
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkHD5ADY7F_cjs = require('./chunk-HD5ADY7F.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Object.defineProperty(exports, "COMPONENT_OVERLAY_Z_INDEX", {
|
|
8
8
|
enumerable: true,
|
|
9
|
-
get: function () { return
|
|
9
|
+
get: function () { return chunkHD5ADY7F_cjs.COMPONENT_OVERLAY_Z_INDEX; }
|
|
10
10
|
});
|
|
11
11
|
Object.defineProperty(exports, "MESSAGE_SOURCE", {
|
|
12
12
|
enumerable: true,
|
|
13
|
-
get: function () { return
|
|
13
|
+
get: function () { return chunkHD5ADY7F_cjs.MESSAGE_SOURCE; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "isHostMessage", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkHD5ADY7F_cjs.isHostMessage; }
|
|
18
18
|
});
|
|
19
19
|
Object.defineProperty(exports, "isRuntimeMessage", {
|
|
20
20
|
enumerable: true,
|
|
21
|
-
get: function () { return
|
|
21
|
+
get: function () { return chunkHD5ADY7F_cjs.isRuntimeMessage; }
|
|
22
22
|
});
|
|
23
23
|
//# sourceMappingURL=protocol.cjs.map
|
|
24
24
|
//# sourceMappingURL=protocol.cjs.map
|
package/dist/protocol.d.cts
CHANGED
|
@@ -190,16 +190,36 @@ interface ModalResultMessage {
|
|
|
190
190
|
requestId: string;
|
|
191
191
|
result?: unknown;
|
|
192
192
|
}
|
|
193
|
+
/** Ask the host to open its agent session window (the floating assistant).
|
|
194
|
+
*
|
|
195
|
+
* - `sessionId` set: the window resumes THAT session (its agent is read from
|
|
196
|
+
* the session). A `prompt` is sent into it.
|
|
197
|
+
* - no `sessionId`: a fresh session with `agentKey` (or the agent currently
|
|
198
|
+
* selected in the window); a `prompt` is sent as its first message. With
|
|
199
|
+
* neither field the window just opens as it is.
|
|
200
|
+
*
|
|
201
|
+
* Ignored on public pages (there is no signed-in user to run an agent as). */
|
|
202
|
+
interface OpenAgentWindowMessage {
|
|
203
|
+
type: 'OPEN_AGENT_WINDOW';
|
|
204
|
+
sessionId?: string;
|
|
205
|
+
agentKey?: string;
|
|
206
|
+
prompt?: string;
|
|
207
|
+
}
|
|
208
|
+
/** Ask the host to minimize its agent session window. The active session and
|
|
209
|
+
* its stream stay alive — reopening resumes it. */
|
|
210
|
+
interface CloseAgentWindowMessage {
|
|
211
|
+
type: 'CLOSE_AGENT_WINDOW';
|
|
212
|
+
}
|
|
193
213
|
interface ErrorMessage {
|
|
194
214
|
type: 'ERROR';
|
|
195
215
|
message: string;
|
|
196
216
|
stack?: string;
|
|
197
217
|
}
|
|
198
|
-
type RuntimeMessage = ReadyMessage | ResizeMessage | NavigateMessage | OpenPeekMessage | OpenCallMessage | OverlayStateMessage | OpenModalMessage | ModalResultMessage | EscapePressedMessage | ErrorMessage;
|
|
218
|
+
type RuntimeMessage = ReadyMessage | ResizeMessage | NavigateMessage | OpenPeekMessage | OpenCallMessage | OverlayStateMessage | OpenModalMessage | ModalResultMessage | EscapePressedMessage | OpenAgentWindowMessage | CloseAgentWindowMessage | ErrorMessage;
|
|
199
219
|
type Envelope<T> = T & {
|
|
200
220
|
source: typeof MESSAGE_SOURCE;
|
|
201
221
|
};
|
|
202
222
|
declare function isHostMessage(data: unknown): data is Envelope<HostMessage>;
|
|
203
223
|
declare function isRuntimeMessage(data: unknown): data is Envelope<RuntimeMessage>;
|
|
204
224
|
|
|
205
|
-
export { type AuthRefreshMessage, COMPONENT_OVERLAY_Z_INDEX, type DataInvalidateMessage, type Envelope, type ErrorMessage, type EscapePressedMessage, type HostMessage, type InitMessage, MESSAGE_SOURCE, type ModalClosedMessage, type ModalResultMessage, type ModalWidth, type NavigateMessage, type OpenCallMessage, type OpenModalMessage, type OpenPeekMessage, type OverlayRect, type OverlayRectMessage, type OverlayStateMessage, type PageContext, type ReadyMessage, type ResizeMessage, type RuntimeMessage, isHostMessage, isRuntimeMessage };
|
|
225
|
+
export { type AuthRefreshMessage, COMPONENT_OVERLAY_Z_INDEX, type CloseAgentWindowMessage, type DataInvalidateMessage, type Envelope, type ErrorMessage, type EscapePressedMessage, type HostMessage, type InitMessage, MESSAGE_SOURCE, type ModalClosedMessage, type ModalResultMessage, type ModalWidth, type NavigateMessage, type OpenAgentWindowMessage, type OpenCallMessage, type OpenModalMessage, type OpenPeekMessage, type OverlayRect, type OverlayRectMessage, type OverlayStateMessage, type PageContext, type ReadyMessage, type ResizeMessage, type RuntimeMessage, isHostMessage, isRuntimeMessage };
|
package/dist/protocol.d.ts
CHANGED
|
@@ -190,16 +190,36 @@ interface ModalResultMessage {
|
|
|
190
190
|
requestId: string;
|
|
191
191
|
result?: unknown;
|
|
192
192
|
}
|
|
193
|
+
/** Ask the host to open its agent session window (the floating assistant).
|
|
194
|
+
*
|
|
195
|
+
* - `sessionId` set: the window resumes THAT session (its agent is read from
|
|
196
|
+
* the session). A `prompt` is sent into it.
|
|
197
|
+
* - no `sessionId`: a fresh session with `agentKey` (or the agent currently
|
|
198
|
+
* selected in the window); a `prompt` is sent as its first message. With
|
|
199
|
+
* neither field the window just opens as it is.
|
|
200
|
+
*
|
|
201
|
+
* Ignored on public pages (there is no signed-in user to run an agent as). */
|
|
202
|
+
interface OpenAgentWindowMessage {
|
|
203
|
+
type: 'OPEN_AGENT_WINDOW';
|
|
204
|
+
sessionId?: string;
|
|
205
|
+
agentKey?: string;
|
|
206
|
+
prompt?: string;
|
|
207
|
+
}
|
|
208
|
+
/** Ask the host to minimize its agent session window. The active session and
|
|
209
|
+
* its stream stay alive — reopening resumes it. */
|
|
210
|
+
interface CloseAgentWindowMessage {
|
|
211
|
+
type: 'CLOSE_AGENT_WINDOW';
|
|
212
|
+
}
|
|
193
213
|
interface ErrorMessage {
|
|
194
214
|
type: 'ERROR';
|
|
195
215
|
message: string;
|
|
196
216
|
stack?: string;
|
|
197
217
|
}
|
|
198
|
-
type RuntimeMessage = ReadyMessage | ResizeMessage | NavigateMessage | OpenPeekMessage | OpenCallMessage | OverlayStateMessage | OpenModalMessage | ModalResultMessage | EscapePressedMessage | ErrorMessage;
|
|
218
|
+
type RuntimeMessage = ReadyMessage | ResizeMessage | NavigateMessage | OpenPeekMessage | OpenCallMessage | OverlayStateMessage | OpenModalMessage | ModalResultMessage | EscapePressedMessage | OpenAgentWindowMessage | CloseAgentWindowMessage | ErrorMessage;
|
|
199
219
|
type Envelope<T> = T & {
|
|
200
220
|
source: typeof MESSAGE_SOURCE;
|
|
201
221
|
};
|
|
202
222
|
declare function isHostMessage(data: unknown): data is Envelope<HostMessage>;
|
|
203
223
|
declare function isRuntimeMessage(data: unknown): data is Envelope<RuntimeMessage>;
|
|
204
224
|
|
|
205
|
-
export { type AuthRefreshMessage, COMPONENT_OVERLAY_Z_INDEX, type DataInvalidateMessage, type Envelope, type ErrorMessage, type EscapePressedMessage, type HostMessage, type InitMessage, MESSAGE_SOURCE, type ModalClosedMessage, type ModalResultMessage, type ModalWidth, type NavigateMessage, type OpenCallMessage, type OpenModalMessage, type OpenPeekMessage, type OverlayRect, type OverlayRectMessage, type OverlayStateMessage, type PageContext, type ReadyMessage, type ResizeMessage, type RuntimeMessage, isHostMessage, isRuntimeMessage };
|
|
225
|
+
export { type AuthRefreshMessage, COMPONENT_OVERLAY_Z_INDEX, type CloseAgentWindowMessage, type DataInvalidateMessage, type Envelope, type ErrorMessage, type EscapePressedMessage, type HostMessage, type InitMessage, MESSAGE_SOURCE, type ModalClosedMessage, type ModalResultMessage, type ModalWidth, type NavigateMessage, type OpenAgentWindowMessage, type OpenCallMessage, type OpenModalMessage, type OpenPeekMessage, type OverlayRect, type OverlayRectMessage, type OverlayStateMessage, type PageContext, type ReadyMessage, type ResizeMessage, type RuntimeMessage, isHostMessage, isRuntimeMessage };
|
package/dist/protocol.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { COMPONENT_OVERLAY_Z_INDEX, MESSAGE_SOURCE, isHostMessage, isRuntimeMessage } from './chunk-
|
|
1
|
+
export { COMPONENT_OVERLAY_Z_INDEX, MESSAGE_SOURCE, isHostMessage, isRuntimeMessage } from './chunk-NOCSMXCW.js';
|
|
2
2
|
//# sourceMappingURL=protocol.js.map
|
|
3
3
|
//# sourceMappingURL=protocol.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteos/component-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.61.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Typed runtime hooks for authoring Proteos custom components — the SDK you import inside a component bundle.",
|
|
6
6
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"src"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@proteos/sdk": "^0.
|
|
45
|
+
"@proteos/sdk": "^0.61.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@tanstack/react-query": ">=5.0.0",
|
package/src/context.tsx
CHANGED
|
@@ -11,12 +11,14 @@ import type {
|
|
|
11
11
|
import { createContext, type ReactNode, useMemo } from 'react'
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
+
type CloseAgentWindowMessage,
|
|
14
15
|
type Envelope,
|
|
15
16
|
isHostMessage,
|
|
16
17
|
MESSAGE_SOURCE,
|
|
17
18
|
type ModalResultMessage,
|
|
18
19
|
type ModalWidth,
|
|
19
20
|
type NavigateMessage,
|
|
21
|
+
type OpenAgentWindowMessage,
|
|
20
22
|
type OpenCallMessage,
|
|
21
23
|
type OpenModalMessage,
|
|
22
24
|
type OpenPeekMessage,
|
|
@@ -45,6 +47,29 @@ export interface OpenModalOptions {
|
|
|
45
47
|
height?: number
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
/** What {@link AgentWindowController.open} opens the host's agent window to. */
|
|
51
|
+
export interface OpenAgentWindowOptions {
|
|
52
|
+
/** Resume this session (its agent comes with it). */
|
|
53
|
+
sessionId?: string
|
|
54
|
+
/** Start a fresh session with this agent. Ignored when `sessionId` is set,
|
|
55
|
+
* and when the host surface pins its agent. Without it (and without a
|
|
56
|
+
* session) the window's currently selected agent is used. */
|
|
57
|
+
agentKey?: string
|
|
58
|
+
/** A message to send — into the resumed session, or as the first message
|
|
59
|
+
* of the fresh one. */
|
|
60
|
+
prompt?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The host's agent session window (the floating assistant), driven from a
|
|
65
|
+
* component. `open()` posts OPEN_AGENT_WINDOW, `close()` posts
|
|
66
|
+
* CLOSE_AGENT_WINDOW — closing minimizes; the session and its stream live on.
|
|
67
|
+
*/
|
|
68
|
+
export interface AgentWindowController {
|
|
69
|
+
open: (options?: OpenAgentWindowOptions) => void
|
|
70
|
+
close: () => void
|
|
71
|
+
}
|
|
72
|
+
|
|
48
73
|
/**
|
|
49
74
|
* Manual control over iframe overlay promotion. The runtime already detects
|
|
50
75
|
* overlays automatically (anything portaled to the iframe's `document.body` —
|
|
@@ -130,6 +155,9 @@ export interface RuntimeContextValue extends RuntimeValue {
|
|
|
130
155
|
props?: Record<string, unknown>,
|
|
131
156
|
options?: OpenModalOptions,
|
|
132
157
|
) => Promise<unknown>
|
|
158
|
+
/** Open / minimize the host's agent session window (posts OPEN_AGENT_WINDOW
|
|
159
|
+
* / CLOSE_AGENT_WINDOW). */
|
|
160
|
+
agentWindow: AgentWindowController
|
|
133
161
|
}
|
|
134
162
|
|
|
135
163
|
export const RuntimeContext = createContext<RuntimeContextValue | null>(null)
|
|
@@ -158,6 +186,29 @@ function openCall(conversationBriefId: string): void {
|
|
|
158
186
|
window.parent?.postMessage(envelope, '*')
|
|
159
187
|
}
|
|
160
188
|
|
|
189
|
+
/** Post OPEN_AGENT_WINDOW — only the fields that were given ride the wire. */
|
|
190
|
+
function openAgentWindow(options?: OpenAgentWindowOptions): void {
|
|
191
|
+
const envelope: Envelope<OpenAgentWindowMessage> = {
|
|
192
|
+
source: MESSAGE_SOURCE,
|
|
193
|
+
type: 'OPEN_AGENT_WINDOW',
|
|
194
|
+
...(options?.sessionId !== undefined ? { sessionId: options.sessionId } : {}),
|
|
195
|
+
...(options?.agentKey !== undefined ? { agentKey: options.agentKey } : {}),
|
|
196
|
+
...(options?.prompt !== undefined ? { prompt: options.prompt } : {}),
|
|
197
|
+
}
|
|
198
|
+
window.parent?.postMessage(envelope, '*')
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Post CLOSE_AGENT_WINDOW — minimize the host's agent window. */
|
|
202
|
+
function closeAgentWindow(): void {
|
|
203
|
+
const envelope: Envelope<CloseAgentWindowMessage> = {
|
|
204
|
+
source: MESSAGE_SOURCE,
|
|
205
|
+
type: 'CLOSE_AGENT_WINDOW',
|
|
206
|
+
}
|
|
207
|
+
window.parent?.postMessage(envelope, '*')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const agentWindow: AgentWindowController = { open: openAgentWindow, close: closeAgentWindow }
|
|
211
|
+
|
|
161
212
|
/** Navigate full-page (default) or into a peek panel (`{ peek: true }`). */
|
|
162
213
|
function navigate(to: string, options?: NavigateOptions): void {
|
|
163
214
|
if (options?.peek) postOpenPeek(to)
|
|
@@ -278,7 +329,7 @@ export function RuntimeProvider({ value, children }: { value: RuntimeValue; chil
|
|
|
278
329
|
// props and pageCtx are all set once), so memoizing on its identity recreates
|
|
279
330
|
// the context only when the runtime genuinely re-inits.
|
|
280
331
|
const ctx = useMemo<RuntimeContextValue>(
|
|
281
|
-
() => ({ ...value, navigate, openCall, openModal }),
|
|
332
|
+
() => ({ ...value, navigate, openCall, openModal, agentWindow }),
|
|
282
333
|
[value],
|
|
283
334
|
)
|
|
284
335
|
return <RuntimeContext.Provider value={ctx}>{children}</RuntimeContext.Provider>
|
package/src/hooks.test.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
|
4
4
|
|
|
5
5
|
import { RuntimeProvider, type RuntimeValue } from './context'
|
|
6
6
|
import {
|
|
7
|
+
useAgentWindow,
|
|
7
8
|
useApiBaseUrl,
|
|
8
9
|
useComponentModal,
|
|
9
10
|
useModalHost,
|
|
@@ -75,6 +76,40 @@ describe('runtime hooks', () => {
|
|
|
75
76
|
)
|
|
76
77
|
})
|
|
77
78
|
|
|
79
|
+
it('useAgentWindow posts OPEN_AGENT_WINDOW with only the fields given, and CLOSE_AGENT_WINDOW', () => {
|
|
80
|
+
const post = vi.spyOn(window.parent, 'postMessage').mockImplementation(() => {})
|
|
81
|
+
const { result } = renderHook(() => useAgentWindow(), { wrapper: wrapper(makeValue()) })
|
|
82
|
+
|
|
83
|
+
result.current.open()
|
|
84
|
+
expect(post).toHaveBeenLastCalledWith(
|
|
85
|
+
{ source: MESSAGE_SOURCE, type: 'OPEN_AGENT_WINDOW' },
|
|
86
|
+
'*',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
result.current.open({ sessionId: 'sess_1', prompt: 'Summarize this' })
|
|
90
|
+
expect(post).toHaveBeenLastCalledWith(
|
|
91
|
+
{
|
|
92
|
+
source: MESSAGE_SOURCE,
|
|
93
|
+
type: 'OPEN_AGENT_WINDOW',
|
|
94
|
+
sessionId: 'sess_1',
|
|
95
|
+
prompt: 'Summarize this',
|
|
96
|
+
},
|
|
97
|
+
'*',
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
result.current.open({ agentKey: 'sales-bot' })
|
|
101
|
+
expect(post).toHaveBeenLastCalledWith(
|
|
102
|
+
{ source: MESSAGE_SOURCE, type: 'OPEN_AGENT_WINDOW', agentKey: 'sales-bot' },
|
|
103
|
+
'*',
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
result.current.close()
|
|
107
|
+
expect(post).toHaveBeenLastCalledWith(
|
|
108
|
+
{ source: MESSAGE_SOURCE, type: 'CLOSE_AGENT_WINDOW' },
|
|
109
|
+
'*',
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
|
|
78
113
|
it('useOverlayExpansion drives the shell controller', () => {
|
|
79
114
|
const overlay = { expand: vi.fn(), collapse: vi.fn(), isExpanded: () => true }
|
|
80
115
|
const { result } = renderHook(() => useOverlayExpansion(), {
|
package/src/hooks.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
type AgentWindowController,
|
|
4
5
|
type ComponentSdk,
|
|
5
6
|
type NavigateOptions,
|
|
6
7
|
type OpenModalOptions,
|
|
@@ -70,6 +71,26 @@ export function useOpenCall(): (conversationBriefId: string) => void {
|
|
|
70
71
|
return useRuntime().openCall
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Open or minimize the HOST's agent session window (the floating assistant)
|
|
76
|
+
* from a component — the iframe cannot render it itself.
|
|
77
|
+
*
|
|
78
|
+
* ```ts
|
|
79
|
+
* const agentWindow = useAgentWindow()
|
|
80
|
+
* agentWindow.open() // just open it
|
|
81
|
+
* agentWindow.open({ sessionId }) // resume a session
|
|
82
|
+
* agentWindow.open({ agentKey: 'sales-bot', prompt }) // fresh session, prompt sent
|
|
83
|
+
* agentWindow.close() // minimize (session lives on)
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* `sessionId` wins over `agentKey` (the session already has an agent); a
|
|
87
|
+
* `prompt` goes into whichever session the window lands on. Ignored on public
|
|
88
|
+
* pages, and by preview hosts.
|
|
89
|
+
*/
|
|
90
|
+
export function useAgentWindow(): AgentWindowController {
|
|
91
|
+
return useRuntime().agentWindow
|
|
92
|
+
}
|
|
93
|
+
|
|
73
94
|
/** The API origin SDK calls resolve against (for building raw asset URLs). */
|
|
74
95
|
export function useApiBaseUrl(): string {
|
|
75
96
|
return useRuntime().apiBaseUrl
|
package/src/index.ts
CHANGED
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
import './global'
|
|
9
9
|
|
|
10
10
|
export type {
|
|
11
|
+
AgentWindowController,
|
|
11
12
|
ComponentSdk,
|
|
12
13
|
NavigateOptions,
|
|
14
|
+
OpenAgentWindowOptions,
|
|
13
15
|
OpenModalOptions,
|
|
14
16
|
OverlayController,
|
|
15
17
|
RuntimeContextValue,
|
|
@@ -18,6 +20,7 @@ export type {
|
|
|
18
20
|
export { RuntimeProvider } from './context'
|
|
19
21
|
export type { FillViewportOptions, HostLocation, ModalHost } from './hooks'
|
|
20
22
|
export {
|
|
23
|
+
useAgentWindow,
|
|
21
24
|
useApiBaseUrl,
|
|
22
25
|
useAuthToken,
|
|
23
26
|
useComponentModal,
|
|
@@ -34,6 +37,7 @@ export {
|
|
|
34
37
|
} from './hooks'
|
|
35
38
|
export {
|
|
36
39
|
type AuthRefreshMessage,
|
|
40
|
+
type CloseAgentWindowMessage,
|
|
37
41
|
COMPONENT_OVERLAY_Z_INDEX,
|
|
38
42
|
type DataInvalidateMessage,
|
|
39
43
|
type Envelope,
|
|
@@ -48,6 +52,7 @@ export {
|
|
|
48
52
|
type ModalResultMessage,
|
|
49
53
|
type ModalWidth,
|
|
50
54
|
type NavigateMessage,
|
|
55
|
+
type OpenAgentWindowMessage,
|
|
51
56
|
type OpenCallMessage,
|
|
52
57
|
type OpenModalMessage,
|
|
53
58
|
type OpenPeekMessage,
|
package/src/protocol.ts
CHANGED
|
@@ -221,6 +221,28 @@ export interface ModalResultMessage {
|
|
|
221
221
|
result?: unknown
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
/** Ask the host to open its agent session window (the floating assistant).
|
|
225
|
+
*
|
|
226
|
+
* - `sessionId` set: the window resumes THAT session (its agent is read from
|
|
227
|
+
* the session). A `prompt` is sent into it.
|
|
228
|
+
* - no `sessionId`: a fresh session with `agentKey` (or the agent currently
|
|
229
|
+
* selected in the window); a `prompt` is sent as its first message. With
|
|
230
|
+
* neither field the window just opens as it is.
|
|
231
|
+
*
|
|
232
|
+
* Ignored on public pages (there is no signed-in user to run an agent as). */
|
|
233
|
+
export interface OpenAgentWindowMessage {
|
|
234
|
+
type: 'OPEN_AGENT_WINDOW'
|
|
235
|
+
sessionId?: string
|
|
236
|
+
agentKey?: string
|
|
237
|
+
prompt?: string
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Ask the host to minimize its agent session window. The active session and
|
|
241
|
+
* its stream stay alive — reopening resumes it. */
|
|
242
|
+
export interface CloseAgentWindowMessage {
|
|
243
|
+
type: 'CLOSE_AGENT_WINDOW'
|
|
244
|
+
}
|
|
245
|
+
|
|
224
246
|
export interface ErrorMessage {
|
|
225
247
|
type: 'ERROR'
|
|
226
248
|
message: string
|
|
@@ -237,6 +259,8 @@ export type RuntimeMessage =
|
|
|
237
259
|
| OpenModalMessage
|
|
238
260
|
| ModalResultMessage
|
|
239
261
|
| EscapePressedMessage
|
|
262
|
+
| OpenAgentWindowMessage
|
|
263
|
+
| CloseAgentWindowMessage
|
|
240
264
|
| ErrorMessage
|
|
241
265
|
|
|
242
266
|
// Wire envelope: the message plus the source discriminator.
|