@runtypelabs/persona 3.5.2 → 3.6.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 (45) hide show
  1. package/dist/index.cjs +30 -30
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +14 -0
  4. package/dist/index.d.ts +14 -0
  5. package/dist/index.global.js +41 -41
  6. package/dist/index.global.js.map +1 -1
  7. package/dist/index.js +29 -29
  8. package/dist/index.js.map +1 -1
  9. package/dist/theme-editor.cjs +17728 -0
  10. package/dist/theme-editor.d.cts +3857 -0
  11. package/dist/theme-editor.d.ts +3857 -0
  12. package/dist/theme-editor.js +17623 -0
  13. package/dist/theme-reference.cjs +1 -1
  14. package/dist/theme-reference.d.cts +14 -0
  15. package/dist/theme-reference.d.ts +14 -0
  16. package/dist/theme-reference.js +1 -1
  17. package/dist/widget.css +29 -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 +4 -4
  22. package/src/components/event-stream-view.ts +1 -1
  23. package/src/components/header-builder.ts +2 -2
  24. package/src/components/launcher.ts +9 -0
  25. package/src/components/message-bubble.ts +9 -3
  26. package/src/components/suggestions.ts +1 -1
  27. package/src/defaults.ts +9 -9
  28. package/src/styles/widget.css +29 -25
  29. package/src/theme-editor/color-utils.ts +252 -0
  30. package/src/theme-editor/index.ts +130 -0
  31. package/src/theme-editor/presets.ts +144 -0
  32. package/src/theme-editor/preview-utils.ts +265 -0
  33. package/src/theme-editor/preview.ts +445 -0
  34. package/src/theme-editor/role-mappings.ts +331 -0
  35. package/src/theme-editor/sections.ts +952 -0
  36. package/src/theme-editor/state.ts +298 -0
  37. package/src/theme-editor/types.ts +177 -0
  38. package/src/theme-editor.ts +2 -0
  39. package/src/types/theme.ts +1 -0
  40. package/src/ui.ts +53 -58
  41. package/src/utils/plugins.ts +1 -1
  42. package/src/utils/theme.test.ts +10 -8
  43. package/src/utils/theme.ts +11 -11
  44. package/src/utils/tokens.ts +88 -41
  45. package/widget.css +0 -1
package/src/ui.ts CHANGED
@@ -703,9 +703,7 @@ export const createAgentExperience = (
703
703
  eventStreamView.update();
704
704
  }
705
705
  if (eventStreamToggleBtn) {
706
- eventStreamToggleBtn.classList.remove("persona-text-persona-muted");
707
- eventStreamToggleBtn.classList.add("persona-text-persona-accent");
708
- eventStreamToggleBtn.style.boxShadow = "inset 0 0 0 1.5px var(--persona-accent, #3b82f6)";
706
+ eventStreamToggleBtn.style.boxShadow = `inset 0 0 0 1.5px ${HEADER_THEME_CSS.actionIconColor}`;
709
707
  const activeClasses = config.features?.eventStream?.classNames?.toggleButtonActive;
710
708
  if (activeClasses) activeClasses.split(/\s+/).forEach(c => c && eventStreamToggleBtn!.classList.add(c));
711
709
  }
@@ -732,8 +730,6 @@ export const createAgentExperience = (
732
730
  }
733
731
  body.style.display = "";
734
732
  if (eventStreamToggleBtn) {
735
- eventStreamToggleBtn.classList.remove("persona-text-persona-accent");
736
- eventStreamToggleBtn.classList.add("persona-text-persona-muted");
737
733
  eventStreamToggleBtn.style.boxShadow = "";
738
734
  const activeClasses = config.features?.eventStream?.classNames?.toggleButtonActive;
739
735
  if (activeClasses) activeClasses.split(/\s+/).forEach(c => c && eventStreamToggleBtn!.classList.remove(c));
@@ -750,10 +746,11 @@ export const createAgentExperience = (
750
746
  let eventStreamToggleBtn: HTMLButtonElement | null = null;
751
747
  if (showEventStreamToggle) {
752
748
  const esClassNames = config.features?.eventStream?.classNames;
753
- const toggleBtnClasses = "persona-inline-flex persona-items-center persona-justify-center persona-rounded-full persona-text-persona-muted hover:persona-bg-gray-100 persona-cursor-pointer persona-border-none persona-bg-transparent persona-p-1" + (esClassNames?.toggleButton ? " " + esClassNames.toggleButton : "");
749
+ const toggleBtnClasses = "persona-inline-flex persona-items-center persona-justify-center persona-rounded-full hover:persona-opacity-80 persona-cursor-pointer persona-border-none persona-bg-transparent persona-p-1" + (esClassNames?.toggleButton ? " " + esClassNames.toggleButton : "");
754
750
  eventStreamToggleBtn = createElement("button", toggleBtnClasses) as HTMLButtonElement;
755
751
  eventStreamToggleBtn.style.width = "28px";
756
752
  eventStreamToggleBtn.style.height = "28px";
753
+ eventStreamToggleBtn.style.color = HEADER_THEME_CSS.actionIconColor;
757
754
  eventStreamToggleBtn.type = "button";
758
755
  eventStreamToggleBtn.setAttribute("aria-label", "Event Stream");
759
756
  eventStreamToggleBtn.title = "Event Stream";
@@ -877,13 +874,18 @@ export const createAgentExperience = (
877
874
  ensureComposerAttachmentSurface(footer);
878
875
  bindComposerRefsFromFooter(footer);
879
876
 
880
- // Apply contentMaxWidth to composer form if configured
877
+ // Apply contentMaxWidth to composer form and attachment previews if configured
881
878
  const contentMaxWidth = config.layout?.contentMaxWidth;
882
879
  if (contentMaxWidth && composerForm) {
883
880
  composerForm.style.maxWidth = contentMaxWidth;
884
881
  composerForm.style.marginLeft = "auto";
885
882
  composerForm.style.marginRight = "auto";
886
883
  }
884
+ if (contentMaxWidth && attachmentPreviewsContainer) {
885
+ attachmentPreviewsContainer.style.maxWidth = contentMaxWidth;
886
+ attachmentPreviewsContainer.style.marginLeft = "auto";
887
+ attachmentPreviewsContainer.style.marginRight = "auto";
888
+ }
887
889
 
888
890
  if (config.attachments?.enabled && attachmentInput && attachmentPreviewsContainer) {
889
891
  attachmentManager = AttachmentManager.fromConfig(config.attachments);
@@ -2388,7 +2390,6 @@ export const createAgentExperience = (
2388
2390
  "persona-shadow-sm",
2389
2391
  "persona-bg-persona-surface",
2390
2392
  "persona-border",
2391
- "persona-border-persona-message-border",
2392
2393
  "persona-text-persona-primary",
2393
2394
  "persona-px-5",
2394
2395
  "persona-py-3"
@@ -2400,6 +2401,7 @@ export const createAgentExperience = (
2400
2401
  "persona-text-persona-primary"
2401
2402
  ].join(" ");
2402
2403
  typingBubble.setAttribute("data-typing-indicator", "true");
2404
+ typingBubble.style.borderColor = "var(--persona-message-assistant-border, var(--persona-border, #e5e7eb))";
2403
2405
 
2404
2406
  typingBubble.appendChild(typingIndicator);
2405
2407
 
@@ -2483,12 +2485,16 @@ export const createAgentExperience = (
2483
2485
  // This allows the browser to update layout (e.g., typing indicator removal) before scrolling
2484
2486
  // Use double RAF to ensure layout has fully settled before starting scroll animation
2485
2487
  // Get the scrollable container using its unique ID (#persona-scroll-container)
2486
- requestAnimationFrame(() => {
2488
+ // Only smooth-scroll if auto-scroll hasn't been blocked by the user scrolling up
2489
+ if (shouldAutoScroll && !isAutoScrollBlocked) {
2487
2490
  requestAnimationFrame(() => {
2488
- const scrollableContainer = getScrollableContainer();
2489
- smoothScrollToBottom(scrollableContainer);
2491
+ requestAnimationFrame(() => {
2492
+ if (!shouldAutoScroll || isAutoScrollBlocked) return;
2493
+ const scrollableContainer = getScrollableContainer();
2494
+ smoothScrollToBottom(scrollableContainer);
2495
+ });
2490
2496
  });
2491
- });
2497
+ }
2492
2498
  };
2493
2499
 
2494
2500
  // Alias for clarity - the implementation handles flicker prevention via typing indicator logic
@@ -2982,17 +2988,16 @@ export const createAgentExperience = (
2982
2988
  iconSize: parseFloat(voiceConfig.iconSize ?? config.sendButton?.size ?? "40") || 24,
2983
2989
  };
2984
2990
 
2985
- // Apply recording state styles from config
2986
- const recordingBackgroundColor = voiceConfig.recordingBackgroundColor ?? "#ef4444";
2991
+ // Apply recording state styles from config or theme tokens
2992
+ const recordingBackgroundColor = voiceConfig.recordingBackgroundColor;
2987
2993
  const recordingIconColor = voiceConfig.recordingIconColor;
2988
2994
  const recordingBorderColor = voiceConfig.recordingBorderColor;
2989
-
2995
+
2990
2996
  micButton.classList.add("persona-voice-recording");
2991
- micButton.style.backgroundColor = recordingBackgroundColor;
2992
-
2997
+ micButton.style.backgroundColor = recordingBackgroundColor ?? "var(--persona-voice-recording-bg, #ef4444)";
2998
+ micButton.style.color = recordingIconColor ?? "var(--persona-voice-recording-indicator, #ffffff)";
2999
+
2993
3000
  if (recordingIconColor) {
2994
- micButton.style.color = recordingIconColor;
2995
- // Update SVG stroke color if present
2996
3001
  const svg = micButton.querySelector("svg");
2997
3002
  if (svg) {
2998
3003
  svg.setAttribute("stroke", recordingIconColor);
@@ -3092,30 +3097,27 @@ export const createAgentExperience = (
3092
3097
  micButton.style.fontSize = "18px";
3093
3098
  micButton.style.lineHeight = "1";
3094
3099
 
3095
- // Use Lucide mic icon with configured color (stroke width 1.5 for minimalist outline style)
3100
+ // Set mic button foreground from config or theme token
3101
+ if (iconColor) {
3102
+ micButton.style.color = iconColor;
3103
+ } else {
3104
+ micButton.style.color = "var(--persona-text, #111827)";
3105
+ }
3106
+
3107
+ // Use Lucide mic icon (stroke width 1.5 for minimalist outline style)
3096
3108
  const iconColorValue = iconColor || "currentColor";
3097
3109
  const micIconSvg = renderLucideIcon(micIconName, micIconSizeNum, iconColorValue, 1.5);
3098
3110
  if (micIconSvg) {
3099
3111
  micButton.appendChild(micIconSvg);
3100
- micButton.style.color = iconColorValue;
3101
3112
  } else {
3102
- // Fallback to text if icon fails
3103
3113
  micButton.textContent = "🎤";
3104
- micButton.style.color = iconColorValue;
3105
3114
  }
3106
-
3115
+
3107
3116
  // Apply background color
3108
3117
  if (backgroundColor) {
3109
3118
  micButton.style.backgroundColor = backgroundColor;
3110
3119
  } else {
3111
- micButton.classList.add("persona-bg-persona-primary");
3112
- }
3113
-
3114
- // Apply icon/text color
3115
- if (iconColor) {
3116
- micButton.style.color = iconColor;
3117
- } else if (!iconColor && !sendButtonConfig?.textColor) {
3118
- micButton.classList.add("persona-text-white");
3120
+ micButton.style.backgroundColor = "";
3119
3121
  }
3120
3122
 
3121
3123
  // Apply border styling
@@ -3187,14 +3189,14 @@ export const createAgentExperience = (
3187
3189
  if (!micButton) return;
3188
3190
  storeOriginalMicStyles();
3189
3191
  const voiceConfig = config.voiceRecognition ?? {};
3190
- const recordingBackgroundColor = voiceConfig.recordingBackgroundColor ?? "#ef4444";
3192
+ const recordingBackgroundColor = voiceConfig.recordingBackgroundColor;
3191
3193
  const recordingIconColor = voiceConfig.recordingIconColor;
3192
3194
  const recordingBorderColor = voiceConfig.recordingBorderColor;
3193
3195
  removeAllVoiceStateClasses();
3194
3196
  micButton.classList.add("persona-voice-recording");
3195
- micButton.style.backgroundColor = recordingBackgroundColor;
3197
+ micButton.style.backgroundColor = recordingBackgroundColor ?? "var(--persona-voice-recording-bg, #ef4444)";
3198
+ micButton.style.color = recordingIconColor ?? "var(--persona-voice-recording-indicator, #ffffff)";
3196
3199
  if (recordingIconColor) {
3197
- micButton.style.color = recordingIconColor;
3198
3200
  const svg = micButton.querySelector("svg");
3199
3201
  if (svg) svg.setAttribute("stroke", recordingIconColor);
3200
3202
  }
@@ -3240,7 +3242,7 @@ export const createAgentExperience = (
3240
3242
  const iconColor = voiceConfig.speakingIconColor
3241
3243
  ?? (interruptionMode === "barge-in" ? (voiceConfig.recordingIconColor ?? originalMicStyles?.color ?? "") : (originalMicStyles?.color ?? ""));
3242
3244
  const bgColor = voiceConfig.speakingBackgroundColor
3243
- ?? (interruptionMode === "barge-in" ? (voiceConfig.recordingBackgroundColor ?? "#ef4444") : (originalMicStyles?.backgroundColor ?? ""));
3245
+ ?? (interruptionMode === "barge-in" ? (voiceConfig.recordingBackgroundColor ?? "var(--persona-voice-recording-bg, #ef4444)") : (originalMicStyles?.backgroundColor ?? ""));
3244
3246
  const borderColor = voiceConfig.speakingBorderColor
3245
3247
  ?? (interruptionMode === "barge-in" ? (voiceConfig.recordingBorderColor ?? "") : (originalMicStyles?.borderColor ?? ""));
3246
3248
 
@@ -3718,10 +3720,11 @@ export const createAgentExperience = (
3718
3720
  // Add header toggle button if not present
3719
3721
  if (!eventStreamToggleBtn && header) {
3720
3722
  const dynEsClassNames = config.features?.eventStream?.classNames;
3721
- const dynToggleBtnClasses = "persona-inline-flex persona-items-center persona-justify-center persona-rounded-full persona-text-persona-muted hover:persona-bg-gray-100 persona-cursor-pointer persona-border-none persona-bg-transparent persona-p-1" + (dynEsClassNames?.toggleButton ? " " + dynEsClassNames.toggleButton : "");
3723
+ const dynToggleBtnClasses = "persona-inline-flex persona-items-center persona-justify-center persona-rounded-full hover:persona-opacity-80 persona-cursor-pointer persona-border-none persona-bg-transparent persona-p-1" + (dynEsClassNames?.toggleButton ? " " + dynEsClassNames.toggleButton : "");
3722
3724
  eventStreamToggleBtn = createElement("button", dynToggleBtnClasses) as HTMLButtonElement;
3723
3725
  eventStreamToggleBtn.style.width = "28px";
3724
3726
  eventStreamToggleBtn.style.height = "28px";
3727
+ eventStreamToggleBtn.style.color = HEADER_THEME_CSS.actionIconColor;
3725
3728
  eventStreamToggleBtn.type = "button";
3726
3729
  eventStreamToggleBtn.setAttribute("aria-label", "Event Stream");
3727
3730
  eventStreamToggleBtn.title = "Event Stream";
@@ -4488,22 +4491,18 @@ export const createAgentExperience = (
4488
4491
  micButton.textContent = "🎤";
4489
4492
  }
4490
4493
 
4491
- // Update colors
4494
+ // Update colors from config or theme tokens
4492
4495
  const backgroundColor = voiceConfig.backgroundColor ?? sendButtonConfig.backgroundColor;
4493
4496
  if (backgroundColor) {
4494
4497
  micButton.style.backgroundColor = backgroundColor;
4495
- micButton.classList.remove("persona-bg-persona-primary");
4496
4498
  } else {
4497
4499
  micButton.style.backgroundColor = "";
4498
- micButton.classList.add("persona-bg-persona-primary");
4499
4500
  }
4500
-
4501
+
4501
4502
  if (iconColor) {
4502
4503
  micButton.style.color = iconColor;
4503
- micButton.classList.remove("persona-text-white");
4504
- } else if (!iconColor && !sendButtonConfig.textColor) {
4505
- micButton.style.color = "";
4506
- micButton.classList.add("persona-text-white");
4504
+ } else {
4505
+ micButton.style.color = "var(--persona-text, #111827)";
4507
4506
  }
4508
4507
 
4509
4508
  // Update border styling
@@ -4729,30 +4728,25 @@ export const createAgentExperience = (
4729
4728
  // Clear existing content
4730
4729
  sendButton.innerHTML = "";
4731
4730
 
4731
+ // Set foreground color from config or theme token
4732
+ if (textColor) {
4733
+ sendButton.style.color = textColor;
4734
+ } else {
4735
+ sendButton.style.color = "var(--persona-button-primary-fg, #ffffff)";
4736
+ }
4737
+
4732
4738
  // Use Lucide icon if iconName is provided, otherwise fall back to iconText
4733
4739
  if (iconName) {
4734
4740
  const iconSize = parseFloat(buttonSize) || 24;
4735
- const iconColor = textColor && typeof textColor === 'string' && textColor.trim() ? textColor.trim() : "currentColor";
4741
+ const iconColor = textColor?.trim() || "currentColor";
4736
4742
  const iconSvg = renderLucideIcon(iconName, iconSize, iconColor, 2);
4737
4743
  if (iconSvg) {
4738
4744
  sendButton.appendChild(iconSvg);
4739
- sendButton.style.color = iconColor;
4740
4745
  } else {
4741
- // Fallback to text if icon fails to render
4742
4746
  sendButton.textContent = iconText;
4743
- if (textColor) {
4744
- sendButton.style.color = textColor;
4745
- } else {
4746
- sendButton.classList.add("persona-text-white");
4747
- }
4748
4747
  }
4749
4748
  } else {
4750
4749
  sendButton.textContent = iconText;
4751
- if (textColor) {
4752
- sendButton.style.color = textColor;
4753
- } else {
4754
- sendButton.classList.add("persona-text-white");
4755
- }
4756
4750
  }
4757
4751
 
4758
4752
  // Update classes
@@ -4762,6 +4756,7 @@ export const createAgentExperience = (
4762
4756
  sendButton.style.backgroundColor = backgroundColor;
4763
4757
  sendButton.classList.remove("persona-bg-persona-primary");
4764
4758
  } else {
4759
+ sendButton.style.backgroundColor = "";
4765
4760
  sendButton.classList.add("persona-bg-persona-primary");
4766
4761
  }
4767
4762
  } else {
@@ -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: {
@@ -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',
@@ -633,6 +660,21 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
633
660
  cssVars['--persona-components-launcher-borderRadius'] ??
634
661
  cssVars['--persona-palette-radius-full'] ??
635
662
  '9999px';
663
+ cssVars['--persona-launcher-bg'] =
664
+ cssVars['--persona-components-launcher-background'] ??
665
+ cssVars['--persona-primary'];
666
+ cssVars['--persona-launcher-fg'] =
667
+ cssVars['--persona-components-launcher-foreground'] ??
668
+ cssVars['--persona-text-inverse'];
669
+ cssVars['--persona-launcher-border'] =
670
+ cssVars['--persona-components-launcher-border'] ??
671
+ cssVars['--persona-border'];
672
+ cssVars['--persona-button-primary-bg'] =
673
+ cssVars['--persona-components-button-primary-background'] ??
674
+ cssVars['--persona-primary'];
675
+ cssVars['--persona-button-primary-fg'] =
676
+ cssVars['--persona-components-button-primary-foreground'] ??
677
+ cssVars['--persona-text-inverse'];
636
678
  cssVars['--persona-button-radius'] =
637
679
  cssVars['--persona-components-button-primary-borderRadius'] ??
638
680
  cssVars['--persona-palette-radius-full'] ??
@@ -681,6 +723,11 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
681
723
  if (headerTokens?.shadow) cssVars['--persona-header-shadow'] = headerTokens.shadow;
682
724
  if (headerTokens?.borderBottom) cssVars['--persona-header-border-bottom'] = headerTokens.borderBottom;
683
725
 
726
+ cssVars['--persona-input-background'] =
727
+ cssVars['--persona-components-input-background'] ?? cssVars['--persona-surface'];
728
+ cssVars['--persona-input-placeholder'] =
729
+ cssVars['--persona-components-input-placeholder'] ?? cssVars['--persona-text-muted'];
730
+
684
731
  cssVars['--persona-message-user-bg'] =
685
732
  cssVars['--persona-components-message-user-background'] ?? cssVars['--persona-accent'];
686
733
  cssVars['--persona-message-user-text'] =
@@ -711,7 +758,7 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
711
758
  cssVars['--persona-md-link-color'] =
712
759
  cssVars['--persona-components-markdown-link-foreground'] ??
713
760
  cssVars['--persona-accent'] ??
714
- '#3b82f6';
761
+ '#0f0f0f';
715
762
 
716
763
  const mdH1Size = cssVars['--persona-components-markdown-heading-h1-fontSize'];
717
764
  if (mdH1Size) cssVars['--persona-md-h1-size'] = mdH1Size;
package/widget.css DELETED
@@ -1 +0,0 @@
1
- @import "./src/widget.css";