@runtypelabs/persona 4.15.0 → 4.16.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.
Files changed (86) hide show
  1. package/dist/codegen.cjs +1 -1
  2. package/dist/codegen.js +1 -1
  3. package/dist/context-mentions.d.cts +283 -9
  4. package/dist/context-mentions.d.ts +283 -9
  5. package/dist/index.cjs +68 -68
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +299 -10
  8. package/dist/index.d.ts +299 -10
  9. package/dist/index.global.js +57 -57
  10. package/dist/index.global.js.map +1 -1
  11. package/dist/index.js +69 -69
  12. package/dist/index.js.map +1 -1
  13. package/dist/launcher.global.js +2 -2
  14. package/dist/launcher.global.js.map +1 -1
  15. package/dist/smart-dom-reader.d.cts +283 -9
  16. package/dist/smart-dom-reader.d.ts +283 -9
  17. package/dist/testing.cjs +3 -3
  18. package/dist/testing.d.cts +7 -0
  19. package/dist/testing.d.ts +7 -0
  20. package/dist/testing.js +3 -3
  21. package/dist/theme-editor-preview.cjs +64 -64
  22. package/dist/theme-editor-preview.d.cts +283 -9
  23. package/dist/theme-editor-preview.d.ts +283 -9
  24. package/dist/theme-editor-preview.js +56 -56
  25. package/dist/theme-editor.cjs +10 -10
  26. package/dist/theme-editor.d.cts +283 -10
  27. package/dist/theme-editor.d.ts +283 -10
  28. package/dist/theme-editor.js +10 -10
  29. package/dist/theme-reference.cjs +1 -1
  30. package/dist/theme-reference.d.cts +66 -3
  31. package/dist/theme-reference.d.ts +66 -3
  32. package/dist/theme-reference.js +1 -1
  33. package/dist/widget.css +1 -1
  34. package/package.json +1 -1
  35. package/src/components/artifact-pane.test.ts +27 -0
  36. package/src/components/artifact-pane.ts +7 -3
  37. package/src/components/header-builder.ts +27 -2
  38. package/src/components/header-layouts.test.ts +56 -0
  39. package/src/components/header-layouts.ts +29 -9
  40. package/src/components/launcher-surface.test.ts +403 -0
  41. package/src/components/launcher.ts +297 -2
  42. package/src/components/panel.ts +69 -74
  43. package/src/components/suggestions.test.ts +40 -0
  44. package/src/components/suggestions.ts +213 -153
  45. package/src/components/welcome.test.ts +121 -0
  46. package/src/components/welcome.ts +305 -0
  47. package/src/components/widget-view.test.ts +5 -3
  48. package/src/components/widget-view.ts +14 -7
  49. package/src/defaults.ts +2 -2
  50. package/src/index-core.ts +12 -1
  51. package/src/launcher-global.ts +10 -7
  52. package/src/plugins/types.ts +66 -1
  53. package/src/styles/widget-styles.test.ts +220 -0
  54. package/src/styles/widget.css +353 -9
  55. package/src/testing/mock-stream.test.ts +19 -0
  56. package/src/testing/mock-stream.ts +20 -0
  57. package/src/theme-editor/sections.ts +30 -1
  58. package/src/theme-reference.ts +26 -4
  59. package/src/types/theme.ts +40 -3
  60. package/src/types.ts +164 -6
  61. package/src/ui.body-top-slot.test.ts +95 -0
  62. package/src/ui.content-width.test.ts +91 -0
  63. package/src/ui.docked.test.ts +7 -4
  64. package/src/ui.launcher-teaser.test.ts +162 -0
  65. package/src/ui.rebuild-composer.test.ts +491 -0
  66. package/src/ui.render-welcome.test.ts +505 -0
  67. package/src/ui.scroll-additive.test.ts +91 -21
  68. package/src/ui.scroll.test.ts +302 -14
  69. package/src/ui.suggest-replies.test.ts +46 -0
  70. package/src/ui.ts +747 -136
  71. package/src/ui.welcome.test.ts +504 -0
  72. package/src/utils/artifact-gate.test.ts +27 -0
  73. package/src/utils/artifact-gate.ts +9 -0
  74. package/src/utils/attachment-manager.test.ts +37 -0
  75. package/src/utils/attachment-manager.ts +15 -0
  76. package/src/utils/auto-follow.test.ts +23 -0
  77. package/src/utils/auto-follow.ts +11 -1
  78. package/src/utils/config-merge.ts +2 -0
  79. package/src/utils/content-width.test.ts +37 -0
  80. package/src/utils/content-width.ts +17 -0
  81. package/src/utils/plugin-storage.test.ts +94 -0
  82. package/src/utils/plugin-storage.ts +98 -0
  83. package/src/utils/theme.test.ts +137 -0
  84. package/src/utils/tokens.ts +49 -3
  85. package/src/welcome.test.ts +213 -0
  86. package/src/welcome.ts +130 -0
package/dist/codegen.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var $=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var R=(e,i)=>{for(var r in i)$(e,r,{get:i[r],enumerable:!0})},W=(e,i,r,s)=>{if(i&&typeof i=="object"||typeof i=="function")for(let a of T(i))!M.call(e,a)&&a!==r&&$(e,a,{get:()=>i[a],enumerable:!(s=_(i,a))||s.enumerable});return e};var H=e=>W($({},"__esModule",{value:!0}),e);var z={};R(z,{generateCodeSnippet:()=>I});module.exports=H(z);var x="4.15.0";var A=x;function w(e){return e?.cdnBase?.trim().replace(/\/+$/,"").replace(/[\\'"<>`]/g,r=>`%${r.charCodeAt(0).toString(16).toUpperCase().padStart(2,"0")}`)||`https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${A}/dist`}function u(e){if(e!==void 0)return typeof e=="string"?e:Array.isArray(e)?`[${e.map(i=>i.toString()).join(", ")}]`:e.toString()}function D(e){if(e)return{getHeaders:u(e.getHeaders),onFeedback:u(e.onFeedback),onCopy:u(e.onCopy),requestMiddleware:u(e.requestMiddleware),actionHandlers:u(e.actionHandlers),actionParsers:u(e.actionParsers),postprocessMessage:u(e.postprocessMessage),contextProviders:u(e.contextProviders),streamParser:u(e.streamParser)}}var j=`({ text, message }: any) => {
1
+ "use strict";var $=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var R=(e,i)=>{for(var r in i)$(e,r,{get:i[r],enumerable:!0})},W=(e,i,r,s)=>{if(i&&typeof i=="object"||typeof i=="function")for(let a of T(i))!M.call(e,a)&&a!==r&&$(e,a,{get:()=>i[a],enumerable:!(s=_(i,a))||s.enumerable});return e};var H=e=>W($({},"__esModule",{value:!0}),e);var z={};R(z,{generateCodeSnippet:()=>I});module.exports=H(z);var x="4.16.0";var A=x;function w(e){return e?.cdnBase?.trim().replace(/\/+$/,"").replace(/[\\'"<>`]/g,r=>`%${r.charCodeAt(0).toString(16).toUpperCase().padStart(2,"0")}`)||`https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${A}/dist`}function u(e){if(e!==void 0)return typeof e=="string"?e:Array.isArray(e)?`[${e.map(i=>i.toString()).join(", ")}]`:e.toString()}function D(e){if(e)return{getHeaders:u(e.getHeaders),onFeedback:u(e.onFeedback),onCopy:u(e.onCopy),requestMiddleware:u(e.requestMiddleware),actionHandlers:u(e.actionHandlers),actionParsers:u(e.actionParsers),postprocessMessage:u(e.postprocessMessage),contextProviders:u(e.contextProviders),streamParser:u(e.streamParser)}}var j=`({ text, message }: any) => {
2
2
  const jsonSource = (message as any).rawContent || text || message.content;
3
3
  if (!jsonSource || typeof jsonSource !== 'string') return null;
4
4
  let cleanJson = jsonSource
package/dist/codegen.js CHANGED
@@ -1,4 +1,4 @@
1
- var S="4.15.0";var x=S;function $(e){return e?.cdnBase?.trim().replace(/\/+$/,"").replace(/[\\'"<>`]/g,r=>`%${r.charCodeAt(0).toString(16).toUpperCase().padStart(2,"0")}`)||`https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${x}/dist`}function u(e){if(e!==void 0)return typeof e=="string"?e:Array.isArray(e)?`[${e.map(i=>i.toString()).join(", ")}]`:e.toString()}function v(e){if(e)return{getHeaders:u(e.getHeaders),onFeedback:u(e.onFeedback),onCopy:u(e.onCopy),requestMiddleware:u(e.requestMiddleware),actionHandlers:u(e.actionHandlers),actionParsers:u(e.actionParsers),postprocessMessage:u(e.postprocessMessage),contextProviders:u(e.contextProviders),streamParser:u(e.streamParser)}}var A=`({ text, message }: any) => {
1
+ var S="4.16.0";var x=S;function $(e){return e?.cdnBase?.trim().replace(/\/+$/,"").replace(/[\\'"<>`]/g,r=>`%${r.charCodeAt(0).toString(16).toUpperCase().padStart(2,"0")}`)||`https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${x}/dist`}function u(e){if(e!==void 0)return typeof e=="string"?e:Array.isArray(e)?`[${e.map(i=>i.toString()).join(", ")}]`:e.toString()}function v(e){if(e)return{getHeaders:u(e.getHeaders),onFeedback:u(e.onFeedback),onCopy:u(e.onCopy),requestMiddleware:u(e.requestMiddleware),actionHandlers:u(e.actionHandlers),actionParsers:u(e.actionParsers),postprocessMessage:u(e.postprocessMessage),contextProviders:u(e.contextProviders),streamParser:u(e.streamParser)}}var A=`({ text, message }: any) => {
2
2
  const jsonSource = (message as any).rawContent || text || message.content;
3
3
  if (!jsonSource || typeof jsonSource !== 'string') return null;
4
4
  let cleanJson = jsonSource
@@ -1,5 +1,26 @@
1
1
  import { IconNode } from 'lucide';
2
2
 
3
+ interface ResolvedWelcomeConfig {
4
+ title: string;
5
+ subtitle: string;
6
+ icon?: AgentWidgetWelcomeIcon;
7
+ variant: AgentWidgetWelcomeVariant;
8
+ dismiss: AgentWidgetWelcomeDismiss;
9
+ /** Undefined under `variant: "hero"`, which suppresses the greeting. */
10
+ message?: string;
11
+ }
12
+
13
+ /**
14
+ * Synchronous key-value store handed to plugin render hooks. Backed by
15
+ * `localStorage`, namespaced per plugin id. The async `storageAdapter` cannot
16
+ * back a synchronous API and `persistState` is not a general key-value surface.
17
+ */
18
+ interface AgentWidgetPluginStorage {
19
+ get: (key: string) => string | null;
20
+ set: (key: string, value: string) => void;
21
+ remove: (key: string) => void;
22
+ }
23
+
3
24
  type AgentWidgetTransformSuggestionsContext = {
4
25
  /**
5
26
  * Fresh copy of the suggestions entering this plugin, already normalized:
@@ -27,6 +48,27 @@ type AgentWidgetRenderSuggestionContext = {
27
48
  */
28
49
  select: () => void;
29
50
  };
51
+ type AgentWidgetRenderWelcomeContext = {
52
+ /** Alias-resolved welcome config; never the raw `copy.welcome*` shape. */
53
+ config: ResolvedWelcomeConfig;
54
+ variant: AgentWidgetWelcomeVariant;
55
+ /** Derived visibility for this render; governs the default renderer only. */
56
+ visible: boolean;
57
+ /** The core-owned welcome card, live-updated by `controller.update()`. */
58
+ defaultRenderer: () => HTMLElement;
59
+ sendMessage: (text: string) => void;
60
+ /** Re-run welcome arbitration: cleanups run, previous content is removed. */
61
+ requestRender: () => void;
62
+ /**
63
+ * Persona's starter renderer, wired through the full select pipeline:
64
+ * `onSuggestionSelect`, cancelable `persona:suggestion:*` events, send/fill.
65
+ */
66
+ renderStarter: (suggestion: AgentWidgetSuggestion) => HTMLElement;
67
+ /** Sync store namespaced by `persistState.keyPrefix` plus the plugin id. */
68
+ storage: AgentWidgetPluginStorage;
69
+ /** Teardown for this render; runs before the next one and on destroy. */
70
+ onCleanup: (fn: () => void) => void;
71
+ };
30
72
  type AgentWidgetSuggestionSelectContext = {
31
73
  suggestion: AgentWidgetResolvedSuggestion;
32
74
  surface: AgentWidgetSuggestionSurface;
@@ -83,6 +125,34 @@ interface AgentWidgetPlugin {
83
125
  defaultRenderer: () => HTMLElement;
84
126
  onToggle: () => void;
85
127
  }) => HTMLElement | null;
128
+ /**
129
+ * Custom renderer for the welcome surface (card, hero, greeting stack).
130
+ * First plugin returning an element wins; null falls through to the default,
131
+ * same contract as `renderSuggestion`.
132
+ *
133
+ * The core owns the welcome host: it stays mounted for the panel's lifetime
134
+ * and content is swapped inside it. A plugin element renders regardless of
135
+ * `ctx.visible` (derived visibility governs the default renderer only) and
136
+ * the host overlays the transcript while that element is active, so a home
137
+ * screen can return over an existing conversation. Return null again to
138
+ * clear the overlay.
139
+ *
140
+ * The ctx carries no controller: plugins are host-instantiated, so close a
141
+ * factory over the init handle (`plugin.attach(controller)`).
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * renderWelcome: ({ config, renderStarter, requestRender, onCleanup }) => {
146
+ * const root = document.createElement("div");
147
+ * root.textContent = config.title;
148
+ * root.appendChild(renderStarter({ label: "Track my order" }));
149
+ * const timer = setInterval(requestRender, 60_000);
150
+ * onCleanup(() => clearInterval(timer));
151
+ * return root;
152
+ * }
153
+ * ```
154
+ */
155
+ renderWelcome?: (context: AgentWidgetRenderWelcomeContext) => HTMLElement | null;
86
156
  /**
87
157
  * Custom renderer for panel header
88
158
  * Return null to use default renderer
@@ -127,6 +197,15 @@ interface AgentWidgetPlugin {
127
197
  * Omitted when `config.voiceRecognition.enabled` is not true.
128
198
  */
129
199
  onVoiceToggle?: () => void;
200
+ /**
201
+ * Re-run composer arbitration and swap the footer in place. The hook runs
202
+ * once at panel construction, so this is how a composer plugin unlocks
203
+ * (returns null) or re-renders later. Pending attachments, the mention
204
+ * affordances, and the composer listener registry are re-wired by the core.
205
+ */
206
+ requestRender: () => void;
207
+ /** Same sync store as the `renderWelcome` ctx, namespaced per plugin id. */
208
+ storage: AgentWidgetPluginStorage;
130
209
  }) => HTMLElement | null;
131
210
  /**
132
211
  * Custom renderer for reasoning bubbles
@@ -507,6 +586,15 @@ interface PanelTokens extends ComponentTokenSet {
507
586
  /** Background of the region revealed behind a detached panel. */
508
587
  canvasBackground?: string;
509
588
  }
589
+ /** Per-element text styling shared by themable text surfaces. */
590
+ interface TextStyleTokens {
591
+ fontFamily?: string;
592
+ fontSize?: string;
593
+ fontWeight?: string;
594
+ lineHeight?: string;
595
+ letterSpacing?: string;
596
+ color?: TokenReference<'color'>;
597
+ }
510
598
  interface HeaderTokens extends ComponentTokenSet {
511
599
  background: TokenReference<'color'>;
512
600
  border: TokenReference<'color'>;
@@ -515,10 +603,14 @@ interface HeaderTokens extends ComponentTokenSet {
515
603
  iconBackground: TokenReference<'color'>;
516
604
  /** Foreground (glyph stroke or emoji text) on the header avatar tile. */
517
605
  iconForeground: TokenReference<'color'>;
518
- /** Header title line (next to the icon, or minimal layout title). */
606
+ /** Legacy alias of `title.color`; `title.color` wins when both are set. */
519
607
  titleForeground: TokenReference<'color'>;
520
- /** Header subtitle line under the title. */
608
+ /** Legacy alias of `subtitle.color`; `subtitle.color` wins when both are set. */
521
609
  subtitleForeground: TokenReference<'color'>;
610
+ /** Header title typography (next to the icon, or minimal layout title). */
611
+ title?: TextStyleTokens;
612
+ /** Header subtitle typography, for the line under the title. */
613
+ subtitle?: TextStyleTokens;
522
614
  /** Default color for clear / close icon buttons when launcher overrides are unset. */
523
615
  actionIconForeground: TokenReference<'color'>;
524
616
  /** Box-shadow on the header (e.g., a fade shadow to replace the default border). */
@@ -548,7 +640,7 @@ interface MessageTokens {
548
640
  }
549
641
  /**
550
642
  * Welcome / intro card rendered above the message list when no messages exist.
551
- * Set `copy.showWelcomeCard: false` to hide it; use `layout.slots["body-top"]`
643
+ * Set `welcome.variant: "none"` to hide it; use `layout.slots["body-top"]`
552
644
  * to replace it wholesale.
553
645
  */
554
646
  interface IntroCardTokens extends ComponentTokenSet {
@@ -557,6 +649,12 @@ interface IntroCardTokens extends ComponentTokenSet {
557
649
  padding?: TokenReference<'spacing'>;
558
650
  /** Box-shadow on the intro card (token ref or raw CSS, e.g. `none`). */
559
651
  shadow?: string;
652
+ /** Full border shorthand on the intro card (raw CSS, e.g. `"1px solid rgba(0,0,0,0.1)"`). @default "none" */
653
+ border?: string;
654
+ /** Welcome title typography. */
655
+ title?: TextStyleTokens;
656
+ /** Welcome subtitle typography. */
657
+ subtitle?: TextStyleTokens;
560
658
  }
561
659
  /** Collapsible widget chrome (tool bubbles, reasoning bubbles, approval bubbles). */
562
660
  interface CollapsibleWidgetTokens {
@@ -599,6 +697,8 @@ interface MarkdownTokens {
599
697
  background?: TokenReference<'color'>;
600
698
  borderColor?: TokenReference<'color'>;
601
699
  textColor?: TokenReference<'color'>;
700
+ /** Corner radius; follows `palette.radius.md` by default so square-corner themes get square code blocks. */
701
+ borderRadius?: TokenReference<'radius'>;
602
702
  };
603
703
  /** Table styling. */
604
704
  table?: {
@@ -656,10 +756,19 @@ interface ToolBubbleTokens {
656
756
  interface ReasoningBubbleTokens {
657
757
  shadow: string;
658
758
  }
759
+ /** Scrollbar appearance shared by every scroller in the widget. */
760
+ interface ScrollbarTokens {
761
+ /** Thumb color. @default semantic.colors.border */
762
+ thumb?: TokenReference<'color'>;
763
+ /** Track color. @default "transparent" */
764
+ track?: TokenReference<'color'>;
765
+ }
659
766
  /** Composer (message input) chrome. */
660
767
  interface ComposerChromeTokens {
661
768
  /** Box-shadow on the composer form (raw CSS, e.g. `none`). */
662
769
  shadow: string;
770
+ /** Border color of the composer form. @default semantic.colors.border */
771
+ borderColor?: TokenReference<'color'>;
663
772
  /** Inner padding of the composer form (raw CSS shorthand). @default "0.75rem 1rem" */
664
773
  padding?: string;
665
774
  /** Gap between the textarea row and the actions row. @default "0.5rem" */
@@ -819,7 +928,10 @@ interface ScrollToBottomTokens extends ComponentTokenSet {
819
928
  }
820
929
  /** Visual tokens shared by one suggestion presentation variant. */
821
930
  interface SuggestionVariantTokens extends ComponentTokenSet {
931
+ /** Space inside one item, between its icon and its copy. */
822
932
  gap?: string;
933
+ /** Space between suggestion items in the container. */
934
+ itemGap?: string;
823
935
  minHeight?: string;
824
936
  fontSize?: string;
825
937
  lineHeight?: string;
@@ -861,6 +973,8 @@ interface ComponentTokens {
861
973
  toolBubble: ToolBubbleTokens;
862
974
  reasoningBubble: ReasoningBubbleTokens;
863
975
  composer: ComposerChromeTokens;
976
+ /** Scrollbar appearance for every scroller in the widget. */
977
+ scrollbar?: ScrollbarTokens;
864
978
  /** Icon button styling tokens. */
865
979
  iconButton?: IconButtonTokens;
866
980
  /** Label button styling tokens. */
@@ -2469,6 +2583,13 @@ type AgentWidgetArtifactsLayoutConfig = {
2469
2583
  * @default 520
2470
2584
  */
2471
2585
  narrowHostMaxWidth?: number;
2586
+ /**
2587
+ * Width of the slide-over artifact drawer (the narrow-host and mobile
2588
+ * presentations; `paneWidth`/`paneMaxWidth` only size the desktop split).
2589
+ * CSS length; `"100%"` makes the drawer cover the whole panel.
2590
+ * @default min(calc(100% - 1rem), 22rem)
2591
+ */
2592
+ drawerWidth?: string;
2472
2593
  /**
2473
2594
  * When true (default), widen the launcher panel while artifacts are visible and not user-dismissed.
2474
2595
  * No-op for inline embed (`launcher.enabled === false`).
@@ -3097,6 +3218,18 @@ type AgentWidgetScrollMode = "follow" | "anchor-top" | "none";
3097
3218
  * restored thread opens at a readable place rather than at the very bottom.
3098
3219
  */
3099
3220
  type AgentWidgetScrollRestorePosition = "bottom" | "last-user-turn";
3221
+ /**
3222
+ * Scrollbar visibility policy for every scroll surface in the widget.
3223
+ *
3224
+ * - `"on-scroll"` hides bars at rest and reveals them on genuine reader
3225
+ * input, keeping them visible while the reader is away from the resting
3226
+ * position: overlay-scrollbar semantics on every platform.
3227
+ * - `"auto"` keeps the themed bar under native visibility semantics: overlay
3228
+ * platforms still auto-hide, classic-scrollbar platforms (Windows,
3229
+ * always-show macOS) show it persistently.
3230
+ * - `"hidden"` never shows an indicator. Scrolling itself is unaffected.
3231
+ */
3232
+ type AgentWidgetScrollbarPolicy = "on-scroll" | "auto" | "hidden";
3100
3233
  type AgentWidgetScrollBehaviorFeature = {
3101
3234
  /** Scroll behavior during streamed responses. @default "anchor-top" */
3102
3235
  mode?: AgentWidgetScrollMode;
@@ -3141,6 +3274,13 @@ type AgentWidgetScrollBehaviorFeature = {
3141
3274
  * @default false
3142
3275
  */
3143
3276
  announce?: boolean;
3277
+ /**
3278
+ * Scrollbar visibility policy, applied to the transcript and every inner
3279
+ * scroller through one root attribute. Themable via
3280
+ * `--persona-scrollbar-thumb` and `--persona-scrollbar-track`.
3281
+ * @default "on-scroll"
3282
+ */
3283
+ scrollbar?: AgentWidgetScrollbarPolicy;
3144
3284
  };
3145
3285
  type AgentWidgetScrollToBottomFeature = {
3146
3286
  /**
@@ -4041,6 +4181,57 @@ type AgentWidgetLauncherConfig = {
4041
4181
  * @example "min(380px, calc(100vw - 48px))"
4042
4182
  */
4043
4183
  collapsedMaxWidth?: string;
4184
+ /**
4185
+ * Proactive nudge bubble anchored above the collapsed floating launcher.
4186
+ * Omit it (the default) and no teaser renders.
4187
+ */
4188
+ teaser?: AgentWidgetLauncherTeaserConfig;
4189
+ };
4190
+ /**
4191
+ * Launcher teaser: a small bubble above the collapsed launcher. Clicking it
4192
+ * opens the panel and consumes the teaser exactly like an explicit dismissal.
4193
+ * Within a single page load the teaser appears at most once under either
4194
+ * frequency.
4195
+ *
4196
+ * @example
4197
+ * ```typescript
4198
+ * launcher: {
4199
+ * teaser: { text: "Need a hand picking a plan?", delayMs: 3000 }
4200
+ * }
4201
+ * ```
4202
+ */
4203
+ type AgentWidgetLauncherTeaserConfig = {
4204
+ /** Bubble copy. An empty string renders no teaser. */
4205
+ text: string;
4206
+ /**
4207
+ * Delay before the bubble appears, in milliseconds.
4208
+ * @default 0
4209
+ */
4210
+ delayMs?: number;
4211
+ /**
4212
+ * How often the teaser may return.
4213
+ *
4214
+ * `"once"`: a dismissal or a click-through is remembered in `localStorage`
4215
+ * under `` `${persistState.keyPrefix ?? "persona-"}teaser-dismissed` `` and
4216
+ * the teaser never returns on that browser. `"always"`: shows on every page
4217
+ * load, tracked in memory only. `persistState: false` downgrades `"once"` to
4218
+ * in-memory too, so the teaser reappears each load.
4219
+ *
4220
+ * Custom `storageAdapter` implementations do not participate in teaser state.
4221
+ *
4222
+ * @default "once"
4223
+ */
4224
+ frequency?: "once" | "always";
4225
+ /**
4226
+ * Render an x affordance that dismisses the teaser without opening the panel.
4227
+ * @default true
4228
+ */
4229
+ dismissible?: boolean;
4230
+ /**
4231
+ * Accessible label for the dismiss affordance.
4232
+ * @default "Dismiss message"
4233
+ */
4234
+ dismissLabel?: string;
4044
4235
  };
4045
4236
  type AgentWidgetSendButtonConfig = {
4046
4237
  borderWidth?: string;
@@ -4784,7 +4975,10 @@ type AgentWidgetSuggestion = string | {
4784
4975
  label: string;
4785
4976
  /** Text placed in the composer or sent. Defaults to `label`. */
4786
4977
  prompt?: string;
4787
- /** Optional supporting copy, most useful with the `card` variant. */
4978
+ /**
4979
+ * Optional supporting copy, most useful with the `card` variant. The
4980
+ * `chip` variant ignores it and renders the label alone.
4981
+ */
4788
4982
  description?: string;
4789
4983
  /** Optional icon from Persona's bundled Lucide registry. */
4790
4984
  icon?: IconName;
@@ -4828,9 +5022,10 @@ type AgentWidgetSuggestionSurfaceConfig = {
4828
5022
  /** Default click behavior, overridable per item. Defaults to `send`. */
4829
5023
  behavior?: AgentWidgetSuggestionBehavior;
4830
5024
  /**
4831
- * Layout for items past the surface width. Defaults to `wrap` for starters
4832
- * and `scroll` for follow-ups. Applies to the `chip` variant only; the
4833
- * `card` and `list` layouts manage their own stacking.
5025
+ * Layout for items past the surface width. Defaults to `wrap` on every
5026
+ * surface; `scroll` opts into a horizontal strip for large sets. Applies
5027
+ * to the `chip` variant only; the `card` and `list` layouts manage their
5028
+ * own stacking.
4834
5029
  */
4835
5030
  overflow?: "scroll" | "wrap";
4836
5031
  /** Maximum visible items. Defaults to 4. */
@@ -4879,6 +5074,70 @@ type AgentWidgetSuggestionsConfig = {
4879
5074
  starters?: AgentWidgetStarterSuggestionsConfig;
4880
5075
  followUps?: AgentWidgetFollowUpSuggestionsConfig;
4881
5076
  };
5077
+ /**
5078
+ * Which welcome surface renders.
5079
+ * `"card"` is the top-of-scroll intro card (default, back-compatible),
5080
+ * `"hero"` centers it in the empty conversation, `"none"` renders no surface.
5081
+ */
5082
+ type AgentWidgetWelcomeVariant = "card" | "hero" | "none";
5083
+ /**
5084
+ * When the welcome surface goes away. `"never"` is the default for `"card"`;
5085
+ * `"on-first-message"` is the default for `"hero"` and cannot be overridden
5086
+ * there.
5087
+ */
5088
+ type AgentWidgetWelcomeDismiss = "never" | "on-first-message";
5089
+ /**
5090
+ * Avatar or logo shown above the welcome title. Discriminated union; the
5091
+ * function form is the escape hatch for custom markup. There is deliberately
5092
+ * no raw HTML string variant (sanitization surface plus accessibility hole).
5093
+ */
5094
+ type AgentWidgetWelcomeIcon = {
5095
+ type: "lucide";
5096
+ name: IconName;
5097
+ }
5098
+ /** `alt` is required, and may be `""` for a decorative logo. */
5099
+ | {
5100
+ type: "image";
5101
+ url: string;
5102
+ alt: string;
5103
+ }
5104
+ /** Emoji or short text glyph. */
5105
+ | {
5106
+ type: "text";
5107
+ text: string;
5108
+ } | (() => HTMLElement | SVGElement);
5109
+ /**
5110
+ * First-open welcome surface. Consolidates the deprecated
5111
+ * `copy.welcomeTitle` / `copy.welcomeSubtitle` / `copy.showWelcomeCard`
5112
+ * options; a field present here wins over its legacy alias.
5113
+ *
5114
+ * All defaults live in the resolver (`resolveWelcomeConfig`), never in
5115
+ * `DEFAULT_WIDGET_CONFIG`: presence on this object is what distinguishes a
5116
+ * host-set value from a filled-in default.
5117
+ */
5118
+ interface AgentWidgetWelcomeConfig {
5119
+ /** Card or hero title. Defaults to "Hello 👋". */
5120
+ title?: string;
5121
+ /** Scope statement in the assistant's voice. Empty string omits it. */
5122
+ subtitle?: string;
5123
+ /** Avatar or logo shown above the title. */
5124
+ icon?: AgentWidgetWelcomeIcon;
5125
+ /** @default "card" */
5126
+ variant?: AgentWidgetWelcomeVariant;
5127
+ /** @default "never" for `card`, always `"on-first-message"` for `hero`. */
5128
+ dismiss?: AgentWidgetWelcomeDismiss;
5129
+ /**
5130
+ * Display-only greeting bubble pinned at transcript position zero. It is UI
5131
+ * chrome derived from config, never a session message: it does not appear in
5132
+ * `getMessages()`, is never persisted or sent to the model, `onMessage` does
5133
+ * not fire for it, and it survives `clearChat()`. Use an assistant
5134
+ * `initialMessages` entry instead when the model should see the greeting.
5135
+ *
5136
+ * Ignored (with a debug-mode warning) when `variant` is `"hero"`: the hero
5137
+ * is the greeting.
5138
+ */
5139
+ message?: string;
5140
+ }
4882
5141
  /**
4883
5142
  * Interface for pluggable stream parsers that extract text from streaming responses.
4884
5143
  * Parsers handle incremental parsing to extract text values from structured formats (JSON, XML, etc.).
@@ -5060,7 +5319,9 @@ type AgentWidgetHeaderLayoutConfig = {
5060
5319
  */
5061
5320
  render?: (context: HeaderRenderContext) => HTMLElement;
5062
5321
  /**
5063
- * Shown after the title in `minimal` header layout (e.g. chevron menu affordance).
5322
+ * Icon action buttons at the trailing edge of the `minimal` header layout,
5323
+ * clustered with the close button and sharing its chrome. For a menu
5324
+ * affordance next to the title, use `titleMenu` instead.
5064
5325
  */
5065
5326
  trailingActions?: AgentWidgetHeaderTrailingAction[];
5066
5327
  /** Called when a `trailingActions` button is clicked. */
@@ -5240,7 +5501,11 @@ type AgentWidgetLayoutConfig = {
5240
5501
  /**
5241
5502
  * Max width for the content area (messages + composer).
5242
5503
  * Applied with `margin: 0 auto` for centering.
5243
- * Accepts any CSS width value (e.g. "90ch", "720px", "80%").
5504
+ * Accepts any CSS width value (e.g. "90ch", "720px", "80%"), or "none"
5505
+ * for a full-width column. Only engages on panels wider than the cap,
5506
+ * so launcher-width panels are unaffected.
5507
+ * @default "768px" ("720px" in composer-bar mode via
5508
+ * `launcher.composerBar.contentMaxWidth`)
5244
5509
  */
5245
5510
  contentMaxWidth?: string;
5246
5511
  };
@@ -5988,7 +6253,9 @@ type AgentWidgetConfig = {
5988
6253
  */
5989
6254
  getHeaders?: AgentWidgetHeadersFunction;
5990
6255
  copy?: {
6256
+ /** @deprecated Use `welcome.title`. */
5991
6257
  welcomeTitle?: string;
6258
+ /** @deprecated Use `welcome.subtitle`. */
5992
6259
  welcomeSubtitle?: string;
5993
6260
  inputPlaceholder?: string;
5994
6261
  sendButtonLabel?: string;
@@ -5996,6 +6263,7 @@ type AgentWidgetConfig = {
5996
6263
  stopButtonLabel?: string;
5997
6264
  /**
5998
6265
  * When false, the welcome / intro card is not shown above the message list.
6266
+ * @deprecated Use `welcome.variant: "none"`.
5999
6267
  * @default true
6000
6268
  */
6001
6269
  showWelcomeCard?: boolean;
@@ -6062,6 +6330,12 @@ type AgentWidgetConfig = {
6062
6330
  initialSelectedArtifactId?: string | null;
6063
6331
  /** Rich starter and follow-up suggestion surfaces. */
6064
6332
  suggestions?: AgentWidgetSuggestionsConfig;
6333
+ /**
6334
+ * First-open welcome surface (card, hero, or none) plus the optional
6335
+ * display-only greeting bubble. Supersedes `copy.welcomeTitle`,
6336
+ * `copy.welcomeSubtitle`, and `copy.showWelcomeCard`.
6337
+ */
6338
+ welcome?: AgentWidgetWelcomeConfig;
6065
6339
  /** @deprecated Prefer `suggestions.starters.items`. */
6066
6340
  suggestionChips?: string[];
6067
6341
  /** @deprecated Prefer semantic tokens under `theme.components.suggestion`. */