@runtypelabs/persona 4.23.1 → 4.24.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/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-9Vxm-PPz.d.cts → types-Db61aI9J.d.cts} +25 -2
- package/dist/animations/{types-9Vxm-PPz.d.ts → types-Db61aI9J.d.ts} +25 -2
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/animations-extra.d.cts +25 -2
- package/dist/animations-extra.d.ts +25 -2
- package/dist/approval-ui-3NMYNXBD.js +1 -0
- package/dist/approval-ui.cjs +2 -2
- package/dist/approval-ui.d.cts +261 -40
- package/dist/approval-ui.d.ts +261 -40
- package/dist/approval-ui.js +2 -2
- package/dist/{artifacts-ui-F4N6VMKF.js → artifacts-ui-IGB5YKLO.js} +4 -4
- package/dist/artifacts-ui.d.cts +256 -28
- package/dist/artifacts-ui.d.ts +256 -28
- package/dist/{chunk-NIKYKSNZ.js → chunk-7CO4T5KB.js} +1 -1
- package/dist/codegen.cjs +11 -11
- package/dist/codegen.js +13 -13
- package/dist/context-mentions.d.cts +256 -28
- package/dist/context-mentions.d.ts +256 -28
- package/dist/event-stream-view.d.cts +256 -28
- package/dist/event-stream-view.d.ts +256 -28
- package/dist/forms-ui.d.cts +291 -30
- package/dist/forms-ui.d.ts +291 -30
- package/dist/history-view.d.cts +25 -2
- package/dist/history-view.d.ts +25 -2
- package/dist/index.cjs +70 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +337 -51
- package/dist/index.d.ts +337 -51
- package/dist/index.global.js +57 -54
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +70 -69
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/launcher.global.js +2 -2
- package/dist/launcher.global.js.map +1 -1
- package/dist/session-reconnect.d.cts +256 -28
- package/dist/session-reconnect.d.ts +256 -28
- package/dist/smart-dom-reader.d.cts +256 -28
- package/dist/smart-dom-reader.d.ts +256 -28
- package/dist/theme-editor-preview.cjs +74 -73
- package/dist/theme-editor-preview.d.cts +263 -28
- package/dist/theme-editor-preview.d.ts +263 -28
- package/dist/theme-editor-preview.js +51 -50
- package/dist/theme-editor.cjs +10 -10
- package/dist/theme-editor.d.cts +276 -30
- package/dist/theme-editor.d.ts +276 -30
- package/dist/theme-editor.js +10 -10
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +1522 -11
- package/dist/theme-reference.d.ts +1522 -11
- package/dist/theme-reference.js +1 -1
- package/dist/widget.css +1 -1
- package/package.json +3 -2
- package/src/__fixtures__/defaults-parity.v4.json +776 -0
- package/src/approval-ui-entry.ts +0 -1
- package/src/approval-ui-loader.ts +5 -4
- package/src/approval-ui.ts +0 -1
- package/src/client-identity-proof.test.ts +193 -0
- package/src/client-session-warm.test.ts +216 -0
- package/src/client-visitor-history.test.ts +6 -0
- package/src/client.tool-pair-replay.test.ts +325 -0
- package/src/client.ts +154 -61
- package/src/codegen.test.ts +24 -0
- package/src/components/activity-row.test.ts +100 -0
- package/src/components/activity-row.ts +136 -0
- package/src/components/approval-actions.test.ts +8 -5
- package/src/components/approval-actions.ts +19 -6
- package/src/components/approval-bubble.test.ts +56 -9
- package/src/components/approval-bubble.ts +36 -41
- package/src/components/composer-builder.ts +5 -2
- package/src/components/composer-parts.ts +10 -2
- package/src/components/header-builder.test.ts +32 -0
- package/src/components/header-builder.ts +7 -4
- package/src/components/header-layouts.test.ts +12 -0
- package/src/components/header-layouts.ts +3 -2
- package/src/components/launcher.ts +11 -2
- package/src/components/message-bubble.test.ts +22 -0
- package/src/components/message-bubble.ts +7 -3
- package/src/components/panel.test.ts +52 -1
- package/src/components/panel.ts +47 -8
- package/src/components/pill-composer-builder.test.ts +2 -0
- package/src/components/pill-composer-builder.ts +0 -1
- package/src/components/reasoning-bubble.ts +29 -17
- package/src/components/suggestions.test.ts +1 -0
- package/src/components/suggestions.ts +0 -16
- package/src/components/tool-bubble.ts +131 -114
- package/src/components/tool-details.test.ts +87 -0
- package/src/components/tool-details.ts +102 -0
- package/src/components/welcome.test.ts +8 -0
- package/src/components/welcome.ts +4 -2
- package/src/defaults-parity.test.ts +158 -0
- package/src/defaults.test.ts +69 -0
- package/src/defaults.ts +128 -49
- package/src/generated/runtype-openapi-contract.ts +28 -13
- package/src/index-core.ts +2 -0
- package/src/install.test.ts +24 -0
- package/src/install.ts +3 -1
- package/src/launcher-global.ts +2 -1
- package/src/launcher-variants.test.ts +52 -0
- package/src/runtime/init-update-reset.test.ts +21 -0
- package/src/session.ts +61 -9
- package/src/styles/widget-styles.test.ts +19 -0
- package/src/styles/widget.css +295 -23
- package/src/theme-editor/index.ts +2 -0
- package/src/theme-editor/preview-utils.test.ts +8 -0
- package/src/theme-editor/preview-utils.ts +7 -3
- package/src/theme-editor/sections.test.ts +31 -0
- package/src/theme-editor/sections.ts +55 -1
- package/src/theme-editor/state-preferences.test.ts +49 -0
- package/src/theme-editor/state.ts +73 -15
- package/src/theme-reference.test.ts +20 -0
- package/src/theme-reference.ts +11 -5
- package/src/tool-call-display-defaults.test.ts +33 -1
- package/src/types/theme.ts +80 -3
- package/src/types.ts +179 -23
- package/src/ui.activity-row.test.ts +91 -0
- package/src/ui.approval-plugin.test.ts +63 -0
- package/src/ui.composer-compact.test.ts +24 -2
- package/src/ui.composer-placement.test.ts +23 -0
- package/src/ui.detached-panel.test.ts +33 -6
- package/src/ui.docked.test.ts +19 -0
- package/src/ui.header-update-stability.test.ts +25 -0
- package/src/ui.identity-proof.test.ts +58 -0
- package/src/ui.launcher-teaser.test.ts +25 -0
- package/src/ui.message-width.test.ts +17 -2
- package/src/ui.reasoning-display.test.ts +92 -0
- package/src/ui.rebuild-composer.test.ts +20 -3
- package/src/ui.scroll.test.ts +32 -0
- package/src/ui.session-init.test.ts +312 -0
- package/src/ui.status-indicator.test.ts +82 -0
- package/src/ui.suggest-replies.test.ts +11 -0
- package/src/ui.ts +363 -74
- package/src/ui.v5-defaults.test.ts +144 -0
- package/src/ui.welcome.test.ts +92 -3
- package/src/utils/__snapshots__/tokens-parity.test.ts.snap +811 -140
- package/src/utils/code-generators.ts +14 -0
- package/src/utils/composer-compact.test.ts +0 -2
- package/src/utils/composer-compact.ts +2 -4
- package/src/utils/composer-placement.ts +2 -3
- package/src/utils/config-merge.test.ts +81 -5
- package/src/utils/config-merge.ts +13 -3
- package/src/utils/defaults-provenance.ts +67 -0
- package/src/utils/dock.ts +7 -1
- package/src/utils/launcher-variant.ts +16 -0
- package/src/utils/panel-config.ts +97 -0
- package/src/utils/status-indicator.ts +39 -0
- package/src/utils/theme.test.ts +182 -0
- package/src/utils/theme.ts +131 -18
- package/src/utils/tokens.test.ts +122 -1
- package/src/utils/tokens.ts +177 -17
- package/src/utils/tool-pair-replay.ts +100 -0
- package/src/welcome.test.ts +23 -0
- package/src/welcome.ts +16 -9
- package/dist/approval-ui-K7R5Q5OC.js +0 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { createAgentExperience } from "./ui";
|
|
4
|
+
|
|
5
|
+
const controllers: ReturnType<typeof createAgentExperience>[] = [];
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
controllers.splice(0).forEach((controller) => controller.destroy());
|
|
8
|
+
document.body.replaceChildren();
|
|
9
|
+
vi.restoreAllMocks();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe.each([false, true])("core defaults lifecycle (v5=%s)", (v5Defaults) => {
|
|
13
|
+
it("applies a live defaults switch to existing messages and header", () => {
|
|
14
|
+
window.scrollTo = vi.fn();
|
|
15
|
+
const mount = document.createElement("div");
|
|
16
|
+
document.body.append(mount);
|
|
17
|
+
const controller = createAgentExperience(mount, {
|
|
18
|
+
apiUrl: "/dispatch", persistState: false,
|
|
19
|
+
future: { v5Defaults }, launcher: { autoExpand: true },
|
|
20
|
+
});
|
|
21
|
+
controllers.push(controller);
|
|
22
|
+
controller.injectUserMessage({ content: "Hello" });
|
|
23
|
+
controller.injectAssistantMessage({ content: "Welcome" });
|
|
24
|
+
const assertState = (v5: boolean) => {
|
|
25
|
+
expect(mount.dataset.personaDefaults).toBe(v5 ? "v5" : "v4");
|
|
26
|
+
const header = mount.querySelector<HTMLElement>(".persona-widget-header")!;
|
|
27
|
+
expect((header.firstElementChild as HTMLElement).style.width).toBe(v5 ? "20px" : "40px");
|
|
28
|
+
expect(mount.style.getPropertyValue("--persona-components-header-padding"))
|
|
29
|
+
.toBe(v5 ? "8px 8px 8px 16px" : "20px 24px");
|
|
30
|
+
const bubble = mount.querySelector<HTMLElement>(".persona-message-row-assistant .persona-message-bubble")!;
|
|
31
|
+
expect(bubble.style.lineHeight).toBe(v5 ? "var(--persona-message-assistant-line-height, 1.75)" : "");
|
|
32
|
+
const status = mount.querySelector<HTMLElement>("[data-persona-composer-status]")!;
|
|
33
|
+
expect(status.style.display).toBe(v5 ? "none" : "");
|
|
34
|
+
};
|
|
35
|
+
assertState(v5Defaults);
|
|
36
|
+
controller.update({ future: { v5Defaults: !v5Defaults } });
|
|
37
|
+
assertState(!v5Defaults);
|
|
38
|
+
controller.update({ future: { v5Defaults } });
|
|
39
|
+
assertState(v5Defaults);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("exposes composer chrome and honors explicit glyph overrides", () => {
|
|
43
|
+
window.scrollTo = vi.fn();
|
|
44
|
+
const mount = document.createElement("div");
|
|
45
|
+
document.body.append(mount);
|
|
46
|
+
const controller = createAgentExperience(mount, {
|
|
47
|
+
apiUrl: "/dispatch", persistState: false, future: { v5Defaults },
|
|
48
|
+
launcher: { autoExpand: true },
|
|
49
|
+
});
|
|
50
|
+
controllers.push(controller);
|
|
51
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-sendIconSize"))
|
|
52
|
+
.toBe(v5Defaults ? "18px" : "");
|
|
53
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-sendButtonRadius"))
|
|
54
|
+
.toBe(v5Defaults ? "9999px" : "");
|
|
55
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-footerBorder"))
|
|
56
|
+
.toBe(v5Defaults ? "none" : "");
|
|
57
|
+
controller.update({
|
|
58
|
+
sendButton: { iconName: "send", iconSize: "15px" },
|
|
59
|
+
theme: { components: { composer: {
|
|
60
|
+
sendIconSize: "22px", sendButtonRadius: "8px", footerBorder: "2px solid red",
|
|
61
|
+
} } },
|
|
62
|
+
});
|
|
63
|
+
const svg = mount.querySelector<SVGElement>("[data-persona-composer-submit] svg")!;
|
|
64
|
+
expect(svg.getAttribute("width")).toBe("15");
|
|
65
|
+
expect(svg.style.width).toBe("");
|
|
66
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-sendButtonRadius")).toBe("8px");
|
|
67
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-footerBorder")).toBe("2px solid red");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("switches composer focus defaults live and retains explicit focus and resting chrome", () => {
|
|
71
|
+
window.scrollTo = vi.fn();
|
|
72
|
+
const mount = document.createElement("div");
|
|
73
|
+
document.body.append(mount);
|
|
74
|
+
const controller = createAgentExperience(mount, {
|
|
75
|
+
apiUrl: "/dispatch", persistState: false, future: { v5Defaults },
|
|
76
|
+
launcher: { autoExpand: true },
|
|
77
|
+
theme: { components: { composer: { borderColor: "#123456", shadow: "0 3px 8px #123456" } } },
|
|
78
|
+
});
|
|
79
|
+
controllers.push(controller);
|
|
80
|
+
const assertDefaults = (v5: boolean) => {
|
|
81
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-focusBorderColor"))
|
|
82
|
+
.toBe(v5 ? "color-mix(in srgb, var(--persona-text) 28%, var(--persona-border))" : "");
|
|
83
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-focusRing"))
|
|
84
|
+
.toBe(v5 ? "1px solid color-mix(in srgb, var(--persona-text) 12%, transparent)" : "");
|
|
85
|
+
expect(mount.style.getPropertyValue("--persona-composer-border-color")).toBe("#123456");
|
|
86
|
+
expect(mount.style.getPropertyValue("--persona-composer-shadow")).toBe("0 3px 8px #123456");
|
|
87
|
+
// No inline outline may suppress the stylesheet's ring.
|
|
88
|
+
expect(mount.querySelector<HTMLElement>("[data-persona-composer-form]")!.style.outline).toBe("");
|
|
89
|
+
};
|
|
90
|
+
assertDefaults(v5Defaults);
|
|
91
|
+
controller.update({ future: { v5Defaults: !v5Defaults } });
|
|
92
|
+
assertDefaults(!v5Defaults);
|
|
93
|
+
controller.update({ theme: { components: { composer: {
|
|
94
|
+
focusBorderColor: "#abcdef", focusRing: "2px solid #fedcba",
|
|
95
|
+
} } } });
|
|
96
|
+
for (const state of [v5Defaults, !v5Defaults]) {
|
|
97
|
+
controller.update({ future: { v5Defaults: state } });
|
|
98
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-focusBorderColor")).toBe("#abcdef");
|
|
99
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-focusRing")).toBe("2px solid #fedcba");
|
|
100
|
+
}
|
|
101
|
+
controller.update({ theme: { components: { composer: { focusRing: "none" } } } });
|
|
102
|
+
expect(mount.style.getPropertyValue("--persona-components-composer-focusRing")).toBe("none");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("switches default placement and fade while retaining explicit choices", () => {
|
|
106
|
+
window.scrollTo = vi.fn();
|
|
107
|
+
const mount = document.createElement("div");
|
|
108
|
+
document.body.append(mount);
|
|
109
|
+
const controller = createAgentExperience(mount, {
|
|
110
|
+
apiUrl: "/dispatch", persistState: false, future: { v5Defaults },
|
|
111
|
+
launcher: { autoExpand: true },
|
|
112
|
+
});
|
|
113
|
+
controllers.push(controller);
|
|
114
|
+
for (const state of [v5Defaults, !v5Defaults]) {
|
|
115
|
+
controller.update({ future: { v5Defaults: state } });
|
|
116
|
+
expect(mount.dataset.personaComposerPlacement).toBe(state ? "overlay" : "block");
|
|
117
|
+
expect(mount.style.getPropertyValue("--persona-composer-overlay-band"))
|
|
118
|
+
.toBe(state ? "linear-gradient(to bottom, transparent, var(--persona-container) 24px)" : "transparent");
|
|
119
|
+
}
|
|
120
|
+
controller.update({ composer: { placement: "block" }, theme: { components: { composer: { overlayBand: "transparent" } } } });
|
|
121
|
+
controller.update({ future: { v5Defaults: !v5Defaults } });
|
|
122
|
+
expect(mount.dataset.personaComposerPlacement).toBe("block");
|
|
123
|
+
expect(mount.style.getPropertyValue("--persona-composer-overlay-band")).toBe("transparent");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("uses fullscreen turn spacing in full-height embeds and honors explicit spacing", () => {
|
|
127
|
+
window.scrollTo = vi.fn();
|
|
128
|
+
const mount = document.createElement("div");
|
|
129
|
+
document.body.append(mount);
|
|
130
|
+
const controller = createAgentExperience(mount, {
|
|
131
|
+
apiUrl: "/dispatch", persistState: false, future: { v5Defaults },
|
|
132
|
+
launcher: { enabled: false, fullHeight: true },
|
|
133
|
+
});
|
|
134
|
+
controllers.push(controller);
|
|
135
|
+
const messages = mount.querySelector<HTMLElement>(".persona-widget-messages")!;
|
|
136
|
+
expect(messages.style.gap).toContain("--persona-components-message-fullscreenGap");
|
|
137
|
+
expect(mount.style.getPropertyValue("--persona-components-message-fullscreenGap"))
|
|
138
|
+
.toBe(v5Defaults ? "28px" : "");
|
|
139
|
+
controller.update({ theme: { components: { message: { gap: "33px" } } } });
|
|
140
|
+
expect(mount.style.getPropertyValue("--persona-components-message-fullscreenGap")).toBe("33px");
|
|
141
|
+
controller.update({ theme: { components: { message: { fullscreenGap: "42px" } } } });
|
|
142
|
+
expect(mount.style.getPropertyValue("--persona-components-message-fullscreenGap")).toBe("42px");
|
|
143
|
+
});
|
|
144
|
+
});
|
package/src/ui.welcome.test.ts
CHANGED
|
@@ -328,8 +328,8 @@ describe("welcome visibility state machine", () => {
|
|
|
328
328
|
});
|
|
329
329
|
|
|
330
330
|
describe("welcome greeting bubble", () => {
|
|
331
|
-
it("renders an assistant-styled bubble above the transcript", () => {
|
|
332
|
-
const { mount } = makeController({ welcome: { message: "Hi, I'm Ada." } });
|
|
331
|
+
it.each([false, true])("renders an assistant-styled bubble above the transcript (v5=%s)", (v5Defaults) => {
|
|
332
|
+
const { mount } = makeController({ future: { v5Defaults }, welcome: { message: "Hi, I'm Ada." } });
|
|
333
333
|
const greeting = greetingHost(mount);
|
|
334
334
|
expect(isVisible(greeting)).toBe(true);
|
|
335
335
|
expect(greeting.textContent).toBe("Hi, I'm Ada.");
|
|
@@ -342,7 +342,7 @@ describe("welcome greeting bubble", () => {
|
|
|
342
342
|
const body = mount.querySelector<HTMLElement>("#persona-scroll-container")!;
|
|
343
343
|
const children = Array.from(body.children);
|
|
344
344
|
const transcript = body.querySelector<HTMLElement>(
|
|
345
|
-
":scope > .persona-
|
|
345
|
+
":scope > .persona-widget-messages"
|
|
346
346
|
)!;
|
|
347
347
|
expect(children.indexOf(greeting)).toBeLessThan(
|
|
348
348
|
children.indexOf(transcript)
|
|
@@ -508,3 +508,92 @@ describe("welcome live updates", () => {
|
|
|
508
508
|
expect(isVisible(welcomeHost(mount))).toBe(true);
|
|
509
509
|
});
|
|
510
510
|
});
|
|
511
|
+
|
|
512
|
+
describe("centered welcome", () => {
|
|
513
|
+
it("groups legacy starters in the V5 welcome and restores them after clear", () => {
|
|
514
|
+
const { mount, controller } = makeController({ future: { v5Defaults: true }, suggestionChips: ["One", "Two", "Three", "Four", "Five"] });
|
|
515
|
+
const host = welcomeHost(mount);
|
|
516
|
+
expect(host.getAttribute("data-persona-welcome-layout")).toBe("centered");
|
|
517
|
+
expect(host.querySelector("h2")?.textContent).toBe("What can I help with?");
|
|
518
|
+
expect(host.querySelectorAll(".persona-suggestion")).toHaveLength(3);
|
|
519
|
+
expect(host.querySelector(".persona-suggestions")?.getAttribute("data-variant")).toBe("chip");
|
|
520
|
+
controller.injectTestMessage({ type: "message", message: assistantMessage() });
|
|
521
|
+
expect(isVisible(host)).toBe(true);
|
|
522
|
+
sendUserMessage(controller);
|
|
523
|
+
expect(isVisible(host)).toBe(false);
|
|
524
|
+
controller.clearChat();
|
|
525
|
+
expect(isVisible(host)).toBe(true);
|
|
526
|
+
expect(host.querySelectorAll(".persona-suggestion")).toHaveLength(3);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it("raises fullscreen empty composers and defaults starters to four cards", () => {
|
|
530
|
+
const { mount } = makeController({ future: { v5Defaults: true }, launcher: { enabled: false, fullHeight: true }, suggestionChips: ["One", "Two", "Three", "Four", "Five"] });
|
|
531
|
+
expect(mount.getAttribute("data-persona-welcome-anchor")).toBe("center");
|
|
532
|
+
expect(welcomeHost(mount).querySelectorAll(".persona-suggestion")).toHaveLength(4);
|
|
533
|
+
expect(welcomeHost(mount).querySelector(".persona-suggestions")?.getAttribute("data-variant")).toBe("card");
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("updates starter density when the floating panel becomes mobile fullscreen", () => {
|
|
538
|
+
const originalWidth = window.innerWidth;
|
|
539
|
+
try {
|
|
540
|
+
Object.defineProperty(window, "innerWidth", { configurable: true, value: 1000 });
|
|
541
|
+
const { mount } = makeController({ future: { v5Defaults: true }, launcher: { enabled: true }, suggestionChips: ["One", "Two", "Three", "Four", "Five"] });
|
|
542
|
+
expect(welcomeHost(mount).querySelectorAll(".persona-suggestion")).toHaveLength(3);
|
|
543
|
+
Object.defineProperty(window, "innerWidth", { configurable: true, value: 500 });
|
|
544
|
+
window.dispatchEvent(new Event("resize"));
|
|
545
|
+
expect(mount.getAttribute("data-persona-welcome-anchor")).toBe("center");
|
|
546
|
+
expect(welcomeHost(mount).querySelectorAll(".persona-suggestion")).toHaveLength(4);
|
|
547
|
+
Object.defineProperty(window, "innerWidth", { configurable: true, value: 1000 });
|
|
548
|
+
window.dispatchEvent(new Event("resize"));
|
|
549
|
+
expect(mount.getAttribute("data-persona-welcome-anchor")).toBe("bottom");
|
|
550
|
+
expect(welcomeHost(mount).querySelectorAll(".persona-suggestion")).toHaveLength(3);
|
|
551
|
+
} finally {
|
|
552
|
+
Object.defineProperty(window, "innerWidth", { configurable: true, value: originalWidth });
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
describe.each([false, true])("explicit welcome layout (V5 defaults: %s)", (v5Defaults) => {
|
|
557
|
+
it("makes top override hero centering in empty and active states", () => {
|
|
558
|
+
const { mount, controller } = makeController({ future: { v5Defaults }, welcome: { layout: "top", variant: "hero", dismiss: "never" } });
|
|
559
|
+
expect(mount.getAttribute("data-persona-welcome-layout-explicit")).toBe("top");
|
|
560
|
+
expect(isVisible(welcomeHost(mount))).toBe(true);
|
|
561
|
+
sendUserMessage(controller);
|
|
562
|
+
expect(mount.getAttribute("data-persona-welcome-layout-explicit")).toBe("top");
|
|
563
|
+
expect(isVisible(welcomeHost(mount))).toBe(true);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it("centers and resets explicit greeting/starters while honoring overrides", () => {
|
|
567
|
+
const { mount, controller } = makeController({
|
|
568
|
+
future: { v5Defaults },
|
|
569
|
+
welcome: { layout: "centered", title: "Custom", subtitle: "Scope", dismiss: "on-first-message", align: "start", anchor: "bottom" },
|
|
570
|
+
suggestions: { starters: { items: ["One", "Two", "Three", "Four", "Five"], maxItems: 2, variant: "list", behavior: "fill" } },
|
|
571
|
+
});
|
|
572
|
+
expect(welcomeHost(mount).getAttribute("data-persona-welcome-layout")).toBe("centered");
|
|
573
|
+
expect(welcomeHost(mount).getAttribute("data-persona-welcome-align")).toBe("start");
|
|
574
|
+
expect(mount.getAttribute("data-persona-welcome-anchor")).toBe("bottom");
|
|
575
|
+
expect(welcomeHost(mount).querySelector("h2")?.textContent).toBe("Custom");
|
|
576
|
+
expect(welcomeHost(mount).querySelectorAll(".persona-suggestion")).toHaveLength(2);
|
|
577
|
+
controller.injectTestMessage({ type: "message", message: assistantMessage() });
|
|
578
|
+
expect(isVisible(welcomeHost(mount))).toBe(true);
|
|
579
|
+
sendUserMessage(controller);
|
|
580
|
+
expect(isVisible(welcomeHost(mount))).toBe(false);
|
|
581
|
+
controller.clearChat();
|
|
582
|
+
expect(isVisible(welcomeHost(mount))).toBe(true);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it("centers the measured fullscreen group and preserves an explicit percentage", () => {
|
|
586
|
+
const { mount, controller } = makeController({ future: { v5Defaults }, launcher: { enabled: false, fullHeight: true }, composer: { placement: "overlay" }, welcome: { layout: "centered" } });
|
|
587
|
+
const footer = mount.querySelector<HTMLElement>(".persona-widget-footer")!;
|
|
588
|
+
const container = footer.parentElement!;
|
|
589
|
+
Object.defineProperty(container, "clientHeight", { configurable: true, value: 800 });
|
|
590
|
+
Object.defineProperty(footer, "offsetHeight", { configurable: true, value: 100 });
|
|
591
|
+
Object.defineProperty(welcomeHost(mount), "offsetHeight", { configurable: true, value: 200 });
|
|
592
|
+
window.dispatchEvent(new Event("resize"));
|
|
593
|
+
// jsdom has no stylesheet geometry: the zero gap/body origin gives
|
|
594
|
+
// (800 - 200 - 100) / 2 rather than the old 44%-top anchoring.
|
|
595
|
+
expect(mount.style.getPropertyValue("--persona-composer-lift")).toBe("250px");
|
|
596
|
+
controller.update({ welcome: { layout: "centered", anchorComposerTop: "40%" } });
|
|
597
|
+
expect(mount.style.getPropertyValue("--persona-composer-lift")).toBe("380px");
|
|
598
|
+
});
|
|
599
|
+
});
|