@runtypelabs/persona 2.3.1 → 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 +222 -5
- package/dist/index.cjs +42 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +832 -571
- package/dist/index.d.ts +832 -571
- package/dist/index.global.js +88 -88
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +42 -42
- package/dist/index.js.map +1 -1
- package/dist/widget.css +257 -67
- package/package.json +2 -4
- package/src/components/artifact-card.ts +39 -5
- package/src/components/artifact-pane.ts +68 -127
- package/src/components/composer-builder.ts +3 -23
- package/src/components/header-builder.ts +29 -34
- package/src/components/header-layouts.ts +109 -41
- package/src/components/launcher.ts +10 -7
- package/src/components/message-bubble.ts +7 -11
- package/src/components/panel.ts +4 -4
- package/src/defaults.ts +22 -93
- package/src/index.ts +20 -7
- package/src/presets.ts +66 -51
- package/src/runtime/host-layout.test.ts +333 -0
- package/src/runtime/host-layout.ts +346 -27
- package/src/runtime/init.test.ts +113 -8
- package/src/runtime/init.ts +1 -1
- package/src/styles/widget.css +257 -67
- package/src/types/theme.ts +76 -0
- package/src/types.ts +86 -97
- package/src/ui.docked.test.ts +203 -7
- package/src/ui.ts +125 -92
- package/src/utils/artifact-gate.ts +1 -1
- package/src/utils/buttons.ts +417 -0
- package/src/utils/code-generators.test.ts +43 -7
- package/src/utils/code-generators.ts +9 -25
- package/src/utils/deep-merge.ts +26 -0
- package/src/utils/dock.ts +18 -5
- package/src/utils/dropdown.ts +178 -0
- package/src/utils/theme.test.ts +90 -15
- package/src/utils/theme.ts +20 -46
- package/src/utils/tokens.ts +108 -11
- package/src/styles/tailwind.css +0 -20
- package/src/utils/migration.ts +0 -220
package/src/types/theme.ts
CHANGED
|
@@ -199,6 +199,20 @@ export interface HeaderTokens extends ComponentTokenSet {
|
|
|
199
199
|
background: TokenReference<'color'>;
|
|
200
200
|
border: TokenReference<'color'>;
|
|
201
201
|
borderRadius: TokenReference<'radius'>;
|
|
202
|
+
/** Background of the rounded avatar tile next to the title (Lucide / emoji / image). */
|
|
203
|
+
iconBackground: TokenReference<'color'>;
|
|
204
|
+
/** Foreground (glyph stroke or emoji text) on the header avatar tile. */
|
|
205
|
+
iconForeground: TokenReference<'color'>;
|
|
206
|
+
/** Header title line (next to the icon, or minimal layout title). */
|
|
207
|
+
titleForeground: TokenReference<'color'>;
|
|
208
|
+
/** Header subtitle line under the title. */
|
|
209
|
+
subtitleForeground: TokenReference<'color'>;
|
|
210
|
+
/** Default color for clear / close icon buttons when launcher overrides are unset. */
|
|
211
|
+
actionIconForeground: TokenReference<'color'>;
|
|
212
|
+
/** Box-shadow on the header (e.g., a fade shadow to replace the default border). */
|
|
213
|
+
shadow?: string;
|
|
214
|
+
/** Override the header bottom border (e.g., `none`). */
|
|
215
|
+
borderBottom?: string;
|
|
202
216
|
}
|
|
203
217
|
|
|
204
218
|
export interface MessageTokens {
|
|
@@ -317,6 +331,10 @@ export interface ArtifactToolbarTokens {
|
|
|
317
331
|
copyMenuShadow?: string;
|
|
318
332
|
copyMenuBorderRadius?: string;
|
|
319
333
|
copyMenuItemHoverBackground?: string;
|
|
334
|
+
/** Base background of icon buttons (defaults to --persona-surface). */
|
|
335
|
+
iconBackground?: string;
|
|
336
|
+
/** Border on the toolbar (e.g., `none` to remove the bottom border). */
|
|
337
|
+
toolbarBorder?: string;
|
|
320
338
|
}
|
|
321
339
|
|
|
322
340
|
/** Artifact tab strip chrome. */
|
|
@@ -326,13 +344,62 @@ export interface ArtifactTabTokens {
|
|
|
326
344
|
activeBorder?: string;
|
|
327
345
|
borderRadius?: string;
|
|
328
346
|
textColor?: string;
|
|
347
|
+
/** Hover background for inactive tabs. */
|
|
348
|
+
hoverBackground?: string;
|
|
349
|
+
/** Tab list container background. */
|
|
350
|
+
listBackground?: string;
|
|
351
|
+
/** Tab list container border color. */
|
|
352
|
+
listBorderColor?: string;
|
|
353
|
+
/** Tab list container padding (CSS shorthand). */
|
|
354
|
+
listPadding?: string;
|
|
329
355
|
}
|
|
330
356
|
|
|
331
357
|
/** Artifact pane chrome. */
|
|
332
358
|
export interface ArtifactPaneTokens {
|
|
359
|
+
/**
|
|
360
|
+
* Background for the artifact column (toolbar + content), resolved from the theme.
|
|
361
|
+
* Defaults to `semantic.colors.container` so the pane matches assistant message surfaces.
|
|
362
|
+
* `features.artifacts.layout.paneBackground` still wins when set (layout escape hatch).
|
|
363
|
+
*/
|
|
364
|
+
background?: string;
|
|
333
365
|
toolbarBackground?: string;
|
|
334
366
|
}
|
|
335
367
|
|
|
368
|
+
/** Icon button chrome (used by createIconButton). */
|
|
369
|
+
export interface IconButtonTokens {
|
|
370
|
+
background?: string;
|
|
371
|
+
border?: string;
|
|
372
|
+
color?: string;
|
|
373
|
+
padding?: string;
|
|
374
|
+
borderRadius?: string;
|
|
375
|
+
hoverBackground?: string;
|
|
376
|
+
hoverColor?: string;
|
|
377
|
+
/** Background when aria-pressed="true". */
|
|
378
|
+
activeBackground?: string;
|
|
379
|
+
/** Border color when aria-pressed="true". */
|
|
380
|
+
activeBorder?: string;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Label button chrome (used by createLabelButton). */
|
|
384
|
+
export interface LabelButtonTokens {
|
|
385
|
+
background?: string;
|
|
386
|
+
border?: string;
|
|
387
|
+
color?: string;
|
|
388
|
+
padding?: string;
|
|
389
|
+
borderRadius?: string;
|
|
390
|
+
hoverBackground?: string;
|
|
391
|
+
fontSize?: string;
|
|
392
|
+
gap?: string;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Toggle group chrome (used by createToggleGroup). */
|
|
396
|
+
export interface ToggleGroupTokens {
|
|
397
|
+
/** Gap between toggle buttons. Default: 0 (connected). */
|
|
398
|
+
gap?: string;
|
|
399
|
+
/** Border radius for first/last buttons. */
|
|
400
|
+
borderRadius?: string;
|
|
401
|
+
}
|
|
402
|
+
|
|
336
403
|
export interface ComponentTokens {
|
|
337
404
|
button: ButtonTokens;
|
|
338
405
|
input: InputTokens;
|
|
@@ -348,6 +415,12 @@ export interface ComponentTokens {
|
|
|
348
415
|
toolBubble: ToolBubbleTokens;
|
|
349
416
|
reasoningBubble: ReasoningBubbleTokens;
|
|
350
417
|
composer: ComposerChromeTokens;
|
|
418
|
+
/** Icon button styling tokens. */
|
|
419
|
+
iconButton?: IconButtonTokens;
|
|
420
|
+
/** Label button styling tokens. */
|
|
421
|
+
labelButton?: LabelButtonTokens;
|
|
422
|
+
/** Toggle group styling tokens. */
|
|
423
|
+
toggleGroup?: ToggleGroupTokens;
|
|
351
424
|
/** Artifact toolbar, tab strip, and pane chrome. */
|
|
352
425
|
artifact?: {
|
|
353
426
|
toolbar?: ArtifactToolbarTokens;
|
|
@@ -384,6 +457,9 @@ export type PersonaTheme = PersonaThemeBase &
|
|
|
384
457
|
PersonaThemeSemantic &
|
|
385
458
|
PersonaThemeComponents;
|
|
386
459
|
|
|
460
|
+
/** Recursive partial for `config.theme` / `config.darkTheme` overrides. */
|
|
461
|
+
export type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T;
|
|
462
|
+
|
|
387
463
|
export interface ResolvedToken {
|
|
388
464
|
path: string;
|
|
389
465
|
value: string;
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentWidgetPlugin } from "./plugins/types";
|
|
2
|
+
import type { DeepPartial, PersonaTheme } from "./types/theme";
|
|
2
3
|
|
|
3
4
|
// ============================================================================
|
|
4
5
|
// Multi-Modal Content Types
|
|
@@ -469,7 +470,9 @@ export type AgentWidgetArtifactsLayoutConfig = {
|
|
|
469
470
|
*/
|
|
470
471
|
unifiedSplitOuterRadius?: string;
|
|
471
472
|
/**
|
|
472
|
-
*
|
|
473
|
+
* Strongest override: solid background for the artifact column (CSS color). Sets `--persona-artifact-pane-bg`
|
|
474
|
+
* on the widget root. Leave unset to use theme `components.artifact.pane.background` (defaults to semantic
|
|
475
|
+
* container) so light/dark stays consistent.
|
|
473
476
|
*/
|
|
474
477
|
paneBackground?: string;
|
|
475
478
|
/**
|
|
@@ -533,6 +536,20 @@ export type AgentWidgetArtifactsFeature = {
|
|
|
533
536
|
type: 'open' | 'download';
|
|
534
537
|
artifactId: string;
|
|
535
538
|
}) => boolean | void;
|
|
539
|
+
/**
|
|
540
|
+
* Custom renderer for artifact reference cards shown in the message thread.
|
|
541
|
+
* Return an HTMLElement to replace the default card, or `null` to use the default.
|
|
542
|
+
*/
|
|
543
|
+
renderCard?: (context: {
|
|
544
|
+
artifact: {
|
|
545
|
+
artifactId: string;
|
|
546
|
+
title: string;
|
|
547
|
+
artifactType: string;
|
|
548
|
+
status: string;
|
|
549
|
+
};
|
|
550
|
+
config: AgentWidgetConfig;
|
|
551
|
+
defaultRenderer: () => HTMLElement;
|
|
552
|
+
}) => HTMLElement | null;
|
|
536
553
|
};
|
|
537
554
|
|
|
538
555
|
export type AgentWidgetFeatureFlags = {
|
|
@@ -667,85 +684,6 @@ export type EventStreamPayloadRenderContext = {
|
|
|
667
684
|
parsedPayload: unknown;
|
|
668
685
|
};
|
|
669
686
|
|
|
670
|
-
export type AgentWidgetTheme = {
|
|
671
|
-
primary?: string;
|
|
672
|
-
secondary?: string;
|
|
673
|
-
surface?: string;
|
|
674
|
-
muted?: string;
|
|
675
|
-
accent?: string;
|
|
676
|
-
container?: string;
|
|
677
|
-
border?: string;
|
|
678
|
-
divider?: string;
|
|
679
|
-
messageBorder?: string;
|
|
680
|
-
inputBackground?: string;
|
|
681
|
-
callToAction?: string;
|
|
682
|
-
callToActionBackground?: string;
|
|
683
|
-
sendButtonBackgroundColor?: string;
|
|
684
|
-
sendButtonTextColor?: string;
|
|
685
|
-
sendButtonBorderColor?: string;
|
|
686
|
-
closeButtonColor?: string;
|
|
687
|
-
closeButtonBackgroundColor?: string;
|
|
688
|
-
closeButtonBorderColor?: string;
|
|
689
|
-
clearChatIconColor?: string;
|
|
690
|
-
clearChatBackgroundColor?: string;
|
|
691
|
-
clearChatBorderColor?: string;
|
|
692
|
-
tooltipBackground?: string;
|
|
693
|
-
tooltipForeground?: string;
|
|
694
|
-
micIconColor?: string;
|
|
695
|
-
micBackgroundColor?: string;
|
|
696
|
-
micBorderColor?: string;
|
|
697
|
-
recordingIconColor?: string;
|
|
698
|
-
recordingBackgroundColor?: string;
|
|
699
|
-
recordingBorderColor?: string;
|
|
700
|
-
inputFontFamily?: "sans-serif" | "serif" | "mono";
|
|
701
|
-
inputFontWeight?: string;
|
|
702
|
-
radiusSm?: string;
|
|
703
|
-
radiusMd?: string;
|
|
704
|
-
radiusLg?: string;
|
|
705
|
-
launcherRadius?: string;
|
|
706
|
-
buttonRadius?: string;
|
|
707
|
-
/**
|
|
708
|
-
* Border style for the chat panel container.
|
|
709
|
-
* @example "1px solid #e5e7eb" | "none"
|
|
710
|
-
* @default "1px solid var(--persona-border)"
|
|
711
|
-
*/
|
|
712
|
-
panelBorder?: string;
|
|
713
|
-
/**
|
|
714
|
-
* Box shadow for the chat panel container.
|
|
715
|
-
* @example "0 25px 50px -12px rgba(0,0,0,0.25)" | "none"
|
|
716
|
-
* @default "0 25px 50px -12px rgba(0,0,0,0.25)"
|
|
717
|
-
*/
|
|
718
|
-
panelShadow?: string;
|
|
719
|
-
/**
|
|
720
|
-
* Border radius for the chat panel container.
|
|
721
|
-
* @example "16px" | "0"
|
|
722
|
-
* @default "16px"
|
|
723
|
-
*/
|
|
724
|
-
panelBorderRadius?: string;
|
|
725
|
-
/**
|
|
726
|
-
* Box-shadow for user message bubbles (bubble message layout).
|
|
727
|
-
* @example "none" | "0 1px 2px rgba(0,0,0,0.05)"
|
|
728
|
-
*/
|
|
729
|
-
messageUserShadow?: string;
|
|
730
|
-
/**
|
|
731
|
-
* Box-shadow for assistant message bubbles (bubble message layout).
|
|
732
|
-
* Overrides the default subtle assistant shadow when set.
|
|
733
|
-
*/
|
|
734
|
-
messageAssistantShadow?: string;
|
|
735
|
-
/**
|
|
736
|
-
* Box-shadow for tool-call / function-call rows.
|
|
737
|
-
*/
|
|
738
|
-
toolBubbleShadow?: string;
|
|
739
|
-
/**
|
|
740
|
-
* Box-shadow for reasoning (“thinking”) rows.
|
|
741
|
-
*/
|
|
742
|
-
reasoningBubbleShadow?: string;
|
|
743
|
-
/**
|
|
744
|
-
* Box-shadow on the composer (input) container.
|
|
745
|
-
*/
|
|
746
|
-
composerShadow?: string;
|
|
747
|
-
};
|
|
748
|
-
|
|
749
687
|
export type AgentWidgetDockConfig = {
|
|
750
688
|
/**
|
|
751
689
|
* Side of the wrapped container where the docked panel should render.
|
|
@@ -758,10 +696,22 @@ export type AgentWidgetDockConfig = {
|
|
|
758
696
|
*/
|
|
759
697
|
width?: string;
|
|
760
698
|
/**
|
|
761
|
-
*
|
|
762
|
-
*
|
|
699
|
+
* When false, the dock column snaps between `0` and `width` with no CSS transition so main
|
|
700
|
+
* content does not reflow during the open/close animation.
|
|
701
|
+
* @default true
|
|
702
|
+
*/
|
|
703
|
+
animate?: boolean;
|
|
704
|
+
/**
|
|
705
|
+
* How the dock panel is shown.
|
|
706
|
+
* - `"resize"` (default): a flex column grows/shrinks between `0` and `width` (main content reflows).
|
|
707
|
+
* - `"overlay"`: panel is absolutely positioned and translates in/out **over** full-width content.
|
|
708
|
+
* - `"push"`: a wide inner track `[content at shell width][panel]` translates horizontally so the panel
|
|
709
|
+
* appears to push the workspace aside **without** animating the content column width (Shopify-style).
|
|
710
|
+
* - `"emerge"`: like `"resize"`, the flex column animates so **page content reflows**; the chat
|
|
711
|
+
* panel keeps a **fixed** `dock.width` (not squeezed while the column grows), clipped by the slot so
|
|
712
|
+
* it appears to emerge at full width like a floating widget.
|
|
763
713
|
*/
|
|
764
|
-
|
|
714
|
+
reveal?: "resize" | "overlay" | "push" | "emerge";
|
|
765
715
|
};
|
|
766
716
|
|
|
767
717
|
export type AgentWidgetLauncherConfig = {
|
|
@@ -1396,6 +1346,17 @@ export type AgentWidgetHeaderTrailingAction = {
|
|
|
1396
1346
|
icon?: string;
|
|
1397
1347
|
label?: string;
|
|
1398
1348
|
ariaLabel?: string;
|
|
1349
|
+
/**
|
|
1350
|
+
* When set, clicking this action opens a dropdown menu.
|
|
1351
|
+
* Menu item selections fire `onAction(menuItemId)`.
|
|
1352
|
+
*/
|
|
1353
|
+
menuItems?: Array<{
|
|
1354
|
+
id: string;
|
|
1355
|
+
label: string;
|
|
1356
|
+
icon?: string;
|
|
1357
|
+
destructive?: boolean;
|
|
1358
|
+
dividerBefore?: boolean;
|
|
1359
|
+
}>;
|
|
1399
1360
|
};
|
|
1400
1361
|
|
|
1401
1362
|
/**
|
|
@@ -1466,6 +1427,41 @@ export type AgentWidgetHeaderLayoutConfig = {
|
|
|
1466
1427
|
* When set, the title row becomes visually interactive (cursor: pointer).
|
|
1467
1428
|
*/
|
|
1468
1429
|
onTitleClick?: () => void;
|
|
1430
|
+
/** Style config for the title row hover effect (minimal layout). */
|
|
1431
|
+
titleRowHover?: {
|
|
1432
|
+
/** Hover background color. */
|
|
1433
|
+
background?: string;
|
|
1434
|
+
/** Hover border color. */
|
|
1435
|
+
border?: string;
|
|
1436
|
+
/** Border radius for the pill shape. */
|
|
1437
|
+
borderRadius?: string;
|
|
1438
|
+
/** Padding inside the pill. */
|
|
1439
|
+
padding?: string;
|
|
1440
|
+
};
|
|
1441
|
+
/**
|
|
1442
|
+
* Replaces the title with a combo button (label + chevron + dropdown menu).
|
|
1443
|
+
* When set, `trailingActions`, `onTitleClick`, and `titleRowHover` are ignored
|
|
1444
|
+
* since the combo button handles all of these internally.
|
|
1445
|
+
*/
|
|
1446
|
+
titleMenu?: {
|
|
1447
|
+
/** Dropdown menu items. */
|
|
1448
|
+
menuItems: Array<{
|
|
1449
|
+
id: string;
|
|
1450
|
+
label: string;
|
|
1451
|
+
icon?: string;
|
|
1452
|
+
destructive?: boolean;
|
|
1453
|
+
dividerBefore?: boolean;
|
|
1454
|
+
}>;
|
|
1455
|
+
/** Called when a menu item is selected. */
|
|
1456
|
+
onSelect: (id: string) => void;
|
|
1457
|
+
/** Hover pill style. */
|
|
1458
|
+
hover?: {
|
|
1459
|
+
background?: string;
|
|
1460
|
+
border?: string;
|
|
1461
|
+
borderRadius?: string;
|
|
1462
|
+
padding?: string;
|
|
1463
|
+
};
|
|
1464
|
+
};
|
|
1469
1465
|
};
|
|
1470
1466
|
|
|
1471
1467
|
/**
|
|
@@ -2256,22 +2252,15 @@ export type AgentWidgetConfig = {
|
|
|
2256
2252
|
*/
|
|
2257
2253
|
showWelcomeCard?: boolean;
|
|
2258
2254
|
};
|
|
2259
|
-
theme?: AgentWidgetTheme;
|
|
2260
2255
|
/**
|
|
2261
|
-
*
|
|
2262
|
-
*
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
*
|
|
2267
|
-
* config: {
|
|
2268
|
-
* theme: { primary: '#111827', surface: '#ffffff' },
|
|
2269
|
-
* darkTheme: { primary: '#f9fafb', surface: '#1f2937' },
|
|
2270
|
-
* colorScheme: 'auto'
|
|
2271
|
-
* }
|
|
2272
|
-
* ```
|
|
2256
|
+
* Semantic design tokens (`palette`, `semantic`, `components`).
|
|
2257
|
+
* Omit for library defaults.
|
|
2258
|
+
*/
|
|
2259
|
+
theme?: DeepPartial<PersonaTheme>;
|
|
2260
|
+
/**
|
|
2261
|
+
* Dark-mode token overrides. Merged over `theme` when the active scheme is dark.
|
|
2273
2262
|
*/
|
|
2274
|
-
darkTheme?:
|
|
2263
|
+
darkTheme?: DeepPartial<PersonaTheme>;
|
|
2275
2264
|
/**
|
|
2276
2265
|
* Color scheme mode for the widget.
|
|
2277
2266
|
* - 'light': Always use light theme (default)
|
package/src/ui.docked.test.ts
CHANGED
|
@@ -10,7 +10,7 @@ describe("createAgentExperience docked mode", () => {
|
|
|
10
10
|
document.body.innerHTML = "";
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
it("
|
|
13
|
+
it("toggles docked panel open/closed; built-in launcher stays hidden (open via controller.open)", () => {
|
|
14
14
|
const mount = document.createElement("div");
|
|
15
15
|
document.body.appendChild(mount);
|
|
16
16
|
|
|
@@ -22,7 +22,6 @@ describe("createAgentExperience docked mode", () => {
|
|
|
22
22
|
dock: {
|
|
23
23
|
side: "right",
|
|
24
24
|
width: "420px",
|
|
25
|
-
collapsedWidth: "72px",
|
|
26
25
|
},
|
|
27
26
|
},
|
|
28
27
|
});
|
|
@@ -40,7 +39,7 @@ describe("createAgentExperience docked mode", () => {
|
|
|
40
39
|
controller.close();
|
|
41
40
|
|
|
42
41
|
expect(wrapper?.style.display).toBe("none");
|
|
43
|
-
expect(launcherButton?.style.display).toBe("");
|
|
42
|
+
expect(launcherButton?.style.display).toBe("none");
|
|
44
43
|
|
|
45
44
|
controller.open();
|
|
46
45
|
|
|
@@ -50,7 +49,141 @@ describe("createAgentExperience docked mode", () => {
|
|
|
50
49
|
controller.destroy();
|
|
51
50
|
});
|
|
52
51
|
|
|
53
|
-
it("
|
|
52
|
+
it("keeps docked panel hidden when closed under mobile fullscreen breakpoint", () => {
|
|
53
|
+
const prevWidth = window.innerWidth;
|
|
54
|
+
try {
|
|
55
|
+
Object.defineProperty(window, "innerWidth", {
|
|
56
|
+
configurable: true,
|
|
57
|
+
value: 480,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const mount = document.createElement("div");
|
|
61
|
+
document.body.appendChild(mount);
|
|
62
|
+
|
|
63
|
+
const controller = createAgentExperience(mount, {
|
|
64
|
+
apiUrl: "https://api.example.com/chat",
|
|
65
|
+
launcher: {
|
|
66
|
+
mountMode: "docked",
|
|
67
|
+
autoExpand: false,
|
|
68
|
+
dock: {
|
|
69
|
+
side: "right",
|
|
70
|
+
width: "420px",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const wrapper = mount.firstElementChild as HTMLElement | null;
|
|
76
|
+
expect(wrapper?.style.display).toBe("none");
|
|
77
|
+
expect(getComputedStyle(wrapper!).display).toBe("none");
|
|
78
|
+
|
|
79
|
+
controller.open();
|
|
80
|
+
expect(wrapper?.style.display).toBe("flex");
|
|
81
|
+
|
|
82
|
+
controller.destroy();
|
|
83
|
+
} finally {
|
|
84
|
+
Object.defineProperty(window, "innerWidth", {
|
|
85
|
+
configurable: true,
|
|
86
|
+
writable: true,
|
|
87
|
+
value: prevWidth,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("keeps docked panel hidden after resize into mobile when closed", () => {
|
|
93
|
+
const prevWidth = window.innerWidth;
|
|
94
|
+
try {
|
|
95
|
+
Object.defineProperty(window, "innerWidth", {
|
|
96
|
+
configurable: true,
|
|
97
|
+
value: 900,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const mount = document.createElement("div");
|
|
101
|
+
document.body.appendChild(mount);
|
|
102
|
+
|
|
103
|
+
const controller = createAgentExperience(mount, {
|
|
104
|
+
apiUrl: "https://api.example.com/chat",
|
|
105
|
+
launcher: {
|
|
106
|
+
mountMode: "docked",
|
|
107
|
+
autoExpand: false,
|
|
108
|
+
dock: {
|
|
109
|
+
side: "right",
|
|
110
|
+
width: "420px",
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const wrapper = mount.firstElementChild as HTMLElement | null;
|
|
116
|
+
expect(wrapper?.style.display).toBe("none");
|
|
117
|
+
|
|
118
|
+
Object.defineProperty(window, "innerWidth", {
|
|
119
|
+
configurable: true,
|
|
120
|
+
value: 480,
|
|
121
|
+
});
|
|
122
|
+
window.dispatchEvent(new Event("resize"));
|
|
123
|
+
|
|
124
|
+
expect(wrapper?.style.display).toBe("none");
|
|
125
|
+
|
|
126
|
+
controller.destroy();
|
|
127
|
+
} finally {
|
|
128
|
+
Object.defineProperty(window, "innerWidth", {
|
|
129
|
+
configurable: true,
|
|
130
|
+
writable: true,
|
|
131
|
+
value: prevWidth,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("collapses the dock width to 0 when the header close button is clicked", () => {
|
|
137
|
+
const target = document.createElement("div");
|
|
138
|
+
document.body.appendChild(target);
|
|
139
|
+
|
|
140
|
+
const hostLayout = createWidgetHostLayout(target, {
|
|
141
|
+
launcher: {
|
|
142
|
+
mountMode: "docked",
|
|
143
|
+
autoExpand: true,
|
|
144
|
+
dock: {
|
|
145
|
+
side: "right",
|
|
146
|
+
width: "420px",
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
const mount = document.createElement("div");
|
|
151
|
+
hostLayout.host.appendChild(mount);
|
|
152
|
+
|
|
153
|
+
const controller = createAgentExperience(mount, {
|
|
154
|
+
apiUrl: "https://api.example.com/chat",
|
|
155
|
+
launcher: {
|
|
156
|
+
mountMode: "docked",
|
|
157
|
+
autoExpand: true,
|
|
158
|
+
dock: {
|
|
159
|
+
side: "right",
|
|
160
|
+
width: "420px",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const syncDockState = () => hostLayout.syncWidgetState(controller.getState());
|
|
166
|
+
const openUnsub = controller.on("widget:opened", syncDockState);
|
|
167
|
+
const closeUnsub = controller.on("widget:closed", syncDockState);
|
|
168
|
+
syncDockState();
|
|
169
|
+
|
|
170
|
+
const dockSlot = hostLayout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
|
|
171
|
+
const closeButton = mount.querySelector<HTMLButtonElement>('[aria-label="Close chat"]');
|
|
172
|
+
|
|
173
|
+
expect(dockSlot?.style.width).toBe("420px");
|
|
174
|
+
expect(closeButton).not.toBeNull();
|
|
175
|
+
|
|
176
|
+
closeButton!.click();
|
|
177
|
+
|
|
178
|
+
expect(dockSlot?.style.width).toBe("0px");
|
|
179
|
+
|
|
180
|
+
openUnsub();
|
|
181
|
+
closeUnsub();
|
|
182
|
+
controller.destroy();
|
|
183
|
+
hostLayout.destroy();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("overlay reveal keeps width when closed; host-layout uses transform", () => {
|
|
54
187
|
const target = document.createElement("div");
|
|
55
188
|
document.body.appendChild(target);
|
|
56
189
|
|
|
@@ -61,7 +194,7 @@ describe("createAgentExperience docked mode", () => {
|
|
|
61
194
|
dock: {
|
|
62
195
|
side: "right",
|
|
63
196
|
width: "420px",
|
|
64
|
-
|
|
197
|
+
reveal: "overlay",
|
|
65
198
|
},
|
|
66
199
|
},
|
|
67
200
|
});
|
|
@@ -76,7 +209,7 @@ describe("createAgentExperience docked mode", () => {
|
|
|
76
209
|
dock: {
|
|
77
210
|
side: "right",
|
|
78
211
|
width: "420px",
|
|
79
|
-
|
|
212
|
+
reveal: "overlay",
|
|
80
213
|
},
|
|
81
214
|
},
|
|
82
215
|
});
|
|
@@ -94,7 +227,70 @@ describe("createAgentExperience docked mode", () => {
|
|
|
94
227
|
|
|
95
228
|
closeButton!.click();
|
|
96
229
|
|
|
97
|
-
expect(dockSlot?.style.width).toBe("
|
|
230
|
+
expect(dockSlot?.style.width).toBe("420px");
|
|
231
|
+
expect(dockSlot?.style.transform).toBe("translateX(100%)");
|
|
232
|
+
|
|
233
|
+
openUnsub();
|
|
234
|
+
closeUnsub();
|
|
235
|
+
controller.destroy();
|
|
236
|
+
hostLayout.destroy();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("push reveal translates the push-track when the header close button is clicked", () => {
|
|
240
|
+
const target = document.createElement("div");
|
|
241
|
+
document.body.appendChild(target);
|
|
242
|
+
|
|
243
|
+
const hostLayout = createWidgetHostLayout(target, {
|
|
244
|
+
launcher: {
|
|
245
|
+
mountMode: "docked",
|
|
246
|
+
autoExpand: true,
|
|
247
|
+
dock: {
|
|
248
|
+
side: "right",
|
|
249
|
+
width: "420px",
|
|
250
|
+
reveal: "push",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
const shell = hostLayout.shell!;
|
|
255
|
+
Object.defineProperty(shell, "clientWidth", { get: () => 1000, configurable: true });
|
|
256
|
+
hostLayout.updateConfig({
|
|
257
|
+
launcher: {
|
|
258
|
+
mountMode: "docked",
|
|
259
|
+
autoExpand: true,
|
|
260
|
+
dock: { side: "right", width: "420px", reveal: "push" },
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const mount = document.createElement("div");
|
|
265
|
+
hostLayout.host.appendChild(mount);
|
|
266
|
+
|
|
267
|
+
const controller = createAgentExperience(mount, {
|
|
268
|
+
apiUrl: "https://api.example.com/chat",
|
|
269
|
+
launcher: {
|
|
270
|
+
mountMode: "docked",
|
|
271
|
+
autoExpand: true,
|
|
272
|
+
dock: {
|
|
273
|
+
side: "right",
|
|
274
|
+
width: "420px",
|
|
275
|
+
reveal: "push",
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const syncDockState = () => hostLayout.syncWidgetState(controller.getState());
|
|
281
|
+
const openUnsub = controller.on("widget:opened", syncDockState);
|
|
282
|
+
const closeUnsub = controller.on("widget:closed", syncDockState);
|
|
283
|
+
syncDockState();
|
|
284
|
+
|
|
285
|
+
const pushTrack = shell.querySelector<HTMLElement>('[data-persona-dock-role="push-track"]');
|
|
286
|
+
const closeButton = mount.querySelector<HTMLButtonElement>('[aria-label="Close chat"]');
|
|
287
|
+
|
|
288
|
+
expect(pushTrack).not.toBeNull();
|
|
289
|
+
expect(pushTrack?.style.transform).toBe("translateX(-420px)");
|
|
290
|
+
|
|
291
|
+
closeButton!.click();
|
|
292
|
+
|
|
293
|
+
expect(pushTrack?.style.transform).toBe("translateX(0)");
|
|
98
294
|
|
|
99
295
|
openUnsub();
|
|
100
296
|
closeUnsub();
|