@runtypelabs/persona 3.34.0 → 3.35.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 +11 -11
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-CthJFfNx.d.cts → types-DgYsuwXL.d.cts} +15 -14
- package/dist/animations/{types-CthJFfNx.d.ts → types-DgYsuwXL.d.ts} +15 -14
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/codegen.cjs +11 -11
- package/dist/codegen.js +10 -10
- package/dist/index.cjs +46 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +514 -158
- package/dist/index.d.ts +514 -158
- package/dist/index.global.js +41 -89
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +46 -44
- 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/markdown-parsers.js +53 -0
- package/dist/plugin-kit.d.cts +4 -4
- package/dist/plugin-kit.d.ts +4 -4
- package/dist/runtype-tts.js +1 -0
- package/dist/smart-dom-reader.cjs +1 -1
- package/dist/smart-dom-reader.d.cts +352 -103
- package/dist/smart-dom-reader.d.ts +352 -103
- package/dist/smart-dom-reader.js +1 -1
- package/dist/theme-editor-preview.cjs +203 -0
- package/dist/theme-editor-preview.d.cts +5748 -0
- package/dist/theme-editor-preview.d.ts +5748 -0
- package/dist/theme-editor-preview.js +203 -0
- package/dist/theme-editor.cjs +14 -131
- package/dist/theme-editor.d.cts +362 -574
- package/dist/theme-editor.d.ts +362 -574
- package/dist/theme-editor.js +14 -131
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +1 -1
- package/dist/theme-reference.d.ts +1 -1
- package/dist/theme-reference.js +1 -1
- package/dist/voice-worklet-player.cjs +80 -0
- package/dist/voice-worklet-player.d.cts +215 -0
- package/dist/voice-worklet-player.d.ts +215 -0
- package/dist/voice-worklet-player.js +80 -0
- package/dist/widget.css +54 -36
- package/package.json +16 -2
- package/src/animations/glyph-cycle.ts +11 -14
- package/src/animations/wipe.ts +1 -1
- package/src/ask-user-question-tool.test.ts +1 -1
- package/src/ask-user-question-tool.ts +6 -8
- package/src/client.test.ts +67 -12
- package/src/client.ts +40 -32
- package/src/codegen.test.ts +1 -1
- package/src/codegen.ts +1 -1
- package/src/components/approval-bubble.test.ts +1 -1
- package/src/components/approval-bubble.ts +3 -3
- package/src/components/artifact-pane.ts +3 -3
- package/src/components/ask-user-question-bubble.test.ts +14 -14
- package/src/components/ask-user-question-bubble.ts +10 -10
- package/src/components/composer-builder.ts +17 -16
- package/src/components/composer-parts.test.ts +54 -0
- package/src/components/composer-parts.ts +95 -126
- package/src/components/demo-carousel.ts +1 -1
- package/src/components/event-stream-view.test.ts +7 -7
- package/src/components/event-stream-view.ts +5 -5
- package/src/components/header-builder.ts +33 -27
- package/src/components/header-layouts.ts +1 -1
- package/src/components/header-parts.test.ts +90 -0
- package/src/components/header-parts.ts +62 -93
- package/src/components/launcher.test.ts +11 -0
- package/src/components/launcher.ts +1 -1
- package/src/components/message-bubble.test.ts +74 -2
- package/src/components/message-bubble.ts +25 -11
- package/src/components/messages.ts +2 -2
- package/src/components/panel.test.ts +1 -1
- package/src/components/panel.ts +92 -97
- package/src/components/pill-composer-builder.ts +28 -24
- package/src/components/reasoning-bubble.ts +2 -2
- package/src/components/suggestions.ts +1 -1
- package/src/components/tool-bubble.ts +1 -1
- package/src/components/widget-view.test.ts +181 -0
- package/src/components/widget-view.ts +234 -0
- package/src/defaults.ts +54 -51
- package/src/generated/runtype-openapi-contract.ts +5 -0
- package/src/index-core.ts +30 -4
- package/src/index-global.ts +73 -6
- package/src/index.ts +7 -1
- package/src/install.test.ts +11 -11
- package/src/install.ts +28 -18
- package/src/launcher-global.ts +25 -14
- package/src/markdown-parsers-eager.ts +20 -0
- package/src/markdown-parsers-entry.ts +4 -0
- package/src/markdown-parsers-loader.ts +49 -0
- package/src/plugin-kit.test.ts +1 -1
- package/src/plugin-kit.ts +7 -7
- package/src/plugins/types.ts +5 -5
- package/src/postprocessors.ts +27 -14
- package/src/runtime/host-layout.test.ts +1 -1
- package/src/runtime/host-layout.ts +4 -5
- package/src/runtime/init.ts +2 -2
- package/src/session.test.ts +5 -6
- package/src/session.ts +333 -227
- package/src/session.voice.test.ts +146 -0
- package/src/session.webmcp.test.ts +24 -25
- package/src/smart-dom-reader.ts +3 -4
- package/src/styles/widget.css +54 -36
- package/src/suggest-replies-tool.test.ts +3 -3
- package/src/suggest-replies-tool.ts +5 -5
- package/src/testing/index.ts +1 -1
- package/src/theme-editor/color-utils.ts +2 -2
- package/src/theme-editor/index.ts +4 -13
- package/src/theme-editor/presets.ts +1 -1
- package/src/theme-editor/preview-utils.ts +22 -20
- package/src/theme-editor/preview.ts +4 -1
- package/src/theme-editor/role-mappings.ts +3 -3
- package/src/theme-editor/sections.ts +21 -20
- package/src/theme-editor/types.ts +1 -1
- package/src/theme-editor/webmcp/index.ts +1 -1
- package/src/theme-editor/webmcp/summary.ts +1 -2
- package/src/theme-editor/webmcp/tools.ts +30 -30
- package/src/theme-editor/webmcp/types.ts +2 -2
- package/src/theme-editor-preview.ts +10 -0
- package/src/theme-reference.ts +26 -26
- package/src/types.ts +368 -99
- package/src/ui.approval-plugin.test.ts +3 -3
- package/src/ui.ask-user-question-plugin.test.ts +9 -9
- package/src/ui.component-directive.test.ts +4 -4
- package/src/ui.composer-bar.test.ts +10 -11
- package/src/ui.composer-keyboard.test.ts +5 -5
- package/src/ui.header-icon-color.test.ts +59 -0
- package/src/ui.scroll.test.ts +5 -6
- package/src/ui.stop-button.test.ts +3 -4
- package/src/ui.suggest-replies.test.ts +2 -2
- package/src/ui.ts +292 -131
- package/src/utils/buttons.ts +1 -1
- package/src/utils/code-generators.ts +1 -1
- package/src/utils/composer-history.ts +3 -3
- package/src/utils/dom-context.test.ts +2 -2
- package/src/utils/dom-context.ts +7 -7
- package/src/utils/dom.test.ts +67 -0
- package/src/utils/dom.ts +75 -0
- package/src/utils/dropdown.ts +2 -2
- package/src/utils/event-stream-buffer.test.ts +2 -2
- package/src/utils/message-fingerprint.test.ts +9 -0
- package/src/utils/message-fingerprint.ts +5 -0
- package/src/utils/morph.ts +1 -1
- package/src/utils/sanitize.ts +32 -16
- package/src/utils/sequence-buffer.test.ts +8 -8
- package/src/utils/sequence-buffer.ts +4 -4
- package/src/utils/smart-dom-adapter.test.ts +1 -1
- package/src/utils/smart-dom-adapter.ts +4 -5
- package/src/utils/speech-text.test.ts +122 -0
- package/src/utils/speech-text.ts +101 -0
- package/src/utils/stream-animation.test.ts +3 -3
- package/src/utils/stream-animation.ts +6 -6
- package/src/utils/theme.test.ts +1 -1
- package/src/utils/throughput-tracker.test.ts +11 -11
- package/src/utils/throughput-tracker.ts +9 -9
- package/src/utils/tokens.ts +13 -13
- package/src/utils/virtual-scroller.ts +1 -1
- package/src/vendor/smart-dom-reader/README.md +4 -4
- package/src/vendor/smart-dom-reader/index.d.ts +1 -1
- package/src/vendor/smart-dom-reader/index.js +2 -2
- package/src/version.ts +1 -1
- package/src/voice/audio-playback-manager.test.ts +159 -0
- package/src/voice/audio-playback-manager.ts +145 -30
- package/src/voice/browser-speech-engine.ts +130 -0
- package/src/voice/fallback-speech-engine.ts +89 -0
- package/src/voice/index.ts +21 -0
- package/src/voice/read-aloud-controller.test.ts +143 -0
- package/src/voice/read-aloud-controller.ts +136 -0
- package/src/voice/runtype-speech-engine.test.ts +280 -0
- package/src/voice/runtype-speech-engine.ts +211 -0
- package/src/voice/runtype-tts-entry.ts +10 -0
- package/src/voice/runtype-tts-loader.test.ts +47 -0
- package/src/voice/runtype-tts-loader.ts +42 -0
- package/src/voice/runtype-voice-provider.ts +332 -537
- package/src/voice/voice-factory.ts +26 -4
- package/src/voice/voice.test.ts +321 -7
- package/src/voice/worklet-playback-engine.ts +272 -0
- package/src/voice-worklet-player.ts +39 -0
- package/src/webmcp-bridge.test.ts +6 -6
- package/src/webmcp-bridge.ts +24 -25
- package/src/webmcp-polyfill.ts +1 -1
- package/src/voice/voice-activity-detector.ts +0 -90
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { AgentWidgetConfig } from "../types";
|
|
2
|
+
import { AgentWidgetPlugin } from "../plugins/types";
|
|
3
|
+
import { createWrapper, buildPanel, PanelElements } from "./panel";
|
|
4
|
+
import { HeaderElements } from "./header-builder";
|
|
5
|
+
import { createLauncherButton, LauncherButton } from "./launcher";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Widget view assembly layer.
|
|
9
|
+
*
|
|
10
|
+
* `ui.ts` owns behavior (session dispatch, streaming, scroll, voice, events);
|
|
11
|
+
* this module owns the one-time structural assembly of the widget's DOM and
|
|
12
|
+
* groups the resulting element references into named regions so callers read
|
|
13
|
+
* `view.composer.textarea` instead of destructuring a ~30-field flat object.
|
|
14
|
+
*
|
|
15
|
+
* The grouped refs are a view over the same nodes returned by `buildPanel`;
|
|
16
|
+
* the raw flat `PanelElements` is still exposed as `view.panelElements` for
|
|
17
|
+
* incremental migration. No framework, no virtual DOM: these are the real
|
|
18
|
+
* elements built by the existing `panel.ts` / `composer-*` / `header-*`
|
|
19
|
+
* builders.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export interface WidgetShellRefs {
|
|
23
|
+
wrapper: HTMLElement;
|
|
24
|
+
panel: HTMLElement;
|
|
25
|
+
/** Composer-bar mode only: viewport-fixed sibling that owns the pill. */
|
|
26
|
+
pillRoot?: HTMLElement;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface WidgetTranscriptRefs {
|
|
30
|
+
container: HTMLElement;
|
|
31
|
+
body: HTMLElement;
|
|
32
|
+
messagesWrapper: HTMLElement;
|
|
33
|
+
/** Absolute slot above the composer where interactive sheets mount. */
|
|
34
|
+
composerOverlay: HTMLElement;
|
|
35
|
+
introTitle: HTMLElement;
|
|
36
|
+
introSubtitle: HTMLElement;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface WidgetHeaderRefs {
|
|
40
|
+
/** The header root element currently mounted in the container. */
|
|
41
|
+
element: HTMLElement;
|
|
42
|
+
iconHolder: HTMLElement;
|
|
43
|
+
headerTitle: HTMLElement;
|
|
44
|
+
headerSubtitle: HTMLElement;
|
|
45
|
+
closeButton: HTMLButtonElement;
|
|
46
|
+
closeButtonWrapper: HTMLElement | null;
|
|
47
|
+
clearChatButton: HTMLButtonElement | null;
|
|
48
|
+
clearChatButtonWrapper: HTMLElement | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface WidgetComposerRefs {
|
|
52
|
+
/** The footer root element currently mounted in the panel. */
|
|
53
|
+
footer: HTMLElement;
|
|
54
|
+
form: HTMLFormElement;
|
|
55
|
+
textarea: HTMLTextAreaElement;
|
|
56
|
+
sendButton: HTMLButtonElement;
|
|
57
|
+
sendButtonWrapper: HTMLElement;
|
|
58
|
+
micButton: HTMLButtonElement | null;
|
|
59
|
+
micButtonWrapper: HTMLElement | null;
|
|
60
|
+
statusText: HTMLElement;
|
|
61
|
+
suggestions: HTMLElement;
|
|
62
|
+
attachmentButton: HTMLButtonElement | null;
|
|
63
|
+
attachmentButtonWrapper: HTMLElement | null;
|
|
64
|
+
attachmentInput: HTMLInputElement | null;
|
|
65
|
+
attachmentPreviewsContainer: HTMLElement | null;
|
|
66
|
+
actionsRow: HTMLElement;
|
|
67
|
+
leftActions: HTMLElement;
|
|
68
|
+
rightActions: HTMLElement;
|
|
69
|
+
setSendButtonMode: (mode: "send" | "stop") => void;
|
|
70
|
+
/** Composer-bar peek banner (undefined for non-composer-bar modes). */
|
|
71
|
+
peekBanner?: HTMLButtonElement;
|
|
72
|
+
peekTextNode?: HTMLElement;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface WidgetView {
|
|
76
|
+
shell: WidgetShellRefs;
|
|
77
|
+
/** Raw flat refs from `buildPanel`, retained for incremental migration. */
|
|
78
|
+
panelElements: PanelElements;
|
|
79
|
+
transcript: WidgetTranscriptRefs;
|
|
80
|
+
header: WidgetHeaderRefs;
|
|
81
|
+
composer: WidgetComposerRefs;
|
|
82
|
+
/**
|
|
83
|
+
* Swap the mounted header element for `next.header` and mirror the header
|
|
84
|
+
* sub-refs (icon/title/subtitle/close) onto `view.header`. Used by the
|
|
85
|
+
* header-layout rebuild path. Returns the new `HeaderElements` for the
|
|
86
|
+
* caller to mirror into its own locals during incremental migration.
|
|
87
|
+
*/
|
|
88
|
+
replaceHeader: (next: HeaderElements) => HeaderElements;
|
|
89
|
+
/**
|
|
90
|
+
* Swap the mounted footer element for `nextFooter` and update
|
|
91
|
+
* `view.composer.footer`. The remaining composer sub-refs become stale
|
|
92
|
+
* after a plugin-provided composer replaces the footer; callers re-derive
|
|
93
|
+
* them from the new footer (see `bindComposerRefsFromFooter` in ui.ts).
|
|
94
|
+
*/
|
|
95
|
+
replaceComposer: (nextFooter: HTMLElement) => void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface CreateWidgetViewOptions {
|
|
99
|
+
config: AgentWidgetConfig;
|
|
100
|
+
/** Whether the panel renders a close affordance (panel is toggleable). */
|
|
101
|
+
showClose: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Build the widget shell + panel once and group the element references into
|
|
106
|
+
* named regions. Equivalent to the previous `createWrapper(config)` +
|
|
107
|
+
* `buildPanel(config, showClose)` pair, plus the grouping/replacement helpers.
|
|
108
|
+
*/
|
|
109
|
+
export const createWidgetView = ({
|
|
110
|
+
config,
|
|
111
|
+
showClose,
|
|
112
|
+
}: CreateWidgetViewOptions): WidgetView => {
|
|
113
|
+
const { wrapper, panel, pillRoot } = createWrapper(config);
|
|
114
|
+
const panelElements = buildPanel(config, showClose);
|
|
115
|
+
|
|
116
|
+
const shell: WidgetShellRefs = { wrapper, panel, pillRoot };
|
|
117
|
+
|
|
118
|
+
const transcript: WidgetTranscriptRefs = {
|
|
119
|
+
container: panelElements.container,
|
|
120
|
+
body: panelElements.body,
|
|
121
|
+
messagesWrapper: panelElements.messagesWrapper,
|
|
122
|
+
composerOverlay: panelElements.composerOverlay,
|
|
123
|
+
introTitle: panelElements.introTitle,
|
|
124
|
+
introSubtitle: panelElements.introSubtitle,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const header: WidgetHeaderRefs = {
|
|
128
|
+
element: panelElements.header,
|
|
129
|
+
iconHolder: panelElements.iconHolder,
|
|
130
|
+
headerTitle: panelElements.headerTitle,
|
|
131
|
+
headerSubtitle: panelElements.headerSubtitle,
|
|
132
|
+
closeButton: panelElements.closeButton,
|
|
133
|
+
closeButtonWrapper: panelElements.closeButtonWrapper,
|
|
134
|
+
clearChatButton: panelElements.clearChatButton,
|
|
135
|
+
clearChatButtonWrapper: panelElements.clearChatButtonWrapper,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const composer: WidgetComposerRefs = {
|
|
139
|
+
footer: panelElements.footer,
|
|
140
|
+
form: panelElements.composerForm,
|
|
141
|
+
textarea: panelElements.textarea,
|
|
142
|
+
sendButton: panelElements.sendButton,
|
|
143
|
+
sendButtonWrapper: panelElements.sendButtonWrapper,
|
|
144
|
+
micButton: panelElements.micButton,
|
|
145
|
+
micButtonWrapper: panelElements.micButtonWrapper,
|
|
146
|
+
statusText: panelElements.statusText,
|
|
147
|
+
suggestions: panelElements.suggestions,
|
|
148
|
+
attachmentButton: panelElements.attachmentButton,
|
|
149
|
+
attachmentButtonWrapper: panelElements.attachmentButtonWrapper,
|
|
150
|
+
attachmentInput: panelElements.attachmentInput,
|
|
151
|
+
attachmentPreviewsContainer: panelElements.attachmentPreviewsContainer,
|
|
152
|
+
actionsRow: panelElements.actionsRow,
|
|
153
|
+
leftActions: panelElements.leftActions,
|
|
154
|
+
rightActions: panelElements.rightActions,
|
|
155
|
+
setSendButtonMode: panelElements.setSendButtonMode,
|
|
156
|
+
peekBanner: panelElements.peekBanner,
|
|
157
|
+
peekTextNode: panelElements.peekTextNode,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const replaceHeader = (next: HeaderElements): HeaderElements => {
|
|
161
|
+
header.element.replaceWith(next.header);
|
|
162
|
+
header.element = next.header;
|
|
163
|
+
header.iconHolder = next.iconHolder;
|
|
164
|
+
header.headerTitle = next.headerTitle;
|
|
165
|
+
header.headerSubtitle = next.headerSubtitle;
|
|
166
|
+
header.closeButton = next.closeButton;
|
|
167
|
+
header.closeButtonWrapper = next.closeButtonWrapper;
|
|
168
|
+
header.clearChatButton = next.clearChatButton;
|
|
169
|
+
header.clearChatButtonWrapper = next.clearChatButtonWrapper;
|
|
170
|
+
return next;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const replaceComposer = (nextFooter: HTMLElement): void => {
|
|
174
|
+
composer.footer.replaceWith(nextFooter);
|
|
175
|
+
composer.footer = nextFooter;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
shell,
|
|
180
|
+
panelElements,
|
|
181
|
+
transcript,
|
|
182
|
+
header,
|
|
183
|
+
composer,
|
|
184
|
+
replaceHeader,
|
|
185
|
+
replaceComposer,
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export interface ResolveLauncherOptions {
|
|
190
|
+
config: AgentWidgetConfig;
|
|
191
|
+
plugins: AgentWidgetPlugin[];
|
|
192
|
+
/** Toggles the panel open/closed; wired to the launcher's click. */
|
|
193
|
+
onToggle: () => void;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface ResolvedLauncher {
|
|
197
|
+
/**
|
|
198
|
+
* The default launcher controller (with `update`/`destroy`), or null when a
|
|
199
|
+
* plugin supplied a custom launcher element.
|
|
200
|
+
*/
|
|
201
|
+
instance: LauncherButton | null;
|
|
202
|
+
/** The element to mount: either the default button or the plugin's element. */
|
|
203
|
+
element: HTMLElement;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Resolve the launcher element, honoring a `renderLauncher` plugin if present.
|
|
208
|
+
* De-duplicates the identical default-or-plugin logic that previously lived at
|
|
209
|
+
* both the initial-build and re-enable call sites in ui.ts.
|
|
210
|
+
*
|
|
211
|
+
* When a plugin returns a custom element, `instance` is null (the plugin owns
|
|
212
|
+
* its own updates); otherwise the default `LauncherButton` controller is
|
|
213
|
+
* returned so the caller can drive `update()`/`destroy()`.
|
|
214
|
+
*/
|
|
215
|
+
export const resolveLauncher = ({
|
|
216
|
+
config,
|
|
217
|
+
plugins,
|
|
218
|
+
onToggle,
|
|
219
|
+
}: ResolveLauncherOptions): ResolvedLauncher => {
|
|
220
|
+
const launcherPlugin = plugins.find((p) => p.renderLauncher);
|
|
221
|
+
if (launcherPlugin?.renderLauncher) {
|
|
222
|
+
const customLauncher = launcherPlugin.renderLauncher({
|
|
223
|
+
config,
|
|
224
|
+
defaultRenderer: () => createLauncherButton(config, onToggle).element,
|
|
225
|
+
onToggle,
|
|
226
|
+
});
|
|
227
|
+
if (customLauncher) {
|
|
228
|
+
return { instance: null, element: customLauncher };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const instance = createLauncherButton(config, onToggle);
|
|
233
|
+
return { instance, element: instance.element };
|
|
234
|
+
};
|
package/src/defaults.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentWidgetConfig } from "./types";
|
|
1
|
+
import type { AgentWidgetConfig, AgentWidgetLauncherConfig } from "./types";
|
|
2
2
|
import type { DeepPartial, PersonaTheme } from "./types/theme";
|
|
3
3
|
import { deepMerge } from "./utils/deep-merge";
|
|
4
4
|
|
|
@@ -12,6 +12,58 @@ export const DEFAULT_FLOATING_LAUNCHER_WIDTH = "min(440px, calc(100vw - 24px))";
|
|
|
12
12
|
/** Max width cap paired with {@link DEFAULT_FLOATING_LAUNCHER_WIDTH} for theme defaults. */
|
|
13
13
|
export const DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH = "440px";
|
|
14
14
|
|
|
15
|
+
export const DEFAULT_LAUNCHER_CONFIG: AgentWidgetLauncherConfig = {
|
|
16
|
+
enabled: true,
|
|
17
|
+
mountMode: "floating",
|
|
18
|
+
dock: {
|
|
19
|
+
side: "right",
|
|
20
|
+
width: "420px",
|
|
21
|
+
},
|
|
22
|
+
title: "Chat Assistant",
|
|
23
|
+
subtitle: "Here to help you get answers fast",
|
|
24
|
+
agentIconText: "💬",
|
|
25
|
+
agentIconName: "bot",
|
|
26
|
+
headerIconName: "bot",
|
|
27
|
+
position: "bottom-right",
|
|
28
|
+
width: DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
29
|
+
heightOffset: 0,
|
|
30
|
+
autoExpand: false,
|
|
31
|
+
callToActionIconHidden: false,
|
|
32
|
+
agentIconSize: "40px",
|
|
33
|
+
headerIconSize: "40px",
|
|
34
|
+
closeButtonSize: "32px",
|
|
35
|
+
// Zero out browser-default <button> padding so the icon gets the full
|
|
36
|
+
// 32x32 content box, matching clearChat.paddingX/Y below. Without this,
|
|
37
|
+
// UA stylesheets add ~1-2px vertical and ~6px horizontal padding that
|
|
38
|
+
// eats into the border-box width and shrinks the rendered icon.
|
|
39
|
+
closeButtonPaddingX: "0px",
|
|
40
|
+
closeButtonPaddingY: "0px",
|
|
41
|
+
callToActionIconName: "arrow-up-right",
|
|
42
|
+
callToActionIconText: "",
|
|
43
|
+
callToActionIconSize: "32px",
|
|
44
|
+
callToActionIconPadding: "5px",
|
|
45
|
+
callToActionIconColor: undefined,
|
|
46
|
+
callToActionIconBackgroundColor: undefined,
|
|
47
|
+
// closeButtonColor / clearChat.iconColor omitted so theme.components.header.actionIconForeground applies.
|
|
48
|
+
closeButtonBackgroundColor: "transparent",
|
|
49
|
+
clearChat: {
|
|
50
|
+
backgroundColor: "transparent",
|
|
51
|
+
borderColor: "transparent",
|
|
52
|
+
enabled: true,
|
|
53
|
+
placement: "inline",
|
|
54
|
+
iconName: "refresh-cw",
|
|
55
|
+
size: "32px",
|
|
56
|
+
showTooltip: true,
|
|
57
|
+
tooltipText: "Clear chat",
|
|
58
|
+
paddingX: "0px",
|
|
59
|
+
paddingY: "0px",
|
|
60
|
+
},
|
|
61
|
+
headerIconHidden: false,
|
|
62
|
+
border: undefined,
|
|
63
|
+
shadow:
|
|
64
|
+
"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
|
|
65
|
+
};
|
|
66
|
+
|
|
15
67
|
/**
|
|
16
68
|
* Default widget configuration
|
|
17
69
|
* Single source of truth for all default values
|
|
@@ -23,56 +75,7 @@ export const DEFAULT_WIDGET_CONFIG: Partial<AgentWidgetConfig> = {
|
|
|
23
75
|
theme: undefined,
|
|
24
76
|
darkTheme: undefined,
|
|
25
77
|
colorScheme: "light",
|
|
26
|
-
launcher:
|
|
27
|
-
enabled: true,
|
|
28
|
-
mountMode: "floating",
|
|
29
|
-
dock: {
|
|
30
|
-
side: "right",
|
|
31
|
-
width: "420px",
|
|
32
|
-
},
|
|
33
|
-
title: "Chat Assistant",
|
|
34
|
-
subtitle: "Here to help you get answers fast",
|
|
35
|
-
agentIconText: "💬",
|
|
36
|
-
agentIconName: "bot",
|
|
37
|
-
headerIconName: "bot",
|
|
38
|
-
position: "bottom-right",
|
|
39
|
-
width: DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
40
|
-
heightOffset: 0,
|
|
41
|
-
autoExpand: false,
|
|
42
|
-
callToActionIconHidden: false,
|
|
43
|
-
agentIconSize: "40px",
|
|
44
|
-
headerIconSize: "40px",
|
|
45
|
-
closeButtonSize: "32px",
|
|
46
|
-
// Zero out browser-default <button> padding so the icon gets the full
|
|
47
|
-
// 32x32 content box, matching clearChat.paddingX/Y below. Without this,
|
|
48
|
-
// UA stylesheets add ~1-2px vertical and ~6px horizontal padding that
|
|
49
|
-
// eats into the border-box width and shrinks the rendered icon.
|
|
50
|
-
closeButtonPaddingX: "0px",
|
|
51
|
-
closeButtonPaddingY: "0px",
|
|
52
|
-
callToActionIconName: "arrow-up-right",
|
|
53
|
-
callToActionIconText: "",
|
|
54
|
-
callToActionIconSize: "32px",
|
|
55
|
-
callToActionIconPadding: "5px",
|
|
56
|
-
callToActionIconColor: undefined,
|
|
57
|
-
callToActionIconBackgroundColor: undefined,
|
|
58
|
-
// closeButtonColor / clearChat.iconColor omitted so theme.components.header.actionIconForeground applies.
|
|
59
|
-
closeButtonBackgroundColor: "transparent",
|
|
60
|
-
clearChat: {
|
|
61
|
-
backgroundColor: "transparent",
|
|
62
|
-
borderColor: "transparent",
|
|
63
|
-
enabled: true,
|
|
64
|
-
placement: "inline",
|
|
65
|
-
iconName: "refresh-cw",
|
|
66
|
-
size: "32px",
|
|
67
|
-
showTooltip: true,
|
|
68
|
-
tooltipText: "Clear chat",
|
|
69
|
-
paddingX: "0px",
|
|
70
|
-
paddingY: "0px",
|
|
71
|
-
},
|
|
72
|
-
headerIconHidden: false,
|
|
73
|
-
border: undefined,
|
|
74
|
-
shadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
|
|
75
|
-
},
|
|
78
|
+
launcher: DEFAULT_LAUNCHER_CONFIG,
|
|
76
79
|
copy: {
|
|
77
80
|
welcomeTitle: "Hello 👋",
|
|
78
81
|
welcomeSubtitle: "Ask anything about your account or products.",
|
|
@@ -285,6 +285,7 @@ export type RuntypeFlowSSEEvent = {
|
|
|
285
285
|
totalSteps?: number;
|
|
286
286
|
type: "flow_start";
|
|
287
287
|
} | {
|
|
288
|
+
claudeManagedAgentId?: string;
|
|
288
289
|
completedAt?: string;
|
|
289
290
|
completedSteps?: number;
|
|
290
291
|
duration?: number;
|
|
@@ -399,6 +400,7 @@ export type RuntypeFlowSSEEvent = {
|
|
|
399
400
|
toolId: string;
|
|
400
401
|
};
|
|
401
402
|
type: "step_complete";
|
|
403
|
+
unresolvedVariables?: Array<string>;
|
|
402
404
|
}) | {
|
|
403
405
|
error: string;
|
|
404
406
|
executionId?: string;
|
|
@@ -836,6 +838,7 @@ export type RuntypeDispatchSSEEvent = {
|
|
|
836
838
|
totalSteps?: number;
|
|
837
839
|
type: "flow_start";
|
|
838
840
|
} | {
|
|
841
|
+
claudeManagedAgentId?: string;
|
|
839
842
|
completedAt?: string;
|
|
840
843
|
completedSteps?: number;
|
|
841
844
|
duration?: number;
|
|
@@ -950,6 +953,7 @@ export type RuntypeDispatchSSEEvent = {
|
|
|
950
953
|
toolId: string;
|
|
951
954
|
};
|
|
952
955
|
type: "step_complete";
|
|
956
|
+
unresolvedVariables?: Array<string>;
|
|
953
957
|
}) | {
|
|
954
958
|
error: string;
|
|
955
959
|
executionId?: string;
|
|
@@ -1253,6 +1257,7 @@ export type RuntypeClientFeedbackRequest = {
|
|
|
1253
1257
|
metadata?: Record<string, unknown>;
|
|
1254
1258
|
rating?: number;
|
|
1255
1259
|
sessionId: string;
|
|
1260
|
+
token: string;
|
|
1256
1261
|
type: "upvote" | "downvote" | "copy" | "csat" | "nps";
|
|
1257
1262
|
};
|
|
1258
1263
|
|
package/src/index-core.ts
CHANGED
|
@@ -82,7 +82,7 @@ export type {
|
|
|
82
82
|
AgentWidgetApproval,
|
|
83
83
|
AgentWidgetApprovalConfig,
|
|
84
84
|
AgentWidgetApprovalDecisionOptions,
|
|
85
|
-
// WebMCP
|
|
85
|
+
// WebMCP: page-discovered tool consumption
|
|
86
86
|
AgentWidgetWebMcpConfig,
|
|
87
87
|
ClientToolDefinition,
|
|
88
88
|
WebMcpConfirmHandler,
|
|
@@ -98,6 +98,7 @@ export type {
|
|
|
98
98
|
EventStreamPayloadRenderContext,
|
|
99
99
|
// Controller event map
|
|
100
100
|
AgentWidgetControllerEventMap,
|
|
101
|
+
AgentWidgetReadAloudEvent,
|
|
101
102
|
// Ask-user-question (built-in answer-pill sheet) types
|
|
102
103
|
AskUserQuestionPayload,
|
|
103
104
|
AskUserQuestionPrompt,
|
|
@@ -245,7 +246,7 @@ export type { AgentWidgetInitHandle };
|
|
|
245
246
|
export type { AgentWidgetPlugin } from "./plugins/types";
|
|
246
247
|
export { pluginRegistry } from "./plugins/registry";
|
|
247
248
|
|
|
248
|
-
// Stream animation plugin API
|
|
249
|
+
// Stream animation plugin API: lets consumers register custom animations
|
|
249
250
|
// that match the built-in surface (typewriter, pop-bubble) and subpath
|
|
250
251
|
// modules (letter-rise, word-fade, wipe, glyph-cycle).
|
|
251
252
|
export {
|
|
@@ -263,7 +264,7 @@ export type {
|
|
|
263
264
|
AgentWidgetStreamAnimationPlaceholder,
|
|
264
265
|
} from "./types";
|
|
265
266
|
|
|
266
|
-
// Action system types
|
|
267
|
+
// Action system types: needed to type the `actionHandlers` / `actionParsers`
|
|
267
268
|
// config options and to author custom handlers/parsers.
|
|
268
269
|
export type {
|
|
269
270
|
AgentWidgetActionHandler,
|
|
@@ -435,7 +436,32 @@ export type {
|
|
|
435
436
|
VoiceProvider,
|
|
436
437
|
VoiceResult,
|
|
437
438
|
VoiceStatus,
|
|
438
|
-
VoiceConfig
|
|
439
|
+
VoiceConfig,
|
|
440
|
+
VoiceMetrics,
|
|
441
|
+
VoicePlaybackEngine,
|
|
442
|
+
PcmStreamPlayer,
|
|
443
|
+
AgentWidgetVoiceStatusEvent,
|
|
444
|
+
ReadAloudState,
|
|
445
|
+
SpeechEngine,
|
|
446
|
+
SpeechRequest,
|
|
447
|
+
SpeechCallbacks
|
|
439
448
|
} from "./types";
|
|
440
449
|
|
|
450
|
+
export {
|
|
451
|
+
BrowserSpeechEngine,
|
|
452
|
+
pickBestVoice,
|
|
453
|
+
ReadAloudController
|
|
454
|
+
} from "./voice";
|
|
455
|
+
export type {
|
|
456
|
+
BrowserSpeechEngineOptions,
|
|
457
|
+
ReadAloudListener,
|
|
458
|
+
FallbackSpeechEngineOptions
|
|
459
|
+
} from "./voice";
|
|
460
|
+
// RuntypeSpeechEngine + FallbackSpeechEngine ship from the
|
|
461
|
+
// `@runtypelabs/persona/voice-worklet-player` subpath, not here: keeping the
|
|
462
|
+
// hosted read-aloud engine out of the main bundle lets the IIFE/CDN build defer
|
|
463
|
+
// it to the `runtype-tts.js` chunk (loaded only for `provider: 'runtype'`). The
|
|
464
|
+
// blessed way to use it is `textToSpeech: { provider: 'runtype' }` — no import
|
|
465
|
+
// needed.
|
|
466
|
+
|
|
441
467
|
export default initAgentWidgetFn;
|
package/src/index-global.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* IIFE entry point
|
|
2
|
+
* IIFE entry point: bundled for `<script>` tag consumers.
|
|
3
3
|
*
|
|
4
4
|
* This file re-exports everything from the main entry AND side-imports all
|
|
5
5
|
* built-in subpath animations so they register automatically. Script-tag
|
|
6
6
|
* users who include the global build don't need extra script tags or
|
|
7
|
-
* registration calls
|
|
7
|
+
* registration calls: setting `features.streamAnimation.type` to any
|
|
8
8
|
* built-in name just works.
|
|
9
9
|
*
|
|
10
10
|
* npm consumers continue to import from the main entry (`import ... from
|
|
11
|
-
* "@runtypelabs/persona"`)
|
|
11
|
+
* "@runtypelabs/persona"`): those animations stay in their subpath
|
|
12
12
|
* modules so bundlers can tree-shake them.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
// Re-export the full public API
|
|
15
|
+
// Re-export the full public API: from `index-core` (NOT `index`) so the
|
|
16
16
|
// dev-only helpers (`generateCodeSnippet`, `createDemoCarousel`) stay out of the
|
|
17
17
|
// CDN/IIFE bundle. npm consumers still get them via the `index.ts` barrel.
|
|
18
18
|
export * from "./index-core";
|
|
@@ -35,7 +35,7 @@ export type { StreamAnimationPlugin, StreamAnimationContext } from "./types";
|
|
|
35
35
|
// ---------------------------------------------------------------------------
|
|
36
36
|
// Deferred WebMCP polyfill loading.
|
|
37
37
|
//
|
|
38
|
-
// This bundle is built with `@mcp-b/webmcp-polyfill` external
|
|
38
|
+
// This bundle is built with `@mcp-b/webmcp-polyfill` external: the bridge's
|
|
39
39
|
// default `import("@mcp-b/webmcp-polyfill")` is a bare specifier no browser
|
|
40
40
|
// can resolve, so register a loader that imports the self-contained
|
|
41
41
|
// `webmcp-polyfill.js` chunk from a sibling URL instead. Mirrors how
|
|
@@ -44,7 +44,7 @@ export type { StreamAnimationPlugin, StreamAnimationContext } from "./types";
|
|
|
44
44
|
|
|
45
45
|
import { setWebMcpPolyfillLoader } from "./webmcp-bridge";
|
|
46
46
|
|
|
47
|
-
// Capture at module-evaluation time
|
|
47
|
+
// Capture at module-evaluation time: `document.currentScript` is null once
|
|
48
48
|
// execution leaves the script's initial synchronous run.
|
|
49
49
|
const widgetScriptSrc: string | null =
|
|
50
50
|
typeof document !== "undefined"
|
|
@@ -70,3 +70,70 @@ setWebMcpPolyfillLoader(() => {
|
|
|
70
70
|
// leaves it untouched (and must not try to bundle it).
|
|
71
71
|
return import(/* @vite-ignore */ chunkUrl);
|
|
72
72
|
});
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Deferred Markdown Parsers (marked + dompurify) loading.
|
|
76
|
+
//
|
|
77
|
+
// This bundle is built with `./markdown-parsers-entry` external: the Markdown
|
|
78
|
+
// and HTML sanitization libraries are kept out of the CDN payload. Register a
|
|
79
|
+
// loader that imports the self-contained `markdown-parsers.js` chunk from a sibling
|
|
80
|
+
// URL. The session prefetches it at init so it's warm before the first message.
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
import { setMarkdownParsersLoader, loadMarkdownParsers } from "./markdown-parsers-loader";
|
|
84
|
+
|
|
85
|
+
setMarkdownParsersLoader(() => {
|
|
86
|
+
const chunkUrl = widgetScriptSrc?.replace(
|
|
87
|
+
/index\.global\.js($|\?)/,
|
|
88
|
+
"markdown-parsers.js$1",
|
|
89
|
+
);
|
|
90
|
+
if (!chunkUrl || chunkUrl === widgetScriptSrc) {
|
|
91
|
+
return Promise.reject(
|
|
92
|
+
new Error(
|
|
93
|
+
"Could not derive the markdown-parsers.js URL from the widget script URL " +
|
|
94
|
+
`(${widgetScriptSrc ?? "unavailable"}). Self-hosted deployments that ` +
|
|
95
|
+
"rename index.global.js should host markdown-parsers.js alongside it.",
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return import(/* @vite-ignore */ chunkUrl);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Kick off the load immediately since it will likely be needed.
|
|
103
|
+
loadMarkdownParsers().catch(err => {
|
|
104
|
+
// It's okay if this fails (e.g. ad blocker), it'll just fall back to plain text.
|
|
105
|
+
if (typeof console !== "undefined") {
|
|
106
|
+
console.warn("[Persona] Failed to pre-load markdown parsers", err);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Deferred Runtype TTS engine loading.
|
|
111
|
+
//
|
|
112
|
+
// This bundle is built with `./runtype-speech-engine` external (see
|
|
113
|
+
// `tsup.global.config.ts`): the hosted read-aloud engine + the
|
|
114
|
+
// AudioPlaybackManager it bundles are kept out of the CDN payload. Register a
|
|
115
|
+
// loader that imports the self-contained `runtype-tts.js` chunk from a sibling
|
|
116
|
+
// URL; the session prefetches it at init when `textToSpeech.provider:'runtype'`
|
|
117
|
+
// is set, so it's warm before the first click. Same pattern as the WebMCP
|
|
118
|
+
// polyfill above.
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
import { setRuntypeTtsLoader } from "./voice/runtype-tts-loader";
|
|
122
|
+
|
|
123
|
+
setRuntypeTtsLoader(() => {
|
|
124
|
+
const chunkUrl = widgetScriptSrc?.replace(
|
|
125
|
+
/index\.global\.js($|\?)/,
|
|
126
|
+
"runtype-tts.js$1",
|
|
127
|
+
);
|
|
128
|
+
if (!chunkUrl || chunkUrl === widgetScriptSrc) {
|
|
129
|
+
return Promise.reject(
|
|
130
|
+
new Error(
|
|
131
|
+
"Could not derive the runtype-tts.js URL from the widget script URL " +
|
|
132
|
+
`(${widgetScriptSrc ?? "unavailable"}). Self-hosted deployments that ` +
|
|
133
|
+
"rename index.global.js should host runtype-tts.js alongside it, or set " +
|
|
134
|
+
"textToSpeech.createEngine to supply a speech engine directly.",
|
|
135
|
+
),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return import(/* @vite-ignore */ chunkUrl);
|
|
139
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* npm package entry (`@runtypelabs/persona`).
|
|
3
3
|
*
|
|
4
4
|
* This is a thin barrel over `index-core.ts` (the shared public API) that adds
|
|
5
|
-
* back the **dev/config-tool-only** helpers
|
|
5
|
+
* back the **dev/config-tool-only** helpers: `generateCodeSnippet` and
|
|
6
6
|
* `createDemoCarousel`. Those are kept out of `index-core.ts` so the IIFE/CDN
|
|
7
7
|
* build (`index-global.ts`, which re-exports from `index-core.ts`) doesn't ship
|
|
8
8
|
* them: a running widget never needs them, only build-time/demo tooling does.
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* `createDemoCarousel`.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
// Register `marked` + `dompurify` synchronously for the bundled npm build so
|
|
16
|
+
// the synchronous markdown/sanitize API renders on first paint. The IIFE/CDN
|
|
17
|
+
// entry (`index-global.ts`) does NOT import this; it lazy-loads the parsers
|
|
18
|
+
// from the `markdown-parsers.js` chunk instead. Must run before any render.
|
|
19
|
+
import "./markdown-parsers-eager";
|
|
20
|
+
|
|
15
21
|
// Full public API (everything except the two dev-only helpers below).
|
|
16
22
|
export * from "./index-core";
|
|
17
23
|
export { default } from "./index-core";
|
package/src/install.test.ts
CHANGED
|
@@ -70,7 +70,7 @@ function provideLauncherBundle() {
|
|
|
70
70
|
/**
|
|
71
71
|
* Force the next dynamically-inserted <script> to error. jsdom never fires
|
|
72
72
|
* load/error on injected scripts, so we trigger `onerror` on a fake timer that
|
|
73
|
-
* `runAllTimersAsync()` flushes
|
|
73
|
+
* `runAllTimersAsync()` flushes: deterministic under fake timers.
|
|
74
74
|
*/
|
|
75
75
|
function failNextScriptLoad() {
|
|
76
76
|
const head = document.head;
|
|
@@ -137,15 +137,15 @@ afterEach(() => {
|
|
|
137
137
|
vi.restoreAllMocks();
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
describe("install.ts
|
|
140
|
+
describe("install.ts: onScriptLoad beacon", () => {
|
|
141
141
|
it("fires synchronously on script execution, before any loading or gating", async () => {
|
|
142
142
|
const events = recordEvents();
|
|
143
143
|
const onScriptLoad = vi.fn();
|
|
144
144
|
|
|
145
|
-
// No config at all
|
|
145
|
+
// No config at all: the beacon must still fire (diagnostics: "did my embed run").
|
|
146
146
|
await install({ onScriptLoad });
|
|
147
147
|
|
|
148
|
-
// Fired during module evaluation
|
|
148
|
+
// Fired during module evaluation: no timer flush needed.
|
|
149
149
|
expect(onScriptLoad).toHaveBeenCalledTimes(1);
|
|
150
150
|
expect(onScriptLoad).toHaveBeenCalledWith({ version: "latest" });
|
|
151
151
|
expect(events.types()[0]).toBe("persona:script-load");
|
|
@@ -160,7 +160,7 @@ describe("install.ts — onScriptLoad beacon", () => {
|
|
|
160
160
|
});
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
describe("install.ts
|
|
163
|
+
describe("install.ts: deferred launcher path", () => {
|
|
164
164
|
it("mounts the critical launcher, fires onLauncherShown, and defers onChatReady until open", async () => {
|
|
165
165
|
markCssLoaded();
|
|
166
166
|
provideFullBundle(); // so loadJS resolves instantly when the user opens
|
|
@@ -186,7 +186,7 @@ describe("install.ts — deferred launcher path", () => {
|
|
|
186
186
|
expect(launcherMount).toHaveBeenCalledTimes(1);
|
|
187
187
|
expect(onLauncherShown).toHaveBeenCalledTimes(1);
|
|
188
188
|
expect(onLauncherShown).toHaveBeenCalledWith({ deferred: true, element: launcherElement });
|
|
189
|
-
// The full widget has NOT been initialized yet
|
|
189
|
+
// The full widget has NOT been initialized yet: that waits for first open.
|
|
190
190
|
expect(initAgentWidget).not.toHaveBeenCalled();
|
|
191
191
|
expect(onChatReady).not.toHaveBeenCalled();
|
|
192
192
|
|
|
@@ -230,7 +230,7 @@ describe("install.ts — deferred launcher path", () => {
|
|
|
230
230
|
});
|
|
231
231
|
});
|
|
232
232
|
|
|
233
|
-
describe("install.ts
|
|
233
|
+
describe("install.ts: eager path", () => {
|
|
234
234
|
it("non-floating (launcher disabled) eager-loads, fires onChatReady, and does NOT fire onLauncherShown", async () => {
|
|
235
235
|
markCssLoaded();
|
|
236
236
|
provideFullBundle();
|
|
@@ -272,7 +272,7 @@ describe("install.ts — eager path", () => {
|
|
|
272
272
|
});
|
|
273
273
|
});
|
|
274
274
|
|
|
275
|
-
describe("install.ts
|
|
275
|
+
describe("install.ts: deferral gate", () => {
|
|
276
276
|
async function expectDecision(config: any, { defers }: { defers: boolean }) {
|
|
277
277
|
markCssLoaded();
|
|
278
278
|
provideFullBundle();
|
|
@@ -345,7 +345,7 @@ describe("install.ts — deferral gate", () => {
|
|
|
345
345
|
});
|
|
346
346
|
});
|
|
347
347
|
|
|
348
|
-
describe("install.ts
|
|
348
|
+
describe("install.ts: onReady deprecation alias", () => {
|
|
349
349
|
it("still calls the deprecated onReady and warns once", async () => {
|
|
350
350
|
markCssLoaded();
|
|
351
351
|
provideFullBundle();
|
|
@@ -376,7 +376,7 @@ describe("install.ts — onReady deprecation alias", () => {
|
|
|
376
376
|
});
|
|
377
377
|
});
|
|
378
378
|
|
|
379
|
-
describe("install.ts
|
|
379
|
+
describe("install.ts: onError", () => {
|
|
380
380
|
it("fires onError with phase 'init' and dispatches persona:error when initialization throws", async () => {
|
|
381
381
|
markCssLoaded();
|
|
382
382
|
provideFullBundle();
|
|
@@ -420,7 +420,7 @@ describe("install.ts — onError", () => {
|
|
|
420
420
|
});
|
|
421
421
|
});
|
|
422
422
|
|
|
423
|
-
describe("install.ts
|
|
423
|
+
describe("install.ts: lifecycle callback safety", () => {
|
|
424
424
|
it("a throwing onScriptLoad callback does not break the rest of installation", async () => {
|
|
425
425
|
markCssLoaded();
|
|
426
426
|
provideFullBundle();
|