@runtypelabs/persona 4.32.0 → 4.32.1

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 (46) hide show
  1. package/dist/activity-ui.cjs +1 -1
  2. package/dist/activity-ui.js +1 -1
  3. package/dist/{chunk-BrptyKSq.js → chunk-B3l7TOOv.js} +1 -1
  4. package/dist/{chunk-CkE90FtA.js → chunk-B4ecQzeK.js} +1 -1
  5. package/dist/{chunk-BWCo3rCm.js → chunk-B_H0l3u2.js} +1 -1
  6. package/dist/{chunk-MdlQetfC.js → chunk-BbXc58OA.js} +1 -1
  7. package/dist/chunk-BkOYJ11B.js +1 -0
  8. package/dist/{chunk-DE0cSmku.js → chunk-BpjtKnGI.js} +1 -1
  9. package/dist/{chunk-CzZBmTLd.js → chunk-C9uOhfk5.js} +1 -1
  10. package/dist/{chunk-CHAwCqpk.js → chunk-CdpVP6LZ.js} +1 -1
  11. package/dist/{chunk-DZg9bFK6.js → chunk-CumoXK3u.js} +1 -1
  12. package/dist/{chunk-FEzOcZof.js → chunk-DM-OJt5q.js} +1 -1
  13. package/dist/{chunk-Cb7qy0c7.js → chunk-DNdeIFJ6.js} +1 -1
  14. package/dist/{chunk-VaT6xry9.js → chunk-DTGvDx8o.js} +1 -1
  15. package/dist/{chunk-ezlpsYGk.js → chunk-DVZ8ri1z.js} +1 -1
  16. package/dist/{chunk-DhedQnek.js → chunk-DgZZqy14.js} +1 -1
  17. package/dist/{chunk-C2IbR39s.js → chunk-UWMfWW2a.js} +1 -1
  18. package/dist/client-dispatch.js +1 -1
  19. package/dist/client-history.js +1 -1
  20. package/dist/codegen.cjs +1 -1
  21. package/dist/codegen.js +1 -1
  22. package/dist/index.cjs +3 -3
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.global.js +1 -1
  25. package/dist/index.global.js.map +1 -1
  26. package/dist/index.js +3 -3
  27. package/dist/index.js.map +1 -1
  28. package/dist/launcher.global.js +1 -1
  29. package/dist/launcher.global.js.map +1 -1
  30. package/dist/theme-editor-preview.cjs +3 -3
  31. package/dist/theme-editor-preview.js +3 -3
  32. package/dist/theme-editor.cjs +1 -1
  33. package/dist/theme-editor.js +1 -1
  34. package/dist/theme-reference.cjs +1 -1
  35. package/dist/theme-reference.js +1 -1
  36. package/dist/voice-runtime.cjs +1 -1
  37. package/dist/voice-runtime.js +1 -1
  38. package/dist/widget.css +1 -1
  39. package/package.json +1 -1
  40. package/src/styles/widget.css +12 -0
  41. package/src/utils/brand.test.ts +50 -0
  42. package/src/utils/brand.ts +6 -2
  43. package/src/utils/tokens.ts +2 -1
  44. package/src/utils/tool-activity-summary.test.ts +8 -0
  45. package/src/utils/tool-activity-summary.ts +2 -1
  46. package/dist/chunk-DDAwu89-.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/persona",
3
- "version": "4.32.0",
3
+ "version": "4.32.1",
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",
@@ -6993,6 +6993,18 @@
6993
6993
  .persona-turn-activity-body .persona-tool-bubble.persona-activity-row {
6994
6994
  --persona-activity-height: var(--persona-components-toolBubble-rowHeight, var(--persona-components-activity-rowHeight, 28px));
6995
6995
  }
6996
+ [data-persona-defaults="v5"] [data-persona-tool-group-stack] .persona-tool-bubble.persona-activity-row {
6997
+ --persona-activity-height: var(--persona-components-toolBubble-rowHeight, var(--persona-components-activity-rowHeight, 24px));
6998
+ }
6999
+ [data-persona-defaults="v5"] [data-persona-tool-group-stack] .persona-activity-header:not(.persona-theme-padding) {
7000
+ padding-block: 2px;
7001
+ line-height: max(18px, calc(var(--persona-activity-label-size) * 1.35));
7002
+ }
7003
+ @media (any-pointer: coarse) {
7004
+ [data-persona-defaults="v5"] [data-persona-tool-group-stack] .persona-tool-bubble.persona-activity-row {
7005
+ --persona-activity-height: var(--persona-components-toolBubble-rowHeight, var(--persona-components-activity-rowHeight, 44px));
7006
+ }
7007
+ }
6996
7008
  .persona-turn-activity-body .persona-reasoning-bubble.persona-activity-row {
6997
7009
  --persona-activity-height: var(--persona-components-reasoningBubble-rowHeight, var(--persona-components-activity-rowHeight, 28px));
6998
7010
  }
@@ -73,6 +73,56 @@ describe('brand derivation', () => {
73
73
  expect(theme.components?.panel?.modes?.floating?.border).toContain('var(--persona-border)');
74
74
  expect(theme.components?.panel?.shadow).toBe(theme.palette?.shadows?.xl);
75
75
  });
76
+ describe.each(['light', 'dark'] as const)('%s message presentation', (colorScheme) => {
77
+ const elevations = [
78
+ ['flat', 'none'],
79
+ ['subtle', '0 2px 8px rgba(0, 0, 0, 0.08)'],
80
+ ['raised', '0 12px 32px rgba(0, 0, 0, 0.18)'],
81
+ ] as const;
82
+ it.each(elevations)('keeps V5 prose plain with %s elevation at every density', (elevation, shadow) => {
83
+ for (const density of ['compact', 'comfortable', 'spacious'] as const) {
84
+ const config = { colorScheme, future: { v5Defaults: true }, brand: { elevation, density } };
85
+ const vars = getCssVariables(getActiveTheme(config));
86
+ expect(vars['--persona-message-assistant-shadow']).toBe('none');
87
+ expect(vars['--persona-message-assistant-border']).toBe('transparent');
88
+ expect(vars['--persona-message-assistant-padding']).toBe('0px');
89
+ // These existing surfaces still follow elevation; this is not a global
90
+ // rule that every default of `none` must opt out of brand styling.
91
+ expect(vars['--persona-message-user-shadow']).toBe(shadow);
92
+ expect(getActiveTheme(config).components.introCard?.shadow).toBe(shadow);
93
+ const v4 = getCssVariables(getActiveTheme({ ...config, future: { v5Defaults: false } }));
94
+ expect(v4['--persona-message-assistant-shadow']).toBe(shadow);
95
+ expect(v4['--persona-message-assistant-border']).not.toBe('transparent');
96
+ }
97
+ });
98
+ it.each(elevations)('lets custom padded bubbles follow %s elevation', (elevation, shadow) => {
99
+ for (const [density, padding] of [['compact', '8px'], ['comfortable', '12px'], ['spacious', '16px']] as const) {
100
+ const theme = { components: { message: { assistant: {
101
+ background: 'semantic.colors.container', padding, borderRadius: '12px',
102
+ border: 'semantic.colors.border', borderWidth: '1px', shadow: 'palette.shadows.sm',
103
+ } } } };
104
+ // Shared overrides and dark-only overrides both remain authoritative.
105
+ for (const overrides of colorScheme === 'dark' ? [{ theme }, { darkTheme: theme }] : [{ theme }]) {
106
+ const vars = getCssVariables(getActiveTheme({
107
+ colorScheme, future: { v5Defaults: true }, brand: { elevation, density }, ...overrides,
108
+ }));
109
+ expect(vars['--persona-message-assistant-shadow']).toBe(shadow);
110
+ expect(vars['--persona-message-assistant-padding']).toBe(padding);
111
+ expect(vars['--persona-message-assistant-border']).not.toBe('transparent');
112
+ expect(vars['--persona-message-assistant-border-width']).toBe('1px');
113
+ }
114
+ }
115
+ });
116
+ it('preserves explicit assistant shadows and borders over brand elevation', () => {
117
+ const vars = getCssVariables(getActiveTheme({
118
+ colorScheme, future: { v5Defaults: true }, brand: { elevation: 'raised' },
119
+ theme: { components: { message: { assistant: { shadow: '0 1px 2px red', border: '#123456' } } } },
120
+ ...(colorScheme === 'dark' ? { darkTheme: { components: { message: { assistant: { shadow: 'none', border: '#abcdef' } } } } } : {}),
121
+ }));
122
+ expect(vars['--persona-message-assistant-shadow']).toBe(colorScheme === 'dark' ? 'none' : '0 1px 2px red');
123
+ expect(vars['--persona-message-assistant-border']).toBe(colorScheme === 'dark' ? '#abcdef' : '#123456');
124
+ });
125
+ });
76
126
  it.each([false, true])('keeps neutral-derived surfaces and elevation coherent (%s)', (v5Defaults) => {
77
127
  const theme = getActiveTheme({ future: { v5Defaults }, brand: { neutral: '#161b22', radius: 'none', elevation: 'flat' } });
78
128
  const vars = getCssVariables(theme);
@@ -111,8 +111,12 @@ export const deriveBrandTheme = (brand?: BrandConfig, scheme: 'light' | 'dark' =
111
111
  const shadow = flat ? 'none' : brand.elevation === 'subtle' ? '0 2px 8px rgba(0, 0, 0, 0.08)' : '0 12px 32px rgba(0, 0, 0, 0.18)';
112
112
  const border = flat ? '1px solid var(--persona-border)' : '1px solid color-mix(in srgb, var(--persona-border) 65%, transparent)';
113
113
  theme.palette = { ...theme.palette, shadows: { sm: shadow, md: shadow, lg: shadow, xl: shadow, '2xl': shadow } };
114
- const shadows = (node: Record<string, unknown>): Record<string, unknown> => {
114
+ const shadows = (node: Record<string, unknown>, path = ''): Record<string, unknown> => {
115
115
  const overlay: Record<string, unknown> = {};
116
+ // V5 assistant defaults are unpadded prose, not a surface to elevate.
117
+ // Explicit theme tokens still win; custom bubbles can opt into the brand
118
+ // shadow scale with `palette.shadows.sm` alongside their padding/background.
119
+ if (v5Defaults && path === 'message.assistant') return overlay;
116
120
  for (const [key, value] of Object.entries(node)) {
117
121
  if (key.toLowerCase().endsWith('shadow')) {
118
122
  overlay[key] = shadow;
@@ -120,7 +124,7 @@ export const deriveBrandTheme = (brand?: BrandConfig, scheme: 'light' | 'dark' =
120
124
  if (borderKey in node) overlay[borderKey] = typeof node[borderKey] === 'string' && (node[borderKey] as string).includes('solid') ? border : flat ? 'semantic.colors.border' : 'color-mix(in srgb, var(--persona-border) 65%, transparent)';
121
125
  }
122
126
  else if (value && typeof value === 'object') {
123
- const child = shadows(value as Record<string, unknown>);
127
+ const child = shadows(value as Record<string, unknown>, path ? `${path}.${key}` : key);
124
128
  if (Object.keys(child).length) overlay[key] = child;
125
129
  }
126
130
  }
@@ -603,7 +603,8 @@ export const DEFAULT_COMPONENTS_V5: ComponentTokens = {
603
603
  },
604
604
  launcher: CIRCLE_LAUNCHER_TOKENS,
605
605
  activity: {
606
- rowHeight: "32px", labelSize: "13px", iconSize: "16px", indent: "0px",
606
+ // CSS owns contextual parent, nested, and coarse-pointer row heights.
607
+ labelSize: "13px", iconSize: "16px", indent: "0px",
607
608
  transcriptGap: "0px", responseGap: "12px",
608
609
  groupIndent: "16px", groupGap: "2px", groupPadding: "2px 0",
609
610
  bodyPadding: "4px 0", bodyFontWeight: "400", sectionGap: "8px", bodySurface: "transparent",
@@ -6,6 +6,7 @@ const tool = (name: string, extra: Partial<AgentWidgetToolCall> = {}): AgentWidg
6
6
 
7
7
  describe("semantic tool activity", () => {
8
8
  it.each([
9
+ ["search_docs", "Searched docs"], ["read_file", "Read a file"],
9
10
  ["web_search", "Searched the web"], ["web_search_preview", "Searched the web"], ["WebSearch", "Searched the web"],
10
11
  ["web_fetch", "Read a web page"], ["WebFetch", "Read a web page"],
11
12
  ["file_search", "Searched files"], ["search_files", "Searched files"], ["Glob", "Searched files"],
@@ -22,6 +23,12 @@ describe("semantic tool activity", () => {
22
23
  ["sql_db_query", "Queried the database"], ["search_gmail", "Searched email"],
23
24
  ])("maps exact alias %s", (name, label) => expect(toolActivityLabel(tool(name))).toBe(label));
24
25
 
26
+ it("summarizes documentation searches and file reads without a custom hook", () => {
27
+ expect(summarizeToolActivity([tool("search_docs"), tool("read_file")])).toBe("Searched docs, read files");
28
+ expect(summarizeToolActivity([tool("search_docs", { status: "running" }), tool("read_file", { status: "pending" })])).toBe("Searching docs, reading files");
29
+ expect(toolActivityLabel(tool("read_file", { status: "running" }))).toBe("Reading a file");
30
+ });
31
+
25
32
  it.each(["search_events", "search_nodes", "read_graph", "open_nodes", "read", "search", "git_commit", "my_web_search", "web_search_custom"])("keeps ambiguous %s generic", name => {
26
33
  expect(toolActivityLabel(tool(name))).toMatch(/^Used /);
27
34
  });
@@ -127,6 +134,7 @@ describe("semantic tool activity", () => {
127
134
  });
128
135
 
129
136
  it.each([
137
+ ["search_docs", "search"], ["read_file", "file-text"],
130
138
  ["web_search", "globe"], ["Read", "file-text"], ["edit_file", "pencil"], ["Bash", "terminal"],
131
139
  ["sql_db_query", "database"], ["search_events", "wrench"], ["my_web_search", "wrench"],
132
140
  ])("maps the completed icon for %s without guessing", (name, icon) => expect(toolActivityIcon(tool(name))).toBe(icon));
@@ -3,10 +3,11 @@ import type { AgentWidgetConfig, AgentWidgetToolCall } from "../types";
3
3
  // Exact documented names only. Ambiguous names such as search_events or
4
4
  // search_nodes need provider metadata, which tool messages do not carry.
5
5
  const categories = [
6
+ ["search_docs", "Searching docs", "Searched docs", "search"],
6
7
  ["web_search|web_search_preview|WebSearch", "Searching the web", "Searched the web", "globe"],
7
8
  ["web_fetch|WebFetch", "Reading web pages", "Read web pages", "globe", "single"],
8
9
  ["file_search|search_files|Glob", "Searching files", "Searched files", "search"],
9
- ["read_text_file|read_multiple_files|Read", "Reading files", "Read files", "file-text", "single"],
10
+ ["read_file|read_text_file|read_multiple_files|Read", "Reading files", "Read files", "file-text", "single"],
10
11
  ["write_file|edit_file|apply_patch|Write|Edit|NotebookEdit", "Editing files", "Edited files", "pencil", "single"],
11
12
  ["list_directory|list_directory_with_sizes|directory_tree", "Browsing files", "Browsed files", "folder"],
12
13
  ["Grep", "Searching code", "Searched code", "search"],
@@ -1 +0,0 @@
1
- const e=`4.32.0`;export{e as t};