@runtypelabs/persona 3.34.1 → 3.35.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 (186) hide show
  1. package/README.md +11 -11
  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-CthJFfNx.d.cts → types-DgYsuwXL.d.cts} +15 -14
  5. package/dist/animations/{types-CthJFfNx.d.ts → types-DgYsuwXL.d.ts} +15 -14
  6. package/dist/animations/wipe.d.cts +1 -1
  7. package/dist/animations/wipe.d.ts +1 -1
  8. package/dist/codegen.cjs +11 -11
  9. package/dist/codegen.js +10 -10
  10. package/dist/index.cjs +46 -44
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +514 -158
  13. package/dist/index.d.ts +514 -158
  14. package/dist/index.global.js +41 -89
  15. package/dist/index.global.js.map +1 -1
  16. package/dist/index.js +46 -44
  17. package/dist/index.js.map +1 -1
  18. package/dist/install.global.js +1 -1
  19. package/dist/install.global.js.map +1 -1
  20. package/dist/launcher.global.js +2 -2
  21. package/dist/launcher.global.js.map +1 -1
  22. package/dist/markdown-parsers.js +53 -0
  23. package/dist/plugin-kit.d.cts +4 -4
  24. package/dist/plugin-kit.d.ts +4 -4
  25. package/dist/runtype-tts.js +1 -0
  26. package/dist/smart-dom-reader.cjs +1 -1
  27. package/dist/smart-dom-reader.d.cts +352 -103
  28. package/dist/smart-dom-reader.d.ts +352 -103
  29. package/dist/smart-dom-reader.js +1 -1
  30. package/dist/theme-editor-preview.cjs +203 -0
  31. package/dist/theme-editor-preview.d.cts +5748 -0
  32. package/dist/theme-editor-preview.d.ts +5748 -0
  33. package/dist/theme-editor-preview.js +203 -0
  34. package/dist/theme-editor.cjs +14 -131
  35. package/dist/theme-editor.d.cts +362 -574
  36. package/dist/theme-editor.d.ts +362 -574
  37. package/dist/theme-editor.js +14 -131
  38. package/dist/theme-reference.cjs +1 -1
  39. package/dist/theme-reference.d.cts +1 -1
  40. package/dist/theme-reference.d.ts +1 -1
  41. package/dist/theme-reference.js +1 -1
  42. package/dist/voice-worklet-player.cjs +80 -0
  43. package/dist/voice-worklet-player.d.cts +215 -0
  44. package/dist/voice-worklet-player.d.ts +215 -0
  45. package/dist/voice-worklet-player.js +80 -0
  46. package/dist/widget.css +54 -36
  47. package/package.json +16 -2
  48. package/src/animations/glyph-cycle.ts +11 -14
  49. package/src/animations/wipe.ts +1 -1
  50. package/src/ask-user-question-tool.test.ts +1 -1
  51. package/src/ask-user-question-tool.ts +6 -8
  52. package/src/client.test.ts +12 -12
  53. package/src/client.ts +30 -31
  54. package/src/codegen.test.ts +1 -1
  55. package/src/codegen.ts +1 -1
  56. package/src/components/approval-bubble.test.ts +1 -1
  57. package/src/components/approval-bubble.ts +3 -3
  58. package/src/components/artifact-pane.ts +3 -3
  59. package/src/components/ask-user-question-bubble.test.ts +14 -14
  60. package/src/components/ask-user-question-bubble.ts +10 -10
  61. package/src/components/composer-builder.ts +17 -16
  62. package/src/components/composer-parts.test.ts +54 -0
  63. package/src/components/composer-parts.ts +95 -126
  64. package/src/components/demo-carousel.ts +1 -1
  65. package/src/components/event-stream-view.test.ts +7 -7
  66. package/src/components/event-stream-view.ts +5 -5
  67. package/src/components/header-builder.ts +33 -27
  68. package/src/components/header-layouts.ts +1 -1
  69. package/src/components/header-parts.test.ts +90 -0
  70. package/src/components/header-parts.ts +62 -93
  71. package/src/components/launcher.test.ts +11 -0
  72. package/src/components/launcher.ts +1 -1
  73. package/src/components/message-bubble.test.ts +74 -2
  74. package/src/components/message-bubble.ts +25 -11
  75. package/src/components/messages.ts +2 -2
  76. package/src/components/panel.test.ts +1 -1
  77. package/src/components/panel.ts +92 -97
  78. package/src/components/pill-composer-builder.ts +28 -24
  79. package/src/components/reasoning-bubble.ts +2 -2
  80. package/src/components/suggestions.ts +1 -1
  81. package/src/components/tool-bubble.ts +1 -1
  82. package/src/components/widget-view.test.ts +181 -0
  83. package/src/components/widget-view.ts +234 -0
  84. package/src/defaults.ts +54 -51
  85. package/src/generated/runtype-openapi-contract.ts +5 -0
  86. package/src/index-core.ts +30 -4
  87. package/src/index-global.ts +73 -6
  88. package/src/index.ts +7 -1
  89. package/src/install.test.ts +11 -11
  90. package/src/install.ts +28 -18
  91. package/src/launcher-global.ts +25 -14
  92. package/src/markdown-parsers-eager.ts +20 -0
  93. package/src/markdown-parsers-entry.ts +4 -0
  94. package/src/markdown-parsers-loader.ts +49 -0
  95. package/src/plugin-kit.test.ts +1 -1
  96. package/src/plugin-kit.ts +7 -7
  97. package/src/plugins/types.ts +5 -5
  98. package/src/postprocessors.ts +27 -14
  99. package/src/runtime/host-layout.test.ts +1 -1
  100. package/src/runtime/host-layout.ts +4 -5
  101. package/src/runtime/init.ts +2 -2
  102. package/src/session.test.ts +5 -6
  103. package/src/session.ts +333 -227
  104. package/src/session.voice.test.ts +146 -0
  105. package/src/session.webmcp.test.ts +24 -25
  106. package/src/smart-dom-reader.ts +3 -4
  107. package/src/styles/widget.css +54 -36
  108. package/src/suggest-replies-tool.test.ts +3 -3
  109. package/src/suggest-replies-tool.ts +5 -5
  110. package/src/testing/index.ts +1 -1
  111. package/src/theme-editor/color-utils.ts +2 -2
  112. package/src/theme-editor/index.ts +4 -13
  113. package/src/theme-editor/presets.ts +1 -1
  114. package/src/theme-editor/preview-utils.ts +22 -20
  115. package/src/theme-editor/preview.ts +4 -1
  116. package/src/theme-editor/role-mappings.ts +3 -3
  117. package/src/theme-editor/sections.ts +21 -20
  118. package/src/theme-editor/types.ts +1 -1
  119. package/src/theme-editor/webmcp/index.ts +1 -1
  120. package/src/theme-editor/webmcp/summary.ts +1 -2
  121. package/src/theme-editor/webmcp/tools.ts +30 -30
  122. package/src/theme-editor/webmcp/types.ts +2 -2
  123. package/src/theme-editor-preview.ts +10 -0
  124. package/src/theme-reference.ts +26 -26
  125. package/src/types.ts +368 -99
  126. package/src/ui.approval-plugin.test.ts +3 -3
  127. package/src/ui.ask-user-question-plugin.test.ts +9 -9
  128. package/src/ui.component-directive.test.ts +4 -4
  129. package/src/ui.composer-bar.test.ts +10 -11
  130. package/src/ui.composer-keyboard.test.ts +5 -5
  131. package/src/ui.header-icon-color.test.ts +59 -0
  132. package/src/ui.scroll.test.ts +5 -6
  133. package/src/ui.stop-button.test.ts +3 -4
  134. package/src/ui.suggest-replies.test.ts +2 -2
  135. package/src/ui.ts +292 -131
  136. package/src/utils/buttons.ts +1 -1
  137. package/src/utils/code-generators.ts +1 -1
  138. package/src/utils/composer-history.ts +3 -3
  139. package/src/utils/dom-context.test.ts +2 -2
  140. package/src/utils/dom-context.ts +7 -7
  141. package/src/utils/dom.test.ts +67 -0
  142. package/src/utils/dom.ts +75 -0
  143. package/src/utils/dropdown.ts +2 -2
  144. package/src/utils/event-stream-buffer.test.ts +2 -2
  145. package/src/utils/message-fingerprint.test.ts +9 -0
  146. package/src/utils/message-fingerprint.ts +5 -0
  147. package/src/utils/morph.ts +1 -1
  148. package/src/utils/sanitize.ts +32 -16
  149. package/src/utils/sequence-buffer.test.ts +8 -8
  150. package/src/utils/sequence-buffer.ts +4 -4
  151. package/src/utils/smart-dom-adapter.test.ts +1 -1
  152. package/src/utils/smart-dom-adapter.ts +4 -5
  153. package/src/utils/speech-text.test.ts +122 -0
  154. package/src/utils/speech-text.ts +101 -0
  155. package/src/utils/stream-animation.test.ts +3 -3
  156. package/src/utils/stream-animation.ts +6 -6
  157. package/src/utils/theme.test.ts +1 -1
  158. package/src/utils/throughput-tracker.test.ts +11 -11
  159. package/src/utils/throughput-tracker.ts +9 -9
  160. package/src/utils/tokens.ts +13 -13
  161. package/src/utils/virtual-scroller.ts +1 -1
  162. package/src/vendor/smart-dom-reader/README.md +4 -4
  163. package/src/vendor/smart-dom-reader/index.d.ts +1 -1
  164. package/src/vendor/smart-dom-reader/index.js +2 -2
  165. package/src/version.ts +1 -1
  166. package/src/voice/audio-playback-manager.test.ts +159 -0
  167. package/src/voice/audio-playback-manager.ts +145 -30
  168. package/src/voice/browser-speech-engine.ts +130 -0
  169. package/src/voice/fallback-speech-engine.ts +89 -0
  170. package/src/voice/index.ts +21 -0
  171. package/src/voice/read-aloud-controller.test.ts +143 -0
  172. package/src/voice/read-aloud-controller.ts +136 -0
  173. package/src/voice/runtype-speech-engine.test.ts +280 -0
  174. package/src/voice/runtype-speech-engine.ts +211 -0
  175. package/src/voice/runtype-tts-entry.ts +10 -0
  176. package/src/voice/runtype-tts-loader.test.ts +47 -0
  177. package/src/voice/runtype-tts-loader.ts +42 -0
  178. package/src/voice/runtype-voice-provider.ts +332 -537
  179. package/src/voice/voice-factory.ts +26 -4
  180. package/src/voice/voice.test.ts +321 -7
  181. package/src/voice/worklet-playback-engine.ts +272 -0
  182. package/src/voice-worklet-player.ts +39 -0
  183. package/src/webmcp-bridge.test.ts +6 -6
  184. package/src/webmcp-bridge.ts +24 -25
  185. package/src/webmcp-polyfill.ts +1 -1
  186. package/src/voice/voice-activity-detector.ts +0 -90
@@ -1027,7 +1027,7 @@
1027
1027
  opacity: 1;
1028
1028
  }
1029
1029
 
1030
- /* Hide tooltips on touch devices hover doesn't exist, and they cause clipping issues */
1030
+ /* Hide tooltips on touch devices: hover doesn't exist, and they cause clipping issues */
1031
1031
  @media (hover: none), (max-width: 500px) {
1032
1032
  .persona-send-button-wrapper:hover .persona-send-button-tooltip,
1033
1033
  .persona-send-button-wrapper:focus-within .persona-send-button-tooltip {
@@ -1144,7 +1144,7 @@
1144
1144
  animation: persona-voice-processing-spin 1.2s linear infinite;
1145
1145
  }
1146
1146
 
1147
- /* Voice speaking animation (gentle pulse slower/subtler than recording) */
1147
+ /* Voice speaking animation (gentle pulse: slower/subtler than recording) */
1148
1148
  @keyframes persona-voice-speaking-pulse {
1149
1149
  0%, 100% { opacity: 1; transform: scale(1); }
1150
1150
  50% { opacity: 0.85; transform: scale(1.03); }
@@ -1759,7 +1759,7 @@
1759
1759
  animation: persona-message-actions-fade-in 0.3s ease-out forwards;
1760
1760
  }
1761
1761
 
1762
- /* ask_user_question collapsed answered state.
1762
+ /* ask_user_question: collapsed answered state.
1763
1763
  * When the user picks an option, the interactive card is replaced with a
1764
1764
  * plain assistant bubble showing the question text. A short fade + slight
1765
1765
  * upward slide sells the "card resolved into history" feel.
@@ -1948,6 +1948,18 @@
1948
1948
  opacity: 0.9;
1949
1949
  }
1950
1950
 
1951
+ /* Read-aloud loading state: spin the loader icon while audio is prepared
1952
+ (near-instant for browser TTS; visible for a hosted/server engine fetch). */
1953
+ .persona-message-action-btn.persona-message-action-loading svg {
1954
+ animation: persona-voice-processing-spin 1s linear infinite;
1955
+ }
1956
+
1957
+ @media (prefers-reduced-motion: reduce) {
1958
+ .persona-message-action-btn.persona-message-action-loading svg {
1959
+ animation: none;
1960
+ }
1961
+ }
1962
+
1951
1963
  /* Vote pop animation */
1952
1964
  @keyframes persona-vote-pop {
1953
1965
  0% { transform: scale(1); }
@@ -2228,7 +2240,7 @@
2228
2240
  background-color: var(--cw-container, #f8fafc);
2229
2241
  }
2230
2242
 
2231
- /* Collapsed live preview blocks match expanded content's 1px border-top with transparent border */
2243
+ /* Collapsed live preview blocks: match expanded content's 1px border-top with transparent border */
2232
2244
  [data-persona-root] [data-persona-collapsed-preview] {
2233
2245
  border-top: 1px solid transparent;
2234
2246
  }
@@ -2380,7 +2392,7 @@
2380
2392
  }
2381
2393
 
2382
2394
  /* ============================================
2383
- Visibility (Tailwind parity prefix persona-)
2395
+ Visibility (Tailwind parity: prefix persona-)
2384
2396
  Shipped widget.css is hand-maintained; these utilities are used in TS but not generated by Tailwind at build time.
2385
2397
  ============================================ */
2386
2398
  [data-persona-root] .persona-hidden {
@@ -2402,7 +2414,7 @@
2402
2414
  gap: var(--persona-artifact-split-gap, 0.5rem);
2403
2415
  }
2404
2416
 
2405
- /* Document toolbar preset optional layout vars: --persona-artifact-doc-toolbar-icon-color, etc. */
2417
+ /* Document toolbar preset: optional layout vars: --persona-artifact-doc-toolbar-icon-color, etc. */
2406
2418
  [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn {
2407
2419
  display: inline-flex;
2408
2420
  align-items: center;
@@ -2495,7 +2507,7 @@
2495
2507
 
2496
2508
  /* ── Composable button utilities ── */
2497
2509
 
2498
- /* Icon button base for all icon-only buttons created by createIconButton() */
2510
+ /* Icon button: base for all icon-only buttons created by createIconButton() */
2499
2511
  [data-persona-root] .persona-icon-btn {
2500
2512
  display: inline-flex;
2501
2513
  align-items: center;
@@ -2525,7 +2537,7 @@
2525
2537
  border-color: var(--persona-icon-btn-active-border, var(--persona-border, #e5e7eb));
2526
2538
  }
2527
2539
 
2528
- /* Label button icon + text button created by createLabelButton() */
2540
+ /* Label button: icon + text button created by createLabelButton() */
2529
2541
  [data-persona-root] .persona-label-btn {
2530
2542
  display: inline-flex;
2531
2543
  align-items: center;
@@ -2644,7 +2656,7 @@
2644
2656
  outline-offset: 2px;
2645
2657
  }
2646
2658
 
2647
- /* Toggle group mutually exclusive button set created by createToggleGroup() */
2659
+ /* Toggle group: mutually exclusive button set created by createToggleGroup() */
2648
2660
  [data-persona-root] .persona-toggle-group {
2649
2661
  display: inline-flex;
2650
2662
  gap: var(--persona-toggle-group-gap, 0);
@@ -2664,7 +2676,7 @@
2664
2676
  border-bottom-right-radius: var(--persona-toggle-group-radius, var(--persona-icon-btn-radius, var(--persona-radius-md, 0.375rem)));
2665
2677
  }
2666
2678
 
2667
- /* Combo button label + chevron with dropdown */
2679
+ /* Combo button: label + chevron with dropdown */
2668
2680
  [data-persona-root] .persona-combo-btn {
2669
2681
  font-size: var(--persona-combo-btn-font-size, 0.8125rem);
2670
2682
  font-weight: var(--persona-combo-btn-font-weight, 600);
@@ -2758,7 +2770,7 @@
2758
2770
  }
2759
2771
  }
2760
2772
 
2761
- /* paneBorderRadius works on any paneAppearance; overflow clips content to rounded shape */
2773
+ /* paneBorderRadius: works on any paneAppearance; overflow clips content to rounded shape */
2762
2774
  [data-persona-root] .persona-artifact-pane {
2763
2775
  border-radius: var(--persona-artifact-pane-radius, 0);
2764
2776
  overflow: hidden;
@@ -2769,7 +2781,7 @@
2769
2781
  );
2770
2782
  }
2771
2783
 
2772
- /* paneAppearance: 'seamless' flush with chat, no border/shadow/gap */
2784
+ /* paneAppearance: 'seamless': flush with chat, no border/shadow/gap */
2773
2785
  /* `position: relative` for resizer overlay; gap 0; positioning is applied in ui.ts */
2774
2786
  [data-persona-root].persona-artifact-appearance-seamless .persona-artifact-split-root {
2775
2787
  position: relative;
@@ -2787,7 +2799,7 @@
2787
2799
  );
2788
2800
  }
2789
2801
 
2790
- /* layout.paneBorder / paneBorderLeft theme overrides (after appearance defaults) */
2802
+ /* layout.paneBorder / paneBorderLeft: theme overrides (after appearance defaults) */
2791
2803
  [data-persona-root].persona-artifact-border-full .persona-artifact-pane {
2792
2804
  border: var(--persona-artifact-pane-border) !important;
2793
2805
  }
@@ -2869,7 +2881,7 @@
2869
2881
  }
2870
2882
 
2871
2883
  /* ============================================================
2872
- Stream animations reveal effects for assistant message text
2884
+ Stream animations: reveal effects for assistant message text
2873
2885
  while streaming. Opt-in via `features.streamAnimation.type`.
2874
2886
  Units are staggered via `--char-index` / `--word-index`
2875
2887
  (set inline on each wrapper span). Timing is configured via
@@ -2879,7 +2891,7 @@
2879
2891
 
2880
2892
  /* Per-char/per-word spans need to be inline-block so `transform` works for
2881
2893
  the rise/fade animations. Each span animates from the moment it is first
2882
- added to the DOM streaming itself provides the visible stagger, so the
2894
+ added to the DOM: streaming itself provides the visible stagger, so the
2883
2895
  CSS animation has no per-index delay. An index-based delay would compound
2884
2896
  with the stream's arrival cadence and leave later chars permanently hidden. */
2885
2897
  [data-persona-root] .persona-stream-char,
@@ -2925,7 +2937,7 @@
2925
2937
  ease-out both;
2926
2938
  }
2927
2939
 
2928
- /* The following animations live in subpath plugin modules their CSS is
2940
+ /* The following animations live in subpath plugin modules: their CSS is
2929
2941
  injected by the plugin when activated, not by the core stylesheet:
2930
2942
  - `wipe` → @runtypelabs/persona/animations/wipe
2931
2943
  - `glyph-cycle` → @runtypelabs/persona/animations/glyph-cycle */
@@ -2955,6 +2967,13 @@
2955
2967
  animation: persona-stream-blink 1s steps(1) infinite;
2956
2968
  }
2957
2969
 
2970
+ /* ---------- inline timestamp (trails the last line of message text) ---------- */
2971
+ [data-persona-root] .persona-timestamp-inline {
2972
+ display: inline-block;
2973
+ margin-left: 0.5rem;
2974
+ white-space: nowrap;
2975
+ }
2976
+
2958
2977
  /* ---------- skeleton placeholder (pre-first-token) ---------- */
2959
2978
  @keyframes persona-stream-skeleton-shimmer {
2960
2979
  0% { background-position: 200% 0; }
@@ -3002,7 +3021,7 @@
3002
3021
  }
3003
3022
 
3004
3023
  /* ========================================================================
3005
- * ask_user_question built-in answer-pill sheet
3024
+ * ask_user_question: built-in answer-pill sheet
3006
3025
  * Slides in over the composer when the assistant invokes `ask_user_question`.
3007
3026
  * Overridable via `config.features.askUserQuestion.styles`.
3008
3027
  * ======================================================================== */
@@ -3011,7 +3030,7 @@
3011
3030
  display: none !important;
3012
3031
  }
3013
3032
 
3014
- /* In-transcript stub small "Awaiting…" chip. */
3033
+ /* In-transcript stub: small "Awaiting…" chip. */
3015
3034
  [data-persona-root] .persona-ask-stub {
3016
3035
  padding: 0.35rem 0.7rem;
3017
3036
  border-radius: 999px;
@@ -3025,7 +3044,7 @@
3025
3044
  font-weight: 500;
3026
3045
  }
3027
3046
 
3028
- /* Sheet container absolute inside composerOverlay. */
3047
+ /* Sheet container: absolute inside composerOverlay. */
3029
3048
  [data-persona-root] .persona-ask-sheet {
3030
3049
  position: absolute;
3031
3050
  left: 0.75rem;
@@ -3053,7 +3072,7 @@
3053
3072
  pointer-events: none;
3054
3073
  }
3055
3074
 
3056
- /* Header row question + close button */
3075
+ /* Header row: question + close button */
3057
3076
  [data-persona-root] .persona-ask-sheet-header {
3058
3077
  margin-bottom: 0.55rem;
3059
3078
  }
@@ -3094,7 +3113,7 @@
3094
3113
  display: none;
3095
3114
  }
3096
3115
 
3097
- /* Option list stacked one per row by default ("rows" layout). The "pills"
3116
+ /* Option list: stacked one per row by default ("rows" layout). The "pills"
3098
3117
  layout opt-in switches to horizontal wrap with compact pills; see also
3099
3118
  horizontalPillsAskPlugin example for a custom variant. */
3100
3119
  [data-persona-root] .persona-ask-pills {
@@ -3111,7 +3130,7 @@
3111
3130
  gap: 0.4rem;
3112
3131
  }
3113
3132
 
3114
- /* Pills layout opt-in horizontal wrap, auto-width compact pills. */
3133
+ /* Pills layout opt-in: horizontal wrap, auto-width compact pills. */
3115
3134
  [data-persona-root] .persona-ask-sheet--pills .persona-ask-pills:not(.persona-ask-pills--rows) {
3116
3135
  flex-direction: row;
3117
3136
  flex-wrap: wrap;
@@ -3188,7 +3207,7 @@
3188
3207
  100% { background-position: -100% 50%; }
3189
3208
  }
3190
3209
 
3191
- /* Row layout full-width stacked rows with always-visible description
3210
+ /* Row layout: full-width stacked rows with always-visible description
3192
3211
  and a right-edge affordance (number badge for single-select, check for
3193
3212
  multi-select). Layered on top of .persona-ask-pill base styles. */
3194
3213
  [data-persona-root] .persona-ask-row {
@@ -3291,7 +3310,7 @@
3291
3310
  height: 3rem;
3292
3311
  }
3293
3312
 
3294
- /* Other row (rows mode) composite row that contains the free-text input
3313
+ /* Other row (rows mode): composite row that contains the free-text input
3295
3314
  * directly. The input fills the row body; the badge sits on the right. */
3296
3315
  [data-persona-root] .persona-ask-row--other {
3297
3316
  cursor: text;
@@ -3358,7 +3377,7 @@
3358
3377
  cursor: not-allowed;
3359
3378
  }
3360
3379
 
3361
- /* Nav row Back / Skip / Next-or-Submit buttons for grouped payloads. */
3380
+ /* Nav row: Back / Skip / Next-or-Submit buttons for grouped payloads. */
3362
3381
  [data-persona-root] .persona-ask-nav-back,
3363
3382
  [data-persona-root] .persona-ask-nav-skip {
3364
3383
  padding: 0.45rem 0.85rem;
@@ -3416,11 +3435,11 @@
3416
3435
  * `applyComposerBarGeometry()` in ui.ts. The pill composer
3417
3436
  * (`pill-composer-builder.ts`) ships with its own clean className
3418
3437
  * (`persona-pill-composer`, no `persona-flex-col` / `persona-rounded-2xl`
3419
- * baggage), so layout CSS does not need to fight utility classes no
3438
+ * baggage), so layout CSS does not need to fight utility classes: no
3420
3439
  * !important. The pill is the visible element in both collapsed and
3421
3440
  * expanded states; only the panel above it appears/disappears.
3422
3441
  *
3423
- * Avoid `transform: scale(...)` on the wrapper it breaks the textarea
3442
+ * Avoid `transform: scale(...)` on the wrapper: it breaks the textarea
3424
3443
  * caret/IME.
3425
3444
  * ======================================================================= */
3426
3445
  .persona-widget-wrapper[data-persona-composer-bar] {
@@ -3446,14 +3465,14 @@
3446
3465
  * so `top/left/transform` resolve to their auto/none defaults) to its
3447
3466
  * expanded position. The default `transform 220ms ease` transition would
3448
3467
  * interpolate `none → translate(...)` and visibly slide the wrapper from
3449
- * its static-default origin toward its target diagonally for modal,
3468
+ * its static-default origin toward its target: diagonally for modal,
3450
3469
  * horizontally from the right for anchored. Neither mode is morphing
3451
3470
  * something the user can see (the wrapper is invisible when collapsed
3452
3471
  * because the pill lives in pillRoot, not the wrapper), so the slide
3453
3472
  * is pure motion noise. Disable geometry transitions for both; the
3454
3473
  * container's opacity fade-in keyframe is the reveal.
3455
3474
  *
3456
- * Fullscreen intentionally keeps its geometry transition that's the
3475
+ * Fullscreen intentionally keeps its geometry transition: that's the
3457
3476
  * one mode where the wrapper genuinely morphs (empty → full viewport),
3458
3477
  * and the staggered fade-in cascade below is built to mask the
3459
3478
  * outer-edge/inner-content desync during that morph. */
@@ -3489,7 +3508,7 @@
3489
3508
  padding: 4px 0;
3490
3509
  }
3491
3510
 
3492
- /* Responsive default width for the collapsed pill applied only when
3511
+ /* Responsive default width for the collapsed pill: applied only when
3493
3512
  * `applyComposerBarGeometry()` leaves the wrapper's inline width empty
3494
3513
  * (i.e., the user did NOT set `composerBar.collapsedMaxWidth`). Inline
3495
3514
  * width from a user config still wins via specificity (inline > class). */
@@ -3543,8 +3562,7 @@
3543
3562
  * In composer-bar mode the pill (`footer`) and peek banner live in a
3544
3563
  * viewport-fixed sibling of the wrapper, NOT inside the wrapper. Reason:
3545
3564
  * modal mode applies `transform: translate(-50%, -50%)` to the wrapper,
3546
- * which establishes a containing block for `position: fixed` descendants
3547
- * a fixed pill inside a transformed wrapper would be positioned relative
3565
+ * which establishes a containing block for `position: fixed` descendants: * a fixed pill inside a transformed wrapper would be positioned relative
3548
3566
  * to the wrapper, not the viewport.
3549
3567
  *
3550
3568
  * Bottom offset and (optionally) width are written inline by ui.ts based
@@ -3574,7 +3592,7 @@
3574
3592
  }
3575
3593
  }
3576
3594
 
3577
- /* Container hidden when the chat is collapsed only the pill is visible. */
3595
+ /* Container hidden when the chat is collapsed: only the pill is visible. */
3578
3596
  [data-persona-composer-bar][data-state="collapsed"] .persona-widget-container {
3579
3597
  display: none;
3580
3598
  }
@@ -3614,7 +3632,7 @@
3614
3632
  transform 150ms ease,
3615
3633
  background-color 150ms ease;
3616
3634
  text-align: left;
3617
- /* Full-width row that matches the pill below text takes the middle
3635
+ /* Full-width row that matches the pill below: text takes the middle
3618
3636
  * (flex: 1 1 auto) and the chevron pins to the right edge. */
3619
3637
  width: 100%;
3620
3638
  align-self: stretch;
@@ -3700,7 +3718,7 @@
3700
3718
  * (including the area behind the fixed pill) so messages scroll under the
3701
3719
  * pill. The body itself drops its bottom padding so its background extends
3702
3720
  * to the viewport edge; reachability for the last bubble comes from
3703
- * padding-bottom on the messages wrapper (body's last child) that
3721
+ * padding-bottom on the messages wrapper (body's last child): that
3704
3722
  * pushes the final message above the pill area rather than hiding it
3705
3723
  * behind. */
3706
3724
  .persona-widget-wrapper[data-persona-composer-bar][data-expanded-size="fullscreen"] .persona-widget-body {
@@ -3726,7 +3744,7 @@
3726
3744
  * so the box appears to materialize as it grows rather than snap into
3727
3745
  * existence at frame 0.
3728
3746
  * - Body content (intro card, messages, composer overlay) fades in AFTER
3729
- * the geometry has settled `animation-delay: 220ms` plus
3747
+ * the geometry has settled: `animation-delay: 220ms` plus
3730
3748
  * `animation-fill-mode: both` keeps it at opacity 0 during the morph.
3731
3749
  *
3732
3750
  * Because `[data-state="collapsed"] .persona-widget-container { display:
@@ -331,11 +331,11 @@ describe("AgentWidgetSession - suggest_replies fire-and-forget auto-resolve", ()
331
331
  for (let i = 0; i < 6; i++) await Promise.resolve();
332
332
  };
333
333
 
334
- it("auto-POSTs ONE /resume with the canned output after stream idle no bridge, no WebMCP config", async () => {
334
+ it("auto-POSTs ONE /resume with the canned output after stream idle: no bridge, no WebMCP config", async () => {
335
335
  const { session, executeSpy, resumeSpy } = makeSession();
336
336
 
337
337
  feed(session, suggestAwait("toolu_S"));
338
- // Not resolved at step_await receipt only after the stream ends.
338
+ // Not resolved at step_await receipt: only after the stream ends.
339
339
  expect(resumeSpy).not.toHaveBeenCalled();
340
340
 
341
341
  endStream(session);
@@ -423,7 +423,7 @@ describe("AgentWidgetSession - suggest_replies fire-and-forget auto-resolve", ()
423
423
  expect(resumeSpy).toHaveBeenCalledTimes(1);
424
424
 
425
425
  // Hydration (e.g. storage restore) wipes webMcpInflightKeys /
426
- // webMcpResolvedKeys only the suggestRepliesResolved metadata persisted
426
+ // webMcpResolvedKeys: only the suggestRepliesResolved metadata persisted
427
427
  // on the message survives to block a replayed step_await.
428
428
  const resolved = (
429
429
  session as unknown as { messages: AgentWidgetMessage[] }
@@ -2,11 +2,11 @@
2
2
  * Built-in `suggest_replies` client tool.
3
3
  *
4
4
  * The widget can advertise this tool to the agent on every dispatch via
5
- * `clientTools[]` (set `features.suggestReplies.expose: true`) the same
5
+ * `clientTools[]` (set `features.suggestReplies.expose: true`): the same
6
6
  * wire surface as `ask_user_question` and WebMCP page tools. When the model
7
7
  * calls it, the execution pauses with a `step_await`
8
8
  * (`awaitReason: "local_tool_required"`); unlike `ask_user_question`, the
9
- * widget resolves it FIRE-AND-FORGET it renders the suggestions as tappable
9
+ * widget resolves it FIRE-AND-FORGET: it renders the suggestions as tappable
10
10
  * chips above the composer and immediately resumes the execution with a
11
11
  * canned "shown" result, so the agent's turn completes without waiting on the
12
12
  * user. Tapping a chip sends its text verbatim as the user's next message.
@@ -26,7 +26,7 @@ import type {
26
26
 
27
27
  export const SUGGEST_REPLIES_TOOL_NAME = "suggest_replies";
28
28
 
29
- /** Renderer cap payloads beyond this are truncated with a console warning. */
29
+ /** Renderer cap: payloads beyond this are truncated with a console warning. */
30
30
  export const SUGGEST_REPLIES_MAX = 4;
31
31
 
32
32
  /**
@@ -64,7 +64,7 @@ export const SUGGEST_REPLIES_CLIENT_TOOL: ClientToolDefinition = {
64
64
  "complete. Each suggestion is sent verbatim as the user's next message, " +
65
65
  'so phrase suggestions in the user\'s voice (e.g. "Tell me more about ' +
66
66
  'pricing"). Keep them short and distinct. The result only confirms the ' +
67
- "suggestions were shown do not add further commentary after calling " +
67
+ "suggestions were shown: do not add further commentary after calling " +
68
68
  "this tool; end your turn.",
69
69
  parametersSchema: SUGGEST_REPLIES_PARAMETERS_SCHEMA,
70
70
  origin: "sdk",
@@ -141,7 +141,7 @@ export const latestAgentSuggestions = (
141
141
  /**
142
142
  * Gate for advertising the tool: `expose` opts it into the agent's catalog,
143
143
  * and `enabled !== false` guarantees the widget will actually auto-resolve
144
- * and render chips for it exposing the tool with the feature disabled
144
+ * and render chips for it: exposing the tool with the feature disabled
145
145
  * would park the execution on a generic tool bubble with no resume coming.
146
146
  */
147
147
  export const shouldExposeSuggestReplies = (
@@ -1,4 +1,4 @@
1
- /** @runtypelabs/persona/testing Helpers for mocking SSE streams in demos, previews, and tests. */
1
+ /** @runtypelabs/persona/testing: Helpers for mocking SSE streams in demos, previews, and tests. */
2
2
 
3
3
  export {
4
4
  createMockSSEStream,
@@ -1,4 +1,4 @@
1
- /** Color parsing, normalization, and scale generation utilities (pure functions no DOM) */
1
+ /** Color parsing, normalization, and scale generation utilities (pure functions: no DOM) */
2
2
 
3
3
  import type { ColorShade } from '../types/theme';
4
4
 
@@ -68,7 +68,7 @@ export function isValidHex(value: string): boolean {
68
68
  * Parse an `rgb()` / `rgba()` string into `#rrggbb`. Accepts integer (0-255) or
69
69
  * percentage channels; any alpha component is dropped. Returns `null` when the
70
70
  * string is not a parseable rgb/rgba value, so callers can fall back. This lets
71
- * the HSL/luminance paths which otherwise `parseInt` hex digits accept the
71
+ * the HSL/luminance paths, which otherwise `parseInt` hex digits, accept the
72
72
  * `rgb()` inputs that `coerceColor` admits without producing `#NaNNaNNaN`.
73
73
  */
74
74
  export function rgbToHex(value: string): string | null {
@@ -1,4 +1,4 @@
1
- /** @runtypelabs/persona/theme-editor Headless theme editor core */
1
+ /** @runtypelabs/persona/theme-editor: Headless theme editor core */
2
2
 
3
3
  // Types
4
4
  export type {
@@ -55,18 +55,9 @@ export {
55
55
  getThemeEditorPreset,
56
56
  } from './presets';
57
57
 
58
- // Preview renderer
59
- export { createThemePreview } from './preview';
60
- export type {
61
- ThemePreviewOptions,
62
- ThemePreviewHandle,
63
- PreviewLifecycleContext,
64
- PreviewDevice,
65
- PreviewShellMode,
66
- CompareMode,
67
- } from './preview';
68
-
69
- // Preview building blocks (for advanced/custom preview renderers)
58
+ // Preview building blocks (for advanced/custom preview renderers).
59
+ // For the full iframe preview helper (`createThemePreview`), use
60
+ // `@runtypelabs/persona/theme-editor/preview`.
70
61
  export {
71
62
  DEVICE_DIMENSIONS,
72
63
  ZOOM_MIN,
@@ -1,4 +1,4 @@
1
- /** Theme editor presets unified collection of built-in presets */
1
+ /** Theme editor presets: unified collection of built-in presets */
2
2
 
3
3
  import type { ThemeEditorPreset } from './types';
4
4
 
@@ -212,9 +212,9 @@ export type PreviewTranscriptEntryPreset =
212
212
  const PREVIEW_TRANSCRIPT_PRESET_LABELS: Record<PreviewTranscriptEntryPreset, string> = {
213
213
  'user-message': 'User message',
214
214
  'assistant-message': 'Assistant message',
215
- 'assistant-code-block': 'Assistant code block',
216
- 'assistant-markdown-table': 'Assistant markdown table',
217
- 'assistant-image': 'Assistant image',
215
+ 'assistant-code-block': 'Assistant: code block',
216
+ 'assistant-markdown-table': 'Assistant: markdown table',
217
+ 'assistant-image': 'Assistant: image',
218
218
  'reasoning-streaming': 'Reasoning (streaming)',
219
219
  'reasoning-complete': 'Reasoning (complete)',
220
220
  'tool-running': 'Tool call (running)',
@@ -290,7 +290,7 @@ export function createPreviewTranscriptEntry(
290
290
  id: `preview-seq-assistant-image-${suffix}`,
291
291
  role: 'assistant',
292
292
  content: [
293
- "Here's the reference diagram you asked for let me know if you'd like a different view:",
293
+ "Here's the reference diagram you asked for: let me know if you'd like a different view:",
294
294
  '',
295
295
  '![Stream animation reference](https://placehold.co/320x200/png?text=Stream+Animation)',
296
296
  '',
@@ -600,22 +600,30 @@ export interface PreviewConfigOptions {
600
600
  appendedMessages?: AgentWidgetMessage[];
601
601
  }
602
602
 
603
- export function buildPreviewConfig(
603
+ function buildPreviewBaseConfig(
604
604
  options: PreviewConfigOptions,
605
605
  shellModeOverride?: 'light' | 'dark'
606
606
  ): AgentWidgetConfig {
607
607
  const theme = options.theme ? createTheme(options.theme, { validate: false }) : createTheme();
608
- const scene = options.scene ?? 'conversation';
609
-
610
- const base = {
608
+ return {
611
609
  ...DEFAULT_WIDGET_CONFIG,
612
610
  ...options.config,
613
611
  theme,
614
612
  darkTheme: options.darkTheme,
615
613
  colorScheme: shellModeOverride ?? (options.config?.colorScheme as string) ?? 'light',
616
614
  } as AgentWidgetConfig;
615
+ }
617
616
 
618
- return applySceneConfig(base, scene, options.appendedMessages ?? []);
617
+ export function buildPreviewConfig(
618
+ options: PreviewConfigOptions,
619
+ shellModeOverride?: 'light' | 'dark'
620
+ ): AgentWidgetConfig {
621
+ const scene = options.scene ?? 'conversation';
622
+ return applySceneConfig(
623
+ buildPreviewBaseConfig(options, shellModeOverride),
624
+ scene,
625
+ options.appendedMessages ?? []
626
+ );
619
627
  }
620
628
 
621
629
  export function buildPreviewConfigWithMessages(
@@ -623,16 +631,10 @@ export function buildPreviewConfigWithMessages(
623
631
  messages: AgentWidgetMessage[],
624
632
  shellModeOverride?: 'light' | 'dark'
625
633
  ): AgentWidgetConfig {
626
- const theme = options.theme ? createTheme(options.theme, { validate: false }) : createTheme();
627
634
  const scene = options.scene ?? 'conversation';
628
-
629
- const base = {
630
- ...DEFAULT_WIDGET_CONFIG,
631
- ...options.config,
632
- theme,
633
- darkTheme: options.darkTheme,
634
- colorScheme: shellModeOverride ?? (options.config?.colorScheme as string) ?? 'light',
635
- } as AgentWidgetConfig;
636
-
637
- return applySceneConfig(base, scene, messages);
635
+ return applySceneConfig(
636
+ buildPreviewBaseConfig(options, shellModeOverride),
637
+ scene,
638
+ messages
639
+ );
638
640
  }
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * Imperative preview renderer for the theme editor.
3
+ * Import from `@runtypelabs/persona/theme-editor/preview` (not the headless
4
+ * `@runtypelabs/persona/theme-editor` subpath, which omits the widget runtime).
5
+ *
3
6
  * Manages iframe-based widget previews with device frames, zoom, scenes, and compare mode.
4
- * No external DOM dependencies only needs a container element to mount into.
7
+ * No external DOM dependencies: only needs a container element to mount into.
5
8
  *
6
9
  * For advanced preview needs (background URLs, inline editing, contrast checking),
7
10
  * use the lifecycle hooks in `ThemePreviewOptions` and import shared building blocks
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Maps high-level editor choices (family + intensity) to concrete palette
5
5
  * token references across multiple component/semantic paths. This is the
6
- * core of the "Interface Roles" editor section one picker writes to
6
+ * core of the "Interface Roles" editor section: one picker writes to
7
7
  * many tokens atomically.
8
8
  *
9
9
  * All functions are pure and headless (no DOM).
@@ -202,7 +202,7 @@ function resolveTarget(
202
202
  return resolveHeaderTarget(family, solid, target);
203
203
  }
204
204
 
205
- // Input has special handling foreground target is the placeholder
205
+ // Input has special handling: foreground target is the placeholder
206
206
  if (roleId === 'role-input') {
207
207
  return resolveInputTarget(family, solid, target);
208
208
  }
@@ -329,7 +329,7 @@ export function detectRoleAssignment(
329
329
  : null;
330
330
  if (!family) return null;
331
331
 
332
- // Try both intensities shade-based guessing doesn't work for all target kinds
332
+ // Try both intensities: shade-based guessing doesn't work for all target kinds
333
333
  for (const intensity of ['solid', 'soft'] as const) {
334
334
  const expected = resolveRoleAssignment(family, intensity, role);
335
335
  const allMatch = role.targets.every((t) => {