@runtypelabs/persona 4.23.2 → 4.25.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-CI7OckWI.d.cts → types-Db61aI9J.d.cts} +2 -0
- package/dist/animations/{types-CI7OckWI.d.ts → types-Db61aI9J.d.ts} +2 -0
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/animations-extra.d.cts +2 -0
- package/dist/animations-extra.d.ts +2 -0
- package/dist/approval-ui-3NMYNXBD.js +1 -0
- package/dist/approval-ui.cjs +2 -2
- package/dist/approval-ui.d.cts +278 -37
- package/dist/approval-ui.d.ts +278 -37
- 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 +273 -25
- package/dist/artifacts-ui.d.ts +273 -25
- 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 +273 -25
- package/dist/context-mentions.d.ts +273 -25
- package/dist/event-stream-view.d.cts +273 -25
- package/dist/event-stream-view.d.ts +273 -25
- package/dist/forms-ui.d.cts +319 -27
- package/dist/forms-ui.d.ts +319 -27
- package/dist/history-view.d.cts +2 -0
- package/dist/history-view.d.ts +2 -0
- package/dist/index.cjs +70 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +344 -39
- package/dist/index.d.ts +344 -39
- 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 +273 -25
- package/dist/session-reconnect.d.ts +273 -25
- package/dist/smart-dom-reader.d.cts +273 -25
- package/dist/smart-dom-reader.d.ts +273 -25
- package/dist/theme-editor-preview.cjs +74 -73
- package/dist/theme-editor-preview.d.cts +280 -25
- package/dist/theme-editor-preview.d.ts +280 -25
- package/dist/theme-editor-preview.js +51 -50
- package/dist/theme-editor.cjs +10 -10
- package/dist/theme-editor.d.cts +293 -27
- package/dist/theme-editor.d.ts +293 -27
- 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/voice-runtime-7DA7MJH4.js +1 -0
- package/dist/voice-runtime.cjs +1 -1
- package/dist/voice-runtime.d.cts +33 -0
- package/dist/voice-runtime.d.ts +33 -0
- package/dist/voice-runtime.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.ts +122 -30
- 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 +2 -2
- package/src/index-core.ts +4 -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 +59 -7
- package/src/session.voice.test.ts +72 -1
- 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 +207 -22
- 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 +422 -74
- package/src/ui.v5-defaults.test.ts +144 -0
- package/src/ui.voice-prewarm.test.ts +222 -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/voice/runtype-voice-provider.ts +238 -27
- package/src/voice/voice.test.ts +272 -2
- package/src/welcome.test.ts +23 -0
- package/src/welcome.ts +16 -9
- package/dist/approval-ui-K7R5Q5OC.js +0 -1
- package/dist/voice-runtime-EFR3R5ES.js +0 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { createAgentExperience } from "./ui";
|
|
4
|
+
import { mount as mountCritical } from "./launcher-global";
|
|
5
|
+
import { getActiveTheme, themeToCssVariables } from "./utils/theme";
|
|
6
|
+
import type { AgentWidgetConfig } from "./types";
|
|
7
|
+
|
|
8
|
+
afterEach(() => { document.body.replaceChildren(); vi.restoreAllMocks(); });
|
|
9
|
+
|
|
10
|
+
describe.each([false, true])("launcher variants (v5=%s)", (v5Defaults) => {
|
|
11
|
+
it.each([undefined, "pill", "circle"] as const)("matches deferred and full rendering for %s", (variant) => {
|
|
12
|
+
window.scrollTo = vi.fn();
|
|
13
|
+
const config: AgentWidgetConfig = {
|
|
14
|
+
apiUrl: "/dispatch", persistState: false, future: { v5Defaults },
|
|
15
|
+
launcher: { ...(variant ? { variant } : {}), title: "Open support", teaser: { text: "Need help?" } },
|
|
16
|
+
};
|
|
17
|
+
const host = document.body.appendChild(document.createElement("div"));
|
|
18
|
+
const target = document.body.appendChild(document.createElement("div"));
|
|
19
|
+
const full = createAgentExperience(host, config);
|
|
20
|
+
const critical = mountCritical({ target, config, onOpen: vi.fn() });
|
|
21
|
+
const button = host.querySelector<HTMLButtonElement>(".persona-launcher-surface > button")!;
|
|
22
|
+
try {
|
|
23
|
+
const circle = variant ? variant === "circle" : v5Defaults;
|
|
24
|
+
expect(button.dataset.personaLauncherVariant).toBe(circle ? "circle" : undefined);
|
|
25
|
+
expect(button.innerHTML).toBe(critical.element.innerHTML);
|
|
26
|
+
expect(button.getAttribute("style")).toBe(critical.element.getAttribute("style"));
|
|
27
|
+
expect(host.style.getPropertyValue("--persona-launcher-bg"))
|
|
28
|
+
.toBe(critical.root.style.getPropertyValue("--persona-launcher-bg"));
|
|
29
|
+
if (circle) expect(button.getAttribute("aria-label")).toBe("Open support");
|
|
30
|
+
full.update({ launcher: { variant: circle ? "pill" : "circle" } });
|
|
31
|
+
critical.update({ ...config, launcher: { ...config.launcher, variant: circle ? "pill" : "circle" } });
|
|
32
|
+
expect(button.dataset.personaLauncherVariant).toBe(circle ? undefined : "circle");
|
|
33
|
+
expect(button.innerHTML).toBe(critical.element.innerHTML);
|
|
34
|
+
} finally { full.destroy(); critical.destroy(); }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it.each(["light", "dark"] as const)("resolves circle geometry and explicit tokens in %s", colorScheme => {
|
|
38
|
+
const config: AgentWidgetConfig = { future: { v5Defaults }, colorScheme, launcher: { variant: "circle" } };
|
|
39
|
+
const css = themeToCssVariables(getActiveTheme(config));
|
|
40
|
+
expect(css["--persona-components-launcher-size"]).toBe("48px");
|
|
41
|
+
expect(css["--persona-components-launcher-offset"]).toBe("20px");
|
|
42
|
+
expect(css["--persona-launcher-shadow"]).toBe("0 8px 24px rgba(0,0,0,.16)");
|
|
43
|
+
expect(css["--persona-launcher-bg"]).not.toBe(css["--persona-launcher-fg"]);
|
|
44
|
+
const custom = themeToCssVariables(getActiveTheme({ ...config, theme: { components: { launcher: {
|
|
45
|
+
size: "56px", iconSize: "20px", offset: "32px", background: "#123456", foreground: "#ffffff", shadow: "none",
|
|
46
|
+
} } } }));
|
|
47
|
+
expect(custom["--persona-components-launcher-size"]).toBe("56px");
|
|
48
|
+
expect(custom["--persona-components-launcher-offset"]).toBe("32px");
|
|
49
|
+
expect(custom["--persona-launcher-bg"]).toBe("#123456");
|
|
50
|
+
expect(custom["--persona-launcher-shadow"]).toBe("none");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -78,4 +78,25 @@ describe("handle.update explicit-undefined reset reaches the controller", () =>
|
|
|
78
78
|
|
|
79
79
|
handle.destroy();
|
|
80
80
|
});
|
|
81
|
+
|
|
82
|
+
it("updates the root defaults-version attribute", () => {
|
|
83
|
+
window.scrollTo = vi.fn();
|
|
84
|
+
document.body.innerHTML = `<div id="target"></div>`;
|
|
85
|
+
|
|
86
|
+
const handle = initAgentWidget({
|
|
87
|
+
target: "#target",
|
|
88
|
+
config: {
|
|
89
|
+
apiUrl: "https://api.example.com/chat",
|
|
90
|
+
launcher: { enabled: false },
|
|
91
|
+
future: { v5Defaults: false },
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
const root = () => document.querySelector<HTMLElement>("[data-persona-root]")!;
|
|
95
|
+
|
|
96
|
+
expect(root().getAttribute("data-persona-defaults")).toBe("v4");
|
|
97
|
+
handle.update({ future: { v5Defaults: true } });
|
|
98
|
+
expect(root().getAttribute("data-persona-defaults")).toBe("v5");
|
|
99
|
+
|
|
100
|
+
handle.destroy();
|
|
101
|
+
});
|
|
81
102
|
});
|
package/src/session.ts
CHANGED
|
@@ -144,6 +144,7 @@ const CONNECTION_CONFIG_KEYS = [
|
|
|
144
144
|
"getStoredSessionId",
|
|
145
145
|
"setStoredSessionId",
|
|
146
146
|
"getIdentityProof",
|
|
147
|
+
"identityProvider",
|
|
147
148
|
"getStoredConversationId",
|
|
148
149
|
"setStoredConversationId",
|
|
149
150
|
"clearStoredConversationId",
|
|
@@ -909,6 +910,33 @@ export class AgentWidgetSession {
|
|
|
909
910
|
}
|
|
910
911
|
}
|
|
911
912
|
|
|
913
|
+
/**
|
|
914
|
+
* Warm the voice path ahead of the mic click (`voiceRecognition.prewarm`).
|
|
915
|
+
* Session-owned providers (`runtype`, `custom`) get `provider.prewarm()` once
|
|
916
|
+
* the lazy voice runtime has installed them; the browser (Web Speech) path
|
|
917
|
+
* warms the client-token session instead. Fire-and-forget and silent.
|
|
918
|
+
*/
|
|
919
|
+
public prewarmVoice(): void {
|
|
920
|
+
if (!usesSessionVoice(this.config.voiceRecognition?.provider)) {
|
|
921
|
+
this.warmClientSession();
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const generation = this.voiceSetupGeneration;
|
|
925
|
+
const run = () => {
|
|
926
|
+
const provider = this.voiceProvider;
|
|
927
|
+
if (!provider?.prewarm || generation !== this.voiceSetupGeneration) return;
|
|
928
|
+
if (this.voiceActive || this.isBargeInActive()) return;
|
|
929
|
+
void Promise.resolve()
|
|
930
|
+
.then(() => provider.prewarm?.())
|
|
931
|
+
.catch(() => {});
|
|
932
|
+
};
|
|
933
|
+
if (!this.voiceProvider && this.voiceSetupPromise) {
|
|
934
|
+
void this.voiceSetupPromise.then(run, () => {});
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
run();
|
|
938
|
+
}
|
|
939
|
+
|
|
912
940
|
/**
|
|
913
941
|
* Cleanup voice resources
|
|
914
942
|
*/
|
|
@@ -974,7 +1002,9 @@ export class AgentWidgetSession {
|
|
|
974
1002
|
clientToken: providerConfig.runtype?.clientToken ?? this.config.clientToken,
|
|
975
1003
|
host: providerConfig.runtype?.host ?? this.config.apiUrl,
|
|
976
1004
|
voiceId: providerConfig.runtype?.voiceId,
|
|
977
|
-
createPlaybackEngine: providerConfig.runtype?.createPlaybackEngine
|
|
1005
|
+
createPlaybackEngine: providerConfig.runtype?.createPlaybackEngine,
|
|
1006
|
+
prewarmMode: providerConfig.runtype?.prewarmMode,
|
|
1007
|
+
attachIdleMs: providerConfig.runtype?.attachIdleMs
|
|
978
1008
|
}
|
|
979
1009
|
};
|
|
980
1010
|
|
|
@@ -1033,12 +1063,29 @@ export class AgentWidgetSession {
|
|
|
1033
1063
|
}
|
|
1034
1064
|
}
|
|
1035
1065
|
|
|
1066
|
+
/**
|
|
1067
|
+
* Early, fire-and-forget client-token init (`sessionInit` trigger or
|
|
1068
|
+
* `controller.initSession()`). Failures stay silent here: no `onError`, no
|
|
1069
|
+
* UI. The send re-runs `initSession()` and reports them as it always has.
|
|
1070
|
+
*/
|
|
1071
|
+
public warmClientSession(): void {
|
|
1072
|
+
// The visitor may send before this resolves; the server welcome still
|
|
1073
|
+
// belongs ahead of that first turn when the transcript started empty.
|
|
1074
|
+
const pristine = !this.messages.length;
|
|
1075
|
+
void this.client.warmSession().then((session) => {
|
|
1076
|
+
// A start-new or credential re-init may have replaced it meanwhile.
|
|
1077
|
+
if (session && this.client.getClientSession() === session) {
|
|
1078
|
+
this.setClientSession(session, pristine);
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1036
1083
|
/**
|
|
1037
1084
|
* Set the client session after initialization
|
|
1038
1085
|
*/
|
|
1039
|
-
public setClientSession(session: ClientSession): void {
|
|
1086
|
+
public setClientSession(session: ClientSession, pristine?: boolean): void {
|
|
1040
1087
|
this.clientSession = session;
|
|
1041
|
-
this.injectWelcomeMessage(session);
|
|
1088
|
+
this.injectWelcomeMessage(session, pristine);
|
|
1042
1089
|
}
|
|
1043
1090
|
|
|
1044
1091
|
/**
|
|
@@ -1046,15 +1093,20 @@ export class AgentWidgetSession {
|
|
|
1046
1093
|
* reopening a stored conversation is not a new thread, and the hydrated
|
|
1047
1094
|
* transcript would otherwise race a welcome bubble.
|
|
1048
1095
|
*/
|
|
1049
|
-
private injectWelcomeMessage(
|
|
1096
|
+
private injectWelcomeMessage(
|
|
1097
|
+
session: ClientSession,
|
|
1098
|
+
pristine = !this.messages.length
|
|
1099
|
+
): void {
|
|
1050
1100
|
if (this.suppressWelcomeInjection) return;
|
|
1051
|
-
if (!session.config.welcomeMessage ||
|
|
1101
|
+
if (!session.config.welcomeMessage || !pristine) return;
|
|
1102
|
+
// A late welcome (the first send raced the init) sorts ahead of that turn.
|
|
1103
|
+
const first = this.messages[0];
|
|
1052
1104
|
const welcomeMessage: AgentWidgetMessage = {
|
|
1053
1105
|
id: `welcome-${Date.now()}`,
|
|
1054
1106
|
role: "assistant",
|
|
1055
1107
|
content: session.config.welcomeMessage,
|
|
1056
|
-
createdAt: new Date().toISOString(),
|
|
1057
|
-
sequence: this.nextSequence()
|
|
1108
|
+
createdAt: first?.createdAt ?? new Date().toISOString(),
|
|
1109
|
+
sequence: first ? (first.sequence ?? 0) - 1 : this.nextSequence()
|
|
1058
1110
|
};
|
|
1059
1111
|
this.appendMessage(welcomeMessage);
|
|
1060
1112
|
}
|
|
@@ -13,7 +13,8 @@ const h = vi.hoisted(() => {
|
|
|
13
13
|
| ((role: 'user' | 'assistant', text: string, isFinal: boolean) => void)
|
|
14
14
|
| null;
|
|
15
15
|
metricsCb: ((m: VoiceMetrics) => void) | null;
|
|
16
|
-
|
|
16
|
+
prewarms: number;
|
|
17
|
+
} = { transcriptCb: null, metricsCb: null, prewarms: 0 };
|
|
17
18
|
|
|
18
19
|
const fakeProvider = {
|
|
19
20
|
type: 'runtype' as const,
|
|
@@ -30,6 +31,9 @@ const h = vi.hoisted(() => {
|
|
|
30
31
|
onMetrics: (cb: typeof state.metricsCb) => {
|
|
31
32
|
state.metricsCb = cb;
|
|
32
33
|
},
|
|
34
|
+
prewarm: () => {
|
|
35
|
+
state.prewarms += 1;
|
|
36
|
+
},
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
return { state, fakeProvider };
|
|
@@ -212,3 +216,70 @@ describe('AgentWidgetSession - Runtype TTS config', () => {
|
|
|
212
216
|
}
|
|
213
217
|
});
|
|
214
218
|
});
|
|
219
|
+
|
|
220
|
+
describe('AgentWidgetSession - prewarmVoice', () => {
|
|
221
|
+
const callbacks = {
|
|
222
|
+
onMessagesChanged: () => {},
|
|
223
|
+
onStatusChanged: () => {},
|
|
224
|
+
onStreamingChanged: () => {},
|
|
225
|
+
onError: () => {},
|
|
226
|
+
};
|
|
227
|
+
const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
228
|
+
const runtypeSession = () =>
|
|
229
|
+
new AgentWidgetSession(
|
|
230
|
+
{
|
|
231
|
+
apiUrl: 'http://localhost:8000',
|
|
232
|
+
voiceRecognition: {
|
|
233
|
+
enabled: true,
|
|
234
|
+
provider: { type: 'runtype', runtype: { agentId: 'a1' } },
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
callbacks,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
beforeEach(() => {
|
|
241
|
+
h.state.prewarms = 0;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("calls the provider's prewarm once setup has installed it", async () => {
|
|
245
|
+
const session = runtypeSession();
|
|
246
|
+
session.setupVoice();
|
|
247
|
+
await flush();
|
|
248
|
+
session.prewarmVoice();
|
|
249
|
+
await flush();
|
|
250
|
+
expect(h.state.prewarms).toBe(1);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('waits for a setup still loading the voice runtime', async () => {
|
|
254
|
+
const session = runtypeSession();
|
|
255
|
+
session.setupVoice();
|
|
256
|
+
session.prewarmVoice();
|
|
257
|
+
expect(h.state.prewarms).toBe(0);
|
|
258
|
+
await flush();
|
|
259
|
+
expect(h.state.prewarms).toBe(1);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('drops a prewarm whose setup was torn down meanwhile', async () => {
|
|
263
|
+
const session = runtypeSession();
|
|
264
|
+
session.setupVoice();
|
|
265
|
+
session.prewarmVoice();
|
|
266
|
+
session.cleanupVoice();
|
|
267
|
+
await flush();
|
|
268
|
+
expect(h.state.prewarms).toBe(0);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('warms the client session on the browser (Web Speech) path', () => {
|
|
272
|
+
const session = new AgentWidgetSession(
|
|
273
|
+
{
|
|
274
|
+
apiUrl: 'http://localhost:8000',
|
|
275
|
+
clientToken: 'ct_test',
|
|
276
|
+
voiceRecognition: { enabled: true, provider: { type: 'browser' } },
|
|
277
|
+
},
|
|
278
|
+
callbacks,
|
|
279
|
+
);
|
|
280
|
+
const warm = vi.spyOn(session, 'warmClientSession').mockImplementation(() => {});
|
|
281
|
+
session.prewarmVoice();
|
|
282
|
+
expect(warm).toHaveBeenCalledOnce();
|
|
283
|
+
expect(h.state.prewarms).toBe(0);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
@@ -189,6 +189,25 @@ describe("suggestion interaction styles", () => {
|
|
|
189
189
|
});
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
describe("collapsible widget chrome styles", () => {
|
|
193
|
+
it("uses prefixed aliases while retaining each legacy alias and literal fallback", () => {
|
|
194
|
+
const expectedDeclarations: Array<[string, number]> = [
|
|
195
|
+
["var(--persona-cw-border, var(--cw-border, #e5e7eb))", 2],
|
|
196
|
+
["var(--persona-cw-container, var(--cw-container, #f9fafb))", 1],
|
|
197
|
+
["var(--persona-cw-surface, var(--cw-surface, #ffffff))", 2],
|
|
198
|
+
["var(--persona-cw-border, var(--cw-border, #f1f5f9))", 1],
|
|
199
|
+
["var(--persona-cw-container, var(--cw-container, #f8fafc))", 1],
|
|
200
|
+
["var(--persona-cw-border, var(--cw-border, #d1d5db))", 1],
|
|
201
|
+
["var(--persona-cw-surface, var(--cw-surface, #fff))", 2],
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
for (const [declaration, count] of expectedDeclarations) {
|
|
205
|
+
expect(widgetCss).toContain(declaration);
|
|
206
|
+
expect(widgetCss.split(declaration).length - 1).toBe(count);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
192
211
|
describe("composer spacing styles", () => {
|
|
193
212
|
it("themes the composer form padding and gap with utility-matching fallbacks", () => {
|
|
194
213
|
const selector =
|