@runtypelabs/persona 3.0.0 → 3.1.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 +2 -2
- package/dist/index.cjs +29 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +68 -68
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +29 -29
- package/dist/index.js.map +1 -1
- package/dist/widget.css +83 -83
- package/package.json +1 -3
- package/src/components/artifact-pane.ts +3 -3
- package/src/components/header-builder.ts +1 -1
- package/src/runtime/host-layout.test.ts +137 -0
- package/src/runtime/host-layout.ts +81 -0
- package/src/runtime/init.test.ts +36 -1
- package/src/runtime/init.ts +1 -1
- package/src/styles/widget.css +83 -83
- package/src/ui.ts +6 -4
- package/src/utils/artifact-gate.ts +1 -1
- package/src/styles/tailwind.css +0 -20
|
@@ -193,4 +193,141 @@ describe("createWidgetHostLayout docked", () => {
|
|
|
193
193
|
|
|
194
194
|
layout.destroy();
|
|
195
195
|
});
|
|
196
|
+
|
|
197
|
+
const withInnerWidth = (width: number, fn: () => void): void => {
|
|
198
|
+
const prev = window.innerWidth;
|
|
199
|
+
try {
|
|
200
|
+
Object.defineProperty(window, "innerWidth", {
|
|
201
|
+
configurable: true,
|
|
202
|
+
value: width,
|
|
203
|
+
});
|
|
204
|
+
fn();
|
|
205
|
+
} finally {
|
|
206
|
+
Object.defineProperty(window, "innerWidth", {
|
|
207
|
+
configurable: true,
|
|
208
|
+
value: prev,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
it("uses fixed fullscreen dock slot on mobile viewport when open", () => {
|
|
214
|
+
withInnerWidth(500, () => {
|
|
215
|
+
const parent = document.createElement("div");
|
|
216
|
+
document.body.appendChild(parent);
|
|
217
|
+
const target = document.createElement("div");
|
|
218
|
+
parent.appendChild(target);
|
|
219
|
+
|
|
220
|
+
const layout = createWidgetHostLayout(target, {
|
|
221
|
+
launcher: {
|
|
222
|
+
mountMode: "docked",
|
|
223
|
+
autoExpand: false,
|
|
224
|
+
dock: { width: "320px" },
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const dockSlot = layout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
|
|
229
|
+
layout.syncWidgetState({ open: true, launcherEnabled: true });
|
|
230
|
+
expect(dockSlot?.style.position).toBe("fixed");
|
|
231
|
+
expect(dockSlot?.style.zIndex).toBe("9999");
|
|
232
|
+
expect(layout.shell?.dataset.personaDockMobileFullscreen).toBe("true");
|
|
233
|
+
|
|
234
|
+
layout.destroy();
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("does not use fixed fullscreen above mobile breakpoint", () => {
|
|
239
|
+
withInnerWidth(800, () => {
|
|
240
|
+
const parent = document.createElement("div");
|
|
241
|
+
document.body.appendChild(parent);
|
|
242
|
+
const target = document.createElement("div");
|
|
243
|
+
parent.appendChild(target);
|
|
244
|
+
|
|
245
|
+
const layout = createWidgetHostLayout(target, {
|
|
246
|
+
launcher: {
|
|
247
|
+
mountMode: "docked",
|
|
248
|
+
autoExpand: false,
|
|
249
|
+
dock: { width: "320px", reveal: "overlay" },
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const dockSlot = layout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
|
|
254
|
+
layout.syncWidgetState({ open: true, launcherEnabled: true });
|
|
255
|
+
expect(dockSlot?.style.position).toBe("absolute");
|
|
256
|
+
expect(layout.shell?.dataset.personaDockMobileFullscreen).toBeUndefined();
|
|
257
|
+
|
|
258
|
+
layout.destroy();
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("respects mobileFullscreen: false on narrow viewport", () => {
|
|
263
|
+
withInnerWidth(500, () => {
|
|
264
|
+
const parent = document.createElement("div");
|
|
265
|
+
document.body.appendChild(parent);
|
|
266
|
+
const target = document.createElement("div");
|
|
267
|
+
parent.appendChild(target);
|
|
268
|
+
|
|
269
|
+
const layout = createWidgetHostLayout(target, {
|
|
270
|
+
launcher: {
|
|
271
|
+
mountMode: "docked",
|
|
272
|
+
autoExpand: false,
|
|
273
|
+
mobileFullscreen: false,
|
|
274
|
+
dock: { width: "320px" },
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const dockSlot = layout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
|
|
279
|
+
layout.syncWidgetState({ open: true, launcherEnabled: true });
|
|
280
|
+
expect(dockSlot?.style.position).toBe("relative");
|
|
281
|
+
expect(layout.shell?.dataset.personaDockMobileFullscreen).toBeUndefined();
|
|
282
|
+
|
|
283
|
+
layout.destroy();
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("respects custom mobileBreakpoint", () => {
|
|
288
|
+
withInnerWidth(900, () => {
|
|
289
|
+
const parent = document.createElement("div");
|
|
290
|
+
document.body.appendChild(parent);
|
|
291
|
+
const target = document.createElement("div");
|
|
292
|
+
parent.appendChild(target);
|
|
293
|
+
|
|
294
|
+
const layout = createWidgetHostLayout(target, {
|
|
295
|
+
launcher: {
|
|
296
|
+
mountMode: "docked",
|
|
297
|
+
autoExpand: false,
|
|
298
|
+
mobileBreakpoint: 1024,
|
|
299
|
+
dock: { width: "320px" },
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const dockSlot = layout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
|
|
304
|
+
layout.syncWidgetState({ open: true, launcherEnabled: true });
|
|
305
|
+
expect(dockSlot?.style.position).toBe("fixed");
|
|
306
|
+
|
|
307
|
+
layout.destroy();
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it("does not use fixed fullscreen when panel is closed on mobile", () => {
|
|
312
|
+
withInnerWidth(500, () => {
|
|
313
|
+
const parent = document.createElement("div");
|
|
314
|
+
document.body.appendChild(parent);
|
|
315
|
+
const target = document.createElement("div");
|
|
316
|
+
parent.appendChild(target);
|
|
317
|
+
|
|
318
|
+
const layout = createWidgetHostLayout(target, {
|
|
319
|
+
launcher: {
|
|
320
|
+
mountMode: "docked",
|
|
321
|
+
autoExpand: false,
|
|
322
|
+
dock: { width: "320px" },
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const dockSlot = layout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
|
|
327
|
+
layout.syncWidgetState({ open: false, launcherEnabled: true });
|
|
328
|
+
expect(dockSlot?.style.position).toBe("relative");
|
|
329
|
+
|
|
330
|
+
layout.destroy();
|
|
331
|
+
});
|
|
332
|
+
});
|
|
196
333
|
});
|
|
@@ -46,6 +46,16 @@ const clearOverlayDockSlotStyles = (dockSlot: HTMLElement): void => {
|
|
|
46
46
|
dockSlot.style.pointerEvents = "";
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
/** Clears viewport-escape fullscreen styles so reveal modes can re-apply dock layout. */
|
|
50
|
+
const clearMobileFullscreenDockSlotStyles = (dockSlot: HTMLElement): void => {
|
|
51
|
+
dockSlot.style.inset = "";
|
|
52
|
+
dockSlot.style.width = "";
|
|
53
|
+
dockSlot.style.height = "";
|
|
54
|
+
dockSlot.style.maxWidth = "";
|
|
55
|
+
dockSlot.style.minWidth = "";
|
|
56
|
+
clearOverlayDockSlotStyles(dockSlot);
|
|
57
|
+
};
|
|
58
|
+
|
|
49
59
|
const clearResizeDockSlotTransition = (dockSlot: HTMLElement): void => {
|
|
50
60
|
dockSlot.style.transition = "";
|
|
51
61
|
};
|
|
@@ -153,6 +163,70 @@ const applyDockStyles = (
|
|
|
153
163
|
host.style.flexDirection = "column";
|
|
154
164
|
host.style.flex = "1 1 auto";
|
|
155
165
|
|
|
166
|
+
const ownerWindow = shell.ownerDocument.defaultView;
|
|
167
|
+
const mobileFullscreenEnabled = config?.launcher?.mobileFullscreen ?? true;
|
|
168
|
+
const mobileBreakpoint = config?.launcher?.mobileBreakpoint ?? 640;
|
|
169
|
+
const isMobileViewport =
|
|
170
|
+
ownerWindow != null ? ownerWindow.innerWidth <= mobileBreakpoint : false;
|
|
171
|
+
const useMobileFullscreen = mobileFullscreenEnabled && isMobileViewport && expanded;
|
|
172
|
+
|
|
173
|
+
if (useMobileFullscreen) {
|
|
174
|
+
shell.dataset.personaDockMobileFullscreen = "true";
|
|
175
|
+
shell.removeAttribute("data-persona-dock-reveal");
|
|
176
|
+
clearPushTrackStyles(pushTrack);
|
|
177
|
+
clearResizeDockSlotTransition(dockSlot);
|
|
178
|
+
clearMobileFullscreenDockSlotStyles(dockSlot);
|
|
179
|
+
resetContentSlotFlexSizing(contentSlot);
|
|
180
|
+
clearEmergeDockStyles(host, dockSlot);
|
|
181
|
+
|
|
182
|
+
shell.style.display = "flex";
|
|
183
|
+
shell.style.flexDirection = "column";
|
|
184
|
+
shell.style.alignItems = "stretch";
|
|
185
|
+
shell.style.overflow = "hidden";
|
|
186
|
+
|
|
187
|
+
contentSlot.style.flex = "1 1 auto";
|
|
188
|
+
contentSlot.style.width = "100%";
|
|
189
|
+
contentSlot.style.minWidth = "0";
|
|
190
|
+
|
|
191
|
+
dockSlot.style.display = "flex";
|
|
192
|
+
dockSlot.style.flexDirection = "column";
|
|
193
|
+
dockSlot.style.position = "fixed";
|
|
194
|
+
dockSlot.style.inset = "0";
|
|
195
|
+
dockSlot.style.width = "100%";
|
|
196
|
+
dockSlot.style.height = "100%";
|
|
197
|
+
dockSlot.style.maxWidth = "100%";
|
|
198
|
+
dockSlot.style.minWidth = "0";
|
|
199
|
+
dockSlot.style.minHeight = "0";
|
|
200
|
+
dockSlot.style.overflow = "hidden";
|
|
201
|
+
dockSlot.style.zIndex = "9999";
|
|
202
|
+
dockSlot.style.transform = "none";
|
|
203
|
+
dockSlot.style.transition = "none";
|
|
204
|
+
dockSlot.style.pointerEvents = "auto";
|
|
205
|
+
dockSlot.style.flex = "none";
|
|
206
|
+
|
|
207
|
+
if (usePush) {
|
|
208
|
+
pushTrack.style.display = "flex";
|
|
209
|
+
pushTrack.style.flexDirection = "column";
|
|
210
|
+
pushTrack.style.width = "100%";
|
|
211
|
+
pushTrack.style.height = "100%";
|
|
212
|
+
pushTrack.style.minHeight = "0";
|
|
213
|
+
pushTrack.style.minWidth = "0";
|
|
214
|
+
pushTrack.style.flex = "1 1 auto";
|
|
215
|
+
pushTrack.style.alignItems = "stretch";
|
|
216
|
+
pushTrack.style.transform = "none";
|
|
217
|
+
pushTrack.style.transition = "none";
|
|
218
|
+
contentSlot.style.flex = "1 1 auto";
|
|
219
|
+
contentSlot.style.width = "100%";
|
|
220
|
+
contentSlot.style.maxWidth = "100%";
|
|
221
|
+
contentSlot.style.minWidth = "0";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
shell.removeAttribute("data-persona-dock-mobile-fullscreen");
|
|
228
|
+
clearMobileFullscreenDockSlotStyles(dockSlot);
|
|
229
|
+
|
|
156
230
|
if (dock.reveal === "overlay") {
|
|
157
231
|
shell.style.display = "flex";
|
|
158
232
|
shell.style.flexDirection = "row";
|
|
@@ -359,6 +433,12 @@ const createDockedLayout = (target: HTMLElement, config?: AgentWidgetConfig): Wi
|
|
|
359
433
|
syncPushResizeObserver();
|
|
360
434
|
};
|
|
361
435
|
|
|
436
|
+
const ownerWindow = shell.ownerDocument.defaultView;
|
|
437
|
+
const onViewportResize = (): void => {
|
|
438
|
+
layout();
|
|
439
|
+
};
|
|
440
|
+
ownerWindow?.addEventListener("resize", onViewportResize);
|
|
441
|
+
|
|
362
442
|
if (resolveDockConfig(config).reveal === "push") {
|
|
363
443
|
pushTrack.appendChild(contentSlot);
|
|
364
444
|
pushTrack.appendChild(dockSlot);
|
|
@@ -388,6 +468,7 @@ const createDockedLayout = (target: HTMLElement, config?: AgentWidgetConfig): Wi
|
|
|
388
468
|
layout();
|
|
389
469
|
},
|
|
390
470
|
destroy() {
|
|
471
|
+
ownerWindow?.removeEventListener("resize", onViewportResize);
|
|
391
472
|
disconnectResizeObserver();
|
|
392
473
|
if (originalParent.isConnected) {
|
|
393
474
|
if (originalNextSibling && originalNextSibling.parentNode === originalParent) {
|
package/src/runtime/init.test.ts
CHANGED
|
@@ -301,6 +301,41 @@ describe("initAgentWidget docked mode", () => {
|
|
|
301
301
|
});
|
|
302
302
|
|
|
303
303
|
expect(handle.host.shadowRoot).not.toBeNull();
|
|
304
|
-
expect(handle.host.shadowRoot?.querySelector("
|
|
304
|
+
expect(handle.host.shadowRoot?.querySelector("[data-persona-root]")).not.toBeNull();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("mounts two widgets with independent roots in light DOM", async () => {
|
|
308
|
+
const { initAgentWidget } = await import("./init");
|
|
309
|
+
document.body.innerHTML = `
|
|
310
|
+
<div id="widget-a"></div>
|
|
311
|
+
<div id="widget-b"></div>
|
|
312
|
+
`;
|
|
313
|
+
|
|
314
|
+
const handleA = initAgentWidget({
|
|
315
|
+
target: "#widget-a",
|
|
316
|
+
config: {
|
|
317
|
+
launcher: { enabled: false },
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const handleB = initAgentWidget({
|
|
322
|
+
target: "#widget-b",
|
|
323
|
+
config: {
|
|
324
|
+
launcher: { enabled: false },
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const roots = document.querySelectorAll("[data-persona-root]");
|
|
329
|
+
expect(roots.length).toBe(2);
|
|
330
|
+
|
|
331
|
+
// Each root should be inside its respective target
|
|
332
|
+
const rootA = document.querySelector("#widget-a [data-persona-root]");
|
|
333
|
+
const rootB = document.querySelector("#widget-b [data-persona-root]");
|
|
334
|
+
expect(rootA).not.toBeNull();
|
|
335
|
+
expect(rootB).not.toBeNull();
|
|
336
|
+
expect(rootA).not.toBe(rootB);
|
|
337
|
+
|
|
338
|
+
handleA.destroy();
|
|
339
|
+
handleB.destroy();
|
|
305
340
|
});
|
|
306
341
|
});
|
package/src/runtime/init.ts
CHANGED
|
@@ -103,7 +103,7 @@ export const initAgentWidget = (
|
|
|
103
103
|
const launcherEnabled = nextConfig?.launcher?.enabled ?? true;
|
|
104
104
|
const shouldFillHost = !launcherEnabled || isDockedMountMode(nextConfig);
|
|
105
105
|
const mount = ownerDocument.createElement("div");
|
|
106
|
-
mount.
|
|
106
|
+
mount.setAttribute("data-persona-root", "true");
|
|
107
107
|
|
|
108
108
|
if (shouldFillHost) {
|
|
109
109
|
mount.style.height = "100%";
|