@runtypelabs/persona 3.5.2 → 3.7.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 (53) hide show
  1. package/dist/index.cjs +46 -46
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +44 -0
  4. package/dist/index.d.ts +44 -0
  5. package/dist/index.global.js +70 -70
  6. package/dist/index.global.js.map +1 -1
  7. package/dist/index.js +46 -46
  8. package/dist/index.js.map +1 -1
  9. package/dist/theme-editor.cjs +18015 -0
  10. package/dist/theme-editor.d.cts +3888 -0
  11. package/dist/theme-editor.d.ts +3888 -0
  12. package/dist/theme-editor.js +17909 -0
  13. package/dist/theme-reference.cjs +1 -1
  14. package/dist/theme-reference.d.cts +33 -0
  15. package/dist/theme-reference.d.ts +33 -0
  16. package/dist/theme-reference.js +1 -1
  17. package/dist/widget.css +69 -25
  18. package/package.json +9 -7
  19. package/src/components/artifact-card.ts +1 -1
  20. package/src/components/composer-builder.ts +16 -29
  21. package/src/components/demo-carousel.ts +5 -5
  22. package/src/components/event-stream-view.test.ts +142 -0
  23. package/src/components/event-stream-view.ts +68 -29
  24. package/src/components/header-builder.ts +2 -2
  25. package/src/components/launcher.ts +9 -0
  26. package/src/components/message-bubble.ts +9 -3
  27. package/src/components/suggestions.ts +1 -1
  28. package/src/defaults.ts +24 -9
  29. package/src/scroll-to-bottom-defaults.test.ts +13 -0
  30. package/src/styles/widget.css +69 -25
  31. package/src/theme-editor/color-utils.ts +252 -0
  32. package/src/theme-editor/index.ts +131 -0
  33. package/src/theme-editor/presets.ts +144 -0
  34. package/src/theme-editor/preview-utils.ts +265 -0
  35. package/src/theme-editor/preview.ts +445 -0
  36. package/src/theme-editor/role-mappings.ts +343 -0
  37. package/src/theme-editor/sections.test.ts +43 -0
  38. package/src/theme-editor/sections.ts +994 -0
  39. package/src/theme-editor/state.ts +298 -0
  40. package/src/theme-editor/types.ts +177 -0
  41. package/src/theme-editor.ts +2 -0
  42. package/src/theme-reference.ts +8 -0
  43. package/src/types/theme.ts +11 -0
  44. package/src/types.ts +22 -0
  45. package/src/ui.scroll.test.ts +554 -0
  46. package/src/ui.ts +223 -133
  47. package/src/utils/auto-follow.test.ts +110 -0
  48. package/src/utils/auto-follow.ts +112 -0
  49. package/src/utils/plugins.ts +1 -1
  50. package/src/utils/theme.test.ts +44 -8
  51. package/src/utils/theme.ts +11 -11
  52. package/src/utils/tokens.ts +137 -41
  53. package/widget.css +0 -1
@@ -0,0 +1,110 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ createFollowStateController,
5
+ getScrollBottomOffset,
6
+ isElementNearBottom,
7
+ resolveFollowStateFromScroll,
8
+ resolveFollowStateFromWheel
9
+ } from "./auto-follow";
10
+
11
+ describe("auto-follow utilities", () => {
12
+ it("tracks pause and resume state", () => {
13
+ const state = createFollowStateController();
14
+
15
+ expect(state.isFollowing()).toBe(true);
16
+ expect(state.pause()).toBe(true);
17
+ expect(state.isFollowing()).toBe(false);
18
+ expect(state.pause()).toBe(false);
19
+ expect(state.resume()).toBe(true);
20
+ expect(state.isFollowing()).toBe(true);
21
+ });
22
+
23
+ it("computes bottom offset and near-bottom status", () => {
24
+ const element = {
25
+ scrollTop: 590,
26
+ scrollHeight: 1000,
27
+ clientHeight: 400
28
+ };
29
+
30
+ expect(getScrollBottomOffset(element)).toBe(600);
31
+ expect(isElementNearBottom(element, 10)).toBe(true);
32
+ expect(isElementNearBottom({ ...element, scrollTop: 560 }, 10)).toBe(false);
33
+ });
34
+
35
+ it("pauses transcript-style auto-follow on upward scroll immediately", () => {
36
+ const result = resolveFollowStateFromScroll({
37
+ following: true,
38
+ currentScrollTop: 597,
39
+ lastScrollTop: 600,
40
+ nearBottom: true,
41
+ userScrollThreshold: 1,
42
+ pauseOnUpwardScroll: true,
43
+ pauseWhenAwayFromBottom: false
44
+ });
45
+
46
+ expect(result.action).toBe("pause");
47
+ });
48
+
49
+ it("resumes event-log-style auto-follow only when scrolling down near bottom", () => {
50
+ const stayPaused = resolveFollowStateFromScroll({
51
+ following: false,
52
+ currentScrollTop: 550,
53
+ lastScrollTop: 560,
54
+ nearBottom: true,
55
+ userScrollThreshold: 1,
56
+ resumeRequiresDownwardScroll: true
57
+ });
58
+ const resume = resolveFollowStateFromScroll({
59
+ following: false,
60
+ currentScrollTop: 590,
61
+ lastScrollTop: 550,
62
+ nearBottom: true,
63
+ userScrollThreshold: 1,
64
+ resumeRequiresDownwardScroll: true
65
+ });
66
+
67
+ expect(stayPaused.action).toBe("none");
68
+ expect(resume.action).toBe("resume");
69
+ });
70
+
71
+ it("keeps transcript-style auto-follow paused near the bottom until scrolling down", () => {
72
+ const stayPaused = resolveFollowStateFromScroll({
73
+ following: false,
74
+ currentScrollTop: 597,
75
+ lastScrollTop: 600,
76
+ nearBottom: true,
77
+ userScrollThreshold: 1,
78
+ resumeRequiresDownwardScroll: true
79
+ });
80
+ const resume = resolveFollowStateFromScroll({
81
+ following: false,
82
+ currentScrollTop: 599,
83
+ lastScrollTop: 597,
84
+ nearBottom: true,
85
+ userScrollThreshold: 1,
86
+ resumeRequiresDownwardScroll: true
87
+ });
88
+
89
+ expect(stayPaused.action).toBe("none");
90
+ expect(resume.action).toBe("resume");
91
+ });
92
+
93
+ it("resolves wheel intent for pause and resume", () => {
94
+ expect(
95
+ resolveFollowStateFromWheel({
96
+ following: true,
97
+ deltaY: -12
98
+ })
99
+ ).toBe("pause");
100
+
101
+ expect(
102
+ resolveFollowStateFromWheel({
103
+ following: false,
104
+ deltaY: 12,
105
+ nearBottom: true,
106
+ resumeWhenNearBottom: true
107
+ })
108
+ ).toBe("resume");
109
+ });
110
+ });
@@ -0,0 +1,112 @@
1
+ export type FollowStateAction = "none" | "pause" | "resume";
2
+
3
+ export type FollowStateController = {
4
+ isFollowing: () => boolean;
5
+ pause: () => boolean;
6
+ resume: () => boolean;
7
+ };
8
+
9
+ export type FollowStateScrollInput = {
10
+ following: boolean;
11
+ currentScrollTop: number;
12
+ lastScrollTop: number;
13
+ nearBottom: boolean;
14
+ userScrollThreshold: number;
15
+ isAutoScrolling?: boolean;
16
+ pauseOnUpwardScroll?: boolean;
17
+ pauseWhenAwayFromBottom?: boolean;
18
+ resumeRequiresDownwardScroll?: boolean;
19
+ };
20
+
21
+ export type FollowStateWheelInput = {
22
+ following: boolean;
23
+ deltaY: number;
24
+ nearBottom?: boolean;
25
+ resumeWhenNearBottom?: boolean;
26
+ };
27
+
28
+ export function createFollowStateController(initiallyFollowing = true): FollowStateController {
29
+ let following = initiallyFollowing;
30
+
31
+ return {
32
+ isFollowing: () => following,
33
+ pause: () => {
34
+ if (!following) return false;
35
+ following = false;
36
+ return true;
37
+ },
38
+ resume: () => {
39
+ if (following) return false;
40
+ following = true;
41
+ return true;
42
+ }
43
+ };
44
+ }
45
+
46
+ export function getScrollBottomOffset(element: Pick<HTMLElement, "scrollHeight" | "clientHeight">): number {
47
+ return Math.max(0, element.scrollHeight - element.clientHeight);
48
+ }
49
+
50
+ export function isElementNearBottom(
51
+ element: Pick<HTMLElement, "scrollTop" | "scrollHeight" | "clientHeight">,
52
+ threshold: number
53
+ ): boolean {
54
+ return getScrollBottomOffset(element) - element.scrollTop <= threshold;
55
+ }
56
+
57
+ export function resolveFollowStateFromScroll(
58
+ input: FollowStateScrollInput
59
+ ): { action: FollowStateAction; delta: number; nextLastScrollTop: number } {
60
+ const {
61
+ following,
62
+ currentScrollTop,
63
+ lastScrollTop,
64
+ nearBottom,
65
+ userScrollThreshold,
66
+ isAutoScrolling = false,
67
+ pauseOnUpwardScroll = false,
68
+ pauseWhenAwayFromBottom = true,
69
+ resumeRequiresDownwardScroll = false
70
+ } = input;
71
+
72
+ const delta = currentScrollTop - lastScrollTop;
73
+
74
+ if (isAutoScrolling || Math.abs(delta) < userScrollThreshold) {
75
+ return { action: "none", delta, nextLastScrollTop: currentScrollTop };
76
+ }
77
+
78
+ if (!following && nearBottom && (!resumeRequiresDownwardScroll || delta > 0)) {
79
+ return { action: "resume", delta, nextLastScrollTop: currentScrollTop };
80
+ }
81
+
82
+ if (following && pauseOnUpwardScroll && delta < 0) {
83
+ return { action: "pause", delta, nextLastScrollTop: currentScrollTop };
84
+ }
85
+
86
+ if (following && pauseWhenAwayFromBottom && !nearBottom) {
87
+ return { action: "pause", delta, nextLastScrollTop: currentScrollTop };
88
+ }
89
+
90
+ return { action: "none", delta, nextLastScrollTop: currentScrollTop };
91
+ }
92
+
93
+ export function resolveFollowStateFromWheel(
94
+ input: FollowStateWheelInput
95
+ ): FollowStateAction {
96
+ const {
97
+ following,
98
+ deltaY,
99
+ nearBottom = false,
100
+ resumeWhenNearBottom = false
101
+ } = input;
102
+
103
+ if (following && deltaY < 0) {
104
+ return "pause";
105
+ }
106
+
107
+ if (!following && resumeWhenNearBottom && deltaY > 0 && nearBottom) {
108
+ return "resume";
109
+ }
110
+
111
+ return "none";
112
+ }
@@ -22,7 +22,7 @@ export function accessibilityPlugin(): PersonaThemePlugin {
22
22
  },
23
23
  cssVariables: {
24
24
  '--persona-accessibility-focus-ring':
25
- '0 0 0 2px var(--persona-semantic-colors-surface, #fff), 0 0 0 4px var(--persona-semantic-colors-interactive-focus, #1d4ed8)',
25
+ '0 0 0 2px var(--persona-semantic-colors-surface, #fff), 0 0 0 4px var(--persona-semantic-colors-interactive-focus, #0f0f0f)',
26
26
  },
27
27
  };
28
28
  }
@@ -139,15 +139,16 @@ describe('theme utils', () => {
139
139
  expect(cssVars['--persona-md-prose-font-family']).toBe('Georgia, serif');
140
140
  });
141
141
 
142
- it('maps header chrome tokens to dedicated CSS variables with semantic fallbacks', () => {
142
+ it('maps header chrome tokens to dedicated CSS variables with palette refs', () => {
143
143
  const theme = createTheme();
144
144
  const cssVars = themeToCssVariables(theme);
145
145
 
146
- expect(cssVars['--persona-header-icon-bg']).toBe(cssVars['--persona-primary']);
147
- expect(cssVars['--persona-header-icon-fg']).toBe(cssVars['--persona-text-inverse']);
148
- expect(cssVars['--persona-header-title-fg']).toBe(cssVars['--persona-primary']);
149
- expect(cssVars['--persona-header-subtitle-fg']).toBe(cssVars['--persona-text-muted']);
150
- expect(cssVars['--persona-header-action-icon-fg']).toBe(cssVars['--persona-muted']);
146
+ // Default header uses solid primary role: icon-bg=primary.600, icon-fg=primary.50, etc.
147
+ expect(cssVars['--persona-header-icon-bg']).toBe('#0f0f0f'); // primary.600
148
+ expect(cssVars['--persona-header-icon-fg']).toBe('#ffffff'); // primary.50
149
+ expect(cssVars['--persona-header-title-fg']).toBe('#ffffff'); // primary.50
150
+ expect(cssVars['--persona-header-subtitle-fg']).toBe('#d4d4d4'); // primary.200
151
+ expect(cssVars['--persona-header-action-icon-fg']).toBe('#d4d4d4'); // primary.200
151
152
 
152
153
  const custom = createTheme({
153
154
  components: {
@@ -172,8 +173,9 @@ describe('theme utils', () => {
172
173
  const theme = createTheme();
173
174
  const cssVars = themeToCssVariables(theme);
174
175
 
175
- expect(cssVars['--persona-components-artifact-pane-background']).toBe('#f3f4f6');
176
- expect(cssVars['--persona-artifact-toolbar-bg']).toBe('#f3f4f6');
176
+ // container defaults to gray.50 now (soft gray surfaces role)
177
+ expect(cssVars['--persona-components-artifact-pane-background']).toBe('#f9fafb');
178
+ expect(cssVars['--persona-artifact-toolbar-bg']).toBe('#f9fafb');
177
179
 
178
180
  const surfacePane = createTheme({
179
181
  components: {
@@ -216,6 +218,40 @@ describe('theme utils', () => {
216
218
  expect(cssVars['--persona-composer-shadow']).toBe('none');
217
219
  });
218
220
 
221
+ it('maps scroll-to-bottom component tokens to dedicated CSS variables', () => {
222
+ const theme = createTheme({
223
+ components: {
224
+ scrollToBottom: {
225
+ background: 'palette.colors.accent.500',
226
+ foreground: 'palette.colors.gray.50',
227
+ border: 'palette.colors.gray.900',
228
+ size: '40px',
229
+ borderRadius: 'palette.radius.full',
230
+ shadow: 'palette.shadows.md',
231
+ padding: '0.5rem 0.875rem',
232
+ gap: '0.5rem',
233
+ fontSize: '0.875rem',
234
+ iconSize: '14px',
235
+ },
236
+ },
237
+ } as any);
238
+
239
+ const cssVars = themeToCssVariables(theme);
240
+
241
+ expect(cssVars['--persona-scroll-to-bottom-bg']).toBe('#06b6d4');
242
+ expect(cssVars['--persona-scroll-to-bottom-fg']).toBe('#f9fafb');
243
+ expect(cssVars['--persona-scroll-to-bottom-border']).toBe('#111827');
244
+ expect(cssVars['--persona-scroll-to-bottom-size']).toBe('40px');
245
+ expect(cssVars['--persona-scroll-to-bottom-radius']).toBe('9999px');
246
+ expect(cssVars['--persona-scroll-to-bottom-shadow']).toBe(
247
+ '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
248
+ );
249
+ expect(cssVars['--persona-scroll-to-bottom-padding']).toBe('0.5rem 0.875rem');
250
+ expect(cssVars['--persona-scroll-to-bottom-gap']).toBe('0.5rem');
251
+ expect(cssVars['--persona-scroll-to-bottom-font-size']).toBe('0.875rem');
252
+ expect(cssVars['--persona-scroll-to-bottom-icon-size']).toBe('14px');
253
+ });
254
+
219
255
  it('lets config.toolCall.shadow override theme tool bubble shadow on the root element', () => {
220
256
  const el = document.createElement('div');
221
257
  applyThemeVariables(el, {
@@ -16,17 +16,17 @@ type WidgetConfig = PersonaWidgetConfig | AgentWidgetConfig;
16
16
  const DARK_PALETTE = {
17
17
  colors: {
18
18
  primary: {
19
- 50: '#eff6ff',
20
- 100: '#dbeafe',
21
- 200: '#bfdbfe',
22
- 300: '#93c5fd',
23
- 400: '#60a5fa',
24
- 500: '#3b82f6',
25
- 600: '#2563eb',
26
- 700: '#1d4ed8',
27
- 800: '#1e40af',
28
- 900: '#1e3a8a',
29
- 950: '#172554',
19
+ 50: '#ffffff',
20
+ 100: '#f5f5f5',
21
+ 200: '#d4d4d4',
22
+ 300: '#a3a3a3',
23
+ 400: '#737373',
24
+ 500: '#171717',
25
+ 600: '#0f0f0f',
26
+ 700: '#0a0a0a',
27
+ 800: '#050505',
28
+ 900: '#030303',
29
+ 950: '#000000',
30
30
  },
31
31
  secondary: {
32
32
  50: '#f5f3ff',
@@ -12,17 +12,17 @@ import type {
12
12
  export const DEFAULT_PALETTE = {
13
13
  colors: {
14
14
  primary: {
15
- 50: '#eff6ff',
16
- 100: '#dbeafe',
17
- 200: '#bfdbfe',
18
- 300: '#93c5fd',
19
- 400: '#60a5fa',
20
- 500: '#3b82f6',
21
- 600: '#2563eb',
22
- 700: '#1d4ed8',
23
- 800: '#1e40af',
24
- 900: '#1e3a8a',
25
- 950: '#172554',
15
+ 50: '#ffffff',
16
+ 100: '#f5f5f5',
17
+ 200: '#d4d4d4',
18
+ 300: '#a3a3a3',
19
+ 400: '#737373',
20
+ 500: '#171717',
21
+ 600: '#0f0f0f',
22
+ 700: '#0a0a0a',
23
+ 800: '#050505',
24
+ 900: '#030303',
25
+ 950: '#000000',
26
26
  },
27
27
  secondary: {
28
28
  50: '#f5f3ff',
@@ -99,6 +99,19 @@ export const DEFAULT_PALETTE = {
99
99
  800: '#991b1b',
100
100
  900: '#7f1d1d',
101
101
  },
102
+ info: {
103
+ 50: '#eff6ff',
104
+ 100: '#dbeafe',
105
+ 200: '#bfdbfe',
106
+ 300: '#93c5fd',
107
+ 400: '#60a5fa',
108
+ 500: '#3b82f6',
109
+ 600: '#2563eb',
110
+ 700: '#1d4ed8',
111
+ 800: '#1e40af',
112
+ 900: '#1e3a8a',
113
+ 950: '#172554',
114
+ },
102
115
  },
103
116
  spacing: {
104
117
  0: '0px',
@@ -176,28 +189,33 @@ export const DEFAULT_PALETTE = {
176
189
  export const DEFAULT_SEMANTIC: SemanticTokens = {
177
190
  colors: {
178
191
  primary: 'palette.colors.primary.500',
179
- secondary: 'palette.colors.gray.500',
192
+ secondary: 'palette.colors.secondary.500',
193
+ // Links/Focus role — solid primary
180
194
  accent: 'palette.colors.primary.600',
195
+ // Surfaces role — soft gray
181
196
  surface: 'palette.colors.gray.50',
182
197
  background: 'palette.colors.gray.50',
183
- container: 'palette.colors.gray.100',
198
+ container: 'palette.colors.gray.50',
184
199
  text: 'palette.colors.gray.900',
185
200
  textMuted: 'palette.colors.gray.500',
186
201
  textInverse: 'palette.colors.gray.50',
202
+ // Borders role — soft gray
187
203
  border: 'palette.colors.gray.200',
188
204
  divider: 'palette.colors.gray.200',
189
205
  interactive: {
190
- default: 'palette.colors.primary.500',
191
- hover: 'palette.colors.primary.600',
192
- focus: 'palette.colors.primary.700',
193
- active: 'palette.colors.primary.800',
206
+ // Primary Actions role — solid primary
207
+ default: 'palette.colors.primary.600',
208
+ hover: 'palette.colors.primary.700',
209
+ // Links/Focus role — solid primary
210
+ focus: 'palette.colors.primary.600',
211
+ active: 'palette.colors.primary.600',
194
212
  disabled: 'palette.colors.gray.300',
195
213
  },
196
214
  feedback: {
197
215
  success: 'palette.colors.success.500',
198
216
  warning: 'palette.colors.warning.500',
199
217
  error: 'palette.colors.error.500',
200
- info: 'palette.colors.primary.500',
218
+ info: 'palette.colors.info.500',
201
219
  },
202
220
  },
203
221
  spacing: {
@@ -219,14 +237,15 @@ export const DEFAULT_SEMANTIC: SemanticTokens = {
219
237
  export const DEFAULT_COMPONENTS: ComponentTokens = {
220
238
  button: {
221
239
  primary: {
222
- background: 'semantic.colors.primary',
223
- foreground: 'semantic.colors.textInverse',
240
+ // Primary Actions role — solid primary
241
+ background: 'palette.colors.primary.500',
242
+ foreground: 'palette.colors.primary.50',
224
243
  borderRadius: 'palette.radius.lg',
225
244
  padding: 'semantic.spacing.md',
226
245
  },
227
246
  secondary: {
228
247
  background: 'semantic.colors.surface',
229
- foreground: 'semantic.colors.text',
248
+ foreground: 'semantic.colors.secondary',
230
249
  borderRadius: 'palette.radius.lg',
231
250
  padding: 'semantic.spacing.md',
232
251
  },
@@ -238,16 +257,20 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
238
257
  },
239
258
  },
240
259
  input: {
241
- background: 'semantic.colors.surface',
242
- placeholder: 'semantic.colors.textMuted',
260
+ // Input role — soft gray
261
+ background: 'palette.colors.gray.50',
262
+ placeholder: 'palette.colors.gray.400',
243
263
  borderRadius: 'palette.radius.lg',
244
264
  padding: 'semantic.spacing.md',
245
265
  focus: {
246
- border: 'semantic.colors.interactive.focus',
247
- ring: 'semantic.colors.interactive.focus',
266
+ border: 'palette.colors.gray.400',
267
+ ring: 'palette.colors.gray.400',
248
268
  },
249
269
  },
250
270
  launcher: {
271
+ background: 'palette.colors.primary.500',
272
+ foreground: 'palette.colors.primary.50',
273
+ border: 'palette.colors.gray.200',
251
274
  size: '60px',
252
275
  iconSize: '28px',
253
276
  borderRadius: 'palette.radius.full',
@@ -262,28 +285,31 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
262
285
  shadow: 'palette.shadows.xl',
263
286
  },
264
287
  header: {
265
- background: 'semantic.colors.surface',
266
- border: 'semantic.colors.border',
288
+ // Header role — solid primary
289
+ background: 'palette.colors.primary.500',
290
+ border: 'palette.colors.primary.600',
267
291
  borderRadius: 'palette.radius.xl palette.radius.xl 0 0',
268
292
  padding: 'semantic.spacing.md',
269
- iconBackground: 'semantic.colors.primary',
270
- iconForeground: 'semantic.colors.textInverse',
271
- titleForeground: 'semantic.colors.primary',
272
- subtitleForeground: 'semantic.colors.textMuted',
273
- actionIconForeground: 'semantic.colors.textMuted',
293
+ iconBackground: 'palette.colors.primary.600',
294
+ iconForeground: 'palette.colors.primary.50',
295
+ titleForeground: 'palette.colors.primary.50',
296
+ subtitleForeground: 'palette.colors.primary.200',
297
+ actionIconForeground: 'palette.colors.primary.200',
274
298
  },
275
299
  message: {
276
300
  user: {
277
- background: 'semantic.colors.primary',
278
- text: 'semantic.colors.textInverse',
301
+ // User Messages role — solid primary
302
+ background: 'palette.colors.primary.500',
303
+ text: 'palette.colors.primary.50',
279
304
  borderRadius: 'palette.radius.lg',
280
305
  shadow: 'palette.shadows.sm',
281
306
  },
282
307
  assistant: {
283
- background: 'semantic.colors.container',
284
- text: 'semantic.colors.text',
308
+ // Assistant Messages role — soft gray
309
+ background: 'palette.colors.gray.50',
310
+ text: 'palette.colors.gray.900',
285
311
  borderRadius: 'palette.radius.lg',
286
- border: 'semantic.colors.border',
312
+ border: 'palette.colors.gray.200',
287
313
  shadow: 'palette.shadows.sm',
288
314
  },
289
315
  },
@@ -298,11 +324,12 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
298
324
  },
299
325
  markdown: {
300
326
  inlineCode: {
301
- background: 'semantic.colors.container',
302
- foreground: 'semantic.colors.text',
327
+ background: 'palette.colors.gray.50',
328
+ foreground: 'palette.colors.gray.900',
303
329
  },
304
330
  link: {
305
- foreground: 'semantic.colors.accent',
331
+ // Links/Focus role — solid primary
332
+ foreground: 'palette.colors.primary.600',
306
333
  },
307
334
  prose: {
308
335
  fontFamily: 'inherit',
@@ -347,6 +374,18 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
347
374
  border: 'palette.colors.gray.200',
348
375
  },
349
376
  },
377
+ scrollToBottom: {
378
+ background: 'components.button.primary.background',
379
+ foreground: 'components.button.primary.foreground',
380
+ border: 'semantic.colors.primary',
381
+ size: '40px',
382
+ borderRadius: 'palette.radius.full',
383
+ shadow: 'palette.shadows.sm',
384
+ padding: '0.5rem 0.875rem',
385
+ gap: '0.5rem',
386
+ fontSize: '0.875rem',
387
+ iconSize: '14px',
388
+ },
350
389
  artifact: {
351
390
  pane: {
352
391
  background: 'semantic.colors.container',
@@ -629,10 +668,26 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
629
668
  cssVars['--persona-radius-md'] = cssVars['--persona-palette-radius-md'] ?? '0.375rem';
630
669
  cssVars['--persona-radius-lg'] = cssVars['--persona-palette-radius-lg'] ?? '0.5rem';
631
670
  cssVars['--persona-radius-xl'] = cssVars['--persona-palette-radius-xl'] ?? '0.75rem';
671
+ cssVars['--persona-radius-full'] = cssVars['--persona-palette-radius-full'] ?? '9999px';
632
672
  cssVars['--persona-launcher-radius'] =
633
673
  cssVars['--persona-components-launcher-borderRadius'] ??
634
674
  cssVars['--persona-palette-radius-full'] ??
635
675
  '9999px';
676
+ cssVars['--persona-launcher-bg'] =
677
+ cssVars['--persona-components-launcher-background'] ??
678
+ cssVars['--persona-primary'];
679
+ cssVars['--persona-launcher-fg'] =
680
+ cssVars['--persona-components-launcher-foreground'] ??
681
+ cssVars['--persona-text-inverse'];
682
+ cssVars['--persona-launcher-border'] =
683
+ cssVars['--persona-components-launcher-border'] ??
684
+ cssVars['--persona-border'];
685
+ cssVars['--persona-button-primary-bg'] =
686
+ cssVars['--persona-components-button-primary-background'] ??
687
+ cssVars['--persona-primary'];
688
+ cssVars['--persona-button-primary-fg'] =
689
+ cssVars['--persona-components-button-primary-foreground'] ??
690
+ cssVars['--persona-text-inverse'];
636
691
  cssVars['--persona-button-radius'] =
637
692
  cssVars['--persona-components-button-primary-borderRadius'] ??
638
693
  cssVars['--persona-palette-radius-full'] ??
@@ -681,6 +736,11 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
681
736
  if (headerTokens?.shadow) cssVars['--persona-header-shadow'] = headerTokens.shadow;
682
737
  if (headerTokens?.borderBottom) cssVars['--persona-header-border-bottom'] = headerTokens.borderBottom;
683
738
 
739
+ cssVars['--persona-input-background'] =
740
+ cssVars['--persona-components-input-background'] ?? cssVars['--persona-surface'];
741
+ cssVars['--persona-input-placeholder'] =
742
+ cssVars['--persona-components-input-placeholder'] ?? cssVars['--persona-text-muted'];
743
+
684
744
  cssVars['--persona-message-user-bg'] =
685
745
  cssVars['--persona-components-message-user-background'] ?? cssVars['--persona-accent'];
686
746
  cssVars['--persona-message-user-text'] =
@@ -695,6 +755,42 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
695
755
  cssVars['--persona-components-message-assistant-border'] ?? cssVars['--persona-border'];
696
756
  cssVars['--persona-message-assistant-shadow'] =
697
757
  cssVars['--persona-components-message-assistant-shadow'] ?? '0 1px 2px 0 rgb(0 0 0 / 0.05)';
758
+ cssVars['--persona-scroll-to-bottom-bg'] =
759
+ cssVars['--persona-components-scrollToBottom-background'] ??
760
+ cssVars['--persona-button-primary-bg'] ??
761
+ cssVars['--persona-accent'];
762
+ cssVars['--persona-scroll-to-bottom-fg'] =
763
+ cssVars['--persona-components-scrollToBottom-foreground'] ??
764
+ cssVars['--persona-button-primary-fg'] ??
765
+ cssVars['--persona-text-inverse'];
766
+ cssVars['--persona-scroll-to-bottom-border'] =
767
+ cssVars['--persona-components-scrollToBottom-border'] ??
768
+ cssVars['--persona-primary'];
769
+ cssVars['--persona-scroll-to-bottom-size'] =
770
+ cssVars['--persona-components-scrollToBottom-size'] ??
771
+ '40px';
772
+ cssVars['--persona-scroll-to-bottom-radius'] =
773
+ cssVars['--persona-components-scrollToBottom-borderRadius'] ??
774
+ cssVars['--persona-button-radius'] ??
775
+ cssVars['--persona-radius-full'] ??
776
+ '9999px';
777
+ cssVars['--persona-scroll-to-bottom-shadow'] =
778
+ cssVars['--persona-components-scrollToBottom-shadow'] ??
779
+ cssVars['--persona-palette-shadows-sm'] ??
780
+ '0 1px 2px 0 rgb(0 0 0 / 0.05)';
781
+ cssVars['--persona-scroll-to-bottom-padding'] =
782
+ cssVars['--persona-components-scrollToBottom-padding'] ??
783
+ '0.5rem 0.875rem';
784
+ cssVars['--persona-scroll-to-bottom-gap'] =
785
+ cssVars['--persona-components-scrollToBottom-gap'] ??
786
+ '0.5rem';
787
+ cssVars['--persona-scroll-to-bottom-font-size'] =
788
+ cssVars['--persona-components-scrollToBottom-fontSize'] ??
789
+ cssVars['--persona-palette-typography-fontSize-sm'] ??
790
+ '0.875rem';
791
+ cssVars['--persona-scroll-to-bottom-icon-size'] =
792
+ cssVars['--persona-components-scrollToBottom-iconSize'] ??
793
+ '14px';
698
794
 
699
795
  cssVars['--persona-tool-bubble-shadow'] =
700
796
  cssVars['--persona-components-toolBubble-shadow'] ?? '0 5px 15px rgba(15, 23, 42, 0.08)';
@@ -711,7 +807,7 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
711
807
  cssVars['--persona-md-link-color'] =
712
808
  cssVars['--persona-components-markdown-link-foreground'] ??
713
809
  cssVars['--persona-accent'] ??
714
- '#3b82f6';
810
+ '#0f0f0f';
715
811
 
716
812
  const mdH1Size = cssVars['--persona-components-markdown-heading-h1-fontSize'];
717
813
  if (mdH1Size) cssVars['--persona-md-h1-size'] = mdH1Size;
package/widget.css DELETED
@@ -1 +0,0 @@
1
- @import "./src/widget.css";