@runtypelabs/persona 2.0.0 → 2.2.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/dist/widget.css CHANGED
@@ -927,6 +927,7 @@
927
927
 
928
928
  .persona-widget-composer {
929
929
  border-radius: var(--persona-input-radius, var(--persona-radius-lg, 0.5rem));
930
+ box-shadow: var(--persona-composer-shadow, none);
930
931
  }
931
932
 
932
933
  .persona-form-grid {
@@ -1168,6 +1169,18 @@
1168
1169
  box-shadow: var(--persona-message-assistant-shadow, 0 1px 2px 0 rgb(0 0 0 / 0.05));
1169
1170
  }
1170
1171
 
1172
+ #persona-root .vanilla-message-user-bubble.persona-shadow-sm {
1173
+ box-shadow: var(--persona-message-user-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
1174
+ }
1175
+
1176
+ #persona-root .vanilla-tool-bubble.persona-shadow-sm {
1177
+ box-shadow: var(--persona-tool-bubble-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
1178
+ }
1179
+
1180
+ #persona-root .vanilla-reasoning-bubble.persona-shadow-sm {
1181
+ box-shadow: var(--persona-reasoning-bubble-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
1182
+ }
1183
+
1171
1184
  /* Artifact markdown (no .vanilla-message-bubble wrapper) */
1172
1185
  #persona-root .persona-markdown-bubble {
1173
1186
  color: var(--persona-text, #111827);
@@ -2051,9 +2064,9 @@
2051
2064
  display: inline-flex;
2052
2065
  align-items: center;
2053
2066
  justify-content: center;
2054
- padding: 0.25rem;
2055
- border-radius: var(--persona-radius-md, 0.375rem);
2056
- border: 1px solid var(--persona-border, #e5e7eb);
2067
+ padding: var(--persona-artifact-toolbar-icon-padding, 0.25rem);
2068
+ border-radius: var(--persona-artifact-toolbar-icon-radius, var(--persona-radius-md, 0.375rem));
2069
+ border: var(--persona-artifact-toolbar-icon-border, 1px solid var(--persona-border, #e5e7eb));
2057
2070
  background: var(--persona-surface, #ffffff);
2058
2071
  color: var(--persona-artifact-doc-toolbar-icon-color, var(--persona-text, #111827));
2059
2072
  cursor: pointer;
@@ -2061,7 +2074,8 @@
2061
2074
  }
2062
2075
 
2063
2076
  #persona-root .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn:hover {
2064
- background: var(--persona-container, #f3f4f6);
2077
+ color: var(--persona-artifact-toolbar-icon-hover-color, inherit);
2078
+ background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-container, #f3f4f6));
2065
2079
  }
2066
2080
 
2067
2081
  #persona-root .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn[aria-pressed="true"] {
@@ -2073,24 +2087,53 @@
2073
2087
  display: inline-flex;
2074
2088
  align-items: center;
2075
2089
  gap: 0.35rem;
2076
- padding: 0.25rem 0.5rem;
2077
- border-radius: var(--persona-radius-md, 0.375rem);
2078
- border: 1px solid var(--persona-border, #e5e7eb);
2079
- background: var(--persona-surface, #ffffff);
2080
- color: var(--persona-artifact-doc-toolbar-icon-color, var(--persona-text, #111827));
2090
+ padding: var(--persona-artifact-toolbar-copy-padding, 0.25rem 0.5rem);
2091
+ border-radius: var(--persona-artifact-toolbar-copy-radius, var(--persona-radius-md, 0.375rem));
2092
+ border: var(--persona-artifact-toolbar-copy-border, 1px solid var(--persona-border, #e5e7eb));
2093
+ background: var(--persona-artifact-toolbar-copy-bg, var(--persona-surface, #ffffff));
2094
+ color: var(--persona-artifact-toolbar-copy-color, var(--persona-artifact-doc-toolbar-icon-color, var(--persona-text, #111827)));
2081
2095
  cursor: pointer;
2082
2096
  font-size: 0.75rem;
2083
2097
  line-height: 1.25;
2084
2098
  }
2085
2099
 
2086
2100
  #persona-root .persona-artifact-toolbar-document .persona-artifact-doc-copy-btn:hover {
2087
- background: var(--persona-container, #f3f4f6);
2101
+ background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-container, #f3f4f6));
2088
2102
  }
2089
2103
 
2090
2104
  #persona-root .persona-artifact-toolbar-document .persona-artifact-doc-copy-label {
2091
2105
  font-weight: 500;
2092
2106
  }
2093
2107
 
2108
+ /* Copy menu dropdown theming */
2109
+ #persona-root .persona-artifact-doc-copy-menu {
2110
+ background: var(--persona-artifact-toolbar-copy-menu-bg, var(--persona-surface, #fff));
2111
+ border: var(--persona-artifact-toolbar-copy-menu-border, 1px solid var(--persona-border, #e5e7eb));
2112
+ box-shadow: var(--persona-artifact-toolbar-copy-menu-shadow, 0 4px 6px -1px rgba(0,0,0,.1));
2113
+ border-radius: var(--persona-artifact-toolbar-copy-menu-radius, 0.375rem);
2114
+ }
2115
+
2116
+ #persona-root .persona-artifact-doc-copy-menu button:hover {
2117
+ background: var(--persona-artifact-toolbar-copy-menu-item-hover-bg, var(--persona-container, #f3f4f6));
2118
+ }
2119
+
2120
+ /* Artifact tab theming */
2121
+ #persona-root .persona-artifact-tab {
2122
+ background: var(--persona-artifact-tab-bg, transparent);
2123
+ border-radius: var(--persona-artifact-tab-radius, 0.5rem);
2124
+ color: var(--persona-artifact-tab-color, inherit);
2125
+ }
2126
+
2127
+ #persona-root .persona-artifact-tab.persona-bg-persona-container {
2128
+ background: var(--persona-artifact-tab-active-bg, var(--persona-container, #f3f4f6));
2129
+ border-color: var(--persona-artifact-tab-active-border, var(--persona-border, #e5e7eb));
2130
+ }
2131
+
2132
+ /* Artifact toolbar background theming */
2133
+ #persona-root .persona-artifact-toolbar {
2134
+ background: var(--persona-artifact-toolbar-bg, var(--persona-surface, #fff));
2135
+ }
2136
+
2094
2137
  /* Draggable split handle (desktop split only; hidden in drawer / narrow host / small viewport) */
2095
2138
  #persona-root .persona-artifact-split-handle {
2096
2139
  width: 6px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/persona",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -47,6 +47,7 @@
47
47
  "engines": {
48
48
  "node": ">=18.17.0"
49
49
  },
50
+ "author": "Runtype",
50
51
  "license": "MIT",
51
52
  "keywords": [
52
53
  "ai",
package/src/client.ts CHANGED
@@ -45,7 +45,6 @@ const DEFAULT_CLIENT_API_BASE = "https://api.runtype.com";
45
45
  * Check if a message has valid (non-empty) content for sending to the API.
46
46
  * Filters out messages with empty content that would cause validation errors.
47
47
  *
48
- * @see https://github.com/anthropics/claude-code/issues/XXX - Empty assistant messages from failed requests
49
48
  */
50
49
  const hasValidContent = (message: AgentWidgetMessage): boolean => {
51
50
  // Check contentParts (multi-modal content)
@@ -127,6 +127,7 @@ export function createArtifactPane(
127
127
  "aside",
128
128
  "persona-artifact-pane persona-flex persona-flex-col persona-min-h-0 persona-min-w-0 persona-bg-persona-surface persona-text-persona-primary persona-border-l persona-border-persona-border"
129
129
  );
130
+ shell.setAttribute("data-persona-theme-zone", "artifact-pane");
130
131
  if (documentChrome) {
131
132
  shell.classList.add("persona-artifact-pane-document");
132
133
  }
@@ -135,6 +136,7 @@ export function createArtifactPane(
135
136
  "div",
136
137
  "persona-artifact-toolbar persona-flex persona-items-center persona-justify-between persona-gap-2 persona-px-2 persona-py-2 persona-border-b persona-border-persona-border persona-shrink-0"
137
138
  );
139
+ toolbar.setAttribute("data-persona-theme-zone", "artifact-toolbar");
138
140
  if (documentChrome) {
139
141
  toolbar.classList.add("persona-artifact-toolbar-document");
140
142
  }
@@ -68,6 +68,7 @@ export const buildComposer = (context: ComposerBuildContext): ComposerElements =
68
68
  "div",
69
69
  "persona-widget-footer persona-border-t-persona-divider persona-bg-persona-surface persona-px-6 persona-py-4"
70
70
  );
71
+ footer.setAttribute("data-persona-theme-zone", "composer");
71
72
 
72
73
  const suggestions = createElement(
73
74
  "div",
@@ -31,6 +31,7 @@ export const buildHeader = (context: HeaderBuildContext): HeaderElements => {
31
31
  "div",
32
32
  "persona-widget-header persona-flex persona-items-center persona-gap-3 persona-px-6 persona-py-5"
33
33
  );
34
+ header.setAttribute("data-persona-theme-zone", "header");
34
35
  header.style.backgroundColor = 'var(--persona-header-bg, var(--persona-surface, #ffffff))';
35
36
  header.style.borderBottomWidth = '1px';
36
37
  header.style.borderBottomStyle = 'solid';
@@ -24,12 +24,32 @@ export type HeaderLayoutRenderer = (context: HeaderLayoutContext) => HeaderEleme
24
24
  * Full header with icon, title, subtitle, clear chat, and close button
25
25
  */
26
26
  export const buildDefaultHeader: HeaderLayoutRenderer = (context) => {
27
- return buildHeader({
27
+ const elements = buildHeader({
28
28
  config: context.config,
29
29
  showClose: context.showClose,
30
30
  onClose: context.onClose,
31
31
  onClearChat: context.onClearChat
32
32
  });
33
+
34
+ // Make the title/subtitle area clickable when onTitleClick is provided
35
+ const onTitleClick = context.layoutHeaderConfig?.onTitleClick;
36
+ if (onTitleClick) {
37
+ const headerCopy = elements.headerTitle.parentElement;
38
+ if (headerCopy) {
39
+ headerCopy.style.cursor = "pointer";
40
+ headerCopy.setAttribute("role", "button");
41
+ headerCopy.setAttribute("tabindex", "0");
42
+ headerCopy.addEventListener("click", () => onTitleClick());
43
+ headerCopy.addEventListener("keydown", (e) => {
44
+ if (e.key === "Enter" || e.key === " ") {
45
+ e.preventDefault();
46
+ onTitleClick();
47
+ }
48
+ });
49
+ }
50
+ }
51
+
52
+ return elements;
33
53
  };
34
54
 
35
55
  /**
@@ -68,6 +88,7 @@ export const buildMinimalHeader: HeaderLayoutRenderer = (context) => {
68
88
  "div",
69
89
  "persona-flex persona-items-center persona-justify-between persona-bg-persona-surface persona-px-6 persona-py-4 persona-border-b-persona-divider"
70
90
  );
91
+ header.setAttribute("data-persona-theme-zone", "header");
71
92
 
72
93
  const titleRow = createElement(
73
94
  "div",
@@ -85,6 +106,25 @@ export const buildMinimalHeader: HeaderLayoutRenderer = (context) => {
85
106
  layoutHeaderConfig?.onAction ?? onHeaderAction
86
107
  );
87
108
 
109
+ // Make title row clickable when onTitleClick is provided
110
+ if (layoutHeaderConfig?.onTitleClick) {
111
+ titleRow.style.cursor = "pointer";
112
+ titleRow.setAttribute("role", "button");
113
+ titleRow.setAttribute("tabindex", "0");
114
+ const handleTitleClick = layoutHeaderConfig.onTitleClick;
115
+ titleRow.addEventListener("click", (e) => {
116
+ // Skip if the click was on a trailing action button
117
+ if ((e.target as HTMLElement).closest("button")) return;
118
+ handleTitleClick();
119
+ });
120
+ titleRow.addEventListener("keydown", (e) => {
121
+ if (e.key === "Enter" || e.key === " ") {
122
+ e.preventDefault();
123
+ handleTitleClick();
124
+ }
125
+ });
126
+ }
127
+
88
128
  header.appendChild(titleRow);
89
129
 
90
130
  // Close button
@@ -470,6 +470,8 @@ export const createStandardBubble = (
470
470
  bubble.id = `bubble-${message.id}`;
471
471
  bubble.setAttribute("data-message-id", message.id);
472
472
 
473
+ bubble.setAttribute("data-persona-theme-zone", message.role === "user" ? "user-message" : "assistant-message");
474
+
473
475
  // Apply component-level color overrides via CSS variables
474
476
  if (message.role === "user") {
475
477
  bubble.style.backgroundColor = 'var(--persona-message-user-bg, var(--persona-accent))';
@@ -116,6 +116,7 @@ export const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelE
116
116
  "div",
117
117
  "persona-widget-container persona-flex persona-h-full persona-w-full persona-flex-1 persona-min-h-0 persona-flex-col persona-bg-persona-surface persona-text-persona-primary persona-rounded-2xl persona-overflow-hidden persona-border persona-border-persona-border"
118
118
  );
119
+ container.setAttribute("data-persona-theme-zone", "container");
119
120
 
120
121
  // Build header using layout config if available, otherwise use standard builder
121
122
  const headerLayoutConfig = config?.layout?.header;
@@ -130,6 +131,7 @@ export const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelE
130
131
  "persona-widget-body persona-flex persona-flex-1 persona-min-h-0 persona-flex-col persona-gap-6 persona-overflow-y-auto persona-bg-persona-container persona-px-6 persona-py-6"
131
132
  );
132
133
  body.id = "persona-scroll-container";
134
+ body.setAttribute("data-persona-theme-zone", "messages");
133
135
 
134
136
  const introCard = createElement(
135
137
  "div",
package/src/index.ts CHANGED
@@ -189,8 +189,10 @@ export {
189
189
  DEFAULT_PALETTE,
190
190
  DEFAULT_SEMANTIC,
191
191
  DEFAULT_COMPONENTS,
192
- validateTheme
192
+ validateTheme,
193
+ THEME_ZONES
193
194
  } from "./utils/tokens";
195
+ export type { ThemeZone } from "./utils/tokens";
194
196
  export {
195
197
  accessibilityPlugin,
196
198
  animationsPlugin,
@@ -219,6 +221,9 @@ export type {
219
221
  SemanticSpacing,
220
222
  SemanticTypography,
221
223
  ComponentTokens,
224
+ ArtifactToolbarTokens,
225
+ ArtifactTabTokens,
226
+ ArtifactPaneTokens,
222
227
  ThemeValidationResult,
223
228
  ThemeValidationError
224
229
  } from "./types/theme";
@@ -245,6 +250,14 @@ export {
245
250
  DEFAULT_DARK_THEME,
246
251
  mergeWithDefaults
247
252
  } from "./defaults";
253
+ export {
254
+ PRESETS,
255
+ getPreset,
256
+ PRESET_SHOP,
257
+ PRESET_MINIMAL,
258
+ PRESET_FULLSCREEN
259
+ } from "./presets";
260
+ export type { WidgetPreset } from "./presets";
248
261
 
249
262
  // Layout system exports
250
263
  export {
package/src/presets.ts ADDED
@@ -0,0 +1,127 @@
1
+ import type { AgentWidgetConfig } from './types';
2
+
3
+ /**
4
+ * A named preset containing partial widget configuration.
5
+ * Apply with: `createAgentExperience(el, { ...PRESET_SHOP.config, apiUrl: '...' })`
6
+ * or via IIFE: `{ ...AgentWidget.PRESETS.shop.config, apiUrl: '...' }`
7
+ */
8
+ export interface WidgetPreset {
9
+ id: string;
10
+ label: string;
11
+ config: Partial<AgentWidgetConfig>;
12
+ }
13
+
14
+ /**
15
+ * Shopping / e-commerce preset.
16
+ * Dark header, rounded launchers, shopping-oriented copy.
17
+ */
18
+ export const PRESET_SHOP: WidgetPreset = {
19
+ id: 'shop',
20
+ label: 'Shopping Assistant',
21
+ config: {
22
+ theme: {
23
+ primary: '#111827',
24
+ accent: '#1d4ed8',
25
+ surface: '#ffffff',
26
+ muted: '#6b7280',
27
+ container: '#f8fafc',
28
+ border: '#f1f5f9',
29
+ divider: '#f1f5f9',
30
+ messageBorder: '#f1f5f9',
31
+ inputBackground: '#ffffff',
32
+ callToAction: '#000000',
33
+ callToActionBackground: '#ffffff',
34
+ sendButtonBackgroundColor: '#111827',
35
+ sendButtonTextColor: '#ffffff',
36
+ radiusSm: '0.75rem',
37
+ radiusMd: '1rem',
38
+ radiusLg: '1.5rem',
39
+ launcherRadius: '9999px',
40
+ buttonRadius: '9999px',
41
+ },
42
+ launcher: {
43
+ title: 'Shopping Assistant',
44
+ subtitle: 'Here to help you find what you need',
45
+ agentIconText: '🛍️',
46
+ position: 'bottom-right',
47
+ width: 'min(400px, calc(100vw - 24px))',
48
+ },
49
+ copy: {
50
+ welcomeTitle: 'Welcome to our shop!',
51
+ welcomeSubtitle: 'I can help you find products and answer questions',
52
+ inputPlaceholder: 'Ask me anything...',
53
+ sendButtonLabel: 'Send',
54
+ },
55
+ suggestionChips: [
56
+ 'What can you help me with?',
57
+ 'Tell me about your features',
58
+ 'How does this work?',
59
+ ],
60
+ },
61
+ };
62
+
63
+ /**
64
+ * Minimal preset.
65
+ * Stripped-down header, no launcher button, suitable for inline embeds.
66
+ */
67
+ export const PRESET_MINIMAL: WidgetPreset = {
68
+ id: 'minimal',
69
+ label: 'Minimal',
70
+ config: {
71
+ launcher: {
72
+ enabled: false,
73
+ fullHeight: true,
74
+ },
75
+ layout: {
76
+ header: {
77
+ layout: 'minimal',
78
+ showCloseButton: false,
79
+ },
80
+ messages: {
81
+ layout: 'minimal',
82
+ },
83
+ },
84
+ theme: {
85
+ panelBorderRadius: '0',
86
+ panelShadow: 'none',
87
+ },
88
+ },
89
+ };
90
+
91
+ /**
92
+ * Fullscreen assistant preset.
93
+ * No launcher, content-max-width constrained, minimal header.
94
+ */
95
+ export const PRESET_FULLSCREEN: WidgetPreset = {
96
+ id: 'fullscreen',
97
+ label: 'Fullscreen Assistant',
98
+ config: {
99
+ launcher: {
100
+ enabled: false,
101
+ fullHeight: true,
102
+ },
103
+ layout: {
104
+ header: {
105
+ layout: 'minimal',
106
+ showCloseButton: false,
107
+ },
108
+ contentMaxWidth: '72ch',
109
+ },
110
+ theme: {
111
+ panelBorderRadius: '0',
112
+ panelShadow: 'none',
113
+ },
114
+ },
115
+ };
116
+
117
+ /** All named presets keyed by ID. */
118
+ export const PRESETS: Record<string, WidgetPreset> = {
119
+ shop: PRESET_SHOP,
120
+ minimal: PRESET_MINIMAL,
121
+ fullscreen: PRESET_FULLSCREEN,
122
+ };
123
+
124
+ /** Look up a preset by ID. */
125
+ export function getPreset(id: string): WidgetPreset | undefined {
126
+ return PRESETS[id];
127
+ }
@@ -927,6 +927,7 @@
927
927
 
928
928
  .persona-widget-composer {
929
929
  border-radius: var(--persona-input-radius, var(--persona-radius-lg, 0.5rem));
930
+ box-shadow: var(--persona-composer-shadow, none);
930
931
  }
931
932
 
932
933
  .persona-form-grid {
@@ -1168,6 +1169,18 @@
1168
1169
  box-shadow: var(--persona-message-assistant-shadow, 0 1px 2px 0 rgb(0 0 0 / 0.05));
1169
1170
  }
1170
1171
 
1172
+ #persona-root .vanilla-message-user-bubble.persona-shadow-sm {
1173
+ box-shadow: var(--persona-message-user-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
1174
+ }
1175
+
1176
+ #persona-root .vanilla-tool-bubble.persona-shadow-sm {
1177
+ box-shadow: var(--persona-tool-bubble-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
1178
+ }
1179
+
1180
+ #persona-root .vanilla-reasoning-bubble.persona-shadow-sm {
1181
+ box-shadow: var(--persona-reasoning-bubble-shadow, 0 5px 15px rgba(15, 23, 42, 0.08));
1182
+ }
1183
+
1171
1184
  /* Artifact markdown (no .vanilla-message-bubble wrapper) */
1172
1185
  #persona-root .persona-markdown-bubble {
1173
1186
  color: var(--persona-text, #111827);
@@ -2051,9 +2064,9 @@
2051
2064
  display: inline-flex;
2052
2065
  align-items: center;
2053
2066
  justify-content: center;
2054
- padding: 0.25rem;
2055
- border-radius: var(--persona-radius-md, 0.375rem);
2056
- border: 1px solid var(--persona-border, #e5e7eb);
2067
+ padding: var(--persona-artifact-toolbar-icon-padding, 0.25rem);
2068
+ border-radius: var(--persona-artifact-toolbar-icon-radius, var(--persona-radius-md, 0.375rem));
2069
+ border: var(--persona-artifact-toolbar-icon-border, 1px solid var(--persona-border, #e5e7eb));
2057
2070
  background: var(--persona-surface, #ffffff);
2058
2071
  color: var(--persona-artifact-doc-toolbar-icon-color, var(--persona-text, #111827));
2059
2072
  cursor: pointer;
@@ -2061,7 +2074,8 @@
2061
2074
  }
2062
2075
 
2063
2076
  #persona-root .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn:hover {
2064
- background: var(--persona-container, #f3f4f6);
2077
+ color: var(--persona-artifact-toolbar-icon-hover-color, inherit);
2078
+ background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-container, #f3f4f6));
2065
2079
  }
2066
2080
 
2067
2081
  #persona-root .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn[aria-pressed="true"] {
@@ -2073,24 +2087,53 @@
2073
2087
  display: inline-flex;
2074
2088
  align-items: center;
2075
2089
  gap: 0.35rem;
2076
- padding: 0.25rem 0.5rem;
2077
- border-radius: var(--persona-radius-md, 0.375rem);
2078
- border: 1px solid var(--persona-border, #e5e7eb);
2079
- background: var(--persona-surface, #ffffff);
2080
- color: var(--persona-artifact-doc-toolbar-icon-color, var(--persona-text, #111827));
2090
+ padding: var(--persona-artifact-toolbar-copy-padding, 0.25rem 0.5rem);
2091
+ border-radius: var(--persona-artifact-toolbar-copy-radius, var(--persona-radius-md, 0.375rem));
2092
+ border: var(--persona-artifact-toolbar-copy-border, 1px solid var(--persona-border, #e5e7eb));
2093
+ background: var(--persona-artifact-toolbar-copy-bg, var(--persona-surface, #ffffff));
2094
+ color: var(--persona-artifact-toolbar-copy-color, var(--persona-artifact-doc-toolbar-icon-color, var(--persona-text, #111827)));
2081
2095
  cursor: pointer;
2082
2096
  font-size: 0.75rem;
2083
2097
  line-height: 1.25;
2084
2098
  }
2085
2099
 
2086
2100
  #persona-root .persona-artifact-toolbar-document .persona-artifact-doc-copy-btn:hover {
2087
- background: var(--persona-container, #f3f4f6);
2101
+ background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-container, #f3f4f6));
2088
2102
  }
2089
2103
 
2090
2104
  #persona-root .persona-artifact-toolbar-document .persona-artifact-doc-copy-label {
2091
2105
  font-weight: 500;
2092
2106
  }
2093
2107
 
2108
+ /* Copy menu dropdown theming */
2109
+ #persona-root .persona-artifact-doc-copy-menu {
2110
+ background: var(--persona-artifact-toolbar-copy-menu-bg, var(--persona-surface, #fff));
2111
+ border: var(--persona-artifact-toolbar-copy-menu-border, 1px solid var(--persona-border, #e5e7eb));
2112
+ box-shadow: var(--persona-artifact-toolbar-copy-menu-shadow, 0 4px 6px -1px rgba(0,0,0,.1));
2113
+ border-radius: var(--persona-artifact-toolbar-copy-menu-radius, 0.375rem);
2114
+ }
2115
+
2116
+ #persona-root .persona-artifact-doc-copy-menu button:hover {
2117
+ background: var(--persona-artifact-toolbar-copy-menu-item-hover-bg, var(--persona-container, #f3f4f6));
2118
+ }
2119
+
2120
+ /* Artifact tab theming */
2121
+ #persona-root .persona-artifact-tab {
2122
+ background: var(--persona-artifact-tab-bg, transparent);
2123
+ border-radius: var(--persona-artifact-tab-radius, 0.5rem);
2124
+ color: var(--persona-artifact-tab-color, inherit);
2125
+ }
2126
+
2127
+ #persona-root .persona-artifact-tab.persona-bg-persona-container {
2128
+ background: var(--persona-artifact-tab-active-bg, var(--persona-container, #f3f4f6));
2129
+ border-color: var(--persona-artifact-tab-active-border, var(--persona-border, #e5e7eb));
2130
+ }
2131
+
2132
+ /* Artifact toolbar background theming */
2133
+ #persona-root .persona-artifact-toolbar {
2134
+ background: var(--persona-artifact-toolbar-bg, var(--persona-surface, #fff));
2135
+ }
2136
+
2094
2137
  /* Draggable split handle (desktop split only; hidden in drawer / narrow host / small viewport) */
2095
2138
  #persona-root .persona-artifact-split-handle {
2096
2139
  width: 6px;
@@ -206,6 +206,8 @@ export interface MessageTokens {
206
206
  background: TokenReference<'color'>;
207
207
  text: TokenReference<'color'>;
208
208
  borderRadius: TokenReference<'radius'>;
209
+ /** User bubble box-shadow (token ref or raw CSS, e.g. `none`). */
210
+ shadow?: string;
209
211
  };
210
212
  assistant: {
211
213
  background: TokenReference<'color'>;
@@ -279,6 +281,58 @@ export interface AttachmentTokens {
279
281
  };
280
282
  }
281
283
 
284
+ /** Tool-call row chrome (collapsible tool bubbles). */
285
+ export interface ToolBubbleTokens {
286
+ /** Box-shadow for tool bubbles (token ref or raw CSS, e.g. `none`). */
287
+ shadow: string;
288
+ }
289
+
290
+ /** Reasoning / “thinking” row chrome. */
291
+ export interface ReasoningBubbleTokens {
292
+ shadow: string;
293
+ }
294
+
295
+ /** Composer (message input) chrome. */
296
+ export interface ComposerChromeTokens {
297
+ /** Box-shadow on the composer form (raw CSS, e.g. `none`). */
298
+ shadow: string;
299
+ }
300
+
301
+ /** Artifact toolbar chrome. */
302
+ export interface ArtifactToolbarTokens {
303
+ iconHoverColor?: string;
304
+ iconHoverBackground?: string;
305
+ iconPadding?: string;
306
+ iconBorderRadius?: string;
307
+ iconBorder?: string;
308
+ toggleGroupGap?: string;
309
+ toggleBorderRadius?: string;
310
+ copyBackground?: string;
311
+ copyBorder?: string;
312
+ copyColor?: string;
313
+ copyBorderRadius?: string;
314
+ copyPadding?: string;
315
+ copyMenuBackground?: string;
316
+ copyMenuBorder?: string;
317
+ copyMenuShadow?: string;
318
+ copyMenuBorderRadius?: string;
319
+ copyMenuItemHoverBackground?: string;
320
+ }
321
+
322
+ /** Artifact tab strip chrome. */
323
+ export interface ArtifactTabTokens {
324
+ background?: string;
325
+ activeBackground?: string;
326
+ activeBorder?: string;
327
+ borderRadius?: string;
328
+ textColor?: string;
329
+ }
330
+
331
+ /** Artifact pane chrome. */
332
+ export interface ArtifactPaneTokens {
333
+ toolbarBackground?: string;
334
+ }
335
+
282
336
  export interface ComponentTokens {
283
337
  button: ButtonTokens;
284
338
  input: InputTokens;
@@ -291,6 +345,15 @@ export interface ComponentTokens {
291
345
  voice: VoiceTokens;
292
346
  approval: ApprovalTokens;
293
347
  attachment: AttachmentTokens;
348
+ toolBubble: ToolBubbleTokens;
349
+ reasoningBubble: ReasoningBubbleTokens;
350
+ composer: ComposerChromeTokens;
351
+ /** Artifact toolbar, tab strip, and pane chrome. */
352
+ artifact?: {
353
+ toolbar?: ArtifactToolbarTokens;
354
+ tab?: ArtifactTabTokens;
355
+ pane?: ArtifactPaneTokens;
356
+ };
294
357
  }
295
358
 
296
359
  export interface PaletteExtras {
package/src/types.ts CHANGED
@@ -525,6 +525,14 @@ export type AgentWidgetArtifactsFeature = {
525
525
  allowedTypes?: PersonaArtifactKind[];
526
526
  /** Split / drawer dimensions and launcher widen behavior */
527
527
  layout?: AgentWidgetArtifactsLayoutConfig;
528
+ /**
529
+ * Called when an artifact card action is triggered (open, download).
530
+ * Return `true` to prevent the default behavior.
531
+ */
532
+ onArtifactAction?: (action: {
533
+ type: 'open' | 'download';
534
+ artifactId: string;
535
+ }) => boolean | void;
528
536
  };
529
537
 
530
538
  export type AgentWidgetFeatureFlags = {
@@ -714,6 +722,28 @@ export type AgentWidgetTheme = {
714
722
  * @default "16px"
715
723
  */
716
724
  panelBorderRadius?: string;
725
+ /**
726
+ * Box-shadow for user message bubbles (bubble message layout).
727
+ * @example "none" | "0 1px 2px rgba(0,0,0,0.05)"
728
+ */
729
+ messageUserShadow?: string;
730
+ /**
731
+ * Box-shadow for assistant message bubbles (bubble message layout).
732
+ * Overrides the default subtle assistant shadow when set.
733
+ */
734
+ messageAssistantShadow?: string;
735
+ /**
736
+ * Box-shadow for tool-call / function-call rows.
737
+ */
738
+ toolBubbleShadow?: string;
739
+ /**
740
+ * Box-shadow for reasoning (“thinking”) rows.
741
+ */
742
+ reasoningBubbleShadow?: string;
743
+ /**
744
+ * Box-shadow on the composer (input) container.
745
+ */
746
+ composerShadow?: string;
717
747
  };
718
748
 
719
749
  export type AgentWidgetDockConfig = {
@@ -1141,6 +1171,8 @@ export type AgentWidgetApprovalConfig = {
1141
1171
  };
1142
1172
 
1143
1173
  export type AgentWidgetToolCallConfig = {
1174
+ /** Box-shadow for tool-call bubbles; overrides `theme.toolBubbleShadow` when set. */
1175
+ shadow?: string;
1144
1176
  backgroundColor?: string;
1145
1177
  borderColor?: string;
1146
1178
  borderWidth?: string;
@@ -1428,6 +1460,12 @@ export type AgentWidgetHeaderLayoutConfig = {
1428
1460
  trailingActions?: AgentWidgetHeaderTrailingAction[];
1429
1461
  /** Called when a `trailingActions` button is clicked. */
1430
1462
  onAction?: (actionId: string) => void;
1463
+ /**
1464
+ * Called when the header title row is clicked.
1465
+ * Useful for dropdown menus or navigation triggered from the header.
1466
+ * When set, the title row becomes visually interactive (cursor: pointer).
1467
+ */
1468
+ onTitleClick?: () => void;
1431
1469
  };
1432
1470
 
1433
1471
  /**