@runtypelabs/persona 4.23.2 → 4.24.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 (156) hide show
  1. package/dist/animations/glyph-cycle.d.cts +1 -1
  2. package/dist/animations/glyph-cycle.d.ts +1 -1
  3. package/dist/animations/{types-CI7OckWI.d.cts → types-Db61aI9J.d.cts} +2 -0
  4. package/dist/animations/{types-CI7OckWI.d.ts → types-Db61aI9J.d.ts} +2 -0
  5. package/dist/animations/wipe.d.cts +1 -1
  6. package/dist/animations/wipe.d.ts +1 -1
  7. package/dist/animations-extra.d.cts +2 -0
  8. package/dist/animations-extra.d.ts +2 -0
  9. package/dist/approval-ui-3NMYNXBD.js +1 -0
  10. package/dist/approval-ui.cjs +2 -2
  11. package/dist/approval-ui.d.cts +215 -37
  12. package/dist/approval-ui.d.ts +215 -37
  13. package/dist/approval-ui.js +2 -2
  14. package/dist/{artifacts-ui-F4N6VMKF.js → artifacts-ui-IGB5YKLO.js} +4 -4
  15. package/dist/artifacts-ui.d.cts +210 -25
  16. package/dist/artifacts-ui.d.ts +210 -25
  17. package/dist/{chunk-NIKYKSNZ.js → chunk-7CO4T5KB.js} +1 -1
  18. package/dist/codegen.cjs +11 -11
  19. package/dist/codegen.js +13 -13
  20. package/dist/context-mentions.d.cts +210 -25
  21. package/dist/context-mentions.d.ts +210 -25
  22. package/dist/event-stream-view.d.cts +210 -25
  23. package/dist/event-stream-view.d.ts +210 -25
  24. package/dist/forms-ui.d.cts +245 -27
  25. package/dist/forms-ui.d.ts +245 -27
  26. package/dist/history-view.d.cts +2 -0
  27. package/dist/history-view.d.ts +2 -0
  28. package/dist/index.cjs +70 -69
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +270 -39
  31. package/dist/index.d.ts +270 -39
  32. package/dist/index.global.js +57 -54
  33. package/dist/index.global.js.map +1 -1
  34. package/dist/index.js +70 -69
  35. package/dist/index.js.map +1 -1
  36. package/dist/install.global.js +1 -1
  37. package/dist/install.global.js.map +1 -1
  38. package/dist/launcher.global.js +2 -2
  39. package/dist/launcher.global.js.map +1 -1
  40. package/dist/session-reconnect.d.cts +210 -25
  41. package/dist/session-reconnect.d.ts +210 -25
  42. package/dist/smart-dom-reader.d.cts +210 -25
  43. package/dist/smart-dom-reader.d.ts +210 -25
  44. package/dist/theme-editor-preview.cjs +74 -73
  45. package/dist/theme-editor-preview.d.cts +217 -25
  46. package/dist/theme-editor-preview.d.ts +217 -25
  47. package/dist/theme-editor-preview.js +51 -50
  48. package/dist/theme-editor.cjs +10 -10
  49. package/dist/theme-editor.d.cts +230 -27
  50. package/dist/theme-editor.d.ts +230 -27
  51. package/dist/theme-editor.js +10 -10
  52. package/dist/theme-reference.cjs +1 -1
  53. package/dist/theme-reference.d.cts +1522 -11
  54. package/dist/theme-reference.d.ts +1522 -11
  55. package/dist/theme-reference.js +1 -1
  56. package/dist/widget.css +1 -1
  57. package/package.json +3 -2
  58. package/src/__fixtures__/defaults-parity.v4.json +776 -0
  59. package/src/approval-ui-entry.ts +0 -1
  60. package/src/approval-ui-loader.ts +5 -4
  61. package/src/approval-ui.ts +0 -1
  62. package/src/client-identity-proof.test.ts +193 -0
  63. package/src/client-session-warm.test.ts +216 -0
  64. package/src/client-visitor-history.test.ts +6 -0
  65. package/src/client.ts +122 -30
  66. package/src/codegen.test.ts +24 -0
  67. package/src/components/activity-row.test.ts +100 -0
  68. package/src/components/activity-row.ts +136 -0
  69. package/src/components/approval-actions.test.ts +8 -5
  70. package/src/components/approval-actions.ts +19 -6
  71. package/src/components/approval-bubble.test.ts +56 -9
  72. package/src/components/approval-bubble.ts +36 -41
  73. package/src/components/composer-builder.ts +5 -2
  74. package/src/components/composer-parts.ts +10 -2
  75. package/src/components/header-builder.test.ts +32 -0
  76. package/src/components/header-builder.ts +7 -4
  77. package/src/components/header-layouts.test.ts +12 -0
  78. package/src/components/header-layouts.ts +3 -2
  79. package/src/components/launcher.ts +11 -2
  80. package/src/components/message-bubble.test.ts +22 -0
  81. package/src/components/message-bubble.ts +7 -3
  82. package/src/components/panel.test.ts +52 -1
  83. package/src/components/panel.ts +47 -8
  84. package/src/components/pill-composer-builder.test.ts +2 -0
  85. package/src/components/pill-composer-builder.ts +0 -1
  86. package/src/components/reasoning-bubble.ts +29 -17
  87. package/src/components/suggestions.test.ts +1 -0
  88. package/src/components/suggestions.ts +0 -16
  89. package/src/components/tool-bubble.ts +131 -114
  90. package/src/components/tool-details.test.ts +87 -0
  91. package/src/components/tool-details.ts +102 -0
  92. package/src/components/welcome.test.ts +8 -0
  93. package/src/components/welcome.ts +4 -2
  94. package/src/defaults-parity.test.ts +158 -0
  95. package/src/defaults.test.ts +69 -0
  96. package/src/defaults.ts +128 -49
  97. package/src/generated/runtype-openapi-contract.ts +2 -2
  98. package/src/index-core.ts +2 -0
  99. package/src/install.test.ts +24 -0
  100. package/src/install.ts +3 -1
  101. package/src/launcher-global.ts +2 -1
  102. package/src/launcher-variants.test.ts +52 -0
  103. package/src/runtime/init-update-reset.test.ts +21 -0
  104. package/src/session.ts +29 -6
  105. package/src/styles/widget-styles.test.ts +19 -0
  106. package/src/styles/widget.css +295 -23
  107. package/src/theme-editor/index.ts +2 -0
  108. package/src/theme-editor/preview-utils.test.ts +8 -0
  109. package/src/theme-editor/preview-utils.ts +7 -3
  110. package/src/theme-editor/sections.test.ts +31 -0
  111. package/src/theme-editor/sections.ts +55 -1
  112. package/src/theme-editor/state-preferences.test.ts +49 -0
  113. package/src/theme-editor/state.ts +73 -15
  114. package/src/theme-reference.test.ts +20 -0
  115. package/src/theme-reference.ts +11 -5
  116. package/src/tool-call-display-defaults.test.ts +33 -1
  117. package/src/types/theme.ts +80 -3
  118. package/src/types.ts +136 -22
  119. package/src/ui.activity-row.test.ts +91 -0
  120. package/src/ui.approval-plugin.test.ts +63 -0
  121. package/src/ui.composer-compact.test.ts +24 -2
  122. package/src/ui.composer-placement.test.ts +23 -0
  123. package/src/ui.detached-panel.test.ts +33 -6
  124. package/src/ui.docked.test.ts +19 -0
  125. package/src/ui.header-update-stability.test.ts +25 -0
  126. package/src/ui.identity-proof.test.ts +58 -0
  127. package/src/ui.launcher-teaser.test.ts +25 -0
  128. package/src/ui.message-width.test.ts +17 -2
  129. package/src/ui.reasoning-display.test.ts +92 -0
  130. package/src/ui.rebuild-composer.test.ts +20 -3
  131. package/src/ui.scroll.test.ts +32 -0
  132. package/src/ui.session-init.test.ts +312 -0
  133. package/src/ui.status-indicator.test.ts +82 -0
  134. package/src/ui.suggest-replies.test.ts +11 -0
  135. package/src/ui.ts +363 -74
  136. package/src/ui.v5-defaults.test.ts +144 -0
  137. package/src/ui.welcome.test.ts +92 -3
  138. package/src/utils/__snapshots__/tokens-parity.test.ts.snap +811 -140
  139. package/src/utils/code-generators.ts +14 -0
  140. package/src/utils/composer-compact.test.ts +0 -2
  141. package/src/utils/composer-compact.ts +2 -4
  142. package/src/utils/composer-placement.ts +2 -3
  143. package/src/utils/config-merge.test.ts +81 -5
  144. package/src/utils/config-merge.ts +13 -3
  145. package/src/utils/defaults-provenance.ts +67 -0
  146. package/src/utils/dock.ts +7 -1
  147. package/src/utils/launcher-variant.ts +16 -0
  148. package/src/utils/panel-config.ts +97 -0
  149. package/src/utils/status-indicator.ts +39 -0
  150. package/src/utils/theme.test.ts +182 -0
  151. package/src/utils/theme.ts +131 -18
  152. package/src/utils/tokens.test.ts +122 -1
  153. package/src/utils/tokens.ts +177 -17
  154. package/src/welcome.test.ts +23 -0
  155. package/src/welcome.ts +16 -9
  156. package/dist/approval-ui-K7R5Q5OC.js +0 -1
@@ -0,0 +1,52 @@
1
+ // @vitest-environment jsdom
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
+ import { createAgentExperience } from "./ui";
4
+ import { mount as mountCritical } from "./launcher-global";
5
+ import { getActiveTheme, themeToCssVariables } from "./utils/theme";
6
+ import type { AgentWidgetConfig } from "./types";
7
+
8
+ afterEach(() => { document.body.replaceChildren(); vi.restoreAllMocks(); });
9
+
10
+ describe.each([false, true])("launcher variants (v5=%s)", (v5Defaults) => {
11
+ it.each([undefined, "pill", "circle"] as const)("matches deferred and full rendering for %s", (variant) => {
12
+ window.scrollTo = vi.fn();
13
+ const config: AgentWidgetConfig = {
14
+ apiUrl: "/dispatch", persistState: false, future: { v5Defaults },
15
+ launcher: { ...(variant ? { variant } : {}), title: "Open support", teaser: { text: "Need help?" } },
16
+ };
17
+ const host = document.body.appendChild(document.createElement("div"));
18
+ const target = document.body.appendChild(document.createElement("div"));
19
+ const full = createAgentExperience(host, config);
20
+ const critical = mountCritical({ target, config, onOpen: vi.fn() });
21
+ const button = host.querySelector<HTMLButtonElement>(".persona-launcher-surface > button")!;
22
+ try {
23
+ const circle = variant ? variant === "circle" : v5Defaults;
24
+ expect(button.dataset.personaLauncherVariant).toBe(circle ? "circle" : undefined);
25
+ expect(button.innerHTML).toBe(critical.element.innerHTML);
26
+ expect(button.getAttribute("style")).toBe(critical.element.getAttribute("style"));
27
+ expect(host.style.getPropertyValue("--persona-launcher-bg"))
28
+ .toBe(critical.root.style.getPropertyValue("--persona-launcher-bg"));
29
+ if (circle) expect(button.getAttribute("aria-label")).toBe("Open support");
30
+ full.update({ launcher: { variant: circle ? "pill" : "circle" } });
31
+ critical.update({ ...config, launcher: { ...config.launcher, variant: circle ? "pill" : "circle" } });
32
+ expect(button.dataset.personaLauncherVariant).toBe(circle ? undefined : "circle");
33
+ expect(button.innerHTML).toBe(critical.element.innerHTML);
34
+ } finally { full.destroy(); critical.destroy(); }
35
+ });
36
+
37
+ it.each(["light", "dark"] as const)("resolves circle geometry and explicit tokens in %s", colorScheme => {
38
+ const config: AgentWidgetConfig = { future: { v5Defaults }, colorScheme, launcher: { variant: "circle" } };
39
+ const css = themeToCssVariables(getActiveTheme(config));
40
+ expect(css["--persona-components-launcher-size"]).toBe("48px");
41
+ expect(css["--persona-components-launcher-offset"]).toBe("20px");
42
+ expect(css["--persona-launcher-shadow"]).toBe("0 8px 24px rgba(0,0,0,.16)");
43
+ expect(css["--persona-launcher-bg"]).not.toBe(css["--persona-launcher-fg"]);
44
+ const custom = themeToCssVariables(getActiveTheme({ ...config, theme: { components: { launcher: {
45
+ size: "56px", iconSize: "20px", offset: "32px", background: "#123456", foreground: "#ffffff", shadow: "none",
46
+ } } } }));
47
+ expect(custom["--persona-components-launcher-size"]).toBe("56px");
48
+ expect(custom["--persona-components-launcher-offset"]).toBe("32px");
49
+ expect(custom["--persona-launcher-bg"]).toBe("#123456");
50
+ expect(custom["--persona-launcher-shadow"]).toBe("none");
51
+ });
52
+ });
@@ -78,4 +78,25 @@ describe("handle.update explicit-undefined reset reaches the controller", () =>
78
78
 
79
79
  handle.destroy();
80
80
  });
81
+
82
+ it("updates the root defaults-version attribute", () => {
83
+ window.scrollTo = vi.fn();
84
+ document.body.innerHTML = `<div id="target"></div>`;
85
+
86
+ const handle = initAgentWidget({
87
+ target: "#target",
88
+ config: {
89
+ apiUrl: "https://api.example.com/chat",
90
+ launcher: { enabled: false },
91
+ future: { v5Defaults: false },
92
+ },
93
+ });
94
+ const root = () => document.querySelector<HTMLElement>("[data-persona-root]")!;
95
+
96
+ expect(root().getAttribute("data-persona-defaults")).toBe("v4");
97
+ handle.update({ future: { v5Defaults: true } });
98
+ expect(root().getAttribute("data-persona-defaults")).toBe("v5");
99
+
100
+ handle.destroy();
101
+ });
81
102
  });
package/src/session.ts CHANGED
@@ -144,6 +144,7 @@ const CONNECTION_CONFIG_KEYS = [
144
144
  "getStoredSessionId",
145
145
  "setStoredSessionId",
146
146
  "getIdentityProof",
147
+ "identityProvider",
147
148
  "getStoredConversationId",
148
149
  "setStoredConversationId",
149
150
  "clearStoredConversationId",
@@ -1033,12 +1034,29 @@ export class AgentWidgetSession {
1033
1034
  }
1034
1035
  }
1035
1036
 
1037
+ /**
1038
+ * Early, fire-and-forget client-token init (`sessionInit` trigger or
1039
+ * `controller.initSession()`). Failures stay silent here: no `onError`, no
1040
+ * UI. The send re-runs `initSession()` and reports them as it always has.
1041
+ */
1042
+ public warmClientSession(): void {
1043
+ // The visitor may send before this resolves; the server welcome still
1044
+ // belongs ahead of that first turn when the transcript started empty.
1045
+ const pristine = !this.messages.length;
1046
+ void this.client.warmSession().then((session) => {
1047
+ // A start-new or credential re-init may have replaced it meanwhile.
1048
+ if (session && this.client.getClientSession() === session) {
1049
+ this.setClientSession(session, pristine);
1050
+ }
1051
+ });
1052
+ }
1053
+
1036
1054
  /**
1037
1055
  * Set the client session after initialization
1038
1056
  */
1039
- public setClientSession(session: ClientSession): void {
1057
+ public setClientSession(session: ClientSession, pristine?: boolean): void {
1040
1058
  this.clientSession = session;
1041
- this.injectWelcomeMessage(session);
1059
+ this.injectWelcomeMessage(session, pristine);
1042
1060
  }
1043
1061
 
1044
1062
  /**
@@ -1046,15 +1064,20 @@ export class AgentWidgetSession {
1046
1064
  * reopening a stored conversation is not a new thread, and the hydrated
1047
1065
  * transcript would otherwise race a welcome bubble.
1048
1066
  */
1049
- private injectWelcomeMessage(session: ClientSession): void {
1067
+ private injectWelcomeMessage(
1068
+ session: ClientSession,
1069
+ pristine = !this.messages.length
1070
+ ): void {
1050
1071
  if (this.suppressWelcomeInjection) return;
1051
- if (!session.config.welcomeMessage || this.messages.length !== 0) return;
1072
+ if (!session.config.welcomeMessage || !pristine) return;
1073
+ // A late welcome (the first send raced the init) sorts ahead of that turn.
1074
+ const first = this.messages[0];
1052
1075
  const welcomeMessage: AgentWidgetMessage = {
1053
1076
  id: `welcome-${Date.now()}`,
1054
1077
  role: "assistant",
1055
1078
  content: session.config.welcomeMessage,
1056
- createdAt: new Date().toISOString(),
1057
- sequence: this.nextSequence()
1079
+ createdAt: first?.createdAt ?? new Date().toISOString(),
1080
+ sequence: first ? (first.sequence ?? 0) - 1 : this.nextSequence()
1058
1081
  };
1059
1082
  this.appendMessage(welcomeMessage);
1060
1083
  }
@@ -189,6 +189,25 @@ describe("suggestion interaction styles", () => {
189
189
  });
190
190
  });
191
191
 
192
+ describe("collapsible widget chrome styles", () => {
193
+ it("uses prefixed aliases while retaining each legacy alias and literal fallback", () => {
194
+ const expectedDeclarations: Array<[string, number]> = [
195
+ ["var(--persona-cw-border, var(--cw-border, #e5e7eb))", 2],
196
+ ["var(--persona-cw-container, var(--cw-container, #f9fafb))", 1],
197
+ ["var(--persona-cw-surface, var(--cw-surface, #ffffff))", 2],
198
+ ["var(--persona-cw-border, var(--cw-border, #f1f5f9))", 1],
199
+ ["var(--persona-cw-container, var(--cw-container, #f8fafc))", 1],
200
+ ["var(--persona-cw-border, var(--cw-border, #d1d5db))", 1],
201
+ ["var(--persona-cw-surface, var(--cw-surface, #fff))", 2],
202
+ ];
203
+
204
+ for (const [declaration, count] of expectedDeclarations) {
205
+ expect(widgetCss).toContain(declaration);
206
+ expect(widgetCss.split(declaration).length - 1).toBe(count);
207
+ }
208
+ });
209
+ });
210
+
192
211
  describe("composer spacing styles", () => {
193
212
  it("themes the composer form padding and gap with utility-matching fallbacks", () => {
194
213
  const selector =
@@ -582,6 +582,7 @@
582
582
  --persona-components-suggestion-chip-foreground,
583
583
  var(--persona-text)
584
584
  );
585
+ font-weight: var(--persona-components-suggestion-chip-fontWeight, 500);
585
586
  font-size: var(--persona-components-suggestion-chip-fontSize, 0.8125rem);
586
587
  line-height: var(--persona-components-suggestion-chip-lineHeight, 1.25);
587
588
  }
@@ -606,6 +607,7 @@
606
607
  var(--persona-text)
607
608
  );
608
609
  box-shadow: var(--persona-components-suggestion-card-shadow, none);
610
+ font-weight: var(--persona-components-suggestion-card-fontWeight, 500);
609
611
  font-size: var(--persona-components-suggestion-card-fontSize, 0.875rem);
610
612
  line-height: var(--persona-components-suggestion-card-lineHeight, 1.35);
611
613
  }
@@ -630,6 +632,7 @@
630
632
  --persona-components-suggestion-list-foreground,
631
633
  var(--persona-text)
632
634
  );
635
+ font-weight: var(--persona-components-suggestion-list-fontWeight, 500);
633
636
  font-size: var(--persona-components-suggestion-list-fontSize, 0.875rem);
634
637
  line-height: var(--persona-components-suggestion-list-lineHeight, 1.35);
635
638
  }
@@ -2812,11 +2815,6 @@
2812
2815
  }
2813
2816
  }
2814
2817
 
2815
- /* Prevent form container from showing focus styles */
2816
- .persona-widget-composer:focus-within {
2817
- outline: none !important;
2818
- }
2819
-
2820
2818
  .persona-widget-composer:focus-within textarea {
2821
2819
  border: none !important;
2822
2820
  outline: none !important;
@@ -2869,6 +2867,25 @@
2869
2867
  border-color: var(--persona-composer-border-color, var(--persona-border, #e5e7eb));
2870
2868
  }
2871
2869
 
2870
+ /* Focus belongs to the whole form, including its action controls. An outline
2871
+ adds no layout size and preserves custom resting shadows. Unset V4 tokens
2872
+ retain the resting border and no ring. Shared by compact and expanded forms. */
2873
+ [data-persona-root] .persona-widget-composer:focus-within {
2874
+ outline: var(--persona-components-composer-focusRing, none);
2875
+ outline-offset: 1px;
2876
+ }
2877
+
2878
+ [data-persona-root] .persona-widget-composer:not(.persona-pill-composer):focus-within {
2879
+ border-color: var(
2880
+ --persona-components-composer-focusBorderColor,
2881
+ var(--persona-composer-border-color, var(--persona-border, #e5e7eb))
2882
+ );
2883
+ }
2884
+
2885
+ [data-persona-root] .persona-pill-composer:focus-within {
2886
+ border-color: var(--persona-components-composer-focusBorderColor, var(--persona-border, #e5e7eb));
2887
+ }
2888
+
2872
2889
  /* Composer type scale, themeable via `components.composer.fontSize` /
2873
2890
  `.lineHeight`. Outranks the `persona-text-sm` utility on the textarea;
2874
2891
  fallbacks match it. Applies to the pill composer's textarea too. */
@@ -2927,6 +2944,15 @@
2927
2944
  Positioning lives here, never inline: a plugin-owned footer is exempt from
2928
2945
  the cssText reset in applyFullHeightStyles, so an inline design would
2929
2946
  diverge for plugin composers. */
2947
+
2948
+ /* Optional composer chrome tokens retain the legacy fallback in V4. */
2949
+ .persona-widget-footer.persona-border-t-persona-divider {
2950
+ border-top: var(--persona-components-composer-footerBorder, 1px solid var(--persona-divider));
2951
+ }
2952
+ [data-persona-composer-submit].persona-composer-control {
2953
+ border-radius: var(--persona-components-composer-sendButtonRadius, var(--persona-button-radius));
2954
+ }
2955
+
2930
2956
  [data-persona-root][data-persona-composer-placement="overlay"] .persona-widget-footer {
2931
2957
  position: absolute;
2932
2958
  inset: auto 0 var(--persona-composer-lift, 0px) 0;
@@ -3087,6 +3113,25 @@
3087
3113
  padding: 0;
3088
3114
  }
3089
3115
 
3116
+ /* Inline per-control styles take precedence over these theme defaults. */
3117
+ .persona-header-control.persona-rounded-full {
3118
+ border-radius: var(--persona-components-header-controlBorderRadius, 9999px);
3119
+ }
3120
+ .persona-header-control:hover,
3121
+ .persona-header-control:focus-visible {
3122
+ --persona-header-control-state-fg: var(--persona-components-header-controlHoverForeground);
3123
+ }
3124
+ .persona-header-control.hover\:persona-bg-gray-100:hover {
3125
+ background-color: var(--persona-components-header-controlHoverBackground);
3126
+ }
3127
+ .persona-header-control:focus-visible {
3128
+ background-color: var(--persona-components-header-controlHoverBackground);
3129
+ outline: var(--persona-components-header-controlFocusOutline, revert);
3130
+ }
3131
+ [data-persona-defaults="v5"] .persona-header-control:focus-visible {
3132
+ outline-offset: 2px;
3133
+ }
3134
+
3090
3135
  .persona-header-control--glyph > svg {
3091
3136
  width: var(--persona-header-control-icon-size, 20px);
3092
3137
  height: var(--persona-header-control-icon-size, 20px);
@@ -3340,6 +3385,7 @@
3340
3385
  .persona-message-assistant-bubble {
3341
3386
  border-radius: var(--persona-message-assistant-radius, var(--persona-radius-lg, 0.5rem));
3342
3387
  border-color: var(--persona-message-assistant-border, var(--persona-border, #e5e7eb));
3388
+ border-width: var(--persona-message-assistant-border-width, 1px);
3343
3389
  }
3344
3390
 
3345
3391
  [data-persona-root] .persona-message-assistant-bubble.persona-shadow-sm {
@@ -3347,11 +3393,11 @@
3347
3393
  }
3348
3394
 
3349
3395
  [data-persona-root] .persona-message-user-bubble.persona-shadow-sm {
3350
- box-shadow: var(--persona-message-user-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
3396
+ box-shadow: var(--persona-message-user-shadow);
3351
3397
  }
3352
3398
 
3353
3399
  [data-persona-root] .persona-tool-bubble.persona-shadow-sm {
3354
- box-shadow: var(--persona-tool-bubble-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
3400
+ box-shadow: var(--persona-tool-bubble-shadow);
3355
3401
  }
3356
3402
 
3357
3403
  /* ==============================
@@ -3435,7 +3481,7 @@
3435
3481
  }
3436
3482
 
3437
3483
  [data-persona-root] .persona-reasoning-bubble.persona-shadow-sm {
3438
- box-shadow: var(--persona-reasoning-bubble-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
3484
+ box-shadow: var(--persona-reasoning-bubble-shadow);
3439
3485
  }
3440
3486
 
3441
3487
  /* Artifact markdown (no .persona-message-bubble wrapper) */
@@ -4363,24 +4409,24 @@
4363
4409
  /* Content areas: themed border and background for tool/reasoning bubbles */
4364
4410
  .persona-tool-bubble .persona-border-t,
4365
4411
  .persona-reasoning-bubble .persona-border-t {
4366
- border-top-color: var(--cw-border, #e5e7eb);
4367
- background-color: var(--cw-container, #f9fafb);
4412
+ border-top-color: var(--persona-cw-border, var(--cw-border, #e5e7eb));
4413
+ background-color: var(--persona-cw-container, var(--cw-container, #f9fafb));
4368
4414
  }
4369
4415
 
4370
4416
  /* Tool bubble code blocks: themed surface and border */
4371
4417
  .persona-tool-bubble pre {
4372
- background-color: var(--cw-surface, #ffffff);
4373
- border-color: var(--cw-border, #f1f5f9);
4418
+ background-color: var(--persona-cw-surface, var(--cw-surface, #ffffff));
4419
+ border-color: var(--persona-cw-border, var(--cw-border, #f1f5f9));
4374
4420
  }
4375
4421
 
4376
4422
  /* Collapsible header hover (tool + reasoning) */
4377
- .persona-tool-bubble button[data-expand-header],
4378
- .persona-reasoning-bubble button[data-expand-header] {
4423
+ .persona-tool-bubble button[data-expand-header]:not(.persona-activity-header),
4424
+ .persona-reasoning-bubble button[data-expand-header]:not(.persona-activity-header) {
4379
4425
  transition: background-color 0.15s ease;
4380
4426
  }
4381
- .persona-tool-bubble button[data-expand-header]:hover,
4382
- .persona-reasoning-bubble button[data-expand-header]:hover {
4383
- background-color: var(--cw-container, #f8fafc);
4427
+ .persona-tool-bubble button[data-expand-header]:not(.persona-activity-header):hover,
4428
+ .persona-reasoning-bubble button[data-expand-header]:not(.persona-activity-header):hover {
4429
+ background-color: var(--persona-cw-container, var(--cw-container, #f8fafc));
4384
4430
  }
4385
4431
 
4386
4432
  /* Collapsed live preview blocks: match expanded content's 1px border-top with transparent border */
@@ -4395,7 +4441,7 @@
4395
4441
  --_dot-size: 6px;
4396
4442
  --_branch-len: 0.75rem;
4397
4443
  --_branch-y: 1.5rem;
4398
- --_rail-color: var(--cw-border, #d1d5db);
4444
+ --_rail-color: var(--persona-cw-border, var(--cw-border, #d1d5db));
4399
4445
  gap: 0;
4400
4446
  }
4401
4447
 
@@ -4431,7 +4477,7 @@
4431
4477
  height: var(--_dot-size);
4432
4478
  border-radius: 999px;
4433
4479
  border: var(--_rail-w) solid var(--_rail-color);
4434
- background: var(--cw-surface, #fff);
4480
+ background: var(--persona-cw-surface, var(--cw-surface, #fff));
4435
4481
  transform: translate(-50%, -50%);
4436
4482
  }
4437
4483
 
@@ -4502,7 +4548,7 @@
4502
4548
  height: var(--_dot-size);
4503
4549
  border-radius: 999px;
4504
4550
  border: var(--_rail-w) solid var(--_rail-color);
4505
- background: var(--cw-surface, #fff);
4551
+ background: var(--persona-cw-surface, var(--cw-surface, #fff));
4506
4552
  transform: translate(-50%, -50%);
4507
4553
  z-index: 1;
4508
4554
  }
@@ -4513,8 +4559,8 @@
4513
4559
 
4514
4560
  /* Approval bubble defaults (overridden by inline styles when config exists) */
4515
4561
  .persona-approval-bubble {
4516
- background-color: var(--cw-surface, #ffffff);
4517
- border-color: var(--cw-border, #e5e7eb);
4562
+ background-color: var(--persona-cw-surface, var(--cw-surface, #ffffff));
4563
+ border-color: var(--persona-cw-border, var(--cw-border, #e5e7eb));
4518
4564
  }
4519
4565
 
4520
4566
  /* Approval button hover/active states */
@@ -6798,7 +6844,9 @@
6798
6844
  border-radius: 9999px;
6799
6845
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
6800
6846
  color: var(--persona-primary, #111827);
6801
- font: inherit;
6847
+ font-family: inherit;
6848
+ font-weight: 400;
6849
+ font-style: normal;
6802
6850
  font-size: 13px;
6803
6851
  line-height: 1.4;
6804
6852
  cursor: pointer;
@@ -7181,3 +7229,227 @@
7181
7229
  opacity: 0.6;
7182
7230
  }
7183
7231
  }
7232
+
7233
+ /* Both the critical and full launchers use these variant and placement rules. */
7234
+ [data-persona-launcher-variant="circle"] {
7235
+ width: var(--persona-components-launcher-size, 48px);
7236
+ height: var(--persona-components-launcher-size, 48px);
7237
+ padding: 0;
7238
+ gap: 0;
7239
+ justify-content: center;
7240
+ color: var(--persona-launcher-fg, var(--persona-text-inverse));
7241
+ }
7242
+ [data-persona-launcher-variant="circle"] [data-role="launcher-icon"] {
7243
+ color: inherit;
7244
+ }
7245
+ [data-persona-launcher-variant="circle"] [data-role="launcher-icon"].persona-bg-persona-primary {
7246
+ background: transparent;
7247
+ }
7248
+ [data-persona-launcher-variant="circle"] [data-role="launcher-icon"] svg {
7249
+ width: var(--persona-components-launcher-iconSize, 24px);
7250
+ height: var(--persona-components-launcher-iconSize, 24px);
7251
+ stroke: currentColor;
7252
+ }
7253
+ .persona-launcher-surface[data-teaser].persona-bottom-6,
7254
+ .persona-launcher-surface > button.persona-bottom-6 {
7255
+ bottom: var(--persona-components-launcher-offset, 1.5rem);
7256
+ }
7257
+ .persona-launcher-surface[data-teaser].persona-top-6,
7258
+ .persona-launcher-surface > button.persona-top-6 {
7259
+ top: var(--persona-components-launcher-offset, 1.5rem);
7260
+ }
7261
+ .persona-launcher-surface[data-teaser].persona-right-6,
7262
+ .persona-launcher-surface > button.persona-right-6 {
7263
+ right: var(--persona-components-launcher-offset, 1.5rem);
7264
+ }
7265
+ .persona-launcher-surface[data-teaser].persona-left-6,
7266
+ .persona-launcher-surface > button.persona-left-6 {
7267
+ left: var(--persona-components-launcher-offset, 1.5rem);
7268
+ }
7269
+
7270
+ /* Center the complete empty greeting group without trapping overflow above
7271
+ the scroll origin. Auto margins collapse naturally when content is tall. */
7272
+ [data-persona-root][data-persona-welcome-layout="centered"][data-persona-conversation-state="empty"]
7273
+ .persona-welcome:not([data-persona-welcome-overlay]) {
7274
+ margin-block: auto;
7275
+ flex-shrink: 0;
7276
+ }
7277
+ [data-persona-root][data-persona-welcome-layout="centered"][data-persona-welcome-anchor="center"][data-persona-conversation-state="empty"]
7278
+ .persona-welcome:not([data-persona-welcome-overlay]) {
7279
+ margin-block: auto 0;
7280
+ }
7281
+ [data-persona-root][data-persona-welcome-layout="centered"]
7282
+ .persona-welcome[data-persona-welcome-overlay] {
7283
+ display: flex;
7284
+ flex-direction: column;
7285
+ justify-content: safe center;
7286
+ }
7287
+ [data-persona-root][data-persona-welcome-layout="centered"][data-persona-welcome-anchor="center"]
7288
+ .persona-welcome[data-persona-welcome-overlay] {
7289
+ justify-content: safe flex-end;
7290
+ }
7291
+ [data-persona-root][data-persona-welcome-layout="centered"][data-persona-welcome-fullscreen="true"]
7292
+ .persona-suggestions[data-variant="card"][data-persona-suggestion-surface="starter"] {
7293
+ grid-template-columns: repeat(2, minmax(0, 1fr));
7294
+ }
7295
+
7296
+ /* An explicit top layout overrides the legacy hero's auto margins. Unspecified
7297
+ V4 heroes retain their original centering in empty and active transcripts. */
7298
+ [data-persona-root][data-persona-welcome-layout-explicit="top"]
7299
+ .persona-welcome:not([data-persona-welcome-overlay]) {
7300
+ margin-block: 0;
7301
+ }
7302
+ [data-persona-root][data-persona-welcome-layout-explicit="top"]
7303
+ .persona-welcome[data-persona-welcome-overlay] {
7304
+ justify-content: flex-start;
7305
+ }
7306
+
7307
+ /* Activity rows share geometry but retain the legacy kind-specific token aliases. */
7308
+ .persona-activity-row {
7309
+ width: 100%; color: var(--persona-muted); background: transparent;
7310
+ --persona-activity-height: var(--persona-components-activity-rowHeight, 32px);
7311
+ --persona-activity-label-size: var(--persona-components-activity-labelSize, 13px);
7312
+ --persona-activity-icon-size: var(--persona-components-activity-iconSize, 16px);
7313
+ --persona-activity-indent: var(--persona-components-activity-indent, 24px);
7314
+ --persona-activity-body-bg: var(--persona-components-activity-bodySurface, transparent);
7315
+ }
7316
+ .persona-tool-bubble.persona-activity-row {
7317
+ --persona-activity-height: var(--persona-components-toolBubble-rowHeight, var(--persona-components-activity-rowHeight, 32px));
7318
+ --persona-activity-label-size: var(--persona-components-toolBubble-labelSize, var(--persona-components-activity-labelSize, 13px));
7319
+ --persona-activity-icon-size: var(--persona-components-toolBubble-iconSize, var(--persona-components-activity-iconSize, 16px));
7320
+ --persona-activity-indent: var(--persona-components-toolBubble-indent, var(--persona-components-activity-indent, 24px));
7321
+ --persona-activity-body-bg: var(--persona-components-toolBubble-bodySurface, var(--persona-components-activity-bodySurface, transparent));
7322
+ }
7323
+ .persona-reasoning-bubble.persona-activity-row {
7324
+ box-shadow: var(--persona-reasoning-bubble-shadow, none);
7325
+ --persona-activity-height: var(--persona-components-reasoningBubble-rowHeight, var(--persona-components-activity-rowHeight, 32px));
7326
+ --persona-activity-label-size: var(--persona-components-reasoningBubble-labelSize, var(--persona-components-activity-labelSize, 13px));
7327
+ --persona-activity-icon-size: var(--persona-components-reasoningBubble-iconSize, var(--persona-components-activity-iconSize, 16px));
7328
+ --persona-activity-indent: var(--persona-components-reasoningBubble-indent, var(--persona-components-activity-indent, 24px));
7329
+ --persona-activity-body-bg: var(--persona-components-reasoningBubble-bodySurface, var(--persona-components-activity-bodySurface, transparent));
7330
+ }
7331
+ .persona-activity-header { display:flex; align-items:center; gap:8px; min-height:var(--persona-activity-height); padding:4px 0; background:transparent; border:0; color:inherit; font:inherit; text-align:left; max-width:100%; cursor:pointer; }
7332
+ /* Activity rows use foreground feedback; card hover surfaces stay on cards. */
7333
+ .persona-activity-header[data-expand-header] { transition:color 0.15s ease; }
7334
+ .persona-activity-header[data-expand-header]:hover,
7335
+ .persona-activity-header[data-expand-header]:focus-visible { color:var(--persona-text); }
7336
+ .persona-activity-header:focus-visible { outline:2px solid var(--persona-primary); outline-offset:3px; border-radius:4px; }
7337
+ .persona-activity-header:not([data-expand-header]) { cursor:default; }
7338
+ .persona-activity-label { min-width:0; }
7339
+ .persona-activity-label, .persona-activity-label > span { font-size:var(--persona-activity-label-size); color:inherit; }
7340
+ .persona-activity-icon { display:inline-flex; flex:none; width:var(--persona-activity-icon-size); height:var(--persona-activity-icon-size); }
7341
+ .persona-activity-icon svg { width:100%; height:100%; }
7342
+ .persona-activity-chevron { margin-left:0; flex:none; }
7343
+ .persona-activity-duration { font-size:12px; white-space:nowrap; }
7344
+ .persona-activity-row > .persona-activity-body { margin-left:var(--persona-activity-indent); padding:var(--persona-components-activity-bodyPadding, 8px 0); border:0; background:var(--persona-activity-body-bg); }
7345
+ .persona-activity-row[data-persona-tool-group] > .persona-activity-body {
7346
+ margin-left:var(--persona-components-activity-groupIndent, var(--persona-activity-indent));
7347
+ padding:var(--persona-components-activity-groupPadding, var(--persona-components-activity-bodyPadding, 8px 0));
7348
+ }
7349
+ .persona-activity-row > .persona-activity-body.persona-space-y-3 > :not([hidden]) ~ :not([hidden]) {
7350
+ margin-top:var(--persona-components-activity-sectionGap, 12px);
7351
+ }
7352
+ .persona-activity-row > [data-persona-tool-group-stack] > .persona-activity-group-child + .persona-activity-group-child {
7353
+ margin-top:var(--persona-components-activity-groupGap, 0px);
7354
+ }
7355
+ .persona-activity-row[data-activity-state="done"] > .persona-activity-header > .persona-activity-icon { color:var(--persona-success, #16a34a); }
7356
+ .persona-activity-row[data-activity-state="error"] > .persona-activity-header > .persona-activity-icon { color:var(--persona-danger, #dc2626); }
7357
+ .persona-activity-row[data-activity-state="denied"] > .persona-activity-header > .persona-activity-icon { color:var(--persona-warning-strong, #b45309); }
7358
+ .persona-activity-row[data-activity-state="awaiting-approval"] > .persona-activity-header > .persona-activity-icon { color:var(--persona-warning, #d97706); }
7359
+ .persona-activity-row[data-activity-state="running"] > .persona-activity-header > .persona-activity-icon { animation:persona-activity-spin 1s linear infinite; }
7360
+ @keyframes persona-activity-spin { to { transform:rotate(360deg); } }
7361
+ @keyframes persona-activity-enter { from { opacity:0; transform:translateY(3px); } to { opacity:1; transform:none; } }
7362
+ .persona-activity-group-child { animation:persona-activity-enter 160ms ease both; animation-delay:var(--persona-activity-stagger, 0ms); }
7363
+ @media (prefers-reduced-motion: reduce) {
7364
+ .persona-activity-row[data-activity-state="running"] > .persona-activity-header > .persona-activity-icon, .persona-activity-group-child { animation:none; }
7365
+ }
7366
+ .persona-activity-row[data-activity-group-shimmer][data-activity-state="running"] > .persona-activity-header > .persona-activity-label {
7367
+ background:linear-gradient(100deg, var(--persona-muted) 25%, var(--persona-text) 50%, var(--persona-muted) 75%);
7368
+ background-size:200% 100%; background-clip:text; -webkit-background-clip:text; color:transparent;
7369
+ animation:persona-activity-shimmer 2s linear infinite;
7370
+ }
7371
+ @keyframes persona-activity-shimmer { to { background-position:-200% 0; } }
7372
+ @media (prefers-reduced-motion: reduce) {
7373
+ .persona-activity-row[data-activity-group-shimmer] > .persona-activity-header > .persona-activity-label { animation:none; background:none; color:var(--persona-muted); }
7374
+ }
7375
+
7376
+ /* Keep the top edge soft only while earlier transcript content is out of view. */
7377
+ .persona-widget-container:has(> .persona-widget-body[data-persona-top-fade="true"]) {
7378
+ background-color: var(--persona-container);
7379
+ }
7380
+ .persona-widget-body[data-persona-top-fade="true"] {
7381
+ -webkit-mask-image: linear-gradient(to bottom, transparent, #000 var(--persona-components-message-topFadeHeight, 18px));
7382
+ mask-image: linear-gradient(to bottom, transparent, #000 var(--persona-components-message-topFadeHeight, 18px));
7383
+ }
7384
+
7385
+ /* Adjust only adjacent transcript activity, preserving normal message gaps. */
7386
+ .persona-widget-messages > [data-message-role]:has(.persona-activity-row) + [data-message-role]:has(.persona-activity-row) {
7387
+ margin-top:calc(var(--persona-components-activity-transcriptGap, var(--persona-transcript-gap)) - var(--persona-transcript-gap));
7388
+ }
7389
+ .persona-widget-messages > [data-message-role]:has(.persona-activity-row) + [data-message-role="assistant"]:not(:has(.persona-activity-row)) {
7390
+ margin-top:calc(var(--persona-components-activity-responseGap, var(--persona-transcript-gap)) - var(--persona-transcript-gap));
7391
+ }
7392
+
7393
+ .persona-reasoning-bubble.persona-activity-row > .persona-activity-body > .persona-whitespace-pre-wrap {
7394
+ font-size:var(--persona-components-activity-bodySize, 12px);
7395
+ line-height:var(--persona-components-activity-bodyLineHeight, inherit);
7396
+ font-weight:var(--persona-components-activity-bodyFontWeight, inherit);
7397
+ }
7398
+
7399
+ /* V5 tool details: a single request/response surface, plain text or structured data. */
7400
+ .persona-tool-detail {
7401
+ position: relative;
7402
+ padding: 10px 12px;
7403
+ border: 0 solid transparent;
7404
+ border-radius: 8px;
7405
+ background: color-mix(in srgb, var(--persona-muted) 5%, transparent);
7406
+ }
7407
+ .persona-tool-detail-label {
7408
+ font-size: 11px;
7409
+ line-height: 1.5;
7410
+ font-weight: 400;
7411
+ color: var(--persona-muted);
7412
+ padding-right: 32px;
7413
+ margin-bottom: 5px;
7414
+ }
7415
+ .persona-tool-detail .persona-tool-detail-value {
7416
+ margin: 0;
7417
+ padding: 0;
7418
+ border: 0;
7419
+ background: transparent;
7420
+ color: var(--persona-text);
7421
+ font-family: inherit;
7422
+ font-weight: 400;
7423
+ font-style: normal;
7424
+ font-size: 13px;
7425
+ line-height: 1.6;
7426
+ white-space: pre-wrap;
7427
+ overflow-wrap: anywhere;
7428
+ max-height: 240px;
7429
+ overflow: auto;
7430
+ }
7431
+ .persona-tool-detail .persona-tool-detail-code { font-family: var(--persona-font-mono, monospace); font-size: 12px; }
7432
+ .persona-tool-detail-copy {
7433
+ position: absolute;
7434
+ top: 8px;
7435
+ right: 8px;
7436
+ display: inline-flex;
7437
+ align-items: center;
7438
+ justify-content: center;
7439
+ width: 28px;
7440
+ height: 28px;
7441
+ padding: 0;
7442
+ border: 0;
7443
+ border-radius: 4px;
7444
+ background: transparent;
7445
+ color: var(--persona-muted);
7446
+ opacity: 0;
7447
+ cursor: pointer;
7448
+ }
7449
+ .persona-tool-detail:hover .persona-tool-detail-copy,
7450
+ .persona-tool-detail:focus-within .persona-tool-detail-copy,
7451
+ .persona-tool-detail-copy[data-copied="true"] { opacity: 1; }
7452
+ .persona-tool-detail-copy:hover { color: var(--persona-text); background: color-mix(in srgb, var(--persona-muted) 12%, transparent); }
7453
+ .persona-tool-detail-copy:focus-visible { outline: 2px solid var(--persona-primary); outline-offset: 2px; }
7454
+ @media (hover: none) { .persona-tool-detail-copy { opacity: 1; } }
7455
+ @media (pointer: coarse) { .persona-tool-detail-copy { width: 40px; height: 40px; top: 2px; right: 2px; } }
@@ -46,6 +46,8 @@ export {
46
46
  STREAM_AND_ARTIFACTS_SUB_GROUP,
47
47
  ARTIFACT_DISPLAY_PREFERENCE_SECTION,
48
48
  ALL_TABS,
49
+ getThemeEditorTabs,
50
+ resolveEditorFieldDefault,
49
51
  scopeSection,
50
52
  findSection,
51
53
  } from './sections';
@@ -16,6 +16,14 @@ describe("theme editor preview demo data", () => {
16
16
  expect(config.suggestionChips?.length).toBeGreaterThan(0);
17
17
  });
18
18
 
19
+ it("keeps an authored v5 defaults opt-in in the preview config", () => {
20
+ const config = buildPreviewConfig({
21
+ config: { future: { v5Defaults: true } },
22
+ });
23
+
24
+ expect(config.future).toEqual({ v5Defaults: true });
25
+ });
26
+
19
27
  it("seeds rich agent suggestions in the follow-ups scene", () => {
20
28
  const config = buildPreviewConfig({ scene: "follow-ups" });
21
29
  const messages = config.initialMessages ?? [];
@@ -7,7 +7,7 @@
7
7
  import type { AgentWidgetConfig } from '../types';
8
8
  import type { AgentWidgetMessage } from '../types';
9
9
  import { createTheme } from '../utils/theme';
10
- import { DEFAULT_WIDGET_CONFIG } from '../defaults';
10
+ import { resolveDefaults } from '../defaults';
11
11
 
12
12
  // ─── Constants ──────────────────────────────────────────────────
13
13
 
@@ -693,9 +693,13 @@ function buildPreviewBaseConfig(
693
693
  options: PreviewConfigOptions,
694
694
  shellModeOverride?: 'light' | 'dark'
695
695
  ): AgentWidgetConfig {
696
- const theme = options.theme ? createTheme(options.theme, { validate: false }) : createTheme();
696
+ const defaults = resolveDefaults(options.config);
697
+ const future = options.config?.future;
698
+ const theme = options.theme
699
+ ? createTheme(options.theme, { validate: false, future })
700
+ : createTheme(undefined, { future });
697
701
  return {
698
- ...DEFAULT_WIDGET_CONFIG,
702
+ ...defaults,
699
703
  ...options.config,
700
704
  theme,
701
705
  darkTheme: options.darkTheme,