@runtypelabs/persona 4.7.0 → 4.8.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/dist/animations/glyph-cycle.cjs +2 -2
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/glyph-cycle.js +2 -2
- package/dist/animations/{types-CSmiKRVa.d.cts → types-BsZtXPKK.d.cts} +43 -3
- package/dist/animations/{types-CSmiKRVa.d.ts → types-BsZtXPKK.d.ts} +43 -3
- package/dist/animations/wipe.cjs +2 -2
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/chunk-5EIIHQLQ.js +1 -0
- package/dist/codegen.cjs +12 -12
- package/dist/codegen.js +14 -14
- package/dist/index.cjs +91 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +877 -172
- package/dist/index.d.ts +877 -172
- package/dist/index.global.js +81 -57
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +91 -68
- 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 +3 -2
- package/dist/launcher.global.js.map +1 -1
- package/dist/markdown-parsers.js +24 -24
- package/dist/plugin-kit.cjs +1 -1
- package/dist/plugin-kit.js +1 -1
- package/dist/runtype-tts-entry-UJAEF7NZ.js +1 -0
- package/dist/runtype-tts.js +1 -1
- package/dist/session-reconnect-JKIJBHS5.js +1 -0
- package/dist/smart-dom-reader.cjs +17 -17
- package/dist/smart-dom-reader.d.cts +753 -15
- package/dist/smart-dom-reader.d.ts +753 -15
- package/dist/smart-dom-reader.js +17 -17
- package/dist/testing.cjs +3 -3
- package/dist/testing.js +3 -3
- package/dist/theme-editor-preview.cjs +81 -58
- package/dist/theme-editor-preview.d.cts +761 -15
- package/dist/theme-editor-preview.d.ts +761 -15
- package/dist/theme-editor-preview.js +81 -58
- package/dist/theme-editor.cjs +6 -6
- package/dist/theme-editor.d.cts +753 -15
- package/dist/theme-editor.d.ts +753 -15
- package/dist/theme-editor.js +10 -10
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +74 -0
- package/dist/theme-reference.d.ts +74 -0
- package/dist/theme-reference.js +1 -1
- package/dist/voice-worklet-player.cjs +2 -2
- package/dist/voice-worklet-player.js +2 -2
- package/dist/webmcp-polyfill.js +2 -2
- package/dist/widget.css +1 -1
- package/package.json +2 -3
- package/src/artifacts-session.test.ts +178 -0
- package/src/client.test.ts +186 -1
- package/src/client.ts +92 -9
- package/src/components/artifact-card.test.ts +333 -0
- package/src/components/artifact-card.ts +75 -28
- package/src/components/artifact-inline.test.ts +1328 -0
- package/src/components/artifact-inline.ts +920 -0
- package/src/components/artifact-pane.test.ts +1042 -0
- package/src/components/artifact-pane.ts +440 -131
- package/src/components/artifact-preview.test.ts +1155 -0
- package/src/components/artifact-preview.ts +994 -0
- package/src/components/pill-composer-builder.test.ts +6 -2
- package/src/components/pill-composer-builder.ts +6 -6
- package/src/components/reasoning-bubble.ts +1 -13
- package/src/components/registry.ts +38 -3
- package/src/components/tool-bubble.ts +1 -13
- package/src/defaults.ts +1 -0
- package/src/generated/runtype-openapi-contract.ts +55 -3
- package/src/index-core.ts +20 -1
- package/src/index.ts +8 -0
- package/src/markdown-parsers-loader.test.ts +158 -0
- package/src/markdown-parsers-loader.ts +74 -9
- package/src/runtime/host-layout.test.ts +163 -0
- package/src/runtime/host-layout.ts +110 -7
- package/src/runtime/init.ts +18 -61
- package/src/session.ts +76 -22
- package/src/styles/widget.css +773 -26
- package/src/theme-editor/preview.ts +2 -0
- package/src/theme-editor/sections.test.ts +26 -1
- package/src/theme-editor/sections.ts +10 -2
- package/src/theme-reference.ts +2 -2
- package/src/tool-call-display-defaults.test.ts +1 -0
- package/src/types/theme.ts +77 -0
- package/src/types.ts +516 -17
- package/src/ui.artifact-pane-gating.test.ts +636 -0
- package/src/ui.component-directive.test.ts +104 -0
- package/src/ui.composer-bar.test.ts +60 -2
- package/src/ui.detached-panel.test.ts +1049 -0
- package/src/ui.tool-display.test.ts +51 -0
- package/src/ui.ts +698 -109
- package/src/utils/artifact-custom-actions.ts +128 -0
- package/src/utils/artifact-display.test.ts +42 -0
- package/src/utils/artifact-display.ts +84 -0
- package/src/utils/artifact-file.test.ts +116 -0
- package/src/utils/artifact-file.ts +117 -0
- package/src/utils/artifact-gate.test.ts +112 -5
- package/src/utils/artifact-gate.ts +39 -14
- package/src/utils/artifact-loading-status.ts +55 -0
- package/src/utils/artifact-status-label.ts +190 -0
- package/src/utils/buttons.ts +7 -1
- package/src/utils/code-highlight.test.ts +186 -0
- package/src/utils/code-highlight.ts +400 -0
- package/src/utils/icons.ts +2 -0
- package/src/utils/roving-tablist.test.ts +152 -0
- package/src/utils/roving-tablist.ts +111 -0
- package/src/utils/spinner.ts +45 -0
- package/src/utils/theme.test.ts +48 -0
- package/src/utils/theme.ts +7 -0
- package/src/utils/tokens.ts +91 -0
- package/src/utils/tool-loading-animation.test.ts +32 -0
- package/src/utils/tool-loading-animation.ts +24 -0
- package/dist/chunk-DFBSCFYN.js +0 -1
- package/dist/runtype-tts-entry-HFUV2UF7.js +0 -1
- package/dist/session-reconnect-U77QFUR7.js +0 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { cx } from "./dom";
|
|
2
|
+
|
|
3
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reusable icon spinner: a small SVG ring with a faint full track plus a
|
|
7
|
+
* rotating arc.
|
|
8
|
+
*
|
|
9
|
+
* Icon-first loading is the norm for preview surfaces (Sandpack, YouTube/Figma/
|
|
10
|
+
* CodePen embeds, ChatGPT/Claude/v0) and every major design system (Apple HIG,
|
|
11
|
+
* Material, Carbon, Polaris, Geist); text-only "Loading…" is used by none, and
|
|
12
|
+
* HIG explicitly warns against the vague word "loading". So the default preview
|
|
13
|
+
* indicator is this spinner, with any work-naming label added only later as an
|
|
14
|
+
* escalation. The arc spins via a GPU-friendly `transform: rotate` keyframe
|
|
15
|
+
* (see `.persona-spinner` in widget.css); under `prefers-reduced-motion` the
|
|
16
|
+
* rotation stops and the static arc still reads as a progress ring.
|
|
17
|
+
*
|
|
18
|
+
* Structure and animation live in CSS (class `persona-spinner`); geometry here
|
|
19
|
+
* is viewBox-relative so the rendered size follows `--persona-artifact-spinner-size`.
|
|
20
|
+
*/
|
|
21
|
+
export function createSpinner(className?: string): SVGSVGElement {
|
|
22
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
23
|
+
svg.setAttribute("class", cx("persona-spinner", className));
|
|
24
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
25
|
+
// Decorative: the loading state is announced by surrounding copy/labels, so
|
|
26
|
+
// the spinner itself must not add redundant noise to the accessibility tree.
|
|
27
|
+
svg.setAttribute("aria-hidden", "true");
|
|
28
|
+
svg.setAttribute("focusable", "false");
|
|
29
|
+
|
|
30
|
+
const track = document.createElementNS(SVG_NS, "circle");
|
|
31
|
+
track.setAttribute("class", "persona-spinner-track");
|
|
32
|
+
track.setAttribute("cx", "12");
|
|
33
|
+
track.setAttribute("cy", "12");
|
|
34
|
+
track.setAttribute("r", "9");
|
|
35
|
+
|
|
36
|
+
const arc = document.createElementNS(SVG_NS, "circle");
|
|
37
|
+
arc.setAttribute("class", "persona-spinner-arc");
|
|
38
|
+
arc.setAttribute("cx", "12");
|
|
39
|
+
arc.setAttribute("cy", "12");
|
|
40
|
+
arc.setAttribute("r", "9");
|
|
41
|
+
|
|
42
|
+
svg.appendChild(track);
|
|
43
|
+
svg.appendChild(arc);
|
|
44
|
+
return svg;
|
|
45
|
+
}
|
package/src/utils/theme.test.ts
CHANGED
|
@@ -192,6 +192,38 @@ describe('theme utils', () => {
|
|
|
192
192
|
expect(surfaceVars['--persona-artifact-toolbar-bg']).toBe('#f9fafb');
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
+
it('maps artifact inline chrome tokens to dedicated CSS variables (with semantic refs)', () => {
|
|
196
|
+
const theme = createTheme({
|
|
197
|
+
components: {
|
|
198
|
+
artifact: {
|
|
199
|
+
inline: {
|
|
200
|
+
background: 'semantic.colors.surface',
|
|
201
|
+
border: '1px solid #e5e7eb',
|
|
202
|
+
borderRadius: '12px',
|
|
203
|
+
chromeBackground: 'semantic.colors.container',
|
|
204
|
+
chromeBorder: '#e5e7eb',
|
|
205
|
+
titleColor: 'palette.colors.gray.900',
|
|
206
|
+
mutedColor: 'palette.colors.gray.500',
|
|
207
|
+
frameHeight: '400px',
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
} as any);
|
|
212
|
+
|
|
213
|
+
const cssVars = themeToCssVariables(theme);
|
|
214
|
+
|
|
215
|
+
// semantic.* refs resolve to concrete colors; plain values pass through.
|
|
216
|
+
// surface and container both default to gray.50.
|
|
217
|
+
expect(cssVars['--persona-artifact-inline-bg']).toBe('#f9fafb');
|
|
218
|
+
expect(cssVars['--persona-artifact-inline-border']).toBe('1px solid #e5e7eb');
|
|
219
|
+
expect(cssVars['--persona-artifact-inline-radius']).toBe('12px');
|
|
220
|
+
expect(cssVars['--persona-artifact-inline-chrome-bg']).toBe('#f9fafb');
|
|
221
|
+
expect(cssVars['--persona-artifact-inline-chrome-border']).toBe('#e5e7eb');
|
|
222
|
+
expect(cssVars['--persona-artifact-inline-title-color']).toBe('#111827');
|
|
223
|
+
expect(cssVars['--persona-artifact-inline-muted-color']).toBe('#6b7280');
|
|
224
|
+
expect(cssVars['--persona-artifact-inline-frame-height']).toBe('400px');
|
|
225
|
+
});
|
|
226
|
+
|
|
195
227
|
it('maps component bubble shadow tokens to consumer CSS variables', () => {
|
|
196
228
|
const cfg = {
|
|
197
229
|
colorScheme: 'light' as const,
|
|
@@ -305,4 +337,20 @@ describe('theme utils', () => {
|
|
|
305
337
|
'0 1px 2px rgba(255,0,0,0.5)'
|
|
306
338
|
);
|
|
307
339
|
});
|
|
340
|
+
|
|
341
|
+
it('stamps the resolved color scheme on the root so scheme-scoped CSS (syntax palette) follows the widget, not the OS', () => {
|
|
342
|
+
const el = document.createElement('div');
|
|
343
|
+
applyThemeVariables(el, { colorScheme: 'light' });
|
|
344
|
+
expect(el.getAttribute('data-persona-color-scheme')).toBe('light');
|
|
345
|
+
applyThemeVariables(el, { colorScheme: 'dark' });
|
|
346
|
+
expect(el.getAttribute('data-persona-color-scheme')).toBe('dark');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('maps components.code.background to --persona-code-bg', () => {
|
|
350
|
+
const theme = createTheme({
|
|
351
|
+
components: { code: { background: '#fafafa' } },
|
|
352
|
+
});
|
|
353
|
+
const cssVars = themeToCssVariables(theme);
|
|
354
|
+
expect(cssVars['--persona-code-bg']).toBe('#fafafa');
|
|
355
|
+
});
|
|
308
356
|
});
|
package/src/utils/theme.ts
CHANGED
|
@@ -194,6 +194,13 @@ export const applyThemeVariables = (
|
|
|
194
194
|
for (const [name, value] of Object.entries(cssVars)) {
|
|
195
195
|
element.style.setProperty(name, value);
|
|
196
196
|
}
|
|
197
|
+
|
|
198
|
+
// Stamp the resolved scheme so stylesheet rules can key scheme-specific
|
|
199
|
+
// defaults (e.g. the syntax-highlight palette) off the widget's own color
|
|
200
|
+
// scheme instead of the OS prefers-color-scheme, which diverges whenever a
|
|
201
|
+
// host pins colorScheme. Re-applied on every theme application, so live
|
|
202
|
+
// config updates and theme-observer callbacks keep it current.
|
|
203
|
+
element.setAttribute("data-persona-color-scheme", getColorScheme(config));
|
|
197
204
|
};
|
|
198
205
|
|
|
199
206
|
export const createThemeObserver = (
|
package/src/utils/tokens.ts
CHANGED
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
14
14
|
} from '../defaults';
|
|
15
15
|
|
|
16
|
+
// Detached/docked panel defaults, shared by the panel token defaults, the alias
|
|
17
|
+
// fallbacks below, host-layout, the theme editor, and the artifact gate.
|
|
18
|
+
export const DEFAULT_PANEL_INSET = '16px';
|
|
19
|
+
export const DEFAULT_PANEL_CANVAS_BACKGROUND = 'transparent';
|
|
20
|
+
|
|
16
21
|
export const DEFAULT_PALETTE = {
|
|
17
22
|
colors: {
|
|
18
23
|
primary: {
|
|
@@ -287,6 +292,8 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
|
|
|
287
292
|
maxHeight: 'calc(100vh - 80px)',
|
|
288
293
|
borderRadius: 'palette.radius.xl',
|
|
289
294
|
shadow: 'palette.shadows.xl',
|
|
295
|
+
inset: DEFAULT_PANEL_INSET,
|
|
296
|
+
canvasBackground: DEFAULT_PANEL_CANVAS_BACKGROUND,
|
|
290
297
|
},
|
|
291
298
|
header: {
|
|
292
299
|
// Header role: solid primary
|
|
@@ -742,6 +749,10 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
|
|
|
742
749
|
cssVars['--persona-components-panel-shadow'] ??
|
|
743
750
|
cssVars['--persona-palette-shadows-xl'] ??
|
|
744
751
|
'0 25px 50px -12px rgba(0, 0, 0, 0.25)';
|
|
752
|
+
cssVars['--persona-panel-inset'] =
|
|
753
|
+
cssVars['--persona-components-panel-inset'] ?? DEFAULT_PANEL_INSET;
|
|
754
|
+
cssVars['--persona-panel-canvas-bg'] =
|
|
755
|
+
cssVars['--persona-components-panel-canvasBackground'] ?? DEFAULT_PANEL_CANVAS_BACKGROUND;
|
|
745
756
|
cssVars['--persona-launcher-shadow'] =
|
|
746
757
|
cssVars['--persona-components-launcher-shadow'] ??
|
|
747
758
|
'0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)';
|
|
@@ -967,6 +978,10 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
|
|
|
967
978
|
if (t.iconBorder) cssVars['--persona-artifact-toolbar-icon-border'] = t.iconBorder;
|
|
968
979
|
if (t.toggleGroupGap) cssVars['--persona-artifact-toolbar-toggle-group-gap'] = t.toggleGroupGap;
|
|
969
980
|
if (t.toggleBorderRadius) cssVars['--persona-artifact-toolbar-toggle-radius'] = t.toggleBorderRadius;
|
|
981
|
+
if (t.toggleGroupPadding) cssVars['--persona-artifact-toolbar-toggle-group-padding'] = t.toggleGroupPadding;
|
|
982
|
+
if (t.toggleGroupBorder) cssVars['--persona-artifact-toolbar-toggle-group-border'] = t.toggleGroupBorder;
|
|
983
|
+
if (t.toggleGroupBorderRadius) cssVars['--persona-artifact-toolbar-toggle-group-radius'] = t.toggleGroupBorderRadius;
|
|
984
|
+
if (t.toggleGroupBackground) cssVars['--persona-artifact-toolbar-toggle-group-bg'] = resolveTokenValue(theme, t.toggleGroupBackground) ?? t.toggleGroupBackground;
|
|
970
985
|
if (t.copyBackground) cssVars['--persona-artifact-toolbar-copy-bg'] = t.copyBackground;
|
|
971
986
|
if (t.copyBorder) cssVars['--persona-artifact-toolbar-copy-border'] = t.copyBorder;
|
|
972
987
|
if (t.copyColor) cssVars['--persona-artifact-toolbar-copy-color'] = t.copyColor;
|
|
@@ -1015,6 +1030,80 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
|
|
|
1015
1030
|
cssVars['--persona-artifact-toolbar-bg'] = toolbarBg;
|
|
1016
1031
|
}
|
|
1017
1032
|
}
|
|
1033
|
+
if (artifact?.card) {
|
|
1034
|
+
const t = artifact.card;
|
|
1035
|
+
if (t.background) cssVars['--persona-artifact-card-bg'] = t.background;
|
|
1036
|
+
if (t.border) cssVars['--persona-artifact-card-border'] = t.border;
|
|
1037
|
+
if (t.borderRadius) cssVars['--persona-artifact-card-radius'] = t.borderRadius;
|
|
1038
|
+
if (t.hoverBackground) cssVars['--persona-artifact-card-hover-bg'] = t.hoverBackground;
|
|
1039
|
+
if (t.hoverBorderColor) cssVars['--persona-artifact-card-hover-border'] = t.hoverBorderColor;
|
|
1040
|
+
}
|
|
1041
|
+
if (artifact?.inline) {
|
|
1042
|
+
const t = artifact.inline;
|
|
1043
|
+
if (t.background) {
|
|
1044
|
+
cssVars['--persona-artifact-inline-bg'] =
|
|
1045
|
+
resolveTokenValue(theme, t.background) ?? t.background;
|
|
1046
|
+
}
|
|
1047
|
+
if (t.border) cssVars['--persona-artifact-inline-border'] = t.border;
|
|
1048
|
+
if (t.borderRadius) cssVars['--persona-artifact-inline-radius'] = t.borderRadius;
|
|
1049
|
+
if (t.chromeBackground) {
|
|
1050
|
+
cssVars['--persona-artifact-inline-chrome-bg'] =
|
|
1051
|
+
resolveTokenValue(theme, t.chromeBackground) ?? t.chromeBackground;
|
|
1052
|
+
}
|
|
1053
|
+
if (t.chromeBorder) {
|
|
1054
|
+
cssVars['--persona-artifact-inline-chrome-border'] =
|
|
1055
|
+
resolveTokenValue(theme, t.chromeBorder) ?? t.chromeBorder;
|
|
1056
|
+
}
|
|
1057
|
+
if (t.titleColor) {
|
|
1058
|
+
cssVars['--persona-artifact-inline-title-color'] =
|
|
1059
|
+
resolveTokenValue(theme, t.titleColor) ?? t.titleColor;
|
|
1060
|
+
}
|
|
1061
|
+
if (t.mutedColor) {
|
|
1062
|
+
cssVars['--persona-artifact-inline-muted-color'] =
|
|
1063
|
+
resolveTokenValue(theme, t.mutedColor) ?? t.mutedColor;
|
|
1064
|
+
}
|
|
1065
|
+
if (t.frameHeight) cssVars['--persona-artifact-inline-frame-height'] = t.frameHeight;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Code (syntax-highlighted artifact source view) tokens.
|
|
1069
|
+
const code = components?.code;
|
|
1070
|
+
if (code) {
|
|
1071
|
+
if (code.keywordColor) cssVars['--persona-code-keyword-color'] = code.keywordColor;
|
|
1072
|
+
if (code.stringColor) cssVars['--persona-code-string-color'] = code.stringColor;
|
|
1073
|
+
if (code.commentColor) cssVars['--persona-code-comment-color'] = code.commentColor;
|
|
1074
|
+
if (code.numberColor) cssVars['--persona-code-number-color'] = code.numberColor;
|
|
1075
|
+
if (code.tagColor) cssVars['--persona-code-tag-color'] = code.tagColor;
|
|
1076
|
+
if (code.attrColor) cssVars['--persona-code-attr-color'] = code.attrColor;
|
|
1077
|
+
if (code.propertyColor) cssVars['--persona-code-property-color'] = code.propertyColor;
|
|
1078
|
+
if (code.lineNumberColor) cssVars['--persona-code-line-number-color'] = code.lineNumberColor;
|
|
1079
|
+
if (code.gutterBorderColor) cssVars['--persona-code-gutter-border-color'] = code.gutterBorderColor;
|
|
1080
|
+
if (code.background)
|
|
1081
|
+
cssVars['--persona-code-bg'] = resolveTokenValue(theme, code.background) ?? code.background;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// Interactive-state defaults. The default preset resolves container === surface,
|
|
1085
|
+
// which turns every hover/active rule that falls back to --persona-container
|
|
1086
|
+
// into a visual no-op; anchor those states one gray step down in that case.
|
|
1087
|
+
// Component config emitted above must keep winning, so only fill vars not set.
|
|
1088
|
+
const stateSurface = cssVars['--persona-surface'];
|
|
1089
|
+
const stateContainer = cssVars['--persona-container'];
|
|
1090
|
+
const gray100 = cssVars['--persona-palette-colors-gray-100'] ?? '#f3f4f6';
|
|
1091
|
+
const gray200 = cssVars['--persona-palette-colors-gray-200'] ?? '#e5e7eb';
|
|
1092
|
+
const gray300 = cssVars['--persona-palette-colors-gray-300'] ?? '#d1d5db';
|
|
1093
|
+
const flatTheme = !stateContainer || stateContainer === stateSurface;
|
|
1094
|
+
const hoverBgDefault = flatTheme ? gray100 : stateContainer;
|
|
1095
|
+
const activeBgDefault = flatTheme ? gray200 : stateContainer;
|
|
1096
|
+
cssVars['--persona-icon-btn-hover-bg'] = cssVars['--persona-icon-btn-hover-bg'] ?? hoverBgDefault;
|
|
1097
|
+
cssVars['--persona-icon-btn-active-bg'] = cssVars['--persona-icon-btn-active-bg'] ?? activeBgDefault;
|
|
1098
|
+
if (flatTheme) {
|
|
1099
|
+
cssVars['--persona-icon-btn-active-border'] =
|
|
1100
|
+
cssVars['--persona-icon-btn-active-border'] ?? gray300;
|
|
1101
|
+
}
|
|
1102
|
+
cssVars['--persona-label-btn-hover-bg'] = cssVars['--persona-label-btn-hover-bg'] ?? hoverBgDefault;
|
|
1103
|
+
cssVars['--persona-artifact-tab-hover-bg'] =
|
|
1104
|
+
cssVars['--persona-artifact-tab-hover-bg'] ?? hoverBgDefault;
|
|
1105
|
+
cssVars['--persona-artifact-card-hover-bg'] =
|
|
1106
|
+
cssVars['--persona-artifact-card-hover-bg'] ?? hoverBgDefault;
|
|
1018
1107
|
|
|
1019
1108
|
return cssVars;
|
|
1020
1109
|
}
|
|
@@ -1041,6 +1130,8 @@ export const THEME_ZONES = {
|
|
|
1041
1130
|
container: 'Main widget container',
|
|
1042
1131
|
'artifact-pane': 'Artifact sidebar',
|
|
1043
1132
|
'artifact-toolbar': 'Artifact toolbar',
|
|
1133
|
+
'artifact-inline': 'Inline artifact block',
|
|
1134
|
+
'artifact-inline-chrome': 'Inline artifact title bar',
|
|
1044
1135
|
} as const;
|
|
1045
1136
|
|
|
1046
1137
|
export type ThemeZone = keyof typeof THEME_ZONES;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { appendCharSpans } from "./tool-loading-animation";
|
|
5
|
+
|
|
6
|
+
const NBSP = String.fromCharCode(0xa0);
|
|
7
|
+
|
|
8
|
+
describe("appendCharSpans", () => {
|
|
9
|
+
it("wraps each character in a persona-tool-char span with a staggered index", () => {
|
|
10
|
+
const container = document.createElement("div");
|
|
11
|
+
const next = appendCharSpans(container, "ab", 0);
|
|
12
|
+
|
|
13
|
+
const spans = container.querySelectorAll(".persona-tool-char");
|
|
14
|
+
expect(spans.length).toBe(2);
|
|
15
|
+
expect((spans[0] as HTMLElement).style.getPropertyValue("--char-index")).toBe("0");
|
|
16
|
+
expect((spans[1] as HTMLElement).style.getPropertyValue("--char-index")).toBe("1");
|
|
17
|
+
expect(spans[0].textContent).toBe("a");
|
|
18
|
+
expect(spans[1].textContent).toBe("b");
|
|
19
|
+
expect(next).toBe(2);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("renders spaces as non-breaking spaces and honors the start index", () => {
|
|
23
|
+
const container = document.createElement("div");
|
|
24
|
+
const next = appendCharSpans(container, "a b", 5);
|
|
25
|
+
|
|
26
|
+
const spans = container.querySelectorAll(".persona-tool-char");
|
|
27
|
+
expect(spans.length).toBe(3);
|
|
28
|
+
expect((spans[0] as HTMLElement).style.getPropertyValue("--char-index")).toBe("5");
|
|
29
|
+
expect(spans[1].textContent).toBe(NBSP);
|
|
30
|
+
expect(next).toBe(8);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createElement } from "./dom";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Append text as individual animated character spans.
|
|
5
|
+
* Each character becomes a `persona-tool-char` span carrying a `--char-index`
|
|
6
|
+
* custom property so the shimmer/rainbow keyframes can stagger their delay.
|
|
7
|
+
* Spaces are rendered as non-breaking spaces so they animate like any other
|
|
8
|
+
* character. Returns the next available character index.
|
|
9
|
+
*/
|
|
10
|
+
export const appendCharSpans = (
|
|
11
|
+
container: HTMLElement,
|
|
12
|
+
text: string,
|
|
13
|
+
startIndex: number
|
|
14
|
+
): number => {
|
|
15
|
+
let idx = startIndex;
|
|
16
|
+
for (const char of text) {
|
|
17
|
+
const span = createElement("span", "persona-tool-char");
|
|
18
|
+
span.style.setProperty("--char-index", String(idx));
|
|
19
|
+
span.textContent = char === " " ? "\u00A0" : char;
|
|
20
|
+
container.appendChild(span);
|
|
21
|
+
idx++;
|
|
22
|
+
}
|
|
23
|
+
return idx;
|
|
24
|
+
};
|
package/dist/chunk-DFBSCFYN.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var a=class{constructor(e=24e3,t={}){this.ctx=null;this.nextStartTime=0;this.activeSources=[];this.finishedCallbacks=[];this.startedCallbacks=[];this.playing=!1;this.streamEnded=!1;this.pendingCount=0;this.started=!1;this.userPaused=!1;this.pendingBuffers=[];this.pendingSamples=0;this.remainder=null;var s;this.sampleRate=e;let i=Math.max(0,(s=t.prebufferMs)!=null?s:0);this.waterlineSamples=Math.round(e*i/1e3),this.buffering=this.waterlineSamples>0}ensureContext(){if(!this.ctx){let t=typeof window!="undefined"?window:void 0;if(!t)throw new Error("AudioPlaybackManager requires a browser environment");let i=t.AudioContext||t.webkitAudioContext;this.ctx=new i({sampleRate:this.sampleRate})}let e=this.ctx;return e.state==="suspended"&&!this.userPaused&&e.resume(),e}enqueue(e){if(e.length===0)return;let t=e;if(this.remainder){let s=new Uint8Array(this.remainder.length+e.length);s.set(this.remainder),s.set(e,this.remainder.length),t=s,this.remainder=null}if(t.length%2!==0&&(this.remainder=new Uint8Array([t[t.length-1]]),t=t.subarray(0,t.length-1)),t.length===0)return;let i=this.pcmToFloat32(t);i.length!==0&&(this.buffering?(this.pendingBuffers.push(i),this.pendingSamples+=i.length,this.pendingSamples>=this.waterlineSamples&&this.releaseBuffer()):this.scheduleSamples(i))}markStreamEnd(){this.pendingBuffers.length>0&&this.releaseBuffer(),this.streamEnded=!0,this.checkFinished()}flush(){for(let e of this.activeSources)try{e.stop(),e.disconnect()}catch{}this.activeSources=[],this.pendingCount=0,this.nextStartTime=0,this.playing=!1,this.streamEnded=!1,this.finishedCallbacks=[],this.startedCallbacks=[],this.remainder=null,this.pendingBuffers=[],this.pendingSamples=0,this.buffering=this.waterlineSamples>0,this.started=!1}isPlaying(){return this.playing}onFinished(e){this.finishedCallbacks.push(e)}onStarted(e){this.startedCallbacks.push(e)}pause(){this.userPaused=!0,this.ctx&&this.ctx.state==="running"&&this.ctx.suspend()}resume(){this.userPaused=!1,this.ctx&&this.ctx.state==="suspended"&&this.ctx.resume()}async destroy(){this.flush(),this.ctx&&(await this.ctx.close(),this.ctx=null)}releaseBuffer(){this.buffering=!1;let e=this.pendingBuffers;this.pendingBuffers=[],this.pendingSamples=0;for(let t of e)this.scheduleSamples(t)}scheduleSamples(e){if(e.length===0)return;let t=this.ensureContext(),i=t.createBuffer(1,e.length,this.sampleRate);i.getChannelData(0).set(e);let s=t.createBufferSource();s.buffer=i,s.connect(t.destination);let n=t.currentTime;if(this.nextStartTime===0?this.nextStartTime=n:this.nextStartTime<n&&(this.nextStartTime=n,this.waterlineSamples>0&&(this.buffering=!0)),s.start(this.nextStartTime),this.nextStartTime+=i.duration,this.activeSources.push(s),this.pendingCount++,this.playing=!0,!this.started){this.started=!0;let r=this.startedCallbacks.slice();this.startedCallbacks=[];for(let h of r)h()}s.onended=()=>{let r=this.activeSources.indexOf(s);r!==-1&&this.activeSources.splice(r,1),this.pendingCount--,this.checkFinished()}}checkFinished(){if(this.streamEnded&&this.pendingCount<=0&&this.pendingBuffers.length===0){this.playing=!1,this.streamEnded=!1;let e=this.finishedCallbacks.slice();this.finishedCallbacks=[];for(let t of e)t()}}pcmToFloat32(e){let t=Math.floor(e.length/2),i=new Float32Array(t),s=new DataView(e.buffer,e.byteOffset,e.byteLength);for(let n=0;n<t;n++){let r=s.getInt16(n*2,!0);i[n]=r/32768}return i}};export{a};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as c}from"./chunk-DFBSCFYN.js";function g(o){return o.replace(/\/+$/,"")}var m=class{constructor(e){this.opts=e;this.id="runtype-tts";this.supportsPause=!0;this.player=null;this.playerPromise=null;this.generation=0}ensurePlayer(){var e,t;return(t=this.playerPromise)!=null?t:this.playerPromise=Promise.resolve(this.opts.createPlaybackEngine?this.opts.createPlaybackEngine():new c(24e3,{prebufferMs:(e=this.opts.prebufferMs)!=null?e:200})).then(r=>this.player=r)}speak(e,t){let r=++this.generation;this.run(r,e,t)}async run(e,t,r){var i,a,p,h;try{let n=await this.ensurePlayer();if(e!==this.generation)return;n.flush(),n.resume(),n.onStarted(()=>{var s;e===this.generation&&((s=r.onStart)==null||s.call(r))}),n.onFinished(()=>{var s;e===this.generation&&((s=r.onEnd)==null||s.call(r))});let u=`${g(this.opts.host)}/v1/agents/${encodeURIComponent(this.opts.agentId)}/speak`,y=await fetch(u,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.opts.clientToken}`},body:JSON.stringify({text:t.text,voice:(i=t.voice)!=null?i:this.opts.voice,format:"pcm"})});if(e!==this.generation)return;if(!y.ok||!y.body)throw new Error(await S(y));let v=y.body.getReader();for(;;){let{done:s,value:d}=await v.read();if(e!==this.generation){await v.cancel().catch(()=>{});return}if(s)break;d&&d.byteLength>0&&n.enqueue(d)}n.markStreamEnd()}catch(n){if(e!==this.generation)return;let u=n instanceof Error?n:new Error(String(n));(p=(a=this.opts).onError)==null||p.call(a,u),(h=r.onError)==null||h.call(r,u)}}pause(){var e;(e=this.player)==null||e.pause()}resume(){var e;(e=this.player)==null||e.resume()}stop(){var e;this.generation++,(e=this.player)==null||e.flush()}destroy(){var e;this.generation++,(e=this.player)==null||e.destroy(),this.player=null,this.playerPromise=null}};async function S(o){var e,t;try{let r=await o.json();return r.detail?`${(e=r.error)!=null?e:`Runtype TTS ${o.status}`}: ${r.detail}`:(t=r.error)!=null?t:`Runtype TTS request failed (${o.status})`}catch{return`Runtype TTS request failed (${o.status})`}}var f=class{constructor(e,t,r={}){this.primary=e;this.fallback=t;this.options=r;this.id="fallback";this.active=e}get supportsPause(){return this.active.supportsPause}speak(e,t){this.active=this.primary;let r=!1;this.primary.speak(e,{onStart:()=>{var i;r=!0,(i=t.onStart)==null||i.call(t)},onEnd:()=>{var i;return(i=t.onEnd)==null?void 0:i.call(t)},onError:i=>{var a,p,h;if(r){(a=t.onError)==null||a.call(t,i);return}(h=(p=this.options).onFallback)==null||h.call(p,i),this.active=this.fallback,this.fallback.speak(e,t)}})}pause(){this.active.pause()}resume(){this.active.resume()}stop(){this.active.stop()}destroy(){var e,t,r,i;(t=(e=this.primary).destroy)==null||t.call(e),(i=(r=this.fallback).destroy)==null||i.call(r)}};export{f as FallbackSpeechEngine,m as RuntypeSpeechEngine};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var k=[1e3,2e3,4e3,8e3,8e3];function T(e){let t=0,r=null,i=null,g=!1,b=()=>{if(i&&(clearTimeout(i),i=null),r){let n=r;r=null,n()}},v=()=>{let n=e.getStatus();n!=="resuming"&&n!=="paused"||b()},p=()=>{typeof document!="undefined"&&document.visibilityState==="hidden"||v()},R=()=>{v()},C=()=>{g||(typeof document!="undefined"&&document.addEventListener("visibilitychange",p),typeof window!="undefined"&&window.addEventListener("online",R),g=!0)},m=()=>{g&&(typeof document!="undefined"&&document.removeEventListener("visibilitychange",p),typeof window!="undefined"&&window.removeEventListener("online",R),g=!1)},M=n=>new Promise(s=>{r=s,i=setTimeout(()=>{i=null,r=null,s()},n)}),E=()=>{var d;let n=e.getResumable();e.clearResumable(),e.setReconnecting(!1),t=0,m(),e.setAbortController(null);let s=!1;for(let c of e.getMessages())c.streaming&&(c.streaming=!1,s=!0);let l=e.buildErrorContent("Connection lost and the response could not be resumed.");l?e.appendMessage({id:`reconnect-failed-${(d=n==null?void 0:n.executionId)!=null?d:e.nextSequence()}`,role:"assistant",content:l,createdAt:new Date().toISOString(),streaming:!1,sequence:e.nextSequence()}):s&&e.notifyMessagesChanged(),e.setStreaming(!1),e.setStatus("idle"),e.onError(new Error("Durable session reconnect failed."))},x=async()=>{var d,c,y,S,w,A;let n=(c=(d=e.config.reconnect)==null?void 0:d.backoffMs)!=null?c:k,s=(S=(y=e.config.reconnect)==null?void 0:y.maxAttempts)!=null?S:n.length,l=e.config.reconnectStream;if(!l){e.setReconnecting(!1);return}for(;e.getResumable()&&t<s;){t+=1,e.setStatus("resuming");let o=e.getResumable(),I=o.lastEventId,u=new AbortController;e.setAbortController(u),e.emitReconnect({phase:"resuming",handle:o,attempt:t});let f=null;try{let a=await l({executionId:o.executionId,after:o.lastEventId,signal:u.signal});a&&a.ok&&a.body&&(f=a.body)}catch{f=null}if(u.signal.aborted)return;if(f){let a=(w=e.getMessages().find(W=>W.id===o.assistantMessageId))==null?void 0:w.content,L=typeof a=="string"?a:"";try{await e.resumeConnect(f,o.assistantMessageId,L)}catch{}if(u.signal.aborted)return;if(!e.getResumable()){e.setReconnecting(!1),t=0,m(),e.emitReconnect({phase:"resumed",handle:o});return}e.getResumable().lastEventId!==I&&(t=0)}if(e.getResumable()&&t<s&&(e.setStatus("paused"),await M((A=n[Math.min(t-1,n.length-1)])!=null?A:1e3),u.signal.aborted))return}e.getResumable()&&E()};return{begin(){t=0,C(),x()},teardown(){i&&(clearTimeout(i),i=null),r=null,t=0,m()},wake:b}}export{T as createReconnectController};
|