@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.
- package/README.md +11 -11
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-CthJFfNx.d.cts → types-DgYsuwXL.d.cts} +15 -14
- package/dist/animations/{types-CthJFfNx.d.ts → types-DgYsuwXL.d.ts} +15 -14
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/codegen.cjs +11 -11
- package/dist/codegen.js +10 -10
- package/dist/index.cjs +46 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +514 -158
- package/dist/index.d.ts +514 -158
- package/dist/index.global.js +41 -89
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +46 -44
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/launcher.global.js +2 -2
- package/dist/launcher.global.js.map +1 -1
- package/dist/markdown-parsers.js +53 -0
- package/dist/plugin-kit.d.cts +4 -4
- package/dist/plugin-kit.d.ts +4 -4
- package/dist/runtype-tts.js +1 -0
- package/dist/smart-dom-reader.cjs +1 -1
- package/dist/smart-dom-reader.d.cts +352 -103
- package/dist/smart-dom-reader.d.ts +352 -103
- package/dist/smart-dom-reader.js +1 -1
- package/dist/theme-editor-preview.cjs +203 -0
- package/dist/theme-editor-preview.d.cts +5748 -0
- package/dist/theme-editor-preview.d.ts +5748 -0
- package/dist/theme-editor-preview.js +203 -0
- package/dist/theme-editor.cjs +14 -131
- package/dist/theme-editor.d.cts +362 -574
- package/dist/theme-editor.d.ts +362 -574
- package/dist/theme-editor.js +14 -131
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +1 -1
- package/dist/theme-reference.d.ts +1 -1
- package/dist/theme-reference.js +1 -1
- package/dist/voice-worklet-player.cjs +80 -0
- package/dist/voice-worklet-player.d.cts +215 -0
- package/dist/voice-worklet-player.d.ts +215 -0
- package/dist/voice-worklet-player.js +80 -0
- package/dist/widget.css +54 -36
- package/package.json +16 -2
- package/src/animations/glyph-cycle.ts +11 -14
- package/src/animations/wipe.ts +1 -1
- package/src/ask-user-question-tool.test.ts +1 -1
- package/src/ask-user-question-tool.ts +6 -8
- package/src/client.test.ts +12 -12
- package/src/client.ts +30 -31
- package/src/codegen.test.ts +1 -1
- package/src/codegen.ts +1 -1
- package/src/components/approval-bubble.test.ts +1 -1
- package/src/components/approval-bubble.ts +3 -3
- package/src/components/artifact-pane.ts +3 -3
- package/src/components/ask-user-question-bubble.test.ts +14 -14
- package/src/components/ask-user-question-bubble.ts +10 -10
- package/src/components/composer-builder.ts +17 -16
- package/src/components/composer-parts.test.ts +54 -0
- package/src/components/composer-parts.ts +95 -126
- package/src/components/demo-carousel.ts +1 -1
- package/src/components/event-stream-view.test.ts +7 -7
- package/src/components/event-stream-view.ts +5 -5
- package/src/components/header-builder.ts +33 -27
- package/src/components/header-layouts.ts +1 -1
- package/src/components/header-parts.test.ts +90 -0
- package/src/components/header-parts.ts +62 -93
- package/src/components/launcher.test.ts +11 -0
- package/src/components/launcher.ts +1 -1
- package/src/components/message-bubble.test.ts +74 -2
- package/src/components/message-bubble.ts +25 -11
- package/src/components/messages.ts +2 -2
- package/src/components/panel.test.ts +1 -1
- package/src/components/panel.ts +92 -97
- package/src/components/pill-composer-builder.ts +28 -24
- package/src/components/reasoning-bubble.ts +2 -2
- package/src/components/suggestions.ts +1 -1
- package/src/components/tool-bubble.ts +1 -1
- package/src/components/widget-view.test.ts +181 -0
- package/src/components/widget-view.ts +234 -0
- package/src/defaults.ts +54 -51
- package/src/generated/runtype-openapi-contract.ts +5 -0
- package/src/index-core.ts +30 -4
- package/src/index-global.ts +73 -6
- package/src/index.ts +7 -1
- package/src/install.test.ts +11 -11
- package/src/install.ts +28 -18
- package/src/launcher-global.ts +25 -14
- package/src/markdown-parsers-eager.ts +20 -0
- package/src/markdown-parsers-entry.ts +4 -0
- package/src/markdown-parsers-loader.ts +49 -0
- package/src/plugin-kit.test.ts +1 -1
- package/src/plugin-kit.ts +7 -7
- package/src/plugins/types.ts +5 -5
- package/src/postprocessors.ts +27 -14
- package/src/runtime/host-layout.test.ts +1 -1
- package/src/runtime/host-layout.ts +4 -5
- package/src/runtime/init.ts +2 -2
- package/src/session.test.ts +5 -6
- package/src/session.ts +333 -227
- package/src/session.voice.test.ts +146 -0
- package/src/session.webmcp.test.ts +24 -25
- package/src/smart-dom-reader.ts +3 -4
- package/src/styles/widget.css +54 -36
- package/src/suggest-replies-tool.test.ts +3 -3
- package/src/suggest-replies-tool.ts +5 -5
- package/src/testing/index.ts +1 -1
- package/src/theme-editor/color-utils.ts +2 -2
- package/src/theme-editor/index.ts +4 -13
- package/src/theme-editor/presets.ts +1 -1
- package/src/theme-editor/preview-utils.ts +22 -20
- package/src/theme-editor/preview.ts +4 -1
- package/src/theme-editor/role-mappings.ts +3 -3
- package/src/theme-editor/sections.ts +21 -20
- package/src/theme-editor/types.ts +1 -1
- package/src/theme-editor/webmcp/index.ts +1 -1
- package/src/theme-editor/webmcp/summary.ts +1 -2
- package/src/theme-editor/webmcp/tools.ts +30 -30
- package/src/theme-editor/webmcp/types.ts +2 -2
- package/src/theme-editor-preview.ts +10 -0
- package/src/theme-reference.ts +26 -26
- package/src/types.ts +368 -99
- package/src/ui.approval-plugin.test.ts +3 -3
- package/src/ui.ask-user-question-plugin.test.ts +9 -9
- package/src/ui.component-directive.test.ts +4 -4
- package/src/ui.composer-bar.test.ts +10 -11
- package/src/ui.composer-keyboard.test.ts +5 -5
- package/src/ui.header-icon-color.test.ts +59 -0
- package/src/ui.scroll.test.ts +5 -6
- package/src/ui.stop-button.test.ts +3 -4
- package/src/ui.suggest-replies.test.ts +2 -2
- package/src/ui.ts +292 -131
- package/src/utils/buttons.ts +1 -1
- package/src/utils/code-generators.ts +1 -1
- package/src/utils/composer-history.ts +3 -3
- package/src/utils/dom-context.test.ts +2 -2
- package/src/utils/dom-context.ts +7 -7
- package/src/utils/dom.test.ts +67 -0
- package/src/utils/dom.ts +75 -0
- package/src/utils/dropdown.ts +2 -2
- package/src/utils/event-stream-buffer.test.ts +2 -2
- package/src/utils/message-fingerprint.test.ts +9 -0
- package/src/utils/message-fingerprint.ts +5 -0
- package/src/utils/morph.ts +1 -1
- package/src/utils/sanitize.ts +32 -16
- package/src/utils/sequence-buffer.test.ts +8 -8
- package/src/utils/sequence-buffer.ts +4 -4
- package/src/utils/smart-dom-adapter.test.ts +1 -1
- package/src/utils/smart-dom-adapter.ts +4 -5
- package/src/utils/speech-text.test.ts +122 -0
- package/src/utils/speech-text.ts +101 -0
- package/src/utils/stream-animation.test.ts +3 -3
- package/src/utils/stream-animation.ts +6 -6
- package/src/utils/theme.test.ts +1 -1
- package/src/utils/throughput-tracker.test.ts +11 -11
- package/src/utils/throughput-tracker.ts +9 -9
- package/src/utils/tokens.ts +13 -13
- package/src/utils/virtual-scroller.ts +1 -1
- package/src/vendor/smart-dom-reader/README.md +4 -4
- package/src/vendor/smart-dom-reader/index.d.ts +1 -1
- package/src/vendor/smart-dom-reader/index.js +2 -2
- package/src/version.ts +1 -1
- package/src/voice/audio-playback-manager.test.ts +159 -0
- package/src/voice/audio-playback-manager.ts +145 -30
- package/src/voice/browser-speech-engine.ts +130 -0
- package/src/voice/fallback-speech-engine.ts +89 -0
- package/src/voice/index.ts +21 -0
- package/src/voice/read-aloud-controller.test.ts +143 -0
- package/src/voice/read-aloud-controller.ts +136 -0
- package/src/voice/runtype-speech-engine.test.ts +280 -0
- package/src/voice/runtype-speech-engine.ts +211 -0
- package/src/voice/runtype-tts-entry.ts +10 -0
- package/src/voice/runtype-tts-loader.test.ts +47 -0
- package/src/voice/runtype-tts-loader.ts +42 -0
- package/src/voice/runtype-voice-provider.ts +332 -537
- package/src/voice/voice-factory.ts +26 -4
- package/src/voice/voice.test.ts +321 -7
- package/src/voice/worklet-playback-engine.ts +272 -0
- package/src/voice-worklet-player.ts +39 -0
- package/src/webmcp-bridge.test.ts +6 -6
- package/src/webmcp-bridge.ts +24 -25
- package/src/webmcp-polyfill.ts +1 -1
- package/src/voice/voice-activity-detector.ts +0 -90
package/src/styles/widget.css
CHANGED
|
@@ -1027,7 +1027,7 @@
|
|
|
1027
1027
|
opacity: 1;
|
|
1028
1028
|
}
|
|
1029
1029
|
|
|
1030
|
-
/* Hide tooltips on touch devices
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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'
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`)
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 = (
|
package/src/testing/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Color parsing, normalization, and scale generation utilities (pure functions
|
|
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
|
|
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
|
|
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
|
|
59
|
-
|
|
60
|
-
|
|
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,
|
|
@@ -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
|
|
216
|
-
'assistant-markdown-table': 'Assistant
|
|
217
|
-
'assistant-image': 'Assistant
|
|
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
|
|
293
|
+
"Here's the reference diagram you asked for: let me know if you'd like a different view:",
|
|
294
294
|
'',
|
|
295
295
|
'',
|
|
296
296
|
'',
|
|
@@ -600,22 +600,30 @@ export interface PreviewConfigOptions {
|
|
|
600
600
|
appendedMessages?: AgentWidgetMessage[];
|
|
601
601
|
}
|
|
602
602
|
|
|
603
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) => {
|