@matterfact/embed 0.2.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/LICENSE +190 -0
- package/README.md +18 -0
- package/dist/chunk-6N5OYQLF.js +300 -0
- package/dist/chunk-6N5OYQLF.js.map +1 -0
- package/dist/chunk-6VW4CCNF.js +3 -0
- package/dist/chunk-6VW4CCNF.js.map +7 -0
- package/dist/chunk-MWRVWYWX.js +2 -0
- package/dist/chunk-MWRVWYWX.js.map +7 -0
- package/dist/chunk-U4IRZU6E.js +2 -0
- package/dist/chunk-U4IRZU6E.js.map +7 -0
- package/dist/chunk-VMYCQB4O.js +289 -0
- package/dist/chunk-VMYCQB4O.js.map +1 -0
- package/dist/context-23EUTK33.js +3 -0
- package/dist/context-23EUTK33.js.map +1 -0
- package/dist/context-CWMQYYFI.js +22 -0
- package/dist/context-CWMQYYFI.js.map +1 -0
- package/dist/embed.js +2 -0
- package/dist/embed.js.map +7 -0
- package/dist/index.cjs +1110 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +437 -0
- package/dist/index.d.ts +437 -0
- package/dist/index.js +326 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +1157 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +35 -0
- package/dist/react.d.ts +35 -0
- package/dist/react.js +357 -0
- package/dist/react.js.map +1 -0
- package/dist/snapshot-KN2KJ2PM.js +444 -0
- package/dist/snapshot-KN2KJ2PM.js.map +1 -0
- package/dist/snapshot-Z7YUSB27.js +458 -0
- package/dist/snapshot-Z7YUSB27.js.map +1 -0
- package/package.json +56 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The matterfact embed bridge protocol.
|
|
3
|
+
*
|
|
4
|
+
* This is the contract between the HOST PAGE (a customer's site, running our
|
|
5
|
+
* loader) and the WIDGET (our chat, running in a cross-origin iframe on
|
|
6
|
+
* app.matterfact.com).
|
|
7
|
+
*
|
|
8
|
+
* Two things to understand before changing anything here:
|
|
9
|
+
*
|
|
10
|
+
* 1. THE IFRAME CANNOT SEE THE PAGE. It is cross-origin — that is the entire
|
|
11
|
+
* point of it. So every fact about the host page (its URL, its DOM, what the
|
|
12
|
+
* user just did) reaches the agent only because the LOADER, which runs as
|
|
13
|
+
* ordinary page JS and *can* see it, chose to send it. The loader is the eye;
|
|
14
|
+
* the widget is the mouth.
|
|
15
|
+
*
|
|
16
|
+
* 2. THIS CONTRACT OUTLIVES THE IFRAME. The next tier is an in-page React SDK
|
|
17
|
+
* with no iframe at all — and it will speak exactly this. If the message
|
|
18
|
+
* shapes are right, "deeper integration" is a transport swap (postMessage ->
|
|
19
|
+
* direct call), not a rewrite. So model the DOMAIN here, not the plumbing.
|
|
20
|
+
*/
|
|
21
|
+
declare const PROTOCOL_VERSION = 1;
|
|
22
|
+
/** Every message is namespaced so we never collide with the host page's own postMessage traffic. */
|
|
23
|
+
declare const CHANNEL = "mf-embed";
|
|
24
|
+
/**
|
|
25
|
+
* A handle on a real element in the host page.
|
|
26
|
+
*
|
|
27
|
+
* Format and semantics are lifted from Playwright's ARIA snapshot (`ref=e12`),
|
|
28
|
+
* for a specific reason: it is the element-handle syntax the frontier models have
|
|
29
|
+
* seen most, via playwright-mcp. We are not being original here on purpose.
|
|
30
|
+
*
|
|
31
|
+
* The ref is an in-page expando plus a snapshot-scoped Map — never an attribute,
|
|
32
|
+
* so it is invisible to the host page's CSS, its serialization, and its own
|
|
33
|
+
* mutation observers. It is resolved back to an element by the LOADER; the agent
|
|
34
|
+
* never touches the DOM, and never learns a selector.
|
|
35
|
+
*/
|
|
36
|
+
type ElementRef = string;
|
|
37
|
+
/** Ambient facts about the page. Cheap, sent on every navigation. */
|
|
38
|
+
interface PageContext {
|
|
39
|
+
url?: string;
|
|
40
|
+
/** A route PATTERN ('/orders/:id') beats a raw path — it tells the agent what KIND of page this is. */
|
|
41
|
+
route?: string;
|
|
42
|
+
path?: string;
|
|
43
|
+
title?: string;
|
|
44
|
+
locale?: string;
|
|
45
|
+
/** 1–2 sentences, written the way you'd brief a colleague. The highest-value field by far. */
|
|
46
|
+
description?: string;
|
|
47
|
+
/** Things the page is about. These also become @-mentionable in the composer, for free. */
|
|
48
|
+
entities?: PageEntity[];
|
|
49
|
+
/** Allowlisted only. Never a dump of app state. */
|
|
50
|
+
data?: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
interface PageEntity {
|
|
53
|
+
/** Host kinds are namespaced (`x:ticker`) so they can never collide with matterfact's own. */
|
|
54
|
+
kind: string;
|
|
55
|
+
id: string;
|
|
56
|
+
label: string;
|
|
57
|
+
/** Pre-rendered, agent-readable. Keep it short. */
|
|
58
|
+
text?: string;
|
|
59
|
+
data?: Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A snapshot of the page as the agent sees it.
|
|
63
|
+
*
|
|
64
|
+
* `yaml` is Playwright's ARIA-snapshot rendering — role, accessible name, state,
|
|
65
|
+
* and a ref on anything interactable:
|
|
66
|
+
*
|
|
67
|
+
* - heading "Orders" [level=1]
|
|
68
|
+
* - textbox "Search orders" [ref=e5]
|
|
69
|
+
* - button "Export CSV" [ref=e9]
|
|
70
|
+
*
|
|
71
|
+
* NOT raw HTML. A real page is tens of thousands of tokens of div soup, it gives
|
|
72
|
+
* the model no handle to act with, and it is a prompt-injection firehose. The
|
|
73
|
+
* a11y tree is a tenth the size and is the only signal that survives in-page —
|
|
74
|
+
* `getEventListeners()` is DevTools-only, so listener-sniffing (the browser-use
|
|
75
|
+
* approach) cannot see a single React handler on a real customer's app.
|
|
76
|
+
*/
|
|
77
|
+
interface DomSnapshot {
|
|
78
|
+
yaml: string;
|
|
79
|
+
/** Monotonic. A ref is only valid within the snapshot that minted it. */
|
|
80
|
+
seq: number;
|
|
81
|
+
/** True when the walk hit its node budget and stopped. The agent must be told it is not seeing everything. */
|
|
82
|
+
truncated: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Where the user's attention is RIGHT NOW.
|
|
86
|
+
*
|
|
87
|
+
* This is the difference between an assistant that can read your page and one that
|
|
88
|
+
* knows what you're doing. A full DOM snapshot answers "what is on this screen"; this
|
|
89
|
+
* answers "what are you looking at and working in" — which is what a colleague
|
|
90
|
+
* leaning over reads first. It is small, so it rides on every turn, where the full
|
|
91
|
+
* snapshot does not.
|
|
92
|
+
*
|
|
93
|
+
* Every field here is a LABEL or a REF, never a value. `selection` is the sharpest
|
|
94
|
+
* intent signal there is ("this is the thing I'm asking about"), and for exactly that
|
|
95
|
+
* reason it is also where a user might have highlighted their own email — so it is
|
|
96
|
+
* redacted and honours data-mf-private like everything else.
|
|
97
|
+
*/
|
|
98
|
+
interface FocusContext {
|
|
99
|
+
/** Text the user has selected. The single strongest "I'm asking about this" signal. Redacted. */
|
|
100
|
+
selection?: string;
|
|
101
|
+
/** The control the user is working IN — by label and ref, never its contents. */
|
|
102
|
+
focused?: {
|
|
103
|
+
ref?: ElementRef;
|
|
104
|
+
label: string;
|
|
105
|
+
role: string;
|
|
106
|
+
};
|
|
107
|
+
/** The refs currently in the viewport: the handful of things actually on screen, out of the whole tree. */
|
|
108
|
+
visibleRefs?: ElementRef[];
|
|
109
|
+
/** How far down the page they are, 0–1. A cheap proxy for "which part am I reading". */
|
|
110
|
+
scroll?: number;
|
|
111
|
+
}
|
|
112
|
+
/** Something the user did. Shaped after PostHog's autocapture — one readable line, not a replay frame. */
|
|
113
|
+
interface ActivityEvent {
|
|
114
|
+
seq: number;
|
|
115
|
+
ts: number;
|
|
116
|
+
type: 'nav' | 'click' | 'input' | 'submit' | 'selection' | 'custom';
|
|
117
|
+
/** Human-readable: 'clicked button "Export CSV"'. Semantic, not coordinates. */
|
|
118
|
+
summary: string;
|
|
119
|
+
ref?: ElementRef;
|
|
120
|
+
data?: Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A tool the HOST page exposes to the agent.
|
|
124
|
+
*
|
|
125
|
+
* Deliberately shaped as a superset of WebMCP (`navigator.modelContext`, W3C
|
|
126
|
+
* WebML CG draft / Chrome origin trial). A customer who adopts WebMCP should be
|
|
127
|
+
* able to hand us their existing tool declarations unchanged.
|
|
128
|
+
*/
|
|
129
|
+
interface HostTool {
|
|
130
|
+
name: string;
|
|
131
|
+
description: string;
|
|
132
|
+
/** JSON Schema. */
|
|
133
|
+
inputSchema: Record<string, unknown>;
|
|
134
|
+
/**
|
|
135
|
+
* A read-only tool may run without asking. Anything that MUTATES the page
|
|
136
|
+
* requires the user to confirm, every time — see the note on ToolCall.
|
|
137
|
+
*/
|
|
138
|
+
readOnly?: boolean;
|
|
139
|
+
}
|
|
140
|
+
type HostToWidget = {
|
|
141
|
+
type: 'host.ready';
|
|
142
|
+
protocol: number;
|
|
143
|
+
origin: string;
|
|
144
|
+
} | {
|
|
145
|
+
type: 'host.context';
|
|
146
|
+
context: PageContext;
|
|
147
|
+
} | {
|
|
148
|
+
type: 'host.focus';
|
|
149
|
+
focus: FocusContext;
|
|
150
|
+
} | {
|
|
151
|
+
type: 'host.snapshot';
|
|
152
|
+
snapshot: DomSnapshot;
|
|
153
|
+
} | {
|
|
154
|
+
type: 'host.region';
|
|
155
|
+
ref: ElementRef;
|
|
156
|
+
yaml: string;
|
|
157
|
+
} | {
|
|
158
|
+
type: 'host.activity';
|
|
159
|
+
events: ActivityEvent[];
|
|
160
|
+
} | {
|
|
161
|
+
type: 'host.tools';
|
|
162
|
+
tools: HostTool[];
|
|
163
|
+
} | {
|
|
164
|
+
type: 'host.toolResult';
|
|
165
|
+
callId: string;
|
|
166
|
+
ok: boolean;
|
|
167
|
+
result?: unknown;
|
|
168
|
+
error?: string;
|
|
169
|
+
} | {
|
|
170
|
+
type: 'host.auth';
|
|
171
|
+
token: string;
|
|
172
|
+
expiresAt: number;
|
|
173
|
+
} | {
|
|
174
|
+
type: 'host.theme';
|
|
175
|
+
mode: 'light' | 'dark';
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* The agent asking the host to do something.
|
|
179
|
+
*
|
|
180
|
+
* THE SECURITY MODEL, stated once: the page's content is UNTRUSTED. Text on the
|
|
181
|
+
* page can try to instruct the agent, and the moment the agent can act, that stops
|
|
182
|
+
* being a bad answer and becomes a bad ACTION. So:
|
|
183
|
+
*
|
|
184
|
+
* - v1 is READ-ONLY. `snapshot` and `activity` flow; no tool mutates the page.
|
|
185
|
+
* - A tool that is not `readOnly` must be confirmed by the USER, every call. Not
|
|
186
|
+
* "unless the host opts out" — every call. The trigger may have been written by
|
|
187
|
+
* the page.
|
|
188
|
+
* - The host is the enforcement point, not the widget. The widget can be lied to;
|
|
189
|
+
* the loader runs in the page and decides what actually happens.
|
|
190
|
+
*/
|
|
191
|
+
interface ToolCall {
|
|
192
|
+
callId: string;
|
|
193
|
+
name: string;
|
|
194
|
+
args: Record<string, unknown>;
|
|
195
|
+
}
|
|
196
|
+
type WidgetToHost = {
|
|
197
|
+
type: 'widget.ready';
|
|
198
|
+
protocol: number;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Ask for the FULL page snapshot. This is the agent PULLING detail, not us pushing
|
|
202
|
+
* it: the focus context rides on every turn, but the whole tree is fetched only
|
|
203
|
+
* when the agent decides it needs to look — cheaper, and it reads what's relevant
|
|
204
|
+
* to the question instead of drowning in the footer nav every turn.
|
|
205
|
+
*/
|
|
206
|
+
| {
|
|
207
|
+
type: 'widget.requestSnapshot';
|
|
208
|
+
}
|
|
209
|
+
/** Zoom in: the a11y sub-tree under one ref (a table, a form, a card the agent cares about). */
|
|
210
|
+
| {
|
|
211
|
+
type: 'widget.readRegion';
|
|
212
|
+
ref: ElementRef;
|
|
213
|
+
} | {
|
|
214
|
+
type: 'widget.callTool';
|
|
215
|
+
call: ToolCall;
|
|
216
|
+
}
|
|
217
|
+
/** Chat opened/closed — the loader resizes the iframe and starts/stops observing. */
|
|
218
|
+
| {
|
|
219
|
+
type: 'widget.setOpen';
|
|
220
|
+
open: boolean;
|
|
221
|
+
} | {
|
|
222
|
+
type: 'widget.resize';
|
|
223
|
+
height: number;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Maximize / restore. Expanded makes the iframe fill the viewport (with a small
|
|
227
|
+
* margin) so the composer's `/` picker, mention previews, and the transcript have
|
|
228
|
+
* room the corner popup can't give them. The loader owns the iframe size, so only
|
|
229
|
+
* it can do this — `widget.resize` (height-only) can't widen the popup.
|
|
230
|
+
*/
|
|
231
|
+
| {
|
|
232
|
+
type: 'widget.setExpanded';
|
|
233
|
+
expanded: boolean;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Launcher drag. The widget tracks the gesture; the host only moves the element.
|
|
237
|
+
*
|
|
238
|
+
* It has to be this way round, and the reason is easy to get wrong: a press that starts
|
|
239
|
+
* inside an iframe is implicitly captured by that iframe, so every pointermove and the
|
|
240
|
+
* pointerup belong to the widget's document until the button comes up. The host cannot
|
|
241
|
+
* see them at all — not even through an overlay of its own, because capture beats hit
|
|
242
|
+
* testing. So the host cannot take over a drag mid-gesture; it can only be told.
|
|
243
|
+
*
|
|
244
|
+
* `dx`/`dy` are deltas in SCREEN coordinates, which is the other half of the trick:
|
|
245
|
+
* client coordinates are measured against the iframe's own viewport, and the iframe is
|
|
246
|
+
* being moved under the cursor as the drag proceeds — so the pointer would appear to
|
|
247
|
+
* stop moving and the launcher would stall after one step. Screen coordinates are
|
|
248
|
+
* absolute and immune to that feedback loop.
|
|
249
|
+
*/
|
|
250
|
+
| {
|
|
251
|
+
type: 'widget.dragStart';
|
|
252
|
+
} | {
|
|
253
|
+
type: 'widget.dragMove';
|
|
254
|
+
dx: number;
|
|
255
|
+
dy: number;
|
|
256
|
+
} | {
|
|
257
|
+
type: 'widget.dragEnd';
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Grow/shrink the iframe to fit the launcher's context menu. The collapsed launcher is
|
|
261
|
+
* only ~64px tall, so a menu rendered inside it would be clipped by the iframe's own
|
|
262
|
+
* bounds — the host has to make room before it can be seen. `widget.resize` can't do
|
|
263
|
+
* this: it's height-only AND ignored while closed.
|
|
264
|
+
*/
|
|
265
|
+
| {
|
|
266
|
+
type: 'widget.setMenu';
|
|
267
|
+
open: boolean;
|
|
268
|
+
height: number;
|
|
269
|
+
}
|
|
270
|
+
/** Put the launcher back in the default corner and forget the dragged position. */
|
|
271
|
+
| {
|
|
272
|
+
type: 'widget.resetPos';
|
|
273
|
+
}
|
|
274
|
+
/** Get out of the way until the page reloads (a menu action, not a setting). */
|
|
275
|
+
| {
|
|
276
|
+
type: 'widget.hide';
|
|
277
|
+
}
|
|
278
|
+
/** The widget has no session; the host must run the hosted-login popup. */
|
|
279
|
+
| {
|
|
280
|
+
type: 'widget.needsAuth';
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* Everything on the wire is wrapped.
|
|
284
|
+
*
|
|
285
|
+
* `channel` keeps us out of the host page's own postMessage traffic (and out of
|
|
286
|
+
* every other widget's). The receiver ALSO checks `event.origin` against an
|
|
287
|
+
* allowlist and `event.source` against the expected window, on every single
|
|
288
|
+
* message — a namespace is not a security boundary, it is a politeness.
|
|
289
|
+
*/
|
|
290
|
+
interface Envelope<T> {
|
|
291
|
+
channel: typeof CHANNEL;
|
|
292
|
+
protocol: number;
|
|
293
|
+
/** Correlates a response to its request; absent for notifications. */
|
|
294
|
+
id?: string;
|
|
295
|
+
payload: T;
|
|
296
|
+
}
|
|
297
|
+
declare function envelope<T>(payload: T, id?: string): Envelope<T>;
|
|
298
|
+
declare function isEnvelope(data: unknown): data is Envelope<unknown>;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* The matterfact embed loader.
|
|
302
|
+
*
|
|
303
|
+
* <script src="https://cdn.matterfact.com/embed.js"
|
|
304
|
+
* data-key="pk_live_acme_..."
|
|
305
|
+
* defer></script>
|
|
306
|
+
*
|
|
307
|
+
* This file runs on EVERY page load of a customer's site, so it is deliberately
|
|
308
|
+
* tiny and deliberately dumb:
|
|
309
|
+
*
|
|
310
|
+
* - zero dependencies
|
|
311
|
+
* - it does NOT read the DOM. The snapshot/activity code is a separate chunk,
|
|
312
|
+
* fetched only when the user actually opens the chat. A customer should pay
|
|
313
|
+
* nothing for a widget nobody clicked.
|
|
314
|
+
* - it does NOT bundle React, or a CSS framework, or an RPC library.
|
|
315
|
+
*
|
|
316
|
+
* `iframe-resizer` is the thing you will be tempted to reach for here. It is
|
|
317
|
+
* GPL-3.0 and cannot ship in this file. The ResizeObserver bridge below is the
|
|
318
|
+
* fifteen lines it would have cost you anyway.
|
|
319
|
+
*/
|
|
320
|
+
/** Returns a token this matterfact deployment's auth provider trusts (Firebase idToken,
|
|
321
|
+
* Entra token, …). May be async; return null when there's no signed-in user. */
|
|
322
|
+
type AuthTokenProvider = () => string | null | Promise<string | null>;
|
|
323
|
+
interface LoaderConfig {
|
|
324
|
+
publishableKey: string;
|
|
325
|
+
/** Origin of the widget. Overridable for dev/self-hosted; defaults to production. */
|
|
326
|
+
origin: string;
|
|
327
|
+
theme: 'light' | 'dark' | 'auto';
|
|
328
|
+
/** Optional label identifying THIS embedding, when one app embeds the agent in more
|
|
329
|
+
* than one place (e.g. "orders" vs "support"). Stamped on runs/threads so usage and
|
|
330
|
+
* history can be told apart per surface. Free-form; the app_id (from the session)
|
|
331
|
+
* is the trusted half. */
|
|
332
|
+
surface: string;
|
|
333
|
+
/**
|
|
334
|
+
* OPTIONAL host-auth passthrough, for TRUSTED first-party embeds (the admin portal,
|
|
335
|
+
* EMC). When set, the host page already holds a token this deployment's auth provider
|
|
336
|
+
* trusts (its Firebase idToken, an Entra token, …); the loader hands it to the widget
|
|
337
|
+
* on `widget.needsAuth` instead of opening a sign-in popup. The backend still verifies
|
|
338
|
+
* the token, so identity can't be forged — this only skips a redundant second sign-in.
|
|
339
|
+
* Called on demand (so tokens can refresh). Absent ⇒ the popup/inline sign-in flow.
|
|
340
|
+
* The `<script>` loader has no config here; it reads window.matterfact.getEmbedAuthToken
|
|
341
|
+
* instead (see provideAuth).
|
|
342
|
+
*/
|
|
343
|
+
authTokenProvider?: AuthTokenProvider;
|
|
344
|
+
/**
|
|
345
|
+
* Render INTO this element instead of floating in the corner — for a host that has its
|
|
346
|
+
* own side panel, tool drawer or tab and wants the agent to live there.
|
|
347
|
+
*
|
|
348
|
+
* This inverts who owns the chrome. In corner mode the widget owns everything: it draws
|
|
349
|
+
* its own launcher and it tells us how big to be (setOpen/setExpanded/resize). Inside
|
|
350
|
+
* someone else's panel, both are theirs — a widget that resized its host's drawer, or
|
|
351
|
+
* drew a second floating bubble inside it, is just broken. So here we fill the
|
|
352
|
+
* container, ignore the size messages, and the widget skips the launcher and renders
|
|
353
|
+
* the chat directly.
|
|
354
|
+
*
|
|
355
|
+
* The host also owns visibility: showing and hiding their own panel IS the open/close
|
|
356
|
+
* control, so there is nothing for us to add.
|
|
357
|
+
*/
|
|
358
|
+
container?: HTMLElement | null;
|
|
359
|
+
}
|
|
360
|
+
declare function readConfig(): LoaderConfig | null;
|
|
361
|
+
declare class EmbedHost {
|
|
362
|
+
private config;
|
|
363
|
+
private iframe;
|
|
364
|
+
private shadow;
|
|
365
|
+
/** Buffered until the widget says it's listening — postMessage before load is dropped silently. */
|
|
366
|
+
private queue;
|
|
367
|
+
private ready;
|
|
368
|
+
private open;
|
|
369
|
+
private expanded;
|
|
370
|
+
/** Null until the user drags; then it pins the corner offset and survives reloads. */
|
|
371
|
+
private pos;
|
|
372
|
+
/** Where the launcher sat when the current drag began; deltas are applied to this. */
|
|
373
|
+
private dragBase;
|
|
374
|
+
/** Loaded on first open. Holds everything that touches the customer's DOM. */
|
|
375
|
+
private context;
|
|
376
|
+
/** The host element; kept so `destroy()` can remove it (React lifecycle). */
|
|
377
|
+
private hostEl;
|
|
378
|
+
/** Rendering into the host's own element: they own the box, the chrome and visibility. */
|
|
379
|
+
private readonly inline;
|
|
380
|
+
constructor(config: LoaderConfig);
|
|
381
|
+
mount(): void;
|
|
382
|
+
/**
|
|
383
|
+
* Every message is checked twice, on every single message — not once at setup.
|
|
384
|
+
*
|
|
385
|
+
* `channel` is a namespace, not a boundary. The origin and source checks are the
|
|
386
|
+
* boundary: any frame on the page can postMessage us, and a page with an ad iframe
|
|
387
|
+
* on it has plenty of frames.
|
|
388
|
+
*/
|
|
389
|
+
private onMessage;
|
|
390
|
+
/**
|
|
391
|
+
* Own a drag for its lifetime.
|
|
392
|
+
*
|
|
393
|
+
* The widget reports the press and then goes quiet: a cross-origin iframe only gets
|
|
394
|
+
* pointer events while the pointer is over it, and a drag leaves that box immediately.
|
|
395
|
+
* So we lay a transparent layer over the whole viewport and track the gesture in the
|
|
396
|
+
* host document, where it can't be lost. The layer also stops the pointer landing on
|
|
397
|
+
* the customer's own UI mid-drag (text selection, hover states, stray clicks).
|
|
398
|
+
*
|
|
399
|
+
* We are the dumb half on purpose — see `widget.dragMove` in the protocol for why the
|
|
400
|
+
* widget has to own the gesture. All we do is take a delta and place the element.
|
|
401
|
+
*/
|
|
402
|
+
private cornerOffset;
|
|
403
|
+
private dragTo;
|
|
404
|
+
/** Reset to the default corner (a menu action — the drag is otherwise sticky). */
|
|
405
|
+
private resetPos;
|
|
406
|
+
private readPos;
|
|
407
|
+
private writePos;
|
|
408
|
+
/**
|
|
409
|
+
* Answer `widget.needsAuth`. If a trusted first-party host has an auth-token provider,
|
|
410
|
+
* call it and hand the token straight to the widget via `host.auth` — no popup. The
|
|
411
|
+
* provider comes from EITHER the programmatic config (the React `<MatterfactAgent
|
|
412
|
+
* getAuthToken>` prop) OR a global the host page sets for the `<script>` loader:
|
|
413
|
+
*
|
|
414
|
+
* window.matterfact = { getEmbedAuthToken: () => getIdToken(user) };
|
|
415
|
+
*
|
|
416
|
+
* Read fresh at call time (not at readConfig), so a global set after the loader booted
|
|
417
|
+
* — e.g. once the host's auth is ready — is still picked up. Otherwise fall back to the
|
|
418
|
+
* hosted-login popup. `expiresAt: 0`: the widget doesn't cache it, it exchanges the
|
|
419
|
+
* token for a rotating embed session anyway.
|
|
420
|
+
*/
|
|
421
|
+
private provideAuth;
|
|
422
|
+
private loadContext;
|
|
423
|
+
private send;
|
|
424
|
+
private flush;
|
|
425
|
+
/** Tear down: stop listening and remove the host element. For the React wrapper's
|
|
426
|
+
* unmount — the vanilla `<script>` loader lives for the page's lifetime and never
|
|
427
|
+
* calls this. */
|
|
428
|
+
destroy(): void;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Programmatic entry — mount the widget from a config object instead of the DOM
|
|
432
|
+
* `<script data-*>`. This is what the React wrapper (`@matterfact/embed/react`) drives;
|
|
433
|
+
* the vanilla loader below reads the script tag and calls this same class.
|
|
434
|
+
*/
|
|
435
|
+
declare function mount(config: LoaderConfig): EmbedHost;
|
|
436
|
+
|
|
437
|
+
export { type ActivityEvent, CHANNEL, type DomSnapshot, type ElementRef, EmbedHost, type Envelope, type FocusContext, type HostToWidget, type HostTool, type LoaderConfig, PROTOCOL_VERSION, type PageContext, type PageEntity, type ToolCall, type WidgetToHost, envelope, isEnvelope, mount, readConfig };
|