@matterfact/embed 0.6.0 → 0.8.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/README.md +99 -1
- package/dist/chunk-PNSYFXXU.js +791 -0
- package/dist/chunk-PNSYFXXU.js.map +1 -0
- package/dist/chunk-R2ZEJARX.js +776 -0
- package/dist/chunk-R2ZEJARX.js.map +1 -0
- package/dist/{chunk-4Q2ROXLR.js → chunk-UD7CAQXV.js} +2 -2
- package/dist/{chunk-7I37ZFAJ.js → chunk-UQCETVRF.js} +2 -2
- package/dist/chunk-W52Q7G4J.js +3 -0
- package/dist/chunk-W52Q7G4J.js.map +7 -0
- package/dist/context-ARBB2XD6.js +3 -0
- package/dist/{context-YX2KXFLI.js.map → context-ARBB2XD6.js.map} +1 -1
- package/dist/{context-WU2F5CBC.js → context-MVGSYIMB.js} +2 -2
- package/dist/embed.js +1 -1
- package/dist/embed.js.map +4 -4
- package/dist/index.cjs +986 -159
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +140 -31
- package/dist/index.d.ts +140 -31
- package/dist/index.js +542 -130
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +1012 -165
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +33 -1
- package/dist/react.d.ts +33 -1
- package/dist/react.js +561 -132
- package/dist/react.js.map +1 -1
- package/dist/{snapshot-MUXE7KXX.js → snapshot-GL4YBMXD.js} +3 -3
- package/dist/{snapshot-MUXE7KXX.js.map → snapshot-GL4YBMXD.js.map} +1 -1
- package/dist/{snapshot-JOGZWESK.js → snapshot-UGTXZVB6.js} +2 -2
- package/examples/embed-demo/.env.example +5 -1
- package/examples/embed-demo/README.md +37 -0
- package/examples/embed-demo/package.json +1 -1
- package/examples/embed-demo/src/App.tsx +82 -44
- package/examples/embed-demo/src/HoistDemo.tsx +390 -0
- package/examples/embed-demo/src/config.ts +34 -4
- package/examples/embed-demo/src/main.tsx +7 -0
- package/examples/embed-demo/src/mockXH.ts +492 -0
- package/examples/embed-demo/src/placement.tsx +43 -0
- package/examples/embed-demo/src/styles.css +147 -2
- package/examples/embed-demo/vite.config.ts +2 -2
- package/package.json +1 -1
- package/dist/chunk-AXKXNYRT.js +0 -381
- package/dist/chunk-AXKXNYRT.js.map +0 -1
- package/dist/chunk-D7R6WVHG.js +0 -2
- package/dist/chunk-D7R6WVHG.js.map +0 -7
- package/dist/chunk-RS6RMZ77.js +0 -396
- package/dist/chunk-RS6RMZ77.js.map +0 -1
- package/dist/context-YX2KXFLI.js +0 -3
- /package/dist/{chunk-4Q2ROXLR.js.map → chunk-UD7CAQXV.js.map} +0 -0
- /package/dist/{chunk-7I37ZFAJ.js.map → chunk-UQCETVRF.js.map} +0 -0
- /package/dist/{context-WU2F5CBC.js.map → context-MVGSYIMB.js.map} +0 -0
- /package/dist/{snapshot-JOGZWESK.js.map → snapshot-UGTXZVB6.js.map} +0 -0
package/dist/react.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { useEffect, useRef } from "react";
|
|
6
6
|
|
|
7
7
|
// src/protocol.ts
|
|
8
|
-
var PROTOCOL_VERSION =
|
|
8
|
+
var PROTOCOL_VERSION = 2;
|
|
9
9
|
var CHANNEL = "mf-embed";
|
|
10
10
|
function envelope(payload, id) {
|
|
11
11
|
return {
|
|
@@ -19,6 +19,213 @@ function isEnvelope(data) {
|
|
|
19
19
|
return typeof data === "object" && data !== null && data.channel === CHANNEL;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// src/geometry.ts
|
|
23
|
+
var MARGIN = 20;
|
|
24
|
+
var LAUNCHER = 56;
|
|
25
|
+
var SNAP_RADIUS = 96;
|
|
26
|
+
var TAB_W = 44;
|
|
27
|
+
var TAB_H = 132;
|
|
28
|
+
var EDGE_DOCK = 64;
|
|
29
|
+
var DEFAULT_FLOAT_W = 420;
|
|
30
|
+
var DEFAULT_FLOAT_H = 640;
|
|
31
|
+
var MIN_FLOAT_W = 320;
|
|
32
|
+
var MIN_FLOAT_H = 420;
|
|
33
|
+
var DEFAULT_DOCK_W = 600;
|
|
34
|
+
var MIN_DOCK_W = 320;
|
|
35
|
+
var CORNERS = ["tl", "tr", "bl", "br"];
|
|
36
|
+
function defaultGeometry() {
|
|
37
|
+
return {
|
|
38
|
+
// Dock is the default: a side panel reads as part of the host app.
|
|
39
|
+
mode: "dock",
|
|
40
|
+
corner: "br",
|
|
41
|
+
x: 0,
|
|
42
|
+
y: 0,
|
|
43
|
+
floatW: DEFAULT_FLOAT_W,
|
|
44
|
+
floatH: DEFAULT_FLOAT_H,
|
|
45
|
+
dockSide: "right",
|
|
46
|
+
tabY: 0,
|
|
47
|
+
// 0 => centre it (see tabTop)
|
|
48
|
+
dockW: DEFAULT_DOCK_W
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function clamp(n, lo, hi) {
|
|
52
|
+
return Math.min(Math.max(n, lo), hi);
|
|
53
|
+
}
|
|
54
|
+
function cornerOrigin(corner, w, h, vw, vh) {
|
|
55
|
+
return {
|
|
56
|
+
x: corner[1] === "l" ? MARGIN : vw - MARGIN - w,
|
|
57
|
+
y: corner[0] === "t" ? MARGIN : vh - MARGIN - h
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function launcherOrigin(g, vw, vh) {
|
|
61
|
+
if (g.corner) return cornerOrigin(g.corner, LAUNCHER, LAUNCHER, vw, vh);
|
|
62
|
+
return {
|
|
63
|
+
x: clamp(g.x, MARGIN, Math.max(MARGIN, vw - MARGIN - LAUNCHER)),
|
|
64
|
+
y: clamp(g.y, MARGIN, Math.max(MARGIN, vh - MARGIN - LAUNCHER))
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function growthCorner(g, vw, vh) {
|
|
68
|
+
if (g.corner) return g.corner;
|
|
69
|
+
const o = launcherOrigin(g, vw, vh);
|
|
70
|
+
return nearestCorner(o.x + LAUNCHER / 2, o.y + LAUNCHER / 2, vw, vh);
|
|
71
|
+
}
|
|
72
|
+
function nearestCorner(cx, cy, vw, vh) {
|
|
73
|
+
return `${cy < vh / 2 ? "t" : "b"}${cx < vw / 2 ? "l" : "r"}`;
|
|
74
|
+
}
|
|
75
|
+
function snapOrFree(x, y, vw, vh) {
|
|
76
|
+
const cx = clamp(x, MARGIN, Math.max(MARGIN, vw - MARGIN - LAUNCHER));
|
|
77
|
+
const cy = clamp(y, MARGIN, Math.max(MARGIN, vh - MARGIN - LAUNCHER));
|
|
78
|
+
for (const corner of CORNERS) {
|
|
79
|
+
const o = cornerOrigin(corner, LAUNCHER, LAUNCHER, vw, vh);
|
|
80
|
+
if (Math.hypot(cx - o.x, cy - o.y) <= SNAP_RADIUS) {
|
|
81
|
+
return { corner, x: cx, y: cy };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { corner: null, x: cx, y: cy };
|
|
85
|
+
}
|
|
86
|
+
function edgeDock(x, w, vw) {
|
|
87
|
+
if (x <= EDGE_DOCK) return "left";
|
|
88
|
+
if (x + w >= vw - EDGE_DOCK) return "right";
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
function dragAnchor(g, vw, vh) {
|
|
92
|
+
if (g.mode === "dock") {
|
|
93
|
+
return { x: g.dockSide === "left" ? 0 : vw - TAB_W, y: tabTop(g, vh) };
|
|
94
|
+
}
|
|
95
|
+
return launcherOrigin(g, vw, vh);
|
|
96
|
+
}
|
|
97
|
+
function visibleBox(g, open, vw, vh) {
|
|
98
|
+
if (g.mode === "dock") {
|
|
99
|
+
const w2 = open ? Math.min(Math.max(g.dockW, MIN_DOCK_W), Math.max(0, vw - 2 * MARGIN)) : TAB_W;
|
|
100
|
+
return { x: g.dockSide === "left" ? 0 : vw - w2, w: w2 };
|
|
101
|
+
}
|
|
102
|
+
const o = launcherOrigin(g, vw, vh);
|
|
103
|
+
if (!open) return { x: o.x, w: LAUNCHER };
|
|
104
|
+
const { w } = clampSize(g.floatW, g.floatH, vw, vh, MIN_FLOAT_W, MIN_FLOAT_H);
|
|
105
|
+
return {
|
|
106
|
+
x: growthCorner(g, vw, vh)[1] === "r" ? o.x + LAUNCHER - w : o.x,
|
|
107
|
+
w
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function placeDrop(anchorX, anchorY, boxLeft, boxW, vw, vh) {
|
|
111
|
+
const snapped = snapOrFree(anchorX, anchorY, vw, vh);
|
|
112
|
+
if (snapped.corner) return { mode: "float", ...snapped };
|
|
113
|
+
const side = edgeDock(boxLeft, boxW, vw);
|
|
114
|
+
if (side) {
|
|
115
|
+
return {
|
|
116
|
+
mode: "dock",
|
|
117
|
+
dockSide: side,
|
|
118
|
+
tabY: clamp(anchorY, 0, Math.max(0, vh - TAB_H))
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return { mode: "float", ...snapped };
|
|
122
|
+
}
|
|
123
|
+
function clampSize(w, h, vw, vh, minW, minH) {
|
|
124
|
+
return {
|
|
125
|
+
w: Math.min(Math.max(w, minW), Math.max(0, vw - 2 * MARGIN)),
|
|
126
|
+
h: Math.min(Math.max(h, minH), Math.max(0, vh - 2 * MARGIN))
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function applyResizeDelta(corner, startW, startH, dx, dy) {
|
|
130
|
+
const wSign = corner[1] === "r" ? -1 : 1;
|
|
131
|
+
const hSign = corner[0] === "b" ? -1 : 1;
|
|
132
|
+
return { w: startW + wSign * dx, h: startH + hSign * dy };
|
|
133
|
+
}
|
|
134
|
+
function applyDockResizeDelta(side, startW, dx) {
|
|
135
|
+
return side === "right" ? startW - dx : startW + dx;
|
|
136
|
+
}
|
|
137
|
+
function parseGeometry(raw) {
|
|
138
|
+
if (!raw) return null;
|
|
139
|
+
try {
|
|
140
|
+
const p = JSON.parse(raw);
|
|
141
|
+
if (!p || p.mode !== "float" && p.mode !== "dock") return null;
|
|
142
|
+
const d = defaultGeometry();
|
|
143
|
+
const num = (v, dv) => typeof v === "number" ? v : dv;
|
|
144
|
+
return {
|
|
145
|
+
mode: p.mode,
|
|
146
|
+
corner: CORNERS.includes(p.corner) ? p.corner : null,
|
|
147
|
+
x: num(p.x, d.x),
|
|
148
|
+
y: num(p.y, d.y),
|
|
149
|
+
floatW: num(p.floatW, d.floatW),
|
|
150
|
+
floatH: num(p.floatH, d.floatH),
|
|
151
|
+
dockSide: p.dockSide === "left" ? "left" : "right",
|
|
152
|
+
tabY: num(p.tabY, d.tabY),
|
|
153
|
+
dockW: num(p.dockW, d.dockW)
|
|
154
|
+
};
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function serializeGeometry(g) {
|
|
160
|
+
return JSON.stringify(g);
|
|
161
|
+
}
|
|
162
|
+
var px = (n) => `${n}px`;
|
|
163
|
+
function boxAt(ox, oy, w, h, corner, vw, vh) {
|
|
164
|
+
const right = corner[1] === "r";
|
|
165
|
+
const bottom = corner[0] === "b";
|
|
166
|
+
const rawX = right ? vw - (ox + LAUNCHER) : ox;
|
|
167
|
+
const rawY = bottom ? vh - (oy + LAUNCHER) : oy;
|
|
168
|
+
const x = clamp(rawX, MARGIN, Math.max(MARGIN, vw - MARGIN - w));
|
|
169
|
+
const y = clamp(rawY, MARGIN, Math.max(MARGIN, vh - MARGIN - h));
|
|
170
|
+
return {
|
|
171
|
+
top: bottom ? "auto" : px(y),
|
|
172
|
+
bottom: bottom ? px(y) : "auto",
|
|
173
|
+
left: right ? "auto" : px(x),
|
|
174
|
+
right: right ? px(x) : "auto",
|
|
175
|
+
width: px(w),
|
|
176
|
+
height: px(h)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function tabTop(g, vh) {
|
|
180
|
+
const max = Math.max(0, vh - TAB_H);
|
|
181
|
+
return clamp(g.tabY || Math.round((vh - TAB_H) / 2), 0, max);
|
|
182
|
+
}
|
|
183
|
+
function tabBox(g, vw, vh) {
|
|
184
|
+
const left = g.dockSide === "left";
|
|
185
|
+
return {
|
|
186
|
+
top: px(tabTop(g, vh)),
|
|
187
|
+
bottom: "auto",
|
|
188
|
+
left: left ? "0px" : "auto",
|
|
189
|
+
right: left ? "auto" : "0px",
|
|
190
|
+
width: px(TAB_W),
|
|
191
|
+
height: px(TAB_H)
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function floatChrome(g, w, h, vw, vh, growth) {
|
|
195
|
+
const o = launcherOrigin(g, vw, vh);
|
|
196
|
+
return boxAt(o.x, o.y, w, h, growth ?? growthCorner(g, vw, vh), vw, vh);
|
|
197
|
+
}
|
|
198
|
+
function dockChrome(g, w, h, vw, vh) {
|
|
199
|
+
const left = g.dockSide === "left";
|
|
200
|
+
return {
|
|
201
|
+
top: px(tabTop(g, vh)),
|
|
202
|
+
bottom: "auto",
|
|
203
|
+
left: left ? "0px" : "auto",
|
|
204
|
+
right: left ? "auto" : "0px",
|
|
205
|
+
width: px(w),
|
|
206
|
+
height: px(h)
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function floatBox(g, vw, vh, growth) {
|
|
210
|
+
const f = clampSize(g.floatW, g.floatH, vw, vh, MIN_FLOAT_W, MIN_FLOAT_H);
|
|
211
|
+
return floatChrome(g, f.w, f.h, vw, vh, growth);
|
|
212
|
+
}
|
|
213
|
+
function dockBox(g, vw, _vh) {
|
|
214
|
+
const w = Math.min(
|
|
215
|
+
Math.max(g.dockW, MIN_DOCK_W),
|
|
216
|
+
Math.max(0, vw - 2 * MARGIN)
|
|
217
|
+
);
|
|
218
|
+
const left = g.dockSide === "left";
|
|
219
|
+
return {
|
|
220
|
+
top: "0px",
|
|
221
|
+
bottom: "0px",
|
|
222
|
+
height: "auto",
|
|
223
|
+
left: left ? "0px" : "auto",
|
|
224
|
+
right: left ? "auto" : "0px",
|
|
225
|
+
width: px(w)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
22
229
|
// src/loader.ts
|
|
23
230
|
function devRequested() {
|
|
24
231
|
try {
|
|
@@ -37,15 +244,42 @@ var EmbedHost = class {
|
|
|
37
244
|
this.queue = [];
|
|
38
245
|
this.ready = false;
|
|
39
246
|
this.open = false;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
247
|
+
/** The persisted box: launcher corner, float size, dock state/side/width. */
|
|
248
|
+
this.geo = defaultGeometry();
|
|
249
|
+
/** Float size at resizeStart; resizeMove deltas apply against it (they're cumulative). */
|
|
250
|
+
this.resizeBase = null;
|
|
251
|
+
/** Docked width at resizeStart — the docked panel resizes in one axis only. */
|
|
252
|
+
this.dockBase = DEFAULT_DOCK_W;
|
|
253
|
+
/** The widget's logical anchor when the drag began, and where it is now. Deltas arrive
|
|
254
|
+
* cumulative from the press, so the current spot is simply origin + delta — no need to
|
|
255
|
+
* read the box back, which stops being the thing we're moving the moment it docks. */
|
|
256
|
+
this.dragFrom = null;
|
|
257
|
+
this.dragAt = { x: 0, y: 0 };
|
|
258
|
+
/** The painted box when the drag began — the edge test measures what you can SEE
|
|
259
|
+
* touching the screen edge, which is not the anchor once the panel is open. */
|
|
260
|
+
this.dragBox = { x: 0, w: 0 };
|
|
261
|
+
/** Growth direction frozen for the drag — see floatChrome's `growth` param. */
|
|
262
|
+
this.dragGrowth = null;
|
|
263
|
+
/**
|
|
264
|
+
* Has this drag been clear of the dock band yet?
|
|
265
|
+
*
|
|
266
|
+
* The resting position is INSIDE the band — a corner-anchored widget sits MARGIN
|
|
267
|
+
* (20px) from the edge, well within EDGE_DOCK (64px) — so without this, picking one
|
|
268
|
+
* up docked it instantly, before it had moved anywhere. Docking now requires
|
|
269
|
+
* ENTERING the band rather than merely starting in it.
|
|
270
|
+
*/
|
|
271
|
+
this.dragLeftBand = false;
|
|
45
272
|
/** Loaded on first open. Holds everything that touches the customer's DOM. */
|
|
46
273
|
this.context = null;
|
|
47
274
|
/** The host element; kept so `destroy()` can remove it (React lifecycle). */
|
|
48
275
|
this.hostEl = null;
|
|
276
|
+
/** Host-auth storm guard (see provideAuth). Counts getAuthToken calls; past a cap we stop
|
|
277
|
+
* calling the provider, rather than joining the storm. This host instance OUTLIVES iframe
|
|
278
|
+
* reloads, so — unlike anything in the widget — the counter survives the very reload loop
|
|
279
|
+
* that drives the storm. (Kept tiny on purpose: this file has a hard per-page size budget;
|
|
280
|
+
* the widget side carries the windowed/retry-after-settle half.) Terse name = fewer bytes
|
|
281
|
+
* in the stub; it is the host-auth request count. */
|
|
282
|
+
this.ac = 0;
|
|
49
283
|
/**
|
|
50
284
|
* Every message is checked twice, on every single message — not once at setup.
|
|
51
285
|
*
|
|
@@ -57,87 +291,10 @@ var EmbedHost = class {
|
|
|
57
291
|
if (event.origin !== this.config.origin) return;
|
|
58
292
|
if (event.source !== this.iframe?.contentWindow) return;
|
|
59
293
|
if (!isEnvelope(event.data)) return;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.send({
|
|
65
|
-
type: "host.ready",
|
|
66
|
-
protocol: PROTOCOL_VERSION,
|
|
67
|
-
origin: location.origin
|
|
68
|
-
});
|
|
69
|
-
this.flush();
|
|
70
|
-
if (this.inline) void this.loadContext();
|
|
71
|
-
break;
|
|
72
|
-
case "widget.setOpen":
|
|
73
|
-
if (this.inline) break;
|
|
74
|
-
this.open = msg.open;
|
|
75
|
-
this.iframe?.setAttribute("data-open", String(msg.open));
|
|
76
|
-
if (!msg.open) {
|
|
77
|
-
this.expanded = false;
|
|
78
|
-
this.iframe?.setAttribute("data-expanded", "false");
|
|
79
|
-
}
|
|
80
|
-
if (msg.open) void this.loadContext();
|
|
81
|
-
break;
|
|
82
|
-
case "widget.setExpanded":
|
|
83
|
-
if (this.inline) break;
|
|
84
|
-
this.expanded = msg.expanded;
|
|
85
|
-
this.iframe?.setAttribute("data-expanded", String(msg.expanded));
|
|
86
|
-
if (this.iframe) this.iframe.style.height = "";
|
|
87
|
-
break;
|
|
88
|
-
case "widget.resize":
|
|
89
|
-
if (this.inline) break;
|
|
90
|
-
if (this.iframe && this.open && !this.expanded) {
|
|
91
|
-
this.iframe.style.height = `${Math.min(msg.height, window.innerHeight - 40)}px`;
|
|
92
|
-
}
|
|
93
|
-
break;
|
|
94
|
-
case "widget.requestContext":
|
|
95
|
-
void this.loadContext().then((m) => m.provideContext());
|
|
96
|
-
break;
|
|
97
|
-
case "widget.requestSnapshot":
|
|
98
|
-
void this.loadContext().then((m) => m.sendSnapshot(this.send));
|
|
99
|
-
break;
|
|
100
|
-
case "widget.readRegion":
|
|
101
|
-
void this.loadContext().then((m) => m.sendRegion(msg.ref, this.send));
|
|
102
|
-
break;
|
|
103
|
-
case "widget.callTool":
|
|
104
|
-
void this.loadContext().then((m) => m.callTool(msg.call, this.send));
|
|
105
|
-
break;
|
|
106
|
-
// Everything below is LAUNCHER chrome: there is no launcher inline (the widget
|
|
107
|
-
// doesn't draw one), and moving or hiding the host's own panel from inside it
|
|
108
|
-
// would be us redecorating their app. An older cached loader could still be told
|
|
109
|
-
// any of these by a newer widget, so they're guarded rather than assumed absent.
|
|
110
|
-
case "widget.dragStart":
|
|
111
|
-
if (this.inline) break;
|
|
112
|
-
this.dragBase = this.cornerOffset();
|
|
113
|
-
break;
|
|
114
|
-
case "widget.dragMove":
|
|
115
|
-
if (this.inline) break;
|
|
116
|
-
this.dragTo(msg.dx, msg.dy);
|
|
117
|
-
break;
|
|
118
|
-
case "widget.dragEnd":
|
|
119
|
-
if (this.inline) break;
|
|
120
|
-
this.dragBase = null;
|
|
121
|
-
this.writePos(this.pos);
|
|
122
|
-
break;
|
|
123
|
-
case "widget.setMenu":
|
|
124
|
-
if (this.inline) break;
|
|
125
|
-
if (this.iframe && !this.open) {
|
|
126
|
-
this.iframe.style.height = msg.open ? `${64 + msg.height}px` : "";
|
|
127
|
-
}
|
|
128
|
-
break;
|
|
129
|
-
case "widget.resetPos":
|
|
130
|
-
if (this.inline) break;
|
|
131
|
-
this.resetPos();
|
|
132
|
-
break;
|
|
133
|
-
case "widget.hide":
|
|
134
|
-
if (this.inline) break;
|
|
135
|
-
if (this.hostEl) this.hostEl.style.display = "none";
|
|
136
|
-
break;
|
|
137
|
-
case "widget.needsAuth":
|
|
138
|
-
void this.provideAuth();
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
294
|
+
this.handle(event.data.payload);
|
|
295
|
+
};
|
|
296
|
+
this.onViewportResize = () => {
|
|
297
|
+
this.place();
|
|
141
298
|
};
|
|
142
299
|
this.send = (msg) => {
|
|
143
300
|
if (!this.ready) {
|
|
@@ -163,34 +320,36 @@ var EmbedHost = class {
|
|
|
163
320
|
].join(";");
|
|
164
321
|
this.config.container.appendChild(host);
|
|
165
322
|
} else {
|
|
166
|
-
this.
|
|
323
|
+
this.geo = this.readGeometry();
|
|
167
324
|
host.style.cssText = [
|
|
168
325
|
"all: initial",
|
|
169
326
|
"position: fixed",
|
|
170
|
-
`right: ${this.pos ? this.pos.right : 20}px`,
|
|
171
|
-
`bottom: ${this.pos ? this.pos.bottom : 20}px`,
|
|
172
327
|
// Below the max so a host that genuinely needs to cover us (a modal, a cookie
|
|
173
328
|
// banner they are legally obliged to show) still can.
|
|
174
329
|
"z-index: 2147483000",
|
|
175
|
-
"contain: layout style"
|
|
330
|
+
"contain: layout style",
|
|
331
|
+
// The host element owns the BOX now (position AND size); the iframe fills it.
|
|
332
|
+
// Transitioning the box is what makes the corner snap and the open/dock read as
|
|
333
|
+
// motion rather than a jump. Suppressed during drag/resize (see those handlers).
|
|
334
|
+
"transition: width .18s ease, height .18s ease, top .18s ease, right .18s ease, bottom .18s ease, left .18s ease"
|
|
176
335
|
].join(";");
|
|
177
336
|
document.body.appendChild(host);
|
|
178
337
|
}
|
|
179
338
|
this.shadow = host.attachShadow({ mode: "closed" });
|
|
180
339
|
const style = document.createElement("style");
|
|
181
|
-
style.textContent = ":host{all:initial}iframe{border:0;display:block;
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
'iframe{border-radius:12px;box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029
|
|
340
|
+
style.textContent = ":host{all:initial}iframe{border:0;display:block;width:100%;height:100%;background:transparent}" + (this.inline ? "" : (
|
|
341
|
+
// color-scheme only on the OPEN panel — on a collapsed launcher it makes the
|
|
342
|
+
// UA paint an opaque canvas base behind our shapes.
|
|
343
|
+
':host([data-mode="float"]) iframe,:host([data-mode="dock"]) iframe{color-scheme:light dark}:host([data-mode="float"]) iframe{border-radius:12px;box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029}:host([data-mode="dock"]) iframe{box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029}:host([data-mode="dock"][data-flush="right"]) iframe{border-radius:12px 0 0 12px}:host([data-mode="dock"][data-flush="left"]) iframe{border-radius:0 12px 12px 0}'
|
|
185
344
|
));
|
|
186
345
|
this.shadow.appendChild(style);
|
|
187
346
|
const iframe = document.createElement("iframe");
|
|
188
347
|
iframe.title = "matterfact assistant";
|
|
189
348
|
iframe.setAttribute(
|
|
190
349
|
"sandbox",
|
|
191
|
-
"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
|
|
350
|
+
"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
|
|
192
351
|
);
|
|
193
|
-
iframe.setAttribute("allow", "microphone");
|
|
352
|
+
iframe.setAttribute("allow", "microphone; clipboard-write");
|
|
194
353
|
iframe.src = `${this.config.origin}/embed/chat?k=${encodeURIComponent(
|
|
195
354
|
this.config.publishableKey
|
|
196
355
|
)}&o=${encodeURIComponent(location.origin)}` + (this.config.surface ? `&s=${encodeURIComponent(this.config.surface)}` : "") + // Either trigger works: the URL param (no redeploy needed) OR the config's
|
|
@@ -199,6 +358,224 @@ var EmbedHost = class {
|
|
|
199
358
|
this.iframe = iframe;
|
|
200
359
|
this.shadow.appendChild(iframe);
|
|
201
360
|
window.addEventListener("message", this.onMessage);
|
|
361
|
+
if (!this.inline) {
|
|
362
|
+
window.addEventListener("resize", this.onViewportResize);
|
|
363
|
+
this.place();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Test seam. The shadow root is CLOSED, so a test cannot reach the iframe to forge a
|
|
368
|
+
* source-valid MessageEvent — this routes a message through the identical logic
|
|
369
|
+
* without weakening the origin/source checks above, which stay the only real door.
|
|
370
|
+
*/
|
|
371
|
+
__testHandle(msg) {
|
|
372
|
+
this.handle(msg);
|
|
373
|
+
}
|
|
374
|
+
handle(msg) {
|
|
375
|
+
switch (msg.type) {
|
|
376
|
+
case "widget.ready":
|
|
377
|
+
this.ready = true;
|
|
378
|
+
this.send({
|
|
379
|
+
type: "host.ready",
|
|
380
|
+
protocol: PROTOCOL_VERSION,
|
|
381
|
+
origin: location.origin
|
|
382
|
+
});
|
|
383
|
+
this.send({ type: "host.theme", mode: this.themeMode() });
|
|
384
|
+
this.flush();
|
|
385
|
+
if (this.inline) void this.loadContext();
|
|
386
|
+
break;
|
|
387
|
+
case "widget.setOpen":
|
|
388
|
+
if (this.inline) break;
|
|
389
|
+
this.open = msg.open;
|
|
390
|
+
this.place();
|
|
391
|
+
if (msg.open) void this.loadContext();
|
|
392
|
+
break;
|
|
393
|
+
case "widget.setMode":
|
|
394
|
+
if (this.inline) break;
|
|
395
|
+
this.geo.mode = msg.mode;
|
|
396
|
+
this.writeGeometry(this.geo);
|
|
397
|
+
this.place();
|
|
398
|
+
break;
|
|
399
|
+
case "widget.setDockSide":
|
|
400
|
+
if (this.inline) break;
|
|
401
|
+
this.geo.dockSide = msg.side;
|
|
402
|
+
this.writeGeometry(this.geo);
|
|
403
|
+
this.place();
|
|
404
|
+
break;
|
|
405
|
+
case "widget.snapCorner":
|
|
406
|
+
if (this.inline) break;
|
|
407
|
+
this.geo.corner = msg.corner;
|
|
408
|
+
this.writeGeometry(this.geo);
|
|
409
|
+
this.place();
|
|
410
|
+
break;
|
|
411
|
+
case "widget.setLauncherRegion": {
|
|
412
|
+
if (this.inline || this.open) break;
|
|
413
|
+
const vw = window.innerWidth;
|
|
414
|
+
const vh = window.innerHeight;
|
|
415
|
+
if (msg.w <= LAUNCHER && msg.h <= LAUNCHER) {
|
|
416
|
+
this.place();
|
|
417
|
+
} else {
|
|
418
|
+
const region = this.geo.mode === "dock" ? dockChrome(this.geo, msg.w, msg.h, vw, vh) : floatChrome(this.geo, msg.w, msg.h, vw, vh);
|
|
419
|
+
this.applyBox(region, msg.h > LAUNCHER ? "menu" : "pill");
|
|
420
|
+
}
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
case "widget.resize":
|
|
424
|
+
if (this.inline) break;
|
|
425
|
+
if (this.hostEl && this.open && this.geo.mode === "float") {
|
|
426
|
+
this.hostEl.style.height = `${Math.min(
|
|
427
|
+
msg.height,
|
|
428
|
+
Math.max(0, window.innerHeight - 2 * MARGIN)
|
|
429
|
+
)}px`;
|
|
430
|
+
}
|
|
431
|
+
break;
|
|
432
|
+
case "widget.resizeStart":
|
|
433
|
+
if (this.inline) break;
|
|
434
|
+
this.resizeBase = { w: this.geo.floatW, h: this.geo.floatH };
|
|
435
|
+
this.dockBase = this.geo.dockW;
|
|
436
|
+
if (this.hostEl) this.hostEl.style.transition = "none";
|
|
437
|
+
break;
|
|
438
|
+
case "widget.resizeMove": {
|
|
439
|
+
if (this.inline || !this.resizeBase) break;
|
|
440
|
+
if (this.geo.mode === "dock") {
|
|
441
|
+
const w = applyDockResizeDelta(
|
|
442
|
+
this.geo.dockSide,
|
|
443
|
+
this.dockBase,
|
|
444
|
+
msg.dx
|
|
445
|
+
);
|
|
446
|
+
this.geo.dockW = Math.min(
|
|
447
|
+
Math.max(w, MIN_DOCK_W),
|
|
448
|
+
Math.max(0, window.innerWidth - 2 * MARGIN)
|
|
449
|
+
);
|
|
450
|
+
} else {
|
|
451
|
+
const next = applyResizeDelta(
|
|
452
|
+
growthCorner(this.geo, window.innerWidth, window.innerHeight),
|
|
453
|
+
this.resizeBase.w,
|
|
454
|
+
this.resizeBase.h,
|
|
455
|
+
msg.dx,
|
|
456
|
+
msg.dy
|
|
457
|
+
);
|
|
458
|
+
const fit = clampSize(
|
|
459
|
+
next.w,
|
|
460
|
+
next.h,
|
|
461
|
+
window.innerWidth,
|
|
462
|
+
window.innerHeight,
|
|
463
|
+
MIN_FLOAT_W,
|
|
464
|
+
MIN_FLOAT_H
|
|
465
|
+
);
|
|
466
|
+
this.geo.floatW = fit.w;
|
|
467
|
+
this.geo.floatH = fit.h;
|
|
468
|
+
}
|
|
469
|
+
this.place();
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
case "widget.resizeEnd":
|
|
473
|
+
if (this.inline) break;
|
|
474
|
+
this.resizeBase = null;
|
|
475
|
+
if (this.hostEl) this.hostEl.style.transition = "";
|
|
476
|
+
this.writeGeometry(this.geo);
|
|
477
|
+
break;
|
|
478
|
+
case "widget.requestContext":
|
|
479
|
+
void this.loadContext().then((m) => m.provideContext());
|
|
480
|
+
break;
|
|
481
|
+
case "widget.requestSnapshot":
|
|
482
|
+
void this.loadContext().then((m) => m.sendSnapshot(this.send));
|
|
483
|
+
break;
|
|
484
|
+
case "widget.readRegion":
|
|
485
|
+
void this.loadContext().then((m) => m.sendRegion(msg.ref, this.send));
|
|
486
|
+
break;
|
|
487
|
+
case "widget.callTool":
|
|
488
|
+
void this.loadContext().then((m) => m.callTool(msg.call, this.send));
|
|
489
|
+
break;
|
|
490
|
+
// Everything below is LAUNCHER chrome: there is no launcher inline (the widget
|
|
491
|
+
// doesn't draw one), and moving or hiding the host's own panel from inside it
|
|
492
|
+
// would be us redecorating their app. An older cached loader could still be told
|
|
493
|
+
// any of these by a newer widget, so they're guarded rather than assumed absent.
|
|
494
|
+
case "widget.dragStart":
|
|
495
|
+
if (this.inline) break;
|
|
496
|
+
this.dragFrom = dragAnchor(
|
|
497
|
+
this.geo,
|
|
498
|
+
window.innerWidth,
|
|
499
|
+
window.innerHeight
|
|
500
|
+
);
|
|
501
|
+
this.dragAt = { ...this.dragFrom };
|
|
502
|
+
this.dragLeftBand = false;
|
|
503
|
+
this.dragGrowth = growthCorner(
|
|
504
|
+
this.geo,
|
|
505
|
+
window.innerWidth,
|
|
506
|
+
window.innerHeight
|
|
507
|
+
);
|
|
508
|
+
this.dragBox = visibleBox(
|
|
509
|
+
this.geo,
|
|
510
|
+
this.open,
|
|
511
|
+
window.innerWidth,
|
|
512
|
+
window.innerHeight
|
|
513
|
+
);
|
|
514
|
+
if (this.hostEl) this.hostEl.style.transition = "none";
|
|
515
|
+
break;
|
|
516
|
+
case "widget.dragMove": {
|
|
517
|
+
if (this.inline || !this.dragFrom) break;
|
|
518
|
+
const vw = window.innerWidth;
|
|
519
|
+
const vh = window.innerHeight;
|
|
520
|
+
this.dragAt = {
|
|
521
|
+
x: this.dragFrom.x + msg.dx,
|
|
522
|
+
y: this.dragFrom.y + msg.dy
|
|
523
|
+
};
|
|
524
|
+
const side = edgeDock(this.dragBox.x + msg.dx, this.dragBox.w, vw);
|
|
525
|
+
if (!side) this.dragLeftBand = true;
|
|
526
|
+
if (side && (this.dragLeftBand || this.geo.mode === "dock")) {
|
|
527
|
+
this.geo.mode = "dock";
|
|
528
|
+
this.geo.dockSide = side;
|
|
529
|
+
this.geo.tabY = Math.max(0, this.dragAt.y);
|
|
530
|
+
} else {
|
|
531
|
+
this.geo.mode = "float";
|
|
532
|
+
this.geo.corner = null;
|
|
533
|
+
this.geo.x = this.dragAt.x;
|
|
534
|
+
this.geo.y = this.dragAt.y;
|
|
535
|
+
}
|
|
536
|
+
this.place();
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
case "widget.dragEnd": {
|
|
540
|
+
if (this.inline || !this.dragFrom) break;
|
|
541
|
+
const at = placeDrop(
|
|
542
|
+
this.dragAt.x,
|
|
543
|
+
this.dragAt.y,
|
|
544
|
+
this.dragBox.x + (this.dragAt.x - this.dragFrom.x),
|
|
545
|
+
this.dragBox.w,
|
|
546
|
+
window.innerWidth,
|
|
547
|
+
window.innerHeight
|
|
548
|
+
);
|
|
549
|
+
this.geo.mode = at.mode;
|
|
550
|
+
if (at.mode === "dock") {
|
|
551
|
+
this.geo.dockSide = at.dockSide;
|
|
552
|
+
this.geo.tabY = at.tabY;
|
|
553
|
+
} else {
|
|
554
|
+
this.geo.corner = at.corner;
|
|
555
|
+
this.geo.x = at.x;
|
|
556
|
+
this.geo.y = at.y;
|
|
557
|
+
}
|
|
558
|
+
this.dragFrom = null;
|
|
559
|
+
this.dragGrowth = null;
|
|
560
|
+
if (this.hostEl) this.hostEl.style.transition = "";
|
|
561
|
+
this.place();
|
|
562
|
+
this.writeGeometry(this.geo);
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
case "widget.resetPos":
|
|
566
|
+
if (this.inline) break;
|
|
567
|
+
this.geo = defaultGeometry();
|
|
568
|
+
this.writeGeometry(this.geo);
|
|
569
|
+
this.place();
|
|
570
|
+
break;
|
|
571
|
+
case "widget.hide":
|
|
572
|
+
if (this.inline) break;
|
|
573
|
+
if (this.hostEl) this.hostEl.style.display = "none";
|
|
574
|
+
break;
|
|
575
|
+
case "widget.needsAuth":
|
|
576
|
+
void this.provideAuth();
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
202
579
|
}
|
|
203
580
|
/**
|
|
204
581
|
* Own a drag for its lifetime.
|
|
@@ -212,47 +589,77 @@ var EmbedHost = class {
|
|
|
212
589
|
* We are the dumb half on purpose — see `widget.dragMove` in the protocol for why the
|
|
213
590
|
* widget has to own the gesture. All we do is take a delta and place the element.
|
|
214
591
|
*/
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
592
|
+
/** Write a box onto the host element and stamp the mode the shadow CSS keys off. */
|
|
593
|
+
applyBox(box, mode) {
|
|
594
|
+
if (!this.hostEl || this.inline) return;
|
|
595
|
+
const s = this.hostEl.style;
|
|
596
|
+
s.top = box.top;
|
|
597
|
+
s.right = box.right;
|
|
598
|
+
s.bottom = box.bottom;
|
|
599
|
+
s.left = box.left;
|
|
600
|
+
s.width = box.width;
|
|
601
|
+
s.height = box.height;
|
|
602
|
+
this.hostEl.dataset.mode = mode;
|
|
603
|
+
if (mode === "tab" || mode === "dock")
|
|
604
|
+
this.hostEl.dataset.flush = this.geo.dockSide;
|
|
605
|
+
else delete this.hostEl.dataset.flush;
|
|
221
606
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
607
|
+
/** Re-apply the box for whatever state we're in: collapsed launcher, float, or dock. */
|
|
608
|
+
/**
|
|
609
|
+
* The growth direction to use RIGHT NOW — frozen while a drag is in flight.
|
|
610
|
+
*
|
|
611
|
+
* Every consumer has to agree on this, which is the bug that made the widget
|
|
612
|
+
* flip-flop under small movements near the viewport centre: the box was frozen but
|
|
613
|
+
* `host.geometry` still published a freshly-derived growth, so the WIDGET kept
|
|
614
|
+
* swapping which corner it drew against (and it animates that), twitching back and
|
|
615
|
+
* forth while the loader held perfectly still.
|
|
616
|
+
*/
|
|
617
|
+
growthNow() {
|
|
618
|
+
return this.dragGrowth ?? growthCorner(this.geo, window.innerWidth, window.innerHeight);
|
|
232
619
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.
|
|
237
|
-
|
|
238
|
-
this.
|
|
239
|
-
|
|
620
|
+
place() {
|
|
621
|
+
const vw = window.innerWidth;
|
|
622
|
+
const vh = window.innerHeight;
|
|
623
|
+
if (!this.open) {
|
|
624
|
+
const dock = this.geo.mode === "dock";
|
|
625
|
+
this.applyBox(
|
|
626
|
+
dock ? tabBox(this.geo, vw, vh) : floatChrome(this.geo, LAUNCHER, LAUNCHER, vw, vh, this.growthNow()),
|
|
627
|
+
dock ? "tab" : "launcher"
|
|
628
|
+
);
|
|
629
|
+
} else if (this.geo.mode === "dock") {
|
|
630
|
+
this.applyBox(dockBox(this.geo, vw, vh), "dock");
|
|
631
|
+
} else {
|
|
632
|
+
this.applyBox(floatBox(this.geo, vw, vh, this.growthNow()), "float");
|
|
240
633
|
}
|
|
634
|
+
this.publishGeometry();
|
|
241
635
|
}
|
|
242
|
-
|
|
636
|
+
/**
|
|
637
|
+
* Tell the widget where it is. It can't see the viewport and doesn't own the box, so
|
|
638
|
+
* without this it draws its launcher against a stale guess — which is what made the
|
|
639
|
+
* circle jump when the launcher sat on the left (see `host.geometry` in the protocol).
|
|
640
|
+
*/
|
|
641
|
+
publishGeometry() {
|
|
642
|
+
this.send({
|
|
643
|
+
type: "host.geometry",
|
|
644
|
+
mode: this.geo.mode,
|
|
645
|
+
dockSide: this.geo.dockSide,
|
|
646
|
+
growth: this.growthNow()
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
/** The theme to publish: an explicit data-theme wins, else the OS preference. */
|
|
650
|
+
themeMode() {
|
|
651
|
+
return this.config.theme === "auto" ? matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : this.config.theme;
|
|
652
|
+
}
|
|
653
|
+
readGeometry() {
|
|
243
654
|
try {
|
|
244
|
-
|
|
245
|
-
if (!raw) return null;
|
|
246
|
-
const p = JSON.parse(raw);
|
|
247
|
-
return typeof p?.right === "number" && typeof p?.bottom === "number" ? { right: p.right, bottom: p.bottom } : null;
|
|
655
|
+
return parseGeometry(localStorage.getItem(POS_KEY)) ?? defaultGeometry();
|
|
248
656
|
} catch {
|
|
249
|
-
return
|
|
657
|
+
return defaultGeometry();
|
|
250
658
|
}
|
|
251
659
|
}
|
|
252
|
-
|
|
660
|
+
writeGeometry(g) {
|
|
253
661
|
try {
|
|
254
|
-
|
|
255
|
-
else localStorage.removeItem(POS_KEY);
|
|
662
|
+
localStorage.setItem(POS_KEY, serializeGeometry(g));
|
|
256
663
|
} catch {
|
|
257
664
|
}
|
|
258
665
|
}
|
|
@@ -276,6 +683,7 @@ var EmbedHost = class {
|
|
|
276
683
|
async provideAuth() {
|
|
277
684
|
const provider = this.config.authTokenProvider ?? globalThis.matterfact?.getEmbedAuthToken;
|
|
278
685
|
if (!provider) return;
|
|
686
|
+
if (++this.ac > 5) return;
|
|
279
687
|
try {
|
|
280
688
|
const token = await provider();
|
|
281
689
|
if (token) this.send({ type: "host.auth", token, expiresAt: 0 });
|
|
@@ -283,7 +691,7 @@ var EmbedHost = class {
|
|
|
283
691
|
}
|
|
284
692
|
}
|
|
285
693
|
loadContext() {
|
|
286
|
-
this.context ?? (this.context = import("./context-
|
|
694
|
+
this.context ?? (this.context = import("./context-MVGSYIMB.js").then((m) => {
|
|
287
695
|
m.start(
|
|
288
696
|
this.send,
|
|
289
697
|
this.config.origin,
|
|
@@ -307,6 +715,7 @@ var EmbedHost = class {
|
|
|
307
715
|
* calls this. */
|
|
308
716
|
destroy() {
|
|
309
717
|
window.removeEventListener("message", this.onMessage);
|
|
718
|
+
window.removeEventListener("resize", this.onViewportResize);
|
|
310
719
|
this.hostEl?.remove();
|
|
311
720
|
this.hostEl = null;
|
|
312
721
|
this.iframe = null;
|
|
@@ -324,6 +733,12 @@ function mount(config) {
|
|
|
324
733
|
// src/react.tsx
|
|
325
734
|
import { jsx } from "react/jsx-runtime";
|
|
326
735
|
var DEFAULT_ORIGIN = "https://app.matterfact.com";
|
|
736
|
+
function writeActionsGlobal(actions) {
|
|
737
|
+
if (typeof window === "undefined") return;
|
|
738
|
+
const w = window;
|
|
739
|
+
const mf = w.matterfact ?? (w.matterfact = {});
|
|
740
|
+
mf.hoist = { ...mf.hoist, actions: actions ?? { navigate: "off" } };
|
|
741
|
+
}
|
|
327
742
|
function MatterfactAgent({
|
|
328
743
|
publishableKey,
|
|
329
744
|
widgetOrigin,
|
|
@@ -335,16 +750,21 @@ function MatterfactAgent({
|
|
|
335
750
|
className,
|
|
336
751
|
style,
|
|
337
752
|
pageContext,
|
|
338
|
-
dev
|
|
753
|
+
dev,
|
|
754
|
+
actions
|
|
339
755
|
}) {
|
|
340
756
|
const authRef = useRef(getAuthToken);
|
|
341
757
|
authRef.current = getAuthToken;
|
|
342
758
|
const pageContextRef = useRef(getPageContext);
|
|
343
759
|
pageContextRef.current = getPageContext;
|
|
760
|
+
const actionsRef = useRef(actions);
|
|
761
|
+
actionsRef.current = actions;
|
|
762
|
+
const actionsKey = JSON.stringify(actions ?? null);
|
|
344
763
|
const slot = useRef(null);
|
|
345
764
|
useEffect(() => {
|
|
346
765
|
if (typeof window === "undefined") return;
|
|
347
766
|
if (inline && !slot.current) return;
|
|
767
|
+
writeActionsGlobal(actionsRef.current);
|
|
348
768
|
const config = {
|
|
349
769
|
publishableKey,
|
|
350
770
|
origin: widgetOrigin || DEFAULT_ORIGIN,
|
|
@@ -367,7 +787,16 @@ function MatterfactAgent({
|
|
|
367
787
|
console.error("[matterfact] failed to mount the embed widget", e);
|
|
368
788
|
}
|
|
369
789
|
return () => host?.destroy();
|
|
370
|
-
}, [
|
|
790
|
+
}, [
|
|
791
|
+
publishableKey,
|
|
792
|
+
widgetOrigin,
|
|
793
|
+
surface,
|
|
794
|
+
theme,
|
|
795
|
+
inline,
|
|
796
|
+
pageContext,
|
|
797
|
+
dev,
|
|
798
|
+
actionsKey
|
|
799
|
+
]);
|
|
371
800
|
if (!inline) return null;
|
|
372
801
|
return /* @__PURE__ */ jsx(
|
|
373
802
|
"div",
|