@proteos/component-sdk 0.61.0 → 0.75.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-NOCSMXCW.js → chunk-IYXDPNMJ.js} +2 -2
- package/dist/chunk-IYXDPNMJ.js.map +1 -0
- package/dist/{chunk-HD5ADY7F.cjs → chunk-Q7PTW7DD.cjs} +2 -2
- package/dist/chunk-Q7PTW7DD.cjs.map +1 -0
- package/dist/index.cjs +59 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -7
- package/dist/index.d.ts +51 -7
- package/dist/index.js +49 -16
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs +5 -5
- package/dist/protocol.d.cts +161 -3
- package/dist/protocol.d.ts +161 -3
- package/dist/protocol.js +1 -1
- package/package.json +2 -2
- package/src/context.tsx +65 -16
- package/src/hooks.test.tsx +81 -3
- package/src/hooks.ts +48 -4
- package/src/index.ts +8 -0
- package/src/protocol.ts +154 -0
- package/dist/chunk-HD5ADY7F.cjs.map +0 -1
- package/dist/chunk-NOCSMXCW.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { AccountClient, AgentClient, ConversationClient, DataClient, FunctionsClient, KnowledgeClient, MetaClient, StorageClient } from '@proteos/sdk';
|
|
4
|
-
import { ModalWidth, PageContext } 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';
|
|
3
|
+
import { AccountClient, AgentClient, ConversationClient, DataClient, FunctionsClient, KnowledgeClient, MetaClient, StorageClient, PageAction } from '@proteos/sdk';
|
|
4
|
+
import { ModalWidth, PageContext, PageActionContext, PageActionOutcome } 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, PageActionRecord, PageActionSettledMessage, PageMode, ReadyMessage, ResizeMessage, RunPageActionMessage, RuntimeMessage, TriggerPhase, isHostMessage, isRuntimeMessage } from './protocol.js';
|
|
6
6
|
|
|
7
7
|
/** Options for {@link RuntimeContextValue.navigate}. */
|
|
8
8
|
interface NavigateOptions {
|
|
@@ -35,6 +35,10 @@ interface OpenAgentWindowOptions {
|
|
|
35
35
|
/** A message to send — into the resumed session, or as the first message
|
|
36
36
|
* of the fresh one. */
|
|
37
37
|
prompt?: string;
|
|
38
|
+
/** Context handed to the model alongside `prompt` (what the component knows:
|
|
39
|
+
* the record on screen, a selection …). Read by the model, never shown in
|
|
40
|
+
* the chat; sent only together with a `prompt`. Not for secrets. */
|
|
41
|
+
context?: string;
|
|
38
42
|
}
|
|
39
43
|
/**
|
|
40
44
|
* The host's agent session window (the floating assistant), driven from a
|
|
@@ -126,6 +130,9 @@ interface RuntimeContextValue extends RuntimeValue {
|
|
|
126
130
|
/** Open / minimize the host's agent session window (posts OPEN_AGENT_WINDOW
|
|
127
131
|
* / CLOSE_AGENT_WINDOW). */
|
|
128
132
|
agentWindow: AgentWindowController;
|
|
133
|
+
/** Run a page action defined inline (posts RUN_PAGE_ACTION); resolves with
|
|
134
|
+
* how its flow ended. */
|
|
135
|
+
runPageAction: (action: PageAction, context?: PageActionContext) => Promise<PageActionOutcome>;
|
|
129
136
|
}
|
|
130
137
|
/**
|
|
131
138
|
* Provides the runtime value + derived helpers to a component subtree. Mounted
|
|
@@ -162,9 +169,13 @@ declare function useSdk(): ComponentSdk;
|
|
|
162
169
|
/** The component's Liquid-resolved props (a snapshot, not a live binding). */
|
|
163
170
|
declare function useProps<T extends Record<string, unknown> = Record<string, unknown>>(): T;
|
|
164
171
|
/**
|
|
165
|
-
* The full page context: host `record`, `entity`,
|
|
166
|
-
* `
|
|
167
|
-
*
|
|
172
|
+
* The full page context: host `record`, `entity`, URL `params`, the viewer as
|
|
173
|
+
* `current_user` (absent on public pages) and the page `mode`
|
|
174
|
+
* (`'view' | 'create' | 'edit'` — what the viewer is doing with the record).
|
|
175
|
+
* The `record` is a snapshot taken at mount (`undefined` on record-less
|
|
176
|
+
* platform pages) — fetch via `useSdk().data` for anything that must stay
|
|
177
|
+
* fresh. `mode` is a snapshot too: the host remounts the frame when the page
|
|
178
|
+
* switches between view and edit.
|
|
168
179
|
*/
|
|
169
180
|
declare function usePageContext(): PageContext;
|
|
170
181
|
/**
|
|
@@ -241,6 +252,39 @@ declare function useOverlayExpansion(): OverlayController;
|
|
|
241
252
|
* `@proteos/ui` — it promotes automatically.
|
|
242
253
|
*/
|
|
243
254
|
declare function useComponentModal(): (componentSlug: string, props?: Record<string, unknown>, options?: OpenModalOptions) => Promise<unknown>;
|
|
255
|
+
/**
|
|
256
|
+
* Run a page action from a component's own button — the very `PageAction` a
|
|
257
|
+
* page or list toolbar carries, defined inline. The HOST runs it exactly as a
|
|
258
|
+
* toolbar press: `action` / `workflow` open the invoke dialog, `intent`
|
|
259
|
+
* prepares a record proposal for review, `agent` opens the agent window,
|
|
260
|
+
* `link` opens a new tab. Resolves once that flow is over.
|
|
261
|
+
*
|
|
262
|
+
* ```ts
|
|
263
|
+
* const runPageAction = usePageAction()
|
|
264
|
+
* const outcome = await runPageAction(
|
|
265
|
+
* {
|
|
266
|
+
* kind: 'intent',
|
|
267
|
+
* intent: 'update_record',
|
|
268
|
+
* entity: 'deal',
|
|
269
|
+
* label: 'Update deal',
|
|
270
|
+
* icon: 'Sparkles',
|
|
271
|
+
* prompt: 'Apply what was agreed',
|
|
272
|
+
* context: '{{ notes }}',
|
|
273
|
+
* },
|
|
274
|
+
* { scope: { notes }, records: [{ entitySlug: 'deal', recordId: deal.id }] },
|
|
275
|
+
* )
|
|
276
|
+
* if (outcome.status === 'done' && outcome.kind === 'intent') refetch()
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
279
|
+
* The action's Liquid templates render against the page scope (`record`,
|
|
280
|
+
* `entity`, `params`, `mode`, `current_user`) with `context.scope` merged over
|
|
281
|
+
* it; `context.records` says what it acts on (default: the page record). See
|
|
282
|
+
* {@link PageActionContext} and {@link PageActionOutcome}. `visible_when`
|
|
283
|
+
* still applies — an action whose rule does not match resolves
|
|
284
|
+
* `unavailable: hidden` without opening anything. One page action at a time;
|
|
285
|
+
* public pages and previews resolve `unavailable: unsupported`.
|
|
286
|
+
*/
|
|
287
|
+
declare function usePageAction(): (action: PageAction, context?: PageActionContext) => Promise<PageActionOutcome>;
|
|
244
288
|
interface ModalHost {
|
|
245
289
|
/** True when this component is rendered as the content of a host modal. */
|
|
246
290
|
isInModal: boolean;
|
|
@@ -290,4 +334,4 @@ interface FillViewportOptions {
|
|
|
290
334
|
*/
|
|
291
335
|
declare function useFillViewportHeight(options?: FillViewportOptions): number | null;
|
|
292
336
|
|
|
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 };
|
|
337
|
+
export { type AgentWindowController, type ComponentSdk, type FillViewportOptions, type HostLocation, type ModalHost, ModalWidth, type NavigateOptions, type OpenAgentWindowOptions, type OpenModalOptions, type OverlayController, PageActionContext, PageActionOutcome, PageContext, type RuntimeContextValue, RuntimeProvider, type RuntimeValue, useAgentWindow, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageAction, 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-IYXDPNMJ.js';
|
|
2
|
+
export { COMPONENT_OVERLAY_Z_INDEX, MESSAGE_SOURCE, isHostMessage, isRuntimeMessage } from './chunk-IYXDPNMJ.js';
|
|
3
3
|
import { createContext, useMemo, useContext, useCallback, useState, useEffect } from 'react';
|
|
4
4
|
import { jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
@@ -26,7 +26,8 @@ function openAgentWindow(options) {
|
|
|
26
26
|
type: "OPEN_AGENT_WINDOW",
|
|
27
27
|
...options?.sessionId !== void 0 ? { sessionId: options.sessionId } : {},
|
|
28
28
|
...options?.agentKey !== void 0 ? { agentKey: options.agentKey } : {},
|
|
29
|
-
...options?.prompt !== void 0 ? { prompt: options.prompt } : {}
|
|
29
|
+
...options?.prompt !== void 0 ? { prompt: options.prompt } : {},
|
|
30
|
+
...options?.context !== void 0 ? { context: options.context } : {}
|
|
30
31
|
};
|
|
31
32
|
window.parent?.postMessage(envelope, "*");
|
|
32
33
|
}
|
|
@@ -43,17 +44,27 @@ function navigate(to, options) {
|
|
|
43
44
|
else postNavigate(to);
|
|
44
45
|
}
|
|
45
46
|
var pendingModals = /* @__PURE__ */ new Map();
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
var pendingPageActions = /* @__PURE__ */ new Map();
|
|
48
|
+
var replyListenerAttached = false;
|
|
49
|
+
function ensureReplyListener() {
|
|
50
|
+
if (replyListenerAttached) return;
|
|
51
|
+
replyListenerAttached = true;
|
|
50
52
|
window.addEventListener("message", (event) => {
|
|
51
53
|
const data = event.data;
|
|
52
|
-
if (!isHostMessage(data)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
if (!isHostMessage(data)) return;
|
|
55
|
+
if (data.type === "MODAL_CLOSED") {
|
|
56
|
+
const resolve = pendingModals.get(data.requestId);
|
|
57
|
+
if (!resolve) return;
|
|
58
|
+
pendingModals.delete(data.requestId);
|
|
59
|
+
resolve(data.result);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (data.type === "PAGE_ACTION_SETTLED") {
|
|
63
|
+
const resolve = pendingPageActions.get(data.requestId);
|
|
64
|
+
if (!resolve) return;
|
|
65
|
+
pendingPageActions.delete(data.requestId);
|
|
66
|
+
resolve(data.outcome);
|
|
67
|
+
}
|
|
57
68
|
});
|
|
58
69
|
}
|
|
59
70
|
var FOCUS_RESTORE_WINDOW_MS = 1e3;
|
|
@@ -79,10 +90,10 @@ function newRequestId() {
|
|
|
79
90
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
80
91
|
return crypto.randomUUID();
|
|
81
92
|
}
|
|
82
|
-
return `
|
|
93
|
+
return `request_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
83
94
|
}
|
|
84
95
|
function openModal(componentSlug, props, options) {
|
|
85
|
-
|
|
96
|
+
ensureReplyListener();
|
|
86
97
|
const requestId = newRequestId();
|
|
87
98
|
const envelope = {
|
|
88
99
|
source: MESSAGE_SOURCE,
|
|
@@ -103,6 +114,25 @@ function openModal(componentSlug, props, options) {
|
|
|
103
114
|
window.parent?.postMessage(envelope, "*");
|
|
104
115
|
});
|
|
105
116
|
}
|
|
117
|
+
function runPageAction(action, context) {
|
|
118
|
+
ensureReplyListener();
|
|
119
|
+
const requestId = newRequestId();
|
|
120
|
+
const envelope = {
|
|
121
|
+
source: MESSAGE_SOURCE,
|
|
122
|
+
type: "RUN_PAGE_ACTION",
|
|
123
|
+
requestId,
|
|
124
|
+
action,
|
|
125
|
+
...context !== void 0 ? { context } : {}
|
|
126
|
+
};
|
|
127
|
+
const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
pendingPageActions.set(requestId, (outcome) => {
|
|
130
|
+
restoreFocus(opener);
|
|
131
|
+
resolve(outcome);
|
|
132
|
+
});
|
|
133
|
+
window.parent?.postMessage(envelope, "*");
|
|
134
|
+
});
|
|
135
|
+
}
|
|
106
136
|
function postModalResult(requestId, result) {
|
|
107
137
|
const envelope = {
|
|
108
138
|
source: MESSAGE_SOURCE,
|
|
@@ -114,7 +144,7 @@ function postModalResult(requestId, result) {
|
|
|
114
144
|
}
|
|
115
145
|
function RuntimeProvider({ value, children }) {
|
|
116
146
|
const ctx = useMemo(
|
|
117
|
-
() => ({ ...value, navigate, openCall, openModal, agentWindow }),
|
|
147
|
+
() => ({ ...value, navigate, openCall, openModal, agentWindow, runPageAction }),
|
|
118
148
|
[value]
|
|
119
149
|
);
|
|
120
150
|
return /* @__PURE__ */ jsx(RuntimeContext.Provider, { value: ctx, children });
|
|
@@ -165,6 +195,9 @@ function useOverlayExpansion() {
|
|
|
165
195
|
function useComponentModal() {
|
|
166
196
|
return useRuntime().openModal;
|
|
167
197
|
}
|
|
198
|
+
function usePageAction() {
|
|
199
|
+
return useRuntime().runPageAction;
|
|
200
|
+
}
|
|
168
201
|
function useModalHost() {
|
|
169
202
|
const requestId = useRuntime().modalRequestId;
|
|
170
203
|
const close = useCallback(
|
|
@@ -226,6 +259,6 @@ function useFillViewportHeight(options = {}) {
|
|
|
226
259
|
return height;
|
|
227
260
|
}
|
|
228
261
|
|
|
229
|
-
export { RuntimeProvider, useAgentWindow, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageContext, useProps, useRuntime, useSdk };
|
|
262
|
+
export { RuntimeProvider, useAgentWindow, useApiBaseUrl, useAuthToken, useComponentModal, useFillViewportHeight, useHostLocation, useModalHost, useNavigate, useOpenCall, useOverlayExpansion, usePageAction, usePageContext, useProps, useRuntime, useSdk };
|
|
230
263
|
//# sourceMappingURL=index.js.map
|
|
231
264
|
//# 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 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"]}
|
|
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 PageAction,\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 PageActionContext,\n type PageActionOutcome,\n type PageContext,\n type RunPageActionMessage,\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 /** Context handed to the model alongside `prompt` (what the component knows:\n * the record on screen, a selection …). Read by the model, never shown in\n * the chat; sent only together with a `prompt`. Not for secrets. */\n context?: 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 /** Run a page action defined inline (posts RUN_PAGE_ACTION); resolves with\n * how its flow ended. */\n runPageAction: (action: PageAction, context?: PageActionContext) => Promise<PageActionOutcome>\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 ...(options?.context !== undefined ? { context: options.context } : {}),\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// ── Host requests (component modals, page actions) ──────────────────────────\n// One module-level listener + a pending map per request kind, rather than a\n// listener per call: the SDK is an import-mapped singleton, so this is\n// per-iframe state.\n\nconst pendingModals = new Map<string, (result: unknown) => void>()\nconst pendingPageActions = new Map<string, (outcome: PageActionOutcome) => void>()\nlet replyListenerAttached = false\n\nfunction ensureReplyListener(): void {\n if (replyListenerAttached) return\n replyListenerAttached = true\n window.addEventListener('message', (event: MessageEvent) => {\n const data: unknown = event.data\n if (!isHostMessage(data)) return\n // An unknown requestId is someone else's request (or a stale reply) — drop it.\n if (data.type === 'MODAL_CLOSED') {\n const resolve = pendingModals.get(data.requestId)\n if (!resolve) return\n pendingModals.delete(data.requestId)\n resolve(data.result)\n return\n }\n if (data.type === 'PAGE_ACTION_SETTLED') {\n const resolve = pendingPageActions.get(data.requestId)\n if (!resolve) return\n pendingPageActions.delete(data.requestId)\n resolve(data.outcome)\n }\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 `request_${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 ensureReplyListener()\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 RUN_PAGE_ACTION and resolve when the host answers PAGE_ACTION_SETTLED.\n * The host's dialogs take focus, so it goes back to the pressed control\n * afterwards — as for a modal. */\nfunction runPageAction(\n action: PageAction,\n context?: PageActionContext,\n): Promise<PageActionOutcome> {\n ensureReplyListener()\n const requestId = newRequestId()\n const envelope: Envelope<RunPageActionMessage> = {\n source: MESSAGE_SOURCE,\n type: 'RUN_PAGE_ACTION',\n requestId,\n action,\n ...(context !== undefined ? { context } : {}),\n }\n const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null\n return new Promise<PageActionOutcome>((resolve) => {\n pendingPageActions.set(requestId, (outcome) => {\n restoreFocus(opener)\n resolve(outcome)\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, runPageAction }),\n [value],\n )\n return <RuntimeContext.Provider value={ctx}>{children}</RuntimeContext.Provider>\n}\n","import type { PageAction } from '@proteos/sdk'\nimport { 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 { PageActionContext, PageActionOutcome, 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`, URL `params`, the viewer as\n * `current_user` (absent on public pages) and the page `mode`\n * (`'view' | 'create' | 'edit'` — what the viewer is doing with the record).\n * The `record` is a snapshot taken at mount (`undefined` on record-less\n * platform pages) — fetch via `useSdk().data` for anything that must stay\n * fresh. `mode` is a snapshot too: the host remounts the frame when the page\n * switches between view and edit.\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\n/**\n * Run a page action from a component's own button — the very `PageAction` a\n * page or list toolbar carries, defined inline. The HOST runs it exactly as a\n * toolbar press: `action` / `workflow` open the invoke dialog, `intent`\n * prepares a record proposal for review, `agent` opens the agent window,\n * `link` opens a new tab. Resolves once that flow is over.\n *\n * ```ts\n * const runPageAction = usePageAction()\n * const outcome = await runPageAction(\n * {\n * kind: 'intent',\n * intent: 'update_record',\n * entity: 'deal',\n * label: 'Update deal',\n * icon: 'Sparkles',\n * prompt: 'Apply what was agreed',\n * context: '{{ notes }}',\n * },\n * { scope: { notes }, records: [{ entitySlug: 'deal', recordId: deal.id }] },\n * )\n * if (outcome.status === 'done' && outcome.kind === 'intent') refetch()\n * ```\n *\n * The action's Liquid templates render against the page scope (`record`,\n * `entity`, `params`, `mode`, `current_user`) with `context.scope` merged over\n * it; `context.records` says what it acts on (default: the page record). See\n * {@link PageActionContext} and {@link PageActionOutcome}. `visible_when`\n * still applies — an action whose rule does not match resolves\n * `unavailable: hidden` without opening anything. One page action at a time;\n * public pages and previews resolve `unavailable: unsupported`.\n */\nexport function usePageAction(): (\n action: PageAction,\n context?: PageActionContext,\n) => Promise<PageActionOutcome> {\n return useRuntime().runPageAction\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 chunkQ7PTW7DD_cjs = require('./chunk-Q7PTW7DD.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 chunkQ7PTW7DD_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 chunkQ7PTW7DD_cjs.MESSAGE_SOURCE; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "isHostMessage", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkQ7PTW7DD_cjs.isHostMessage; }
|
|
18
18
|
});
|
|
19
19
|
Object.defineProperty(exports, "isRuntimeMessage", {
|
|
20
20
|
enumerable: true,
|
|
21
|
-
get: function () { return
|
|
21
|
+
get: function () { return chunkQ7PTW7DD_cjs.isRuntimeMessage; }
|
|
22
22
|
});
|
|
23
23
|
//# sourceMappingURL=protocol.cjs.map
|
|
24
24
|
//# sourceMappingURL=protocol.cjs.map
|
package/dist/protocol.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PageAction } from '@proteos/sdk';
|
|
2
|
+
|
|
1
3
|
declare const MESSAGE_SOURCE: "proteos-component-runtime";
|
|
2
4
|
/**
|
|
3
5
|
* z-index the component iframe is promoted to while one of its own overlays
|
|
@@ -28,6 +30,34 @@ interface OverlayRect {
|
|
|
28
30
|
width: number;
|
|
29
31
|
height: number;
|
|
30
32
|
}
|
|
33
|
+
/** What the viewer is doing with the record on the host page.
|
|
34
|
+
*
|
|
35
|
+
* - `view` — a read-only record page, or a page without a record
|
|
36
|
+
* (platform, kiosk, public, previews).
|
|
37
|
+
* - `create` — the form for a record that is not saved yet (`record` holds
|
|
38
|
+
* the seed values only, no `id`).
|
|
39
|
+
* - `edit` — the form over an existing record (`record` is the SAVED row;
|
|
40
|
+
* in-flight edits are not reflected).
|
|
41
|
+
*
|
|
42
|
+
* Inside an action's / workflow's invoke dialog (a trigger page) `mode` is
|
|
43
|
+
* `create` while the person fills the params and `view` once the run is on
|
|
44
|
+
* its way; `phase` says where the run is.
|
|
45
|
+
*
|
|
46
|
+
* A snapshot like `record`: the host remounts the frame when the page
|
|
47
|
+
* switches between view and edit, so a new INIT carries the new mode. Also
|
|
48
|
+
* the Liquid scope key `mode` when props are resolved. */
|
|
49
|
+
type PageMode = 'view' | 'create' | 'edit';
|
|
50
|
+
/** Where an invocation is in the invoke dialog's lifecycle — set only on a
|
|
51
|
+
* trigger page (the dialog of an action / workflow); absent everywhere else.
|
|
52
|
+
*
|
|
53
|
+
* - `input` — the form over the params (`record` = the seeded params).
|
|
54
|
+
* - `running` — the run is in flight (`record` = the submitted params;
|
|
55
|
+
* `params.execution` for a workflow).
|
|
56
|
+
* - `result` — the run ended (`record` = the params plus the action's
|
|
57
|
+
* returns; `params.result` / `params.execution`).
|
|
58
|
+
*
|
|
59
|
+
* Also the Liquid scope key `phase`. */
|
|
60
|
+
type TriggerPhase = 'input' | 'running' | 'result';
|
|
31
61
|
interface PageContext {
|
|
32
62
|
record?: Record<string, unknown>;
|
|
33
63
|
entity?: {
|
|
@@ -35,6 +65,8 @@ interface PageContext {
|
|
|
35
65
|
[key: string]: unknown;
|
|
36
66
|
};
|
|
37
67
|
params?: Record<string, unknown>;
|
|
68
|
+
mode: PageMode;
|
|
69
|
+
phase?: TriggerPhase;
|
|
38
70
|
/** The signed-in viewer (absent on public pages and offline previews). The
|
|
39
71
|
* host resolves `{{ current_user.* }}` and the bare `current_user` prop
|
|
40
72
|
* sentinel against it before posting props. */
|
|
@@ -55,6 +87,21 @@ interface InitMessage {
|
|
|
55
87
|
authToken: string;
|
|
56
88
|
/** API origin the bundle endpoint + SDK calls resolve against. */
|
|
57
89
|
apiBaseUrl: string;
|
|
90
|
+
/** Content hash (hex SHA-256) of the component's deployed bundle, from the
|
|
91
|
+
* component's `bundle_checksum`. The runtime appends it as `?v=` to the
|
|
92
|
+
* bundle request so the server can mark the response immutable — the
|
|
93
|
+
* browser then serves every later mount from its cache without a round
|
|
94
|
+
* trip. Absent when the host does not know it yet (metadata still
|
|
95
|
+
* loading, or a row deployed before checksum stamping); the bundle then
|
|
96
|
+
* revalidates as before. */
|
|
97
|
+
bundleVersion?: string;
|
|
98
|
+
/** Key under which the runtime shares its query cache with sibling and
|
|
99
|
+
* successor frames of the same component through the host (same
|
|
100
|
+
* origin): the host warms a new frame from the last state a frame with
|
|
101
|
+
* this key left behind. Covers the slug AND the resolved props, so two
|
|
102
|
+
* placements configured differently never share. Absent on hosts that
|
|
103
|
+
* do not keep a store (previews, public pages). */
|
|
104
|
+
cacheKey?: string;
|
|
58
105
|
/** Resolved props for the component (Liquid templating happens host-side). */
|
|
59
106
|
props: Record<string, unknown>;
|
|
60
107
|
pageCtx: PageContext;
|
|
@@ -96,7 +143,58 @@ interface ModalClosedMessage {
|
|
|
96
143
|
requestId: string;
|
|
97
144
|
result?: unknown;
|
|
98
145
|
}
|
|
99
|
-
|
|
146
|
+
/** How a RUN_PAGE_ACTION ended, by what the button did:
|
|
147
|
+
*
|
|
148
|
+
* - `done` — the run went through: an action's `returns` (null when it
|
|
149
|
+
* returned none), a workflow's accepted `executionId`, the
|
|
150
|
+
* record an intent created / updated.
|
|
151
|
+
* - `failed` — the invocation failed and the person closed the error.
|
|
152
|
+
* - `cancelled` — the person stepped out before anything ran or saved.
|
|
153
|
+
* - `handed_over` — an `agent` / `link` button: the chat or the tab is open;
|
|
154
|
+
* what happens there is not reported back.
|
|
155
|
+
* - `unavailable` — nothing was opened: `invalid` (the action does not parse),
|
|
156
|
+
* `hidden` (its `visible_when` does not match, an intent
|
|
157
|
+
* has no record to act on, a link no address to open),
|
|
158
|
+
* `busy` (another page action is still open), `unsupported`
|
|
159
|
+
* (a public page, a host without page actions — a preview —
|
|
160
|
+
* or an agent button where there is no agent window). */
|
|
161
|
+
type PageActionOutcome = {
|
|
162
|
+
status: 'done';
|
|
163
|
+
kind: 'action';
|
|
164
|
+
returns: Record<string, unknown> | null;
|
|
165
|
+
} | {
|
|
166
|
+
status: 'done';
|
|
167
|
+
kind: 'workflow';
|
|
168
|
+
executionId: string;
|
|
169
|
+
} | {
|
|
170
|
+
status: 'done';
|
|
171
|
+
kind: 'intent';
|
|
172
|
+
mode: 'create' | 'update';
|
|
173
|
+
entitySlug: string;
|
|
174
|
+
recordId: string;
|
|
175
|
+
title: string;
|
|
176
|
+
} | {
|
|
177
|
+
status: 'failed';
|
|
178
|
+
error: {
|
|
179
|
+
code: string;
|
|
180
|
+
message: string;
|
|
181
|
+
};
|
|
182
|
+
} | {
|
|
183
|
+
status: 'cancelled';
|
|
184
|
+
} | {
|
|
185
|
+
status: 'handed_over';
|
|
186
|
+
kind: 'agent' | 'link';
|
|
187
|
+
} | {
|
|
188
|
+
status: 'unavailable';
|
|
189
|
+
reason: 'invalid' | 'hidden' | 'busy' | 'unsupported';
|
|
190
|
+
};
|
|
191
|
+
/** Resolution of a RUN_PAGE_ACTION request, once the run's flow is over. */
|
|
192
|
+
interface PageActionSettledMessage {
|
|
193
|
+
type: 'PAGE_ACTION_SETTLED';
|
|
194
|
+
requestId: string;
|
|
195
|
+
outcome: PageActionOutcome;
|
|
196
|
+
}
|
|
197
|
+
type HostMessage = InitMessage | AuthRefreshMessage | DataInvalidateMessage | OverlayRectMessage | ModalClosedMessage | PageActionSettledMessage;
|
|
100
198
|
interface ReadyMessage {
|
|
101
199
|
type: 'READY';
|
|
102
200
|
}
|
|
@@ -204,22 +302,82 @@ interface OpenAgentWindowMessage {
|
|
|
204
302
|
sessionId?: string;
|
|
205
303
|
agentKey?: string;
|
|
206
304
|
prompt?: string;
|
|
305
|
+
/** Model-only context sent with `prompt`; never rendered. */
|
|
306
|
+
context?: string;
|
|
207
307
|
}
|
|
208
308
|
/** Ask the host to minimize its agent session window. The active session and
|
|
209
309
|
* its stream stay alive — reopening resumes it. */
|
|
210
310
|
interface CloseAgentWindowMessage {
|
|
211
311
|
type: 'CLOSE_AGENT_WINDOW';
|
|
212
312
|
}
|
|
313
|
+
/** A record write the component made through its sdk (`data.records.*`)
|
|
314
|
+
* succeeded. Posted by the runtime shell — not by component code — so the
|
|
315
|
+
* host can refresh what it shows of that entity and, when the component
|
|
316
|
+
* sits in a surface that reports edits (the Ava workspace), tell the agent.
|
|
317
|
+
* `patch` is what was submitted, `record` what the server returned. */
|
|
318
|
+
interface RecordWrittenMessage {
|
|
319
|
+
type: 'RECORD_WRITTEN';
|
|
320
|
+
entitySlug: string;
|
|
321
|
+
operation: 'create' | 'update' | 'delete' | 'batch_upsert';
|
|
322
|
+
recordId?: string;
|
|
323
|
+
patch?: Record<string, unknown>;
|
|
324
|
+
record?: Record<string, unknown>;
|
|
325
|
+
/** Rows in a batch upsert. */
|
|
326
|
+
count?: number;
|
|
327
|
+
}
|
|
328
|
+
/** A record a page action acts on, as the component names it. */
|
|
329
|
+
interface PageActionRecord {
|
|
330
|
+
entitySlug: string;
|
|
331
|
+
recordId: string;
|
|
332
|
+
/** Shown when the person picks among several records for an intent. */
|
|
333
|
+
title?: string;
|
|
334
|
+
/** The entity's display name, shown beside `title` in that pick. */
|
|
335
|
+
entityName?: string;
|
|
336
|
+
}
|
|
337
|
+
/** What the component knows that the button needs — layered over what the
|
|
338
|
+
* host already knows about the page. */
|
|
339
|
+
interface PageActionContext {
|
|
340
|
+
/** Merged over the page scope (`record`, `entity`, `params`, `mode`,
|
|
341
|
+
* `current_user`) the action's Liquid templates render against — `params`
|
|
342
|
+
* / `inputs` prefill, `prompt`, `context`, `entity`, `record`, `url`.
|
|
343
|
+
* Shallow: a key given here replaces the page's. `current_user` is always
|
|
344
|
+
* the host's. */
|
|
345
|
+
scope?: Record<string, unknown>;
|
|
346
|
+
/** The records the button acts on. Default: the page record (none on a page
|
|
347
|
+
* without one).
|
|
348
|
+
*
|
|
349
|
+
* - `action` with entity scope — the one record it is invoked on.
|
|
350
|
+
* - `action` with entity_batch scope — every record, in ONE invocation.
|
|
351
|
+
* - `intent` `update_record` without a `record` — the records of the
|
|
352
|
+
* intent's entity it may update: one is updated, several are offered
|
|
353
|
+
* to pick from. */
|
|
354
|
+
records?: PageActionRecord[];
|
|
355
|
+
}
|
|
356
|
+
/** Ask the host to run a page action — the same button a page or list toolbar
|
|
357
|
+
* carries, defined inline by the component. The host dispatches it exactly
|
|
358
|
+
* as a toolbar press (invoke dialog, record proposal review, agent window,
|
|
359
|
+
* new tab) and answers PAGE_ACTION_SETTLED with the same `requestId` once
|
|
360
|
+
* the flow is over.
|
|
361
|
+
*
|
|
362
|
+
* One page action at a time: while one is open (from any component) the
|
|
363
|
+
* host answers further requests `unavailable: busy`. */
|
|
364
|
+
interface RunPageActionMessage {
|
|
365
|
+
type: 'RUN_PAGE_ACTION';
|
|
366
|
+
/** Correlates the eventual PAGE_ACTION_SETTLED. Generated by the runtime. */
|
|
367
|
+
requestId: string;
|
|
368
|
+
action: PageAction;
|
|
369
|
+
context?: PageActionContext;
|
|
370
|
+
}
|
|
213
371
|
interface ErrorMessage {
|
|
214
372
|
type: 'ERROR';
|
|
215
373
|
message: string;
|
|
216
374
|
stack?: string;
|
|
217
375
|
}
|
|
218
|
-
type RuntimeMessage = ReadyMessage | ResizeMessage | NavigateMessage | OpenPeekMessage | OpenCallMessage | OverlayStateMessage | OpenModalMessage | ModalResultMessage | EscapePressedMessage | OpenAgentWindowMessage | CloseAgentWindowMessage | ErrorMessage;
|
|
376
|
+
type RuntimeMessage = ReadyMessage | ResizeMessage | NavigateMessage | OpenPeekMessage | OpenCallMessage | OverlayStateMessage | OpenModalMessage | ModalResultMessage | EscapePressedMessage | OpenAgentWindowMessage | CloseAgentWindowMessage | RecordWrittenMessage | RunPageActionMessage | ErrorMessage;
|
|
219
377
|
type Envelope<T> = T & {
|
|
220
378
|
source: typeof MESSAGE_SOURCE;
|
|
221
379
|
};
|
|
222
380
|
declare function isHostMessage(data: unknown): data is Envelope<HostMessage>;
|
|
223
381
|
declare function isRuntimeMessage(data: unknown): data is Envelope<RuntimeMessage>;
|
|
224
382
|
|
|
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 };
|
|
383
|
+
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 PageActionContext, type PageActionOutcome, type PageActionRecord, type PageActionSettledMessage, type PageContext, type PageMode, type ReadyMessage, type RecordWrittenMessage, type ResizeMessage, type RunPageActionMessage, type RuntimeMessage, type TriggerPhase, isHostMessage, isRuntimeMessage };
|