@runtypelabs/persona 4.14.0 → 4.16.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 (119) hide show
  1. package/README.md +4 -4
  2. package/dist/animations/glyph-cycle.d.cts +1 -1
  3. package/dist/animations/glyph-cycle.d.ts +1 -1
  4. package/dist/animations/{types-DveIaNx6.d.cts → types-BpWGm7_6.d.cts} +17 -0
  5. package/dist/animations/{types-DveIaNx6.d.ts → types-BpWGm7_6.d.ts} +17 -0
  6. package/dist/animations/wipe.d.cts +1 -1
  7. package/dist/animations/wipe.d.ts +1 -1
  8. package/dist/chunk-QJ4LV2KW.js +1 -0
  9. package/dist/codegen.cjs +12 -11
  10. package/dist/codegen.js +14 -13
  11. package/dist/{context-mentions-ONG7A7M6.js → context-mentions-BTSTIBFC.js} +1 -1
  12. package/dist/context-mentions.cjs +11 -8
  13. package/dist/context-mentions.d.cts +520 -21
  14. package/dist/context-mentions.d.ts +520 -21
  15. package/dist/context-mentions.js +11 -8
  16. package/dist/index.cjs +69 -68
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +595 -46
  19. package/dist/index.d.ts +595 -46
  20. package/dist/index.global.js +60 -57
  21. package/dist/index.global.js.map +1 -1
  22. package/dist/index.js +69 -68
  23. package/dist/index.js.map +1 -1
  24. package/dist/launcher.global.js +5 -2
  25. package/dist/launcher.global.js.map +1 -1
  26. package/dist/smart-dom-reader.d.cts +520 -21
  27. package/dist/smart-dom-reader.d.ts +520 -21
  28. package/dist/testing.cjs +3 -3
  29. package/dist/testing.d.cts +7 -0
  30. package/dist/testing.d.ts +7 -0
  31. package/dist/testing.js +3 -3
  32. package/dist/theme-editor-preview.cjs +67 -67
  33. package/dist/theme-editor-preview.d.cts +534 -22
  34. package/dist/theme-editor-preview.d.ts +534 -22
  35. package/dist/theme-editor-preview.js +57 -57
  36. package/dist/theme-editor.cjs +10 -10
  37. package/dist/theme-editor.d.cts +521 -23
  38. package/dist/theme-editor.d.ts +521 -23
  39. package/dist/theme-editor.js +10 -10
  40. package/dist/theme-reference.cjs +1 -1
  41. package/dist/theme-reference.d.cts +104 -3
  42. package/dist/theme-reference.d.ts +104 -3
  43. package/dist/theme-reference.js +1 -1
  44. package/dist/widget.css +1 -1
  45. package/package.json +1 -1
  46. package/src/ask-user-question-tool.ts +10 -7
  47. package/src/components/artifact-pane.test.ts +27 -0
  48. package/src/components/artifact-pane.ts +7 -3
  49. package/src/components/artifact-preview.ts +1 -1
  50. package/src/components/composer-parts.test.ts +103 -4
  51. package/src/components/composer-parts.ts +53 -15
  52. package/src/components/header-builder.ts +27 -2
  53. package/src/components/header-layouts.test.ts +56 -0
  54. package/src/components/header-layouts.ts +29 -9
  55. package/src/components/launcher-surface.test.ts +403 -0
  56. package/src/components/launcher.ts +297 -2
  57. package/src/components/messages.ts +5 -2
  58. package/src/components/panel.ts +92 -71
  59. package/src/components/pill-composer-builder.test.ts +18 -0
  60. package/src/components/suggestions.test.ts +273 -0
  61. package/src/components/suggestions.ts +528 -86
  62. package/src/components/welcome.test.ts +121 -0
  63. package/src/components/welcome.ts +305 -0
  64. package/src/components/widget-view.test.ts +5 -3
  65. package/src/components/widget-view.ts +18 -7
  66. package/src/defaults.ts +7 -5
  67. package/src/generated/runtype-openapi-contract.ts +17 -0
  68. package/src/index-core.ts +29 -2
  69. package/src/launcher-global.ts +11 -8
  70. package/src/plugins/types.ts +136 -3
  71. package/src/presets.ts +3 -3
  72. package/src/session.ts +10 -8
  73. package/src/styles/widget-styles.test.ts +382 -0
  74. package/src/styles/widget.css +778 -4
  75. package/src/suggest-replies-tool.test.ts +233 -2
  76. package/src/suggest-replies-tool.ts +115 -39
  77. package/src/testing/mock-stream.test.ts +19 -0
  78. package/src/testing/mock-stream.ts +20 -0
  79. package/src/theme-editor/preview-utils.test.ts +34 -0
  80. package/src/theme-editor/preview-utils.ts +77 -2
  81. package/src/theme-editor/sections.test.ts +33 -0
  82. package/src/theme-editor/sections.ts +83 -6
  83. package/src/theme-reference.ts +34 -5
  84. package/src/types/theme.ts +72 -3
  85. package/src/types.ts +312 -18
  86. package/src/ui.body-top-slot.test.ts +95 -0
  87. package/src/ui.content-width.test.ts +91 -0
  88. package/src/ui.detached-panel.test.ts +1 -1
  89. package/src/ui.docked.test.ts +7 -4
  90. package/src/ui.launcher-teaser.test.ts +162 -0
  91. package/src/ui.rebuild-composer.test.ts +491 -0
  92. package/src/ui.render-welcome.test.ts +505 -0
  93. package/src/ui.scroll-additive.test.ts +91 -21
  94. package/src/ui.scroll.test.ts +302 -14
  95. package/src/ui.suggest-replies.test.ts +1112 -1
  96. package/src/ui.ts +1008 -149
  97. package/src/ui.welcome.test.ts +504 -0
  98. package/src/utils/artifact-file.test.ts +1 -1
  99. package/src/utils/artifact-gate.test.ts +27 -0
  100. package/src/utils/artifact-gate.ts +9 -0
  101. package/src/utils/attachment-manager.test.ts +37 -0
  102. package/src/utils/attachment-manager.ts +15 -0
  103. package/src/utils/auto-follow.test.ts +23 -0
  104. package/src/utils/auto-follow.ts +11 -1
  105. package/src/utils/code-generators.test.ts +40 -0
  106. package/src/utils/code-generators.ts +22 -0
  107. package/src/utils/config-merge.test.ts +24 -0
  108. package/src/utils/config-merge.ts +2 -0
  109. package/src/utils/content-width.test.ts +37 -0
  110. package/src/utils/content-width.ts +17 -0
  111. package/src/utils/icons.test.ts +26 -0
  112. package/src/utils/icons.ts +8 -0
  113. package/src/utils/plugin-storage.test.ts +94 -0
  114. package/src/utils/plugin-storage.ts +98 -0
  115. package/src/utils/theme.test.ts +216 -0
  116. package/src/utils/tokens.ts +113 -3
  117. package/src/welcome.test.ts +213 -0
  118. package/src/welcome.ts +130 -0
  119. package/dist/chunk-MMUPR2JW.js +0 -1
@@ -195,7 +195,12 @@ export function buildSrcdoc(
195
195
 
196
196
  // ─── Preview Messages ───────────────────────────────────────────
197
197
 
198
- export type PreviewScene = 'home' | 'conversation' | 'minimized' | 'artifact';
198
+ export type PreviewScene =
199
+ | 'home'
200
+ | 'conversation'
201
+ | 'follow-ups'
202
+ | 'minimized'
203
+ | 'artifact';
199
204
 
200
205
  export type PreviewTranscriptEntryPreset =
201
206
  | 'user-message'
@@ -543,8 +548,78 @@ export function createPreviewMessages(
543
548
  config?: Partial<AgentWidgetConfig>,
544
549
  appendedMessages: AgentWidgetMessage[] = []
545
550
  ): AgentWidgetMessage[] {
551
+ if (scene === 'follow-ups') {
552
+ const now = Date.now();
553
+ return [
554
+ {
555
+ id: 'preview-follow-up-user',
556
+ role: 'user',
557
+ content: 'What can Persona help me build?',
558
+ createdAt: new Date(now - 120000).toISOString(),
559
+ },
560
+ {
561
+ id: 'preview-follow-up-assistant',
562
+ role: 'assistant',
563
+ content:
564
+ 'Persona gives you a themeable streaming chat interface with flexible integrations and interaction patterns. Where would you like to go next?',
565
+ createdAt: new Date(now - 60000).toISOString(),
566
+ },
567
+ {
568
+ id: 'preview-follow-up-suggestions',
569
+ role: 'assistant',
570
+ content: '',
571
+ createdAt: new Date(now - 59000).toISOString(),
572
+ streaming: false,
573
+ variant: 'tool',
574
+ toolCall: {
575
+ id: 'preview-follow-up-suggestions',
576
+ name: 'suggest_replies',
577
+ status: 'complete',
578
+ args: {
579
+ suggestions: [
580
+ {
581
+ id: 'preview-implementation',
582
+ label: 'Show me an implementation',
583
+ prompt: 'Show me a practical implementation',
584
+ description: 'See a complete integration example',
585
+ icon: 'code-2',
586
+ emphasis: 'primary',
587
+ },
588
+ {
589
+ id: 'preview-theming',
590
+ label: 'Explore theming',
591
+ prompt: 'Tell me how theming works',
592
+ description: 'Customize colors, type, and component styles',
593
+ icon: 'palette',
594
+ },
595
+ {
596
+ id: 'preview-security',
597
+ label: 'Review security',
598
+ prompt: 'Explain the security model',
599
+ description: 'Learn about isolation and safe rendering',
600
+ icon: 'shield-check',
601
+ },
602
+ {
603
+ id: 'preview-support',
604
+ label: 'Talk to support',
605
+ prompt: 'Help me contact support',
606
+ description: 'Get help choosing the right approach',
607
+ icon: 'messages-square',
608
+ },
609
+ ],
610
+ },
611
+ chunks: [],
612
+ },
613
+ },
614
+ ...appendedMessages,
615
+ ];
616
+ }
546
617
  if (scene === 'home') {
547
- return [{ id: 'preview-home-1', role: 'assistant', content: 'Hi there! How can we help today?', createdAt: new Date().toISOString() }];
618
+ // Home is the pre-conversation state: welcome copy + starter suggestions
619
+ // already provide the greeting and entry points. Seeding an assistant
620
+ // bubble here creates a duplicate greeting that the default runtime never
621
+ // enters unless a host explicitly configures its own initialMessages.
622
+ return [];
548
623
  }
549
624
  if (scene === 'minimized') {
550
625
  return [{ id: 'preview-min-1', role: 'assistant', content: 'We are here whenever you are ready.', createdAt: new Date().toISOString() }];
@@ -60,6 +60,39 @@ describe("theme editor scroll-to-bottom controls", () => {
60
60
  expect(fieldPaths).toContain("theme.components.scrollToBottom.iconSize");
61
61
  });
62
62
 
63
+ it("exposes suggestion behavior and component token controls", () => {
64
+ const suggestions = CONFIGURE_SECTIONS.find(
65
+ (section) => section.id === "suggestions"
66
+ );
67
+ const configPaths =
68
+ suggestions?.fields.map((field) => field.path) ?? [];
69
+ expect(configPaths).toEqual(
70
+ expect.arrayContaining([
71
+ "suggestions.starters.variant",
72
+ "suggestions.starters.placement",
73
+ "suggestions.starters.behavior",
74
+ "suggestions.followUps.variant",
75
+ "suggestions.followUps.placement",
76
+ "suggestions.followUps.behavior",
77
+ "suggestions.followUps.overflow",
78
+ ])
79
+ );
80
+
81
+ const componentPaths = COMPONENTS_SECTIONS.flatMap((section) =>
82
+ section.fields.map((field) => field.path)
83
+ );
84
+ expect(componentPaths).toEqual(
85
+ expect.arrayContaining([
86
+ "theme.components.suggestion.chip.background",
87
+ "theme.components.suggestion.chip.hoverBackground",
88
+ "theme.components.suggestion.chip.focusRing",
89
+ "theme.components.suggestion.card.background",
90
+ "theme.components.suggestion.card.shadow",
91
+ "theme.components.suggestion.list.background",
92
+ ])
93
+ );
94
+ });
95
+
63
96
  it("exposes a shadow control for every themeable component", () => {
64
97
  const fieldPaths = COMPONENTS_SECTIONS.flatMap((section) => section.fields.map((field) => field.path));
65
98
 
@@ -369,6 +369,18 @@ const inputShapeSectionDef: SectionDef = {
369
369
  ],
370
370
  };
371
371
 
372
+ const composerSpacingSectionDef: SectionDef = {
373
+ id: 'comp-composer-spacing',
374
+ title: 'Composer Spacing',
375
+ collapsed: true,
376
+ fields: [
377
+ { id: 'composer-padding', label: 'Padding', type: 'text', path: 'theme.components.composer.padding', defaultValue: '0.75rem 1rem' },
378
+ { id: 'composer-gap', label: 'Row Gap', description: 'Space between the textarea row and the actions row', type: 'text', path: 'theme.components.composer.gap', defaultValue: '0.5rem' },
379
+ { id: 'composer-font-size', label: 'Font Size', type: 'text', path: 'theme.components.composer.fontSize', defaultValue: '0.875rem' },
380
+ { id: 'composer-line-height', label: 'Line Height', type: 'text', path: 'theme.components.composer.lineHeight', defaultValue: '1.25rem' },
381
+ ],
382
+ };
383
+
372
384
  const buttonShapeSectionDef: SectionDef = {
373
385
  id: 'comp-button-shape',
374
386
  title: 'Button Shape',
@@ -466,6 +478,35 @@ const scrollToBottomSectionDef: SectionDef = {
466
478
  ],
467
479
  };
468
480
 
481
+ const suggestionStyleSectionDef: SectionDef = {
482
+ id: 'comp-suggestions',
483
+ title: 'Suggestions',
484
+ description: 'Style starter cards, follow-up chips, and list rows.',
485
+ collapsed: true,
486
+ fields: [
487
+ { id: 'suggestion-chip-bg', label: 'Chip Background', type: 'token-ref', path: 'theme.components.suggestion.chip.background', defaultValue: 'semantic.colors.surface', tokenRef: { tokenType: 'color' } },
488
+ { id: 'suggestion-chip-fg', label: 'Chip Text', type: 'token-ref', path: 'theme.components.suggestion.chip.foreground', defaultValue: 'semantic.colors.text', tokenRef: { tokenType: 'color' } },
489
+ { id: 'suggestion-chip-border', label: 'Chip Border', type: 'token-ref', path: 'theme.components.suggestion.chip.border', defaultValue: 'semantic.colors.border', tokenRef: { tokenType: 'color' } },
490
+ { id: 'suggestion-chip-hover', label: 'Chip Hover', type: 'token-ref', path: 'theme.components.suggestion.chip.hoverBackground', defaultValue: 'palette.colors.gray.100', tokenRef: { tokenType: 'color' } },
491
+ { id: 'suggestion-chip-focus', label: 'Chip Focus Ring', type: 'token-ref', path: 'theme.components.suggestion.chip.focusRing', defaultValue: 'semantic.colors.interactive.focus', tokenRef: { tokenType: 'color' } },
492
+ { id: 'suggestion-card-bg', label: 'Card Background', type: 'token-ref', path: 'theme.components.suggestion.card.background', defaultValue: 'semantic.colors.surface', tokenRef: { tokenType: 'color' } },
493
+ { id: 'suggestion-card-fg', label: 'Card Text', type: 'token-ref', path: 'theme.components.suggestion.card.foreground', defaultValue: 'semantic.colors.text', tokenRef: { tokenType: 'color' } },
494
+ { id: 'suggestion-card-border', label: 'Card Border', type: 'token-ref', path: 'theme.components.suggestion.card.border', defaultValue: 'semantic.colors.border', tokenRef: { tokenType: 'color' } },
495
+ { id: 'suggestion-card-hover', label: 'Card Hover', type: 'token-ref', path: 'theme.components.suggestion.card.hoverBackground', defaultValue: 'palette.colors.gray.100', tokenRef: { tokenType: 'color' } },
496
+ { id: 'suggestion-card-shadow', label: 'Card Shadow', type: 'select', path: 'theme.components.suggestion.card.shadow', defaultValue: 'palette.shadows.sm', options: [
497
+ { value: 'palette.shadows.none', label: 'None' },
498
+ { value: 'palette.shadows.sm', label: 'Small' },
499
+ { value: 'palette.shadows.md', label: 'Medium' },
500
+ { value: 'palette.shadows.lg', label: 'Large' },
501
+ ] },
502
+ { id: 'suggestion-list-bg', label: 'List Background', type: 'color', path: 'theme.components.suggestion.list.background', defaultValue: 'transparent' },
503
+ { id: 'suggestion-list-border', label: 'List Border', type: 'token-ref', path: 'theme.components.suggestion.list.border', defaultValue: 'semantic.colors.border', tokenRef: { tokenType: 'color' } },
504
+ { id: 'suggestion-chip-item-gap', label: 'Chip Spacing', description: 'Space between chips', type: 'text', path: 'theme.components.suggestion.chip.itemGap', defaultValue: '8px' },
505
+ { id: 'suggestion-card-item-gap', label: 'Card Spacing', description: 'Space between cards', type: 'text', path: 'theme.components.suggestion.card.itemGap', defaultValue: '8px' },
506
+ { id: 'suggestion-list-item-gap', label: 'List Spacing', description: 'Space between list rows', type: 'text', path: 'theme.components.suggestion.list.itemGap', defaultValue: '8px' },
507
+ ],
508
+ };
509
+
469
510
  // Reusable shadow-scale options matching the launcher / panel / scroll-to-bottom selects.
470
511
  const SHADOW_SCALE_OPTIONS: { value: string; label: string }[] = [
471
512
  { value: 'palette.shadows.none', label: 'None' },
@@ -497,12 +538,39 @@ const componentShadowsSectionDef: SectionDef = {
497
538
  };
498
539
 
499
540
  /** Shared shape sections (not scoped to light/dark) */
541
+ // Blank keeps the built-in fallback: parseOptionalCssValue drops the path so
542
+ // the CSS variable never emits.
543
+ const TEXT_FONT_WEIGHT_OPTIONS: { value: string; label: string }[] = [
544
+ { value: '', label: 'Default' },
545
+ { value: '400', label: 'Normal (400)' },
546
+ { value: '500', label: 'Medium (500)' },
547
+ { value: '600', label: 'Semibold (600)' },
548
+ { value: '700', label: 'Bold (700)' },
549
+ ];
550
+
551
+ const textStylesSectionDef: SectionDef = {
552
+ id: 'comp-text-styles',
553
+ title: 'Header & Welcome Text',
554
+ description: 'Typography for the header title and the welcome greeting. Leave blank to keep the built-in defaults.',
555
+ collapsed: true,
556
+ fields: [
557
+ { id: 'header-title-font-family', label: 'Header Title Font', description: 'CSS font-family, e.g. Georgia, serif', type: 'text', path: 'theme.components.header.title.fontFamily', defaultValue: '', parseValue: parseOptionalCssValue },
558
+ { id: 'header-title-font-size', label: 'Header Title Size', description: 'CSS length, e.g. 1.125rem', type: 'text', path: 'theme.components.header.title.fontSize', defaultValue: '', parseValue: parseOptionalCssValue },
559
+ { id: 'header-title-font-weight', label: 'Header Title Weight', type: 'select', path: 'theme.components.header.title.fontWeight', defaultValue: '', options: TEXT_FONT_WEIGHT_OPTIONS, parseValue: parseOptionalCssValue },
560
+ { id: 'welcome-title-font-family', label: 'Welcome Title Font', description: 'CSS font-family, e.g. Georgia, serif', type: 'text', path: 'theme.components.introCard.title.fontFamily', defaultValue: '', parseValue: parseOptionalCssValue },
561
+ { id: 'welcome-title-font-size', label: 'Welcome Title Size', description: 'CSS length, e.g. 1.5rem', type: 'text', path: 'theme.components.introCard.title.fontSize', defaultValue: '', parseValue: parseOptionalCssValue },
562
+ { id: 'welcome-title-font-weight', label: 'Welcome Title Weight', type: 'select', path: 'theme.components.introCard.title.fontWeight', defaultValue: '', options: TEXT_FONT_WEIGHT_OPTIONS, parseValue: parseOptionalCssValue },
563
+ ],
564
+ };
565
+
500
566
  export const COMPONENT_SHAPE_SECTIONS: SectionDef[] = [
501
567
  panelLayoutSectionDef,
502
568
  launcherLayoutSectionDef,
503
569
  messageShapeSectionDef,
504
570
  inputShapeSectionDef,
571
+ composerSpacingSectionDef,
505
572
  buttonShapeSectionDef,
573
+ textStylesSectionDef,
506
574
  componentShadowsSectionDef,
507
575
  ];
508
576
 
@@ -513,6 +581,7 @@ export const COMPONENT_COLOR_SECTIONS: SectionDef[] = [
513
581
  inputColorsSectionDef,
514
582
  buttonColorsSectionDef,
515
583
  scrollToBottomSectionDef,
584
+ suggestionStyleSectionDef,
516
585
  ];
517
586
 
518
587
  export const COMPONENTS_SECTIONS: SectionDef[] = [
@@ -526,9 +595,9 @@ export const COMPONENTS_SECTIONS: SectionDef[] = [
526
595
 
527
596
  const MB = 1024 * 1024;
528
597
  const DEFAULT_SUGGESTION_CHIPS = [
529
- 'What can you help me with?',
530
- 'Tell me about your features',
531
- 'How does this work?',
598
+ 'Show me what you can help with',
599
+ 'Walk me through getting started',
600
+ 'Answer a question about this page',
532
601
  ];
533
602
 
534
603
  const ATTACHMENT_TYPE_PRESETS: Record<string, string[]> = {
@@ -569,16 +638,24 @@ const copySectionDef: SectionDef = {
569
638
  fields: [
570
639
  { id: 'copy-show-welcome-card', label: 'Show Welcome Card', type: 'toggle', path: 'copy.showWelcomeCard', defaultValue: true },
571
640
  { id: 'copy-welcome-title', label: 'Welcome Title', type: 'text', path: 'copy.welcomeTitle', defaultValue: 'Hello 👋' },
572
- { id: 'copy-welcome-subtitle', label: 'Welcome Subtitle', type: 'text', path: 'copy.welcomeSubtitle', defaultValue: 'Ask anything about your account or products.' },
641
+ { id: 'copy-welcome-subtitle', label: 'Welcome Subtitle', type: 'text', path: 'copy.welcomeSubtitle', defaultValue: 'I can answer questions and help you get things done here.' },
573
642
  { id: 'copy-placeholder', label: 'Input Placeholder', type: 'text', path: 'copy.inputPlaceholder', defaultValue: 'Type your message…' },
574
643
  { id: 'copy-send-label', label: 'Send Button Label', type: 'text', path: 'copy.sendButtonLabel', defaultValue: 'Send' },
575
644
  ],
576
645
  };
577
646
 
578
647
  const suggestionsSectionDef: SectionDef = {
579
- id: 'suggestions', title: 'Suggestion Chips', description: 'Configure chip content and styling.', collapsed: true,
648
+ id: 'suggestions', title: 'Suggestions', description: 'Configure starter prompts and agent follow-ups.', collapsed: true,
580
649
  fields: [
581
- { id: 'suggestions-list', label: 'Suggestions', description: 'Add, edit, and remove chips directly.', type: 'chip-list', path: 'suggestionChips', defaultValue: DEFAULT_SUGGESTION_CHIPS },
650
+ { id: 'suggestions-list', label: 'Starter Prompts', description: 'Add, edit, and remove starter prompts directly.', type: 'chip-list', path: 'suggestionChips', defaultValue: DEFAULT_SUGGESTION_CHIPS },
651
+ { id: 'suggestions-starter-variant', label: 'Starter Style', type: 'select', path: 'suggestions.starters.variant', defaultValue: 'card', options: [{ value: 'card', label: 'Cards' }, { value: 'chip', label: 'Chips' }, { value: 'list', label: 'List' }] },
652
+ { id: 'suggestions-starter-placement', label: 'Starter Placement', type: 'select', path: 'suggestions.starters.placement', defaultValue: 'auto', options: [{ value: 'auto', label: 'Automatic' }, { value: 'welcome', label: 'Welcome Surface' }, { value: 'composer', label: 'Above Composer' }] },
653
+ { id: 'suggestions-starter-behavior', label: 'Starter Click', type: 'select', path: 'suggestions.starters.behavior', defaultValue: 'send', options: [{ value: 'send', label: 'Send Immediately' }, { value: 'fill', label: 'Fill Composer' }] },
654
+ { id: 'suggestions-starter-overflow', label: 'Starter Overflow', type: 'select', path: 'suggestions.starters.overflow', defaultValue: 'wrap', options: [{ value: 'wrap', label: 'Wrap Rows' }, { value: 'scroll', label: 'Horizontal Scroll' }] },
655
+ { id: 'suggestions-followup-variant', label: 'Follow-up Style', type: 'select', path: 'suggestions.followUps.variant', defaultValue: 'chip', options: [{ value: 'chip', label: 'Chips' }, { value: 'card', label: 'Cards' }, { value: 'list', label: 'List' }] },
656
+ { id: 'suggestions-followup-placement', label: 'Follow-up Placement', type: 'select', path: 'suggestions.followUps.placement', defaultValue: 'auto', options: [{ value: 'auto', label: 'Automatic' }, { value: 'after-message', label: 'After Messages' }, { value: 'composer', label: 'Above Composer' }] },
657
+ { id: 'suggestions-followup-behavior', label: 'Follow-up Click', type: 'select', path: 'suggestions.followUps.behavior', defaultValue: 'send', options: [{ value: 'send', label: 'Send Immediately' }, { value: 'fill', label: 'Fill Composer' }] },
658
+ { id: 'suggestions-followup-overflow', label: 'Follow-up Overflow', type: 'select', path: 'suggestions.followUps.overflow', defaultValue: 'scroll', options: [{ value: 'scroll', label: 'Horizontal Scroll' }, { value: 'wrap', label: 'Wrap Rows' }] },
582
659
  ],
583
660
  };
584
661
 
@@ -120,7 +120,9 @@ export const THEME_TOKEN_DOCS = {
120
120
  header: {
121
121
  description: 'Chat panel header.',
122
122
  properties:
123
- 'background, border, borderRadius, padding, iconBackground, iconForeground, titleForeground, subtitleForeground, actionIconForeground, shadow, borderBottom.',
123
+ 'background, border, borderRadius, padding, iconBackground, iconForeground, actionIconForeground, shadow, borderBottom, title and subtitle (each fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, color).',
124
+ legacy:
125
+ 'titleForeground and subtitleForeground are legacy aliases of header.title.color and header.subtitle.color. Both still work; the title/subtitle color wins when both are set.',
124
126
  },
125
127
  message: {
126
128
  description: 'Chat message bubbles.',
@@ -131,7 +133,19 @@ export const THEME_TOKEN_DOCS = {
131
133
  markdown: {
132
134
  description: 'Markdown rendering in messages and artifact pane.',
133
135
  properties:
134
- 'inlineCode (background, foreground), link.foreground, prose.fontFamily, heading.h1/h2 (fontSize, fontWeight).',
136
+ 'inlineCode (background, foreground), link.foreground, prose.fontFamily, heading.h1/h2 (fontSize, fontWeight), codeBlock (background, borderColor, textColor, borderRadius: follows palette.radius.md), table (headerBackground, borderColor), hr.color, blockquote (borderColor, background, textColor).',
137
+ },
138
+ introCard: {
139
+ description:
140
+ 'Welcome surface above the message list. Flat by default (transparent, no shadow).',
141
+ properties:
142
+ 'background, borderRadius, padding, shadow, border (full CSS shorthand, default none), title and subtitle (each fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, color).',
143
+ },
144
+ suggestion: {
145
+ description:
146
+ 'Starter prompt and follow-up suggestion chrome. Three variants: chip, card, list.',
147
+ properties:
148
+ 'background, foreground, border, borderRadius, padding, shadow, gap (icon to copy inside one item), itemGap (space between items, default 8px), minHeight, fontSize, lineHeight, iconSize, hoverBackground, hoverForeground, hoverBorder, pressedBackground, focusRing, disabledOpacity.',
135
149
  },
136
150
  voice:
137
151
  'recording (indicator, background, border), processing (icon, background), speaking (icon).',
@@ -141,8 +155,11 @@ export const THEME_TOKEN_DOCS = {
141
155
  scrollToBottom:
142
156
  'Floating scroll-to-bottom affordance shared by transcript and event stream: background, foreground, border, size, borderRadius, shadow, padding, gap, fontSize, iconSize.',
143
157
  toolBubble: 'shadow: tool call row box-shadow.',
158
+ scrollbar:
159
+ 'Shared scrollbar appearance for every scroller: thumb (default semantic.colors.border), track (default transparent).',
144
160
  reasoningBubble: 'shadow: reasoning/thinking row box-shadow.',
145
- composer: 'shadow: message input form box-shadow.',
161
+ composer:
162
+ 'Message input form: shadow (box-shadow), borderColor, padding (form inset), gap (textarea row to actions row), fontSize and lineHeight (textarea type).',
146
163
  artifact:
147
164
  'toolbar (icon styling, copy menu), tab (background, active states), pane (background, toolbarBackground).',
148
165
  },
@@ -234,7 +251,7 @@ export const THEME_TOKEN_DOCS = {
234
251
  copy: {
235
252
  description: 'Widget text content.',
236
253
  properties:
237
- 'showWelcomeCard (boolean), welcomeTitle, welcomeSubtitle, inputPlaceholder, sendButtonLabel.',
254
+ 'inputPlaceholder, sendButtonLabel, stopButtonLabel. (showWelcomeCard, welcomeTitle, welcomeSubtitle are deprecated aliases of the welcome namespace.)',
238
255
  },
239
256
  voiceRecognition: {
240
257
  description: 'Voice input configuration.',
@@ -245,8 +262,20 @@ export const THEME_TOKEN_DOCS = {
245
262
  properties:
246
263
  'enabled, provider ("browser" | "runtype"), browserFallback, voice, rate, pitch.',
247
264
  },
265
+ suggestions: {
266
+ description:
267
+ 'Rich starter and follow-up suggestion surfaces. Strings remain valid item shorthand.',
268
+ properties:
269
+ 'starters.items, starters.variant ("card" | "chip" | "list"), starters.placement ("auto" | "welcome" | "composer"), starters.behavior ("send" | "fill"), starters.overflow ("scroll" | "wrap"), starters.maxItems, followUps.enabled, followUps.expose, followUps.variant, followUps.placement ("auto" | "after-message" | "composer"), followUps.behavior, followUps.overflow ("scroll" | "wrap"), followUps.maxItems.',
270
+ },
271
+ welcome: {
272
+ description:
273
+ 'First-open welcome surface. Supersedes copy.welcomeTitle / welcomeSubtitle / showWelcomeCard.',
274
+ properties:
275
+ 'title, subtitle, icon ({ type: "lucide", name } | { type: "image", url, alt } | { type: "text", text } | function), variant ("card" | "hero" | "none"), dismiss ("never" | "on-first-message"), message (display-only greeting bubble, ignored under "hero").',
276
+ },
248
277
  suggestionChips:
249
- 'string[]: Suggested prompts shown to the user.',
278
+ 'Deprecated string[] shorthand for starter prompts. Prefer suggestions.starters.items.',
250
279
  messageActions: {
251
280
  description: 'Message action buttons (copy, upvote, downvote).',
252
281
  properties:
@@ -202,6 +202,16 @@ export interface PanelTokens extends ComponentTokenSet {
202
202
  canvasBackground?: string;
203
203
  }
204
204
 
205
+ /** Per-element text styling shared by themable text surfaces. */
206
+ export interface TextStyleTokens {
207
+ fontFamily?: string;
208
+ fontSize?: string;
209
+ fontWeight?: string;
210
+ lineHeight?: string;
211
+ letterSpacing?: string;
212
+ color?: TokenReference<'color'>;
213
+ }
214
+
205
215
  export interface HeaderTokens extends ComponentTokenSet {
206
216
  background: TokenReference<'color'>;
207
217
  border: TokenReference<'color'>;
@@ -210,10 +220,14 @@ export interface HeaderTokens extends ComponentTokenSet {
210
220
  iconBackground: TokenReference<'color'>;
211
221
  /** Foreground (glyph stroke or emoji text) on the header avatar tile. */
212
222
  iconForeground: TokenReference<'color'>;
213
- /** Header title line (next to the icon, or minimal layout title). */
223
+ /** Legacy alias of `title.color`; `title.color` wins when both are set. */
214
224
  titleForeground: TokenReference<'color'>;
215
- /** Header subtitle line under the title. */
225
+ /** Legacy alias of `subtitle.color`; `subtitle.color` wins when both are set. */
216
226
  subtitleForeground: TokenReference<'color'>;
227
+ /** Header title typography (next to the icon, or minimal layout title). */
228
+ title?: TextStyleTokens;
229
+ /** Header subtitle typography, for the line under the title. */
230
+ subtitle?: TextStyleTokens;
217
231
  /** Default color for clear / close icon buttons when launcher overrides are unset. */
218
232
  actionIconForeground: TokenReference<'color'>;
219
233
  /** Box-shadow on the header (e.g., a fade shadow to replace the default border). */
@@ -245,7 +259,7 @@ export interface MessageTokens {
245
259
 
246
260
  /**
247
261
  * Welcome / intro card rendered above the message list when no messages exist.
248
- * Set `copy.showWelcomeCard: false` to hide it; use `layout.slots["body-top"]`
262
+ * Set `welcome.variant: "none"` to hide it; use `layout.slots["body-top"]`
249
263
  * to replace it wholesale.
250
264
  */
251
265
  export interface IntroCardTokens extends ComponentTokenSet {
@@ -254,6 +268,12 @@ export interface IntroCardTokens extends ComponentTokenSet {
254
268
  padding?: TokenReference<'spacing'>;
255
269
  /** Box-shadow on the intro card (token ref or raw CSS, e.g. `none`). */
256
270
  shadow?: string;
271
+ /** Full border shorthand on the intro card (raw CSS, e.g. `"1px solid rgba(0,0,0,0.1)"`). @default "none" */
272
+ border?: string;
273
+ /** Welcome title typography. */
274
+ title?: TextStyleTokens;
275
+ /** Welcome subtitle typography. */
276
+ subtitle?: TextStyleTokens;
257
277
  }
258
278
 
259
279
  /** Collapsible widget chrome (tool bubbles, reasoning bubbles, approval bubbles). */
@@ -298,6 +318,8 @@ export interface MarkdownTokens {
298
318
  background?: TokenReference<'color'>;
299
319
  borderColor?: TokenReference<'color'>;
300
320
  textColor?: TokenReference<'color'>;
321
+ /** Corner radius; follows `palette.radius.md` by default so square-corner themes get square code blocks. */
322
+ borderRadius?: TokenReference<'radius'>;
301
323
  };
302
324
  /** Table styling. */
303
325
  table?: {
@@ -361,10 +383,28 @@ export interface ReasoningBubbleTokens {
361
383
  shadow: string;
362
384
  }
363
385
 
386
+ /** Scrollbar appearance shared by every scroller in the widget. */
387
+ export interface ScrollbarTokens {
388
+ /** Thumb color. @default semantic.colors.border */
389
+ thumb?: TokenReference<'color'>;
390
+ /** Track color. @default "transparent" */
391
+ track?: TokenReference<'color'>;
392
+ }
393
+
364
394
  /** Composer (message input) chrome. */
365
395
  export interface ComposerChromeTokens {
366
396
  /** Box-shadow on the composer form (raw CSS, e.g. `none`). */
367
397
  shadow: string;
398
+ /** Border color of the composer form. @default semantic.colors.border */
399
+ borderColor?: TokenReference<'color'>;
400
+ /** Inner padding of the composer form (raw CSS shorthand). @default "0.75rem 1rem" */
401
+ padding?: string;
402
+ /** Gap between the textarea row and the actions row. @default "0.5rem" */
403
+ gap?: string;
404
+ /** Composer textarea font-size. @default "0.875rem" */
405
+ fontSize?: string;
406
+ /** Composer textarea line-height. @default "1.25rem" */
407
+ lineHeight?: string;
368
408
  }
369
409
 
370
410
  /** Artifact toolbar chrome. */
@@ -524,6 +564,31 @@ export interface ScrollToBottomTokens extends ComponentTokenSet {
524
564
  iconSize?: string;
525
565
  }
526
566
 
567
+ /** Visual tokens shared by one suggestion presentation variant. */
568
+ export interface SuggestionVariantTokens extends ComponentTokenSet {
569
+ /** Space inside one item, between its icon and its copy. */
570
+ gap?: string;
571
+ /** Space between suggestion items in the container. */
572
+ itemGap?: string;
573
+ minHeight?: string;
574
+ fontSize?: string;
575
+ lineHeight?: string;
576
+ iconSize?: string;
577
+ hoverBackground?: TokenReference<'color'>;
578
+ hoverForeground?: TokenReference<'color'>;
579
+ hoverBorder?: TokenReference<'color'>;
580
+ pressedBackground?: TokenReference<'color'>;
581
+ focusRing?: TokenReference<'color'>;
582
+ disabledOpacity?: string;
583
+ }
584
+
585
+ /** Starter and follow-up suggestion chrome. */
586
+ export interface SuggestionTokens {
587
+ chip: SuggestionVariantTokens;
588
+ card: SuggestionVariantTokens;
589
+ list: SuggestionVariantTokens;
590
+ }
591
+
527
592
  /** Toggle group chrome (used by createToggleGroup). */
528
593
  export interface ToggleGroupTokens {
529
594
  /** Gap between toggle buttons. Default: 0 (connected). */
@@ -549,12 +614,16 @@ export interface ComponentTokens {
549
614
  toolBubble: ToolBubbleTokens;
550
615
  reasoningBubble: ReasoningBubbleTokens;
551
616
  composer: ComposerChromeTokens;
617
+ /** Scrollbar appearance for every scroller in the widget. */
618
+ scrollbar?: ScrollbarTokens;
552
619
  /** Icon button styling tokens. */
553
620
  iconButton?: IconButtonTokens;
554
621
  /** Label button styling tokens. */
555
622
  labelButton?: LabelButtonTokens;
556
623
  /** Scroll-to-bottom indicator styling tokens. */
557
624
  scrollToBottom?: ScrollToBottomTokens;
625
+ /** Starter prompt and follow-up suggestion styling tokens. */
626
+ suggestion?: SuggestionTokens;
558
627
  /** Toggle group styling tokens. */
559
628
  toggleGroup?: ToggleGroupTokens;
560
629
  /** Artifact toolbar, tab strip, and pane chrome. */