@runtypelabs/persona 4.14.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.
- package/README.md +4 -4
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-DveIaNx6.d.cts → types-BpWGm7_6.d.cts} +17 -0
- package/dist/animations/{types-DveIaNx6.d.ts → types-BpWGm7_6.d.ts} +17 -0
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/chunk-QJ4LV2KW.js +1 -0
- package/dist/codegen.cjs +12 -11
- package/dist/codegen.js +14 -13
- package/dist/{context-mentions-ONG7A7M6.js → context-mentions-BTSTIBFC.js} +1 -1
- package/dist/context-mentions.cjs +11 -8
- package/dist/context-mentions.d.cts +520 -21
- package/dist/context-mentions.d.ts +520 -21
- package/dist/context-mentions.js +11 -8
- package/dist/index.cjs +69 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +595 -46
- package/dist/index.d.ts +595 -46
- package/dist/index.global.js +60 -57
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +69 -68
- package/dist/index.js.map +1 -1
- package/dist/launcher.global.js +5 -2
- package/dist/launcher.global.js.map +1 -1
- package/dist/smart-dom-reader.d.cts +520 -21
- package/dist/smart-dom-reader.d.ts +520 -21
- package/dist/testing.cjs +3 -3
- package/dist/testing.d.cts +7 -0
- package/dist/testing.d.ts +7 -0
- package/dist/testing.js +3 -3
- package/dist/theme-editor-preview.cjs +67 -67
- package/dist/theme-editor-preview.d.cts +534 -22
- package/dist/theme-editor-preview.d.ts +534 -22
- package/dist/theme-editor-preview.js +57 -57
- package/dist/theme-editor.cjs +10 -10
- package/dist/theme-editor.d.cts +521 -23
- package/dist/theme-editor.d.ts +521 -23
- package/dist/theme-editor.js +10 -10
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +104 -3
- package/dist/theme-reference.d.ts +104 -3
- package/dist/theme-reference.js +1 -1
- package/dist/widget.css +1 -1
- package/package.json +1 -1
- package/src/ask-user-question-tool.ts +10 -7
- package/src/components/artifact-pane.test.ts +27 -0
- package/src/components/artifact-pane.ts +7 -3
- package/src/components/artifact-preview.ts +1 -1
- package/src/components/composer-parts.test.ts +103 -4
- package/src/components/composer-parts.ts +53 -15
- package/src/components/header-builder.ts +27 -2
- package/src/components/header-layouts.test.ts +56 -0
- package/src/components/header-layouts.ts +29 -9
- package/src/components/launcher-surface.test.ts +403 -0
- package/src/components/launcher.ts +297 -2
- package/src/components/messages.ts +5 -2
- package/src/components/panel.ts +92 -71
- package/src/components/pill-composer-builder.test.ts +18 -0
- package/src/components/suggestions.test.ts +273 -0
- package/src/components/suggestions.ts +528 -86
- package/src/components/welcome.test.ts +121 -0
- package/src/components/welcome.ts +305 -0
- package/src/components/widget-view.test.ts +5 -3
- package/src/components/widget-view.ts +18 -7
- package/src/defaults.ts +7 -5
- package/src/generated/runtype-openapi-contract.ts +17 -0
- package/src/index-core.ts +29 -2
- package/src/launcher-global.ts +11 -8
- package/src/plugins/types.ts +136 -3
- package/src/presets.ts +3 -3
- package/src/session.ts +10 -8
- package/src/styles/widget-styles.test.ts +382 -0
- package/src/styles/widget.css +778 -4
- package/src/suggest-replies-tool.test.ts +233 -2
- package/src/suggest-replies-tool.ts +115 -39
- package/src/testing/mock-stream.test.ts +19 -0
- package/src/testing/mock-stream.ts +20 -0
- package/src/theme-editor/preview-utils.test.ts +34 -0
- package/src/theme-editor/preview-utils.ts +77 -2
- package/src/theme-editor/sections.test.ts +33 -0
- package/src/theme-editor/sections.ts +83 -6
- package/src/theme-reference.ts +34 -5
- package/src/types/theme.ts +72 -3
- package/src/types.ts +312 -18
- package/src/ui.body-top-slot.test.ts +95 -0
- package/src/ui.content-width.test.ts +91 -0
- package/src/ui.detached-panel.test.ts +1 -1
- package/src/ui.docked.test.ts +7 -4
- package/src/ui.launcher-teaser.test.ts +162 -0
- package/src/ui.rebuild-composer.test.ts +491 -0
- package/src/ui.render-welcome.test.ts +505 -0
- package/src/ui.scroll-additive.test.ts +91 -21
- package/src/ui.scroll.test.ts +302 -14
- package/src/ui.suggest-replies.test.ts +1112 -1
- package/src/ui.ts +1008 -149
- package/src/ui.welcome.test.ts +504 -0
- package/src/utils/artifact-file.test.ts +1 -1
- package/src/utils/artifact-gate.test.ts +27 -0
- package/src/utils/artifact-gate.ts +9 -0
- package/src/utils/attachment-manager.test.ts +37 -0
- package/src/utils/attachment-manager.ts +15 -0
- package/src/utils/auto-follow.test.ts +23 -0
- package/src/utils/auto-follow.ts +11 -1
- package/src/utils/code-generators.test.ts +40 -0
- package/src/utils/code-generators.ts +22 -0
- package/src/utils/config-merge.test.ts +24 -0
- package/src/utils/config-merge.ts +2 -0
- package/src/utils/content-width.test.ts +37 -0
- package/src/utils/content-width.ts +17 -0
- package/src/utils/icons.test.ts +26 -0
- package/src/utils/icons.ts +8 -0
- package/src/utils/plugin-storage.test.ts +94 -0
- package/src/utils/plugin-storage.ts +98 -0
- package/src/utils/theme.test.ts +216 -0
- package/src/utils/tokens.ts +113 -3
- package/src/welcome.test.ts +213 -0
- package/src/welcome.ts +130 -0
- package/dist/chunk-MMUPR2JW.js +0 -1
package/src/types.ts
CHANGED
|
@@ -1251,6 +1251,13 @@ export type AgentWidgetArtifactsLayoutConfig = {
|
|
|
1251
1251
|
* @default 520
|
|
1252
1252
|
*/
|
|
1253
1253
|
narrowHostMaxWidth?: number;
|
|
1254
|
+
/**
|
|
1255
|
+
* Width of the slide-over artifact drawer (the narrow-host and mobile
|
|
1256
|
+
* presentations; `paneWidth`/`paneMaxWidth` only size the desktop split).
|
|
1257
|
+
* CSS length; `"100%"` makes the drawer cover the whole panel.
|
|
1258
|
+
* @default min(calc(100% - 1rem), 22rem)
|
|
1259
|
+
*/
|
|
1260
|
+
drawerWidth?: string;
|
|
1254
1261
|
/**
|
|
1255
1262
|
* When true (default), widen the launcher panel while artifacts are visible and not user-dismissed.
|
|
1256
1263
|
* No-op for inline embed (`launcher.enabled === false`).
|
|
@@ -1894,6 +1901,19 @@ export type AgentWidgetScrollMode = "follow" | "anchor-top" | "none";
|
|
|
1894
1901
|
*/
|
|
1895
1902
|
export type AgentWidgetScrollRestorePosition = "bottom" | "last-user-turn";
|
|
1896
1903
|
|
|
1904
|
+
/**
|
|
1905
|
+
* Scrollbar visibility policy for every scroll surface in the widget.
|
|
1906
|
+
*
|
|
1907
|
+
* - `"on-scroll"` hides bars at rest and reveals them on genuine reader
|
|
1908
|
+
* input, keeping them visible while the reader is away from the resting
|
|
1909
|
+
* position: overlay-scrollbar semantics on every platform.
|
|
1910
|
+
* - `"auto"` keeps the themed bar under native visibility semantics: overlay
|
|
1911
|
+
* platforms still auto-hide, classic-scrollbar platforms (Windows,
|
|
1912
|
+
* always-show macOS) show it persistently.
|
|
1913
|
+
* - `"hidden"` never shows an indicator. Scrolling itself is unaffected.
|
|
1914
|
+
*/
|
|
1915
|
+
export type AgentWidgetScrollbarPolicy = "on-scroll" | "auto" | "hidden";
|
|
1916
|
+
|
|
1897
1917
|
export type AgentWidgetScrollBehaviorFeature = {
|
|
1898
1918
|
/** Scroll behavior during streamed responses. @default "anchor-top" */
|
|
1899
1919
|
mode?: AgentWidgetScrollMode;
|
|
@@ -1938,6 +1958,13 @@ export type AgentWidgetScrollBehaviorFeature = {
|
|
|
1938
1958
|
* @default false
|
|
1939
1959
|
*/
|
|
1940
1960
|
announce?: boolean;
|
|
1961
|
+
/**
|
|
1962
|
+
* Scrollbar visibility policy, applied to the transcript and every inner
|
|
1963
|
+
* scroller through one root attribute. Themable via
|
|
1964
|
+
* `--persona-scrollbar-thumb` and `--persona-scrollbar-track`.
|
|
1965
|
+
* @default "on-scroll"
|
|
1966
|
+
*/
|
|
1967
|
+
scrollbar?: AgentWidgetScrollbarPolicy;
|
|
1941
1968
|
};
|
|
1942
1969
|
|
|
1943
1970
|
export type AgentWidgetScrollToBottomFeature = {
|
|
@@ -2257,10 +2284,13 @@ export type AgentWidgetFeatureFlags = {
|
|
|
2257
2284
|
*/
|
|
2258
2285
|
askUserQuestion?: AgentWidgetAskUserQuestionFeature;
|
|
2259
2286
|
/**
|
|
2260
|
-
* Built-in `suggest_replies`
|
|
2261
|
-
*
|
|
2262
|
-
*
|
|
2263
|
-
*
|
|
2287
|
+
* Built-in `suggest_replies` follow-ups. When the assistant invokes the
|
|
2288
|
+
* tool, the widget shows the suggestions using `suggestions.followUps` and
|
|
2289
|
+
* immediately resumes the execution: fire-and-forget, no user input awaited.
|
|
2290
|
+
*
|
|
2291
|
+
* @deprecated Both keys moved to `suggestions.followUps`: use
|
|
2292
|
+
* `suggestions.followUps.enabled` and `suggestions.followUps.expose`. This
|
|
2293
|
+
* alias keeps working per key, with `suggestions.followUps` winning.
|
|
2264
2294
|
*/
|
|
2265
2295
|
suggestReplies?: AgentWidgetSuggestRepliesFeature;
|
|
2266
2296
|
};
|
|
@@ -2327,11 +2357,12 @@ export type WidgetPreferenceSlice = {
|
|
|
2327
2357
|
};
|
|
2328
2358
|
|
|
2329
2359
|
/**
|
|
2330
|
-
* Feature config for the built-in `suggest_replies`
|
|
2331
|
-
*
|
|
2332
|
-
*
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2360
|
+
* Feature config for the built-in `suggest_replies` follow-ups.
|
|
2361
|
+
*
|
|
2362
|
+
* @deprecated Both keys moved to `suggestions.followUps`
|
|
2363
|
+
* (`suggestions.followUps.enabled` / `suggestions.followUps.expose`), which
|
|
2364
|
+
* also owns presentation, placement, and send/fill behavior. Resolution is
|
|
2365
|
+
* per key with `suggestions.followUps` winning; removal waits for 5.0.
|
|
2335
2366
|
*/
|
|
2336
2367
|
export type AgentWidgetSuggestRepliesFeature = {
|
|
2337
2368
|
/**
|
|
@@ -2339,14 +2370,18 @@ export type AgentWidgetSuggestRepliesFeature = {
|
|
|
2339
2370
|
* renders as a regular tool bubble and is NOT auto-resumed: only set this
|
|
2340
2371
|
* with no server-side `suggest_replies` declaration, or the execution
|
|
2341
2372
|
* parks awaiting a resume that never comes.
|
|
2373
|
+
* @deprecated Use `suggestions.followUps.enabled`.
|
|
2342
2374
|
*/
|
|
2343
2375
|
enabled?: boolean;
|
|
2344
2376
|
/**
|
|
2345
2377
|
* Advertise the built-in `suggest_replies` tool to the agent on every
|
|
2346
2378
|
* dispatch via `clientTools[]`: no server-side `runtimeTools` declaration
|
|
2347
|
-
* needed. Defaults to `false
|
|
2348
|
-
*
|
|
2349
|
-
*
|
|
2379
|
+
* needed. Defaults to `false`. Declare the tool in one place: the API
|
|
2380
|
+
* dedupes a double declaration by name with `clientTools[]` winning, so the
|
|
2381
|
+
* model still sees exactly one tool, but the widget's description and schema
|
|
2382
|
+
* win and the effective tool type becomes `local` (the execution pauses and
|
|
2383
|
+
* the widget auto-resumes it). Ignored when `enabled` is `false`.
|
|
2384
|
+
* @deprecated Use `suggestions.followUps.expose`.
|
|
2350
2385
|
*/
|
|
2351
2386
|
expose?: boolean;
|
|
2352
2387
|
};
|
|
@@ -2421,10 +2456,13 @@ export type AgentWidgetAskUserQuestionFeature = {
|
|
|
2421
2456
|
* matching {@link AskUserQuestionPayload}; when the model calls it, the
|
|
2422
2457
|
* existing answer-pill sheet renders and the answer resumes the execution.
|
|
2423
2458
|
*
|
|
2424
|
-
* Defaults to `false
|
|
2425
|
-
*
|
|
2426
|
-
*
|
|
2427
|
-
*
|
|
2459
|
+
* Defaults to `false`. Declare the tool in one place: the API dedupes a
|
|
2460
|
+
* double declaration by name with `clientTools[]` winning, so the model
|
|
2461
|
+
* still sees exactly one tool, but the widget's description and schema win
|
|
2462
|
+
* and the effective tool type becomes `local` (the execution pauses and the
|
|
2463
|
+
* widget resumes it with the user's answer). Ignored when `enabled` is
|
|
2464
|
+
* `false`: never offer the agent a question tool the widget can't render an
|
|
2465
|
+
* answer UI for.
|
|
2428
2466
|
*/
|
|
2429
2467
|
expose?: boolean;
|
|
2430
2468
|
/** Slide-in animation duration in ms. Defaults to 180. */
|
|
@@ -2904,6 +2942,58 @@ export type AgentWidgetLauncherConfig = {
|
|
|
2904
2942
|
* @example "min(380px, calc(100vw - 48px))"
|
|
2905
2943
|
*/
|
|
2906
2944
|
collapsedMaxWidth?: string;
|
|
2945
|
+
/**
|
|
2946
|
+
* Proactive nudge bubble anchored above the collapsed floating launcher.
|
|
2947
|
+
* Omit it (the default) and no teaser renders.
|
|
2948
|
+
*/
|
|
2949
|
+
teaser?: AgentWidgetLauncherTeaserConfig;
|
|
2950
|
+
};
|
|
2951
|
+
|
|
2952
|
+
/**
|
|
2953
|
+
* Launcher teaser: a small bubble above the collapsed launcher. Clicking it
|
|
2954
|
+
* opens the panel and consumes the teaser exactly like an explicit dismissal.
|
|
2955
|
+
* Within a single page load the teaser appears at most once under either
|
|
2956
|
+
* frequency.
|
|
2957
|
+
*
|
|
2958
|
+
* @example
|
|
2959
|
+
* ```typescript
|
|
2960
|
+
* launcher: {
|
|
2961
|
+
* teaser: { text: "Need a hand picking a plan?", delayMs: 3000 }
|
|
2962
|
+
* }
|
|
2963
|
+
* ```
|
|
2964
|
+
*/
|
|
2965
|
+
export type AgentWidgetLauncherTeaserConfig = {
|
|
2966
|
+
/** Bubble copy. An empty string renders no teaser. */
|
|
2967
|
+
text: string;
|
|
2968
|
+
/**
|
|
2969
|
+
* Delay before the bubble appears, in milliseconds.
|
|
2970
|
+
* @default 0
|
|
2971
|
+
*/
|
|
2972
|
+
delayMs?: number;
|
|
2973
|
+
/**
|
|
2974
|
+
* How often the teaser may return.
|
|
2975
|
+
*
|
|
2976
|
+
* `"once"`: a dismissal or a click-through is remembered in `localStorage`
|
|
2977
|
+
* under `` `${persistState.keyPrefix ?? "persona-"}teaser-dismissed` `` and
|
|
2978
|
+
* the teaser never returns on that browser. `"always"`: shows on every page
|
|
2979
|
+
* load, tracked in memory only. `persistState: false` downgrades `"once"` to
|
|
2980
|
+
* in-memory too, so the teaser reappears each load.
|
|
2981
|
+
*
|
|
2982
|
+
* Custom `storageAdapter` implementations do not participate in teaser state.
|
|
2983
|
+
*
|
|
2984
|
+
* @default "once"
|
|
2985
|
+
*/
|
|
2986
|
+
frequency?: "once" | "always";
|
|
2987
|
+
/**
|
|
2988
|
+
* Render an x affordance that dismisses the teaser without opening the panel.
|
|
2989
|
+
* @default true
|
|
2990
|
+
*/
|
|
2991
|
+
dismissible?: boolean;
|
|
2992
|
+
/**
|
|
2993
|
+
* Accessible label for the dismiss affordance.
|
|
2994
|
+
* @default "Dismiss message"
|
|
2995
|
+
*/
|
|
2996
|
+
dismissLabel?: string;
|
|
2907
2997
|
};
|
|
2908
2998
|
|
|
2909
2999
|
export type AgentWidgetSendButtonConfig = {
|
|
@@ -2913,6 +3003,10 @@ export type AgentWidgetSendButtonConfig = {
|
|
|
2913
3003
|
paddingY?: string;
|
|
2914
3004
|
iconText?: string;
|
|
2915
3005
|
iconName?: string;
|
|
3006
|
+
/** Icon glyph box in icon mode, e.g. "20px". Defaults to half of `size`. */
|
|
3007
|
+
iconSize?: string;
|
|
3008
|
+
/** Stroke width for the icon glyph, in viewBox units. Default 2. */
|
|
3009
|
+
iconStrokeWidth?: number;
|
|
2916
3010
|
useIcon?: boolean;
|
|
2917
3011
|
tooltipText?: string;
|
|
2918
3012
|
showTooltip?: boolean;
|
|
@@ -3728,6 +3822,187 @@ export type AgentWidgetSuggestionChipsConfig = {
|
|
|
3728
3822
|
paddingY?: string;
|
|
3729
3823
|
};
|
|
3730
3824
|
|
|
3825
|
+
/** A suggested next action shown to the user. Strings remain supported as shorthand. */
|
|
3826
|
+
export type AgentWidgetSuggestion =
|
|
3827
|
+
| string
|
|
3828
|
+
| {
|
|
3829
|
+
/** Stable analytics / DOM identity. Falls back to the prompt or label. */
|
|
3830
|
+
id?: string;
|
|
3831
|
+
/** Concise visible action label. */
|
|
3832
|
+
label: string;
|
|
3833
|
+
/** Text placed in the composer or sent. Defaults to `label`. */
|
|
3834
|
+
prompt?: string;
|
|
3835
|
+
/**
|
|
3836
|
+
* Optional supporting copy, most useful with the `card` variant. The
|
|
3837
|
+
* `chip` variant ignores it and renders the label alone.
|
|
3838
|
+
*/
|
|
3839
|
+
description?: string;
|
|
3840
|
+
/** Optional icon from Persona's bundled Lucide registry. */
|
|
3841
|
+
icon?: IconName;
|
|
3842
|
+
/**
|
|
3843
|
+
* CSS color for this item's glyph. Tints the icon only: label, border,
|
|
3844
|
+
* and background stay neutral. Ignored without an `icon`.
|
|
3845
|
+
*/
|
|
3846
|
+
iconColor?: string;
|
|
3847
|
+
/** Override the surface's click behavior for this item. */
|
|
3848
|
+
behavior?: AgentWidgetSuggestionBehavior;
|
|
3849
|
+
/** Visually promote one high-confidence action. */
|
|
3850
|
+
emphasis?: "default" | "primary";
|
|
3851
|
+
};
|
|
3852
|
+
|
|
3853
|
+
export type AgentWidgetSuggestionBehavior = "send" | "fill";
|
|
3854
|
+
export type AgentWidgetSuggestionVariant = "chip" | "card" | "list";
|
|
3855
|
+
export type AgentWidgetSuggestionSurface = "starter" | "followUp";
|
|
3856
|
+
/**
|
|
3857
|
+
* Who produced the rendered suggestions: static config, the agent's
|
|
3858
|
+
* `suggest_replies` tool, or `controller.setFollowUpSuggestions`.
|
|
3859
|
+
*/
|
|
3860
|
+
export type AgentWidgetSuggestionSource = "config" | "agent" | "host";
|
|
3861
|
+
|
|
3862
|
+
/**
|
|
3863
|
+
* A suggestion after Persona has normalized shorthand strings and resolved the
|
|
3864
|
+
* effective click behavior. Plugin hooks receive this stable shape.
|
|
3865
|
+
*/
|
|
3866
|
+
export type AgentWidgetResolvedSuggestion = {
|
|
3867
|
+
id: string;
|
|
3868
|
+
label: string;
|
|
3869
|
+
prompt: string;
|
|
3870
|
+
description?: string;
|
|
3871
|
+
icon?: IconName;
|
|
3872
|
+
/** CSS color for the glyph only; never the label, border, or background. */
|
|
3873
|
+
iconColor?: string;
|
|
3874
|
+
behavior: AgentWidgetSuggestionBehavior;
|
|
3875
|
+
emphasis: "default" | "primary";
|
|
3876
|
+
};
|
|
3877
|
+
|
|
3878
|
+
/** Presentation keys shared by every suggestion surface. */
|
|
3879
|
+
export type AgentWidgetSuggestionSurfaceConfig = {
|
|
3880
|
+
/** Item density. Defaults to `card` for starters and `chip` for follow-ups. */
|
|
3881
|
+
variant?: AgentWidgetSuggestionVariant;
|
|
3882
|
+
/** Default click behavior, overridable per item. Defaults to `send`. */
|
|
3883
|
+
behavior?: AgentWidgetSuggestionBehavior;
|
|
3884
|
+
/**
|
|
3885
|
+
* Layout for items past the surface width. Defaults to `wrap` on every
|
|
3886
|
+
* surface; `scroll` opts into a horizontal strip for large sets. Applies
|
|
3887
|
+
* to the `chip` variant only; the `card` and `list` layouts manage their
|
|
3888
|
+
* own stacking.
|
|
3889
|
+
*/
|
|
3890
|
+
overflow?: "scroll" | "wrap";
|
|
3891
|
+
/** Maximum visible items. Defaults to 4. */
|
|
3892
|
+
maxItems?: number;
|
|
3893
|
+
};
|
|
3894
|
+
|
|
3895
|
+
export type AgentWidgetStarterSuggestionsConfig =
|
|
3896
|
+
AgentWidgetSuggestionSurfaceConfig & {
|
|
3897
|
+
/** Suggestions shown before the first user message. */
|
|
3898
|
+
items?: AgentWidgetSuggestion[];
|
|
3899
|
+
/**
|
|
3900
|
+
* `auto` uses the welcome surface when the welcome card renders and the
|
|
3901
|
+
* composer otherwise. Explicit values are literal: `welcome` renders
|
|
3902
|
+
* nothing when the welcome card is hidden. Defaults to `auto`.
|
|
3903
|
+
*/
|
|
3904
|
+
placement?: "auto" | "welcome" | "composer";
|
|
3905
|
+
};
|
|
3906
|
+
|
|
3907
|
+
export type AgentWidgetFollowUpSuggestionsConfig =
|
|
3908
|
+
AgentWidgetSuggestionSurfaceConfig & {
|
|
3909
|
+
/**
|
|
3910
|
+
* Enable the built-in `suggest_replies` follow-ups. Defaults to true. When
|
|
3911
|
+
* false, `suggest_replies` renders as a regular tool bubble and is NOT
|
|
3912
|
+
* auto-resumed: only set this with no server-side `suggest_replies`
|
|
3913
|
+
* declaration, or the execution parks awaiting a resume that never comes.
|
|
3914
|
+
*/
|
|
3915
|
+
enabled?: boolean;
|
|
3916
|
+
/**
|
|
3917
|
+
* Advertise the built-in `suggest_replies` tool to the agent on every
|
|
3918
|
+
* dispatch via `clientTools[]`: no server-side `runtimeTools` declaration
|
|
3919
|
+
* needed. Defaults to `false`. Declare the tool in one place: the API
|
|
3920
|
+
* dedupes a double declaration by name with `clientTools[]` winning, so
|
|
3921
|
+
* the model still sees exactly one tool, but the widget's description and
|
|
3922
|
+
* schema win and the effective tool type becomes `local` (the execution
|
|
3923
|
+
* pauses and the widget auto-resumes it). Ignored when `enabled` is
|
|
3924
|
+
* `false`.
|
|
3925
|
+
*/
|
|
3926
|
+
expose?: boolean;
|
|
3927
|
+
/**
|
|
3928
|
+
* `auto` places follow-ups after the transcript in regular panels and above
|
|
3929
|
+
* the composer in composer-bar mode. Defaults to `auto`.
|
|
3930
|
+
*/
|
|
3931
|
+
placement?: "auto" | "after-message" | "composer";
|
|
3932
|
+
// No `items`: follow-up items are contextual and come from the agent's
|
|
3933
|
+
// `suggest_replies` tool or `controller.setFollowUpSuggestions`, not static
|
|
3934
|
+
// config. Static lists are `starters`.
|
|
3935
|
+
};
|
|
3936
|
+
|
|
3937
|
+
/**
|
|
3938
|
+
* Structured suggestions configuration. The legacy `suggestionChips` and
|
|
3939
|
+
* `suggestionChipsConfig` fields remain supported for backwards compatibility.
|
|
3940
|
+
*/
|
|
3941
|
+
export type AgentWidgetSuggestionsConfig = {
|
|
3942
|
+
starters?: AgentWidgetStarterSuggestionsConfig;
|
|
3943
|
+
followUps?: AgentWidgetFollowUpSuggestionsConfig;
|
|
3944
|
+
};
|
|
3945
|
+
|
|
3946
|
+
/**
|
|
3947
|
+
* Which welcome surface renders.
|
|
3948
|
+
* `"card"` is the top-of-scroll intro card (default, back-compatible),
|
|
3949
|
+
* `"hero"` centers it in the empty conversation, `"none"` renders no surface.
|
|
3950
|
+
*/
|
|
3951
|
+
export type AgentWidgetWelcomeVariant = "card" | "hero" | "none";
|
|
3952
|
+
|
|
3953
|
+
/**
|
|
3954
|
+
* When the welcome surface goes away. `"never"` is the default for `"card"`;
|
|
3955
|
+
* `"on-first-message"` is the default for `"hero"` and cannot be overridden
|
|
3956
|
+
* there.
|
|
3957
|
+
*/
|
|
3958
|
+
export type AgentWidgetWelcomeDismiss = "never" | "on-first-message";
|
|
3959
|
+
|
|
3960
|
+
/**
|
|
3961
|
+
* Avatar or logo shown above the welcome title. Discriminated union; the
|
|
3962
|
+
* function form is the escape hatch for custom markup. There is deliberately
|
|
3963
|
+
* no raw HTML string variant (sanitization surface plus accessibility hole).
|
|
3964
|
+
*/
|
|
3965
|
+
export type AgentWidgetWelcomeIcon =
|
|
3966
|
+
| { type: "lucide"; name: IconName }
|
|
3967
|
+
/** `alt` is required, and may be `""` for a decorative logo. */
|
|
3968
|
+
| { type: "image"; url: string; alt: string }
|
|
3969
|
+
/** Emoji or short text glyph. */
|
|
3970
|
+
| { type: "text"; text: string }
|
|
3971
|
+
| (() => HTMLElement | SVGElement);
|
|
3972
|
+
|
|
3973
|
+
/**
|
|
3974
|
+
* First-open welcome surface. Consolidates the deprecated
|
|
3975
|
+
* `copy.welcomeTitle` / `copy.welcomeSubtitle` / `copy.showWelcomeCard`
|
|
3976
|
+
* options; a field present here wins over its legacy alias.
|
|
3977
|
+
*
|
|
3978
|
+
* All defaults live in the resolver (`resolveWelcomeConfig`), never in
|
|
3979
|
+
* `DEFAULT_WIDGET_CONFIG`: presence on this object is what distinguishes a
|
|
3980
|
+
* host-set value from a filled-in default.
|
|
3981
|
+
*/
|
|
3982
|
+
export interface AgentWidgetWelcomeConfig {
|
|
3983
|
+
/** Card or hero title. Defaults to "Hello 👋". */
|
|
3984
|
+
title?: string;
|
|
3985
|
+
/** Scope statement in the assistant's voice. Empty string omits it. */
|
|
3986
|
+
subtitle?: string;
|
|
3987
|
+
/** Avatar or logo shown above the title. */
|
|
3988
|
+
icon?: AgentWidgetWelcomeIcon;
|
|
3989
|
+
/** @default "card" */
|
|
3990
|
+
variant?: AgentWidgetWelcomeVariant;
|
|
3991
|
+
/** @default "never" for `card`, always `"on-first-message"` for `hero`. */
|
|
3992
|
+
dismiss?: AgentWidgetWelcomeDismiss;
|
|
3993
|
+
/**
|
|
3994
|
+
* Display-only greeting bubble pinned at transcript position zero. It is UI
|
|
3995
|
+
* chrome derived from config, never a session message: it does not appear in
|
|
3996
|
+
* `getMessages()`, is never persisted or sent to the model, `onMessage` does
|
|
3997
|
+
* not fire for it, and it survives `clearChat()`. Use an assistant
|
|
3998
|
+
* `initialMessages` entry instead when the model should see the greeting.
|
|
3999
|
+
*
|
|
4000
|
+
* Ignored (with a debug-mode warning) when `variant` is `"hero"`: the hero
|
|
4001
|
+
* is the greeting.
|
|
4002
|
+
*/
|
|
4003
|
+
message?: string;
|
|
4004
|
+
}
|
|
4005
|
+
|
|
3731
4006
|
/**
|
|
3732
4007
|
* Interface for pluggable stream parsers that extract text from streaming responses.
|
|
3733
4008
|
* Parsers handle incremental parsing to extract text values from structured formats (JSON, XML, etc.).
|
|
@@ -4020,7 +4295,9 @@ export type AgentWidgetHeaderLayoutConfig = {
|
|
|
4020
4295
|
*/
|
|
4021
4296
|
render?: (context: HeaderRenderContext) => HTMLElement;
|
|
4022
4297
|
/**
|
|
4023
|
-
*
|
|
4298
|
+
* Icon action buttons at the trailing edge of the `minimal` header layout,
|
|
4299
|
+
* clustered with the close button and sharing its chrome. For a menu
|
|
4300
|
+
* affordance next to the title, use `titleMenu` instead.
|
|
4024
4301
|
*/
|
|
4025
4302
|
trailingActions?: AgentWidgetHeaderTrailingAction[];
|
|
4026
4303
|
/** Called when a `trailingActions` button is clicked. */
|
|
@@ -4216,7 +4493,11 @@ export type AgentWidgetLayoutConfig = {
|
|
|
4216
4493
|
/**
|
|
4217
4494
|
* Max width for the content area (messages + composer).
|
|
4218
4495
|
* Applied with `margin: 0 auto` for centering.
|
|
4219
|
-
* Accepts any CSS width value (e.g. "90ch", "720px", "80%")
|
|
4496
|
+
* Accepts any CSS width value (e.g. "90ch", "720px", "80%"), or "none"
|
|
4497
|
+
* for a full-width column. Only engages on panels wider than the cap,
|
|
4498
|
+
* so launcher-width panels are unaffected.
|
|
4499
|
+
* @default "768px" ("720px" in composer-bar mode via
|
|
4500
|
+
* `launcher.composerBar.contentMaxWidth`)
|
|
4220
4501
|
*/
|
|
4221
4502
|
contentMaxWidth?: string;
|
|
4222
4503
|
};
|
|
@@ -4976,7 +5257,9 @@ export type AgentWidgetConfig = {
|
|
|
4976
5257
|
*/
|
|
4977
5258
|
getHeaders?: AgentWidgetHeadersFunction;
|
|
4978
5259
|
copy?: {
|
|
5260
|
+
/** @deprecated Use `welcome.title`. */
|
|
4979
5261
|
welcomeTitle?: string;
|
|
5262
|
+
/** @deprecated Use `welcome.subtitle`. */
|
|
4980
5263
|
welcomeSubtitle?: string;
|
|
4981
5264
|
inputPlaceholder?: string;
|
|
4982
5265
|
sendButtonLabel?: string;
|
|
@@ -4984,6 +5267,7 @@ export type AgentWidgetConfig = {
|
|
|
4984
5267
|
stopButtonLabel?: string;
|
|
4985
5268
|
/**
|
|
4986
5269
|
* When false, the welcome / intro card is not shown above the message list.
|
|
5270
|
+
* @deprecated Use `welcome.variant: "none"`.
|
|
4987
5271
|
* @default true
|
|
4988
5272
|
*/
|
|
4989
5273
|
showWelcomeCard?: boolean;
|
|
@@ -5048,7 +5332,17 @@ export type AgentWidgetConfig = {
|
|
|
5048
5332
|
* with `initialArtifacts`.
|
|
5049
5333
|
*/
|
|
5050
5334
|
initialSelectedArtifactId?: string | null;
|
|
5335
|
+
/** Rich starter and follow-up suggestion surfaces. */
|
|
5336
|
+
suggestions?: AgentWidgetSuggestionsConfig;
|
|
5337
|
+
/**
|
|
5338
|
+
* First-open welcome surface (card, hero, or none) plus the optional
|
|
5339
|
+
* display-only greeting bubble. Supersedes `copy.welcomeTitle`,
|
|
5340
|
+
* `copy.welcomeSubtitle`, and `copy.showWelcomeCard`.
|
|
5341
|
+
*/
|
|
5342
|
+
welcome?: AgentWidgetWelcomeConfig;
|
|
5343
|
+
/** @deprecated Prefer `suggestions.starters.items`. */
|
|
5051
5344
|
suggestionChips?: string[];
|
|
5345
|
+
/** @deprecated Prefer semantic tokens under `theme.components.suggestion`. */
|
|
5052
5346
|
suggestionChipsConfig?: AgentWidgetSuggestionChipsConfig;
|
|
5053
5347
|
debug?: boolean;
|
|
5054
5348
|
formEndpoint?: string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { createAgentExperience } from "./ui";
|
|
6
|
+
|
|
7
|
+
const createMount = () => {
|
|
8
|
+
const mount = document.createElement("div");
|
|
9
|
+
document.body.appendChild(mount);
|
|
10
|
+
return mount;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe("layout.slots['body-top']", () => {
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
document.body.innerHTML = "";
|
|
16
|
+
vi.restoreAllMocks();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("resolves defaultContent() to the intro card (regression: queried stale utility classes)", () => {
|
|
20
|
+
window.scrollTo = vi.fn();
|
|
21
|
+
const mount = createMount();
|
|
22
|
+
let resolved: HTMLElement | null | undefined;
|
|
23
|
+
|
|
24
|
+
createAgentExperience(mount, {
|
|
25
|
+
apiUrl: "https://api.example.com/chat",
|
|
26
|
+
launcher: { enabled: false },
|
|
27
|
+
layout: {
|
|
28
|
+
slots: {
|
|
29
|
+
"body-top": ({ defaultContent }) => {
|
|
30
|
+
resolved = defaultContent();
|
|
31
|
+
return null;
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(resolved).not.toBeNull();
|
|
38
|
+
expect(resolved!.hasAttribute("data-persona-intro-card")).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("replaces the intro card with the slot element instead of prepending beside it", () => {
|
|
42
|
+
window.scrollTo = vi.fn();
|
|
43
|
+
const mount = createMount();
|
|
44
|
+
|
|
45
|
+
createAgentExperience(mount, {
|
|
46
|
+
apiUrl: "https://api.example.com/chat",
|
|
47
|
+
launcher: { enabled: false },
|
|
48
|
+
layout: {
|
|
49
|
+
slots: {
|
|
50
|
+
"body-top": () => {
|
|
51
|
+
const custom = document.createElement("div");
|
|
52
|
+
custom.setAttribute("data-test-slot", "body-top");
|
|
53
|
+
return custom;
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const body = mount.querySelector("#persona-scroll-container")!;
|
|
60
|
+
expect(body.querySelector("[data-test-slot='body-top']")).not.toBeNull();
|
|
61
|
+
expect(body.querySelector("[data-persona-intro-card]")).toBeNull();
|
|
62
|
+
expect(
|
|
63
|
+
(body.firstElementChild as HTMLElement).getAttribute("data-test-slot")
|
|
64
|
+
).toBe("body-top");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("supports composing around the default intro card", () => {
|
|
68
|
+
window.scrollTo = vi.fn();
|
|
69
|
+
const mount = createMount();
|
|
70
|
+
|
|
71
|
+
createAgentExperience(mount, {
|
|
72
|
+
apiUrl: "https://api.example.com/chat",
|
|
73
|
+
launcher: { enabled: false },
|
|
74
|
+
layout: {
|
|
75
|
+
slots: {
|
|
76
|
+
"body-top": ({ defaultContent }) => {
|
|
77
|
+
const wrapper = document.createElement("div");
|
|
78
|
+
wrapper.setAttribute("data-test-slot", "wrapper");
|
|
79
|
+
const banner = document.createElement("p");
|
|
80
|
+
banner.textContent = "Welcome back";
|
|
81
|
+
wrapper.appendChild(banner);
|
|
82
|
+
const card = defaultContent();
|
|
83
|
+
if (card) wrapper.appendChild(card);
|
|
84
|
+
return wrapper;
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const body = mount.querySelector("#persona-scroll-container")!;
|
|
91
|
+
const wrapper = body.querySelector("[data-test-slot='wrapper']");
|
|
92
|
+
expect(wrapper).not.toBeNull();
|
|
93
|
+
expect(wrapper!.querySelector("[data-persona-intro-card]")).not.toBeNull();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { createAgentExperience } from "./ui";
|
|
6
|
+
|
|
7
|
+
const mounts: HTMLElement[] = [];
|
|
8
|
+
const controllers: ReturnType<typeof createAgentExperience>[] = [];
|
|
9
|
+
|
|
10
|
+
const makeController = (config: Record<string, unknown> = {}) => {
|
|
11
|
+
const mount = document.createElement("div");
|
|
12
|
+
document.body.appendChild(mount);
|
|
13
|
+
mounts.push(mount);
|
|
14
|
+
const controller = createAgentExperience(mount, {
|
|
15
|
+
apiUrl: "https://api.example.com/chat",
|
|
16
|
+
launcher: { enabled: false },
|
|
17
|
+
persistState: false,
|
|
18
|
+
suggestionChips: [],
|
|
19
|
+
...config,
|
|
20
|
+
} as unknown as Parameters<typeof createAgentExperience>[1]);
|
|
21
|
+
controllers.push(controller);
|
|
22
|
+
return { mount, controller };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const wrapper = (mount: HTMLElement) =>
|
|
26
|
+
mount.querySelector<HTMLElement>("#persona-scroll-container > .persona-widget-messages")!;
|
|
27
|
+
|
|
28
|
+
describe("default content column", () => {
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
controllers.splice(0).forEach((controller) => controller.destroy());
|
|
31
|
+
mounts.splice(0).forEach((mount) => mount.remove());
|
|
32
|
+
document.body.innerHTML = "";
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("keeps the transcript wrapper :empty pre-conversation so CSS drops it from the flex flow", () => {
|
|
36
|
+
// The stylesheet's .persona-widget-messages:empty { display: none } only
|
|
37
|
+
// works if nothing seeds the wrapper with placeholder nodes.
|
|
38
|
+
const { mount, controller } = makeController();
|
|
39
|
+
const messagesWrapper = wrapper(mount);
|
|
40
|
+
expect(messagesWrapper.matches(":empty")).toBe(true);
|
|
41
|
+
|
|
42
|
+
controller.injectUserMessage({ content: "hi" });
|
|
43
|
+
expect(messagesWrapper.matches(":empty")).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("caps and centers the transcript at 768px by default", () => {
|
|
47
|
+
const { mount } = makeController();
|
|
48
|
+
const messagesWrapper = wrapper(mount);
|
|
49
|
+
expect(messagesWrapper.style.maxWidth).toBe("768px");
|
|
50
|
+
expect(messagesWrapper.style.marginLeft).toBe("auto");
|
|
51
|
+
expect(messagesWrapper.style.marginRight).toBe("auto");
|
|
52
|
+
expect(messagesWrapper.style.width).toBe("100%");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("honors an explicit layout.contentMaxWidth", () => {
|
|
56
|
+
const { mount } = makeController({ layout: { contentMaxWidth: "90ch" } });
|
|
57
|
+
expect(wrapper(mount).style.maxWidth).toBe("90ch");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("opts out with \"none\" for a full-width column", () => {
|
|
61
|
+
const { mount } = makeController({ layout: { contentMaxWidth: "none" } });
|
|
62
|
+
expect(wrapper(mount).style.maxWidth).toBe("none");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("caps the composer status text to the same column as the composer form", () => {
|
|
66
|
+
const { mount, controller } = makeController();
|
|
67
|
+
const statusText = mount.querySelector<HTMLElement>("[data-persona-composer-status]")!;
|
|
68
|
+
expect(statusText.style.maxWidth).toBe("768px");
|
|
69
|
+
expect(statusText.style.marginLeft).toBe("auto");
|
|
70
|
+
expect(statusText.style.marginRight).toBe("auto");
|
|
71
|
+
|
|
72
|
+
controller.update({ layout: { contentMaxWidth: "90ch" } });
|
|
73
|
+
expect(statusText.style.maxWidth).toBe("90ch");
|
|
74
|
+
|
|
75
|
+
controller.update({ layout: { contentMaxWidth: "" } });
|
|
76
|
+
expect(statusText.style.maxWidth).toBe("");
|
|
77
|
+
expect(statusText.style.marginLeft).toBe("");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("publishes the resolved column as a CSS var for plugin content", () => {
|
|
81
|
+
const { mount, controller } = makeController();
|
|
82
|
+
expect(
|
|
83
|
+
mount.style.getPropertyValue("--persona-content-max-width")
|
|
84
|
+
).toBe("768px");
|
|
85
|
+
|
|
86
|
+
controller.update({ layout: { contentMaxWidth: "90ch" } });
|
|
87
|
+
expect(
|
|
88
|
+
mount.style.getPropertyValue("--persona-content-max-width")
|
|
89
|
+
).toBe("90ch");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -907,7 +907,7 @@ describe("createAgentExperience welded artifact split chrome", () => {
|
|
|
907
907
|
|
|
908
908
|
controller.hideArtifacts();
|
|
909
909
|
|
|
910
|
-
//
|
|
910
|
+
// Same as pane-closed: container carries the border, panel does not.
|
|
911
911
|
expect(mount.classList.contains("persona-artifact-welded-split")).toBe(false);
|
|
912
912
|
expect(container?.style.border).toContain("--persona-panel-border");
|
|
913
913
|
expect(panel?.style.border).toBe("");
|
package/src/ui.docked.test.ts
CHANGED
|
@@ -27,24 +27,27 @@ describe("createAgentExperience docked mode", () => {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
const wrapper = mount.firstElementChild as HTMLElement | null;
|
|
30
|
-
|
|
30
|
+
// The launcher mounts inside its surface wrapper; visibility is driven on
|
|
31
|
+
// the wrapper so a teaser bubble hides with the button.
|
|
32
|
+
const launcherSurface = mount.lastElementChild as HTMLElement | null;
|
|
33
|
+
const launcherButton = launcherSurface?.querySelector("button") ?? null;
|
|
31
34
|
|
|
32
35
|
expect(wrapper).not.toBeNull();
|
|
33
36
|
expect(wrapper?.style.display).toBe("flex");
|
|
34
37
|
expect(launcherButton).not.toBeNull();
|
|
35
38
|
expect(launcherButton?.tagName).toBe("BUTTON");
|
|
36
|
-
expect(
|
|
39
|
+
expect(launcherSurface?.style.display).toBe("none");
|
|
37
40
|
expect(launcherButton?.className).toContain("persona-mt-4");
|
|
38
41
|
|
|
39
42
|
controller.close();
|
|
40
43
|
|
|
41
44
|
expect(wrapper?.style.display).toBe("none");
|
|
42
|
-
expect(
|
|
45
|
+
expect(launcherSurface?.style.display).toBe("none");
|
|
43
46
|
|
|
44
47
|
controller.open();
|
|
45
48
|
|
|
46
49
|
expect(wrapper?.style.display).toBe("flex");
|
|
47
|
-
expect(
|
|
50
|
+
expect(launcherSurface?.style.display).toBe("none");
|
|
48
51
|
|
|
49
52
|
controller.destroy();
|
|
50
53
|
});
|