@runtypelabs/persona 3.8.2 → 3.9.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 +1 -1
- package/dist/index.cjs +43 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.global.js +57 -57
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +43 -43
- package/dist/index.js.map +1 -1
- package/dist/theme-editor.cjs +895 -816
- package/dist/theme-editor.js +895 -816
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.js +1 -1
- package/package.json +1 -1
- package/src/client.test.ts +346 -0
- package/src/client.ts +72 -3
- package/src/components/panel.ts +2 -1
- package/src/defaults.ts +11 -1
- package/src/index.ts +2 -0
- package/src/presets.ts +2 -1
- package/src/theme-editor/sections.ts +7 -3
- package/src/theme-reference.ts +1 -1
- package/src/ui.ts +24 -5
- package/src/utils/tokens.ts +6 -2
package/src/client.ts
CHANGED
|
@@ -1018,6 +1018,7 @@ export class AgentWidgetClient {
|
|
|
1018
1018
|
const assistantMessageRef = { current: null as AgentWidgetMessage | null };
|
|
1019
1019
|
// Track current partId for message segmentation at tool boundaries
|
|
1020
1020
|
const partIdState = { current: null as string | null };
|
|
1021
|
+
let didSplitByPartId = false;
|
|
1021
1022
|
const reasoningMessages = new Map<string, AgentWidgetMessage>();
|
|
1022
1023
|
const toolMessages = new Map<string, AgentWidgetMessage>();
|
|
1023
1024
|
const reasoningContext = {
|
|
@@ -1060,11 +1061,22 @@ export class AgentWidgetClient {
|
|
|
1060
1061
|
payload.step_id
|
|
1061
1062
|
);
|
|
1062
1063
|
|
|
1064
|
+
const baseAssistantId = assistantMessageId;
|
|
1065
|
+
let assistantIdConsumed = false;
|
|
1066
|
+
|
|
1063
1067
|
const ensureAssistantMessage = () => {
|
|
1064
1068
|
if (assistantMessage) return assistantMessage;
|
|
1069
|
+
let id: string;
|
|
1070
|
+
if (!assistantIdConsumed && baseAssistantId) {
|
|
1071
|
+
id = baseAssistantId;
|
|
1072
|
+
assistantIdConsumed = true;
|
|
1073
|
+
} else if (baseAssistantId && partIdState.current) {
|
|
1074
|
+
id = `${baseAssistantId}_${partIdState.current}`;
|
|
1075
|
+
} else {
|
|
1076
|
+
id = `assistant-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
1077
|
+
}
|
|
1065
1078
|
assistantMessage = {
|
|
1066
|
-
|
|
1067
|
-
id: assistantMessageId ?? `assistant-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
1079
|
+
id,
|
|
1068
1080
|
role: "assistant",
|
|
1069
1081
|
content: "",
|
|
1070
1082
|
createdAt: new Date().toISOString(),
|
|
@@ -1507,6 +1519,31 @@ export class AgentWidgetClient {
|
|
|
1507
1519
|
if (callKey) {
|
|
1508
1520
|
toolContext.byCall.delete(callKey);
|
|
1509
1521
|
}
|
|
1522
|
+
} else if (payloadType === "text_start") {
|
|
1523
|
+
// Lifecycle event: a new text segment is beginning (emitted at tool boundaries)
|
|
1524
|
+
const incomingPartId = payload.partId;
|
|
1525
|
+
if (incomingPartId !== undefined && partIdState.current !== null && incomingPartId !== partIdState.current) {
|
|
1526
|
+
const prev = assistantMessage as AgentWidgetMessage | null;
|
|
1527
|
+
if (prev) {
|
|
1528
|
+
prev.streaming = false;
|
|
1529
|
+
emitMessage(prev);
|
|
1530
|
+
assistantMessage = null;
|
|
1531
|
+
didSplitByPartId = true;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
if (incomingPartId !== undefined) {
|
|
1535
|
+
partIdState.current = incomingPartId;
|
|
1536
|
+
}
|
|
1537
|
+
} else if (payloadType === "text_end") {
|
|
1538
|
+
// Lifecycle event: current text segment ended (tool call about to start)
|
|
1539
|
+
// Seal the current assistant message so the next segment gets a new one
|
|
1540
|
+
const prev = assistantMessage as AgentWidgetMessage | null;
|
|
1541
|
+
if (prev) {
|
|
1542
|
+
prev.streaming = false;
|
|
1543
|
+
emitMessage(prev);
|
|
1544
|
+
assistantMessage = null;
|
|
1545
|
+
didSplitByPartId = true;
|
|
1546
|
+
}
|
|
1510
1547
|
} else if (payloadType === "step_chunk" || payloadType === "step_delta") {
|
|
1511
1548
|
// Only process chunks for prompt steps, not tool/context steps
|
|
1512
1549
|
const stepType = (payload as any).stepType;
|
|
@@ -1515,7 +1552,27 @@ export class AgentWidgetClient {
|
|
|
1515
1552
|
// Skip tool-related chunks - they're handled by tool_start/tool_complete
|
|
1516
1553
|
continue;
|
|
1517
1554
|
}
|
|
1555
|
+
|
|
1556
|
+
// partId-based segmentation: when partId changes, seal current message
|
|
1557
|
+
// and start a new one so text and tools render in chronological order
|
|
1558
|
+
const incomingPartId = payload.partId;
|
|
1559
|
+
if (incomingPartId !== undefined && partIdState.current !== null && incomingPartId !== partIdState.current) {
|
|
1560
|
+
const prev = assistantMessage as AgentWidgetMessage | null;
|
|
1561
|
+
if (prev) {
|
|
1562
|
+
prev.streaming = false;
|
|
1563
|
+
emitMessage(prev);
|
|
1564
|
+
assistantMessage = null;
|
|
1565
|
+
didSplitByPartId = true;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
if (incomingPartId !== undefined) {
|
|
1569
|
+
partIdState.current = incomingPartId;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1518
1572
|
const assistant = ensureAssistantMessage();
|
|
1573
|
+
if (incomingPartId !== undefined && !assistant.partId) {
|
|
1574
|
+
assistant.partId = incomingPartId;
|
|
1575
|
+
}
|
|
1519
1576
|
// Support various field names: text, delta, content, chunk (Runtype uses 'chunk')
|
|
1520
1577
|
const chunk = payload.text ?? payload.delta ?? payload.content ?? payload.chunk ?? "";
|
|
1521
1578
|
if (chunk) {
|
|
@@ -1822,7 +1879,19 @@ export class AgentWidgetClient {
|
|
|
1822
1879
|
}
|
|
1823
1880
|
} else if (payloadType === "flow_complete") {
|
|
1824
1881
|
const finalContent = payload.result?.response;
|
|
1825
|
-
if (
|
|
1882
|
+
if (didSplitByPartId) {
|
|
1883
|
+
// Content was split into multiple assistant messages — the full response
|
|
1884
|
+
// in flow_complete would overwrite the last segment. Just finalize streaming.
|
|
1885
|
+
if (assistantMessage !== null) {
|
|
1886
|
+
const msg: AgentWidgetMessage = assistantMessage;
|
|
1887
|
+
streamParsers.delete(msg.id);
|
|
1888
|
+
rawContentBuffers.delete(msg.id);
|
|
1889
|
+
if (msg.streaming !== false) {
|
|
1890
|
+
msg.streaming = false;
|
|
1891
|
+
emitMessage(msg);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
} else if (finalContent !== undefined && finalContent !== null) {
|
|
1826
1895
|
const assistant = ensureAssistantMessage();
|
|
1827
1896
|
// Check if we have raw content buffer that needs final processing
|
|
1828
1897
|
const rawBuffer = rawContentBuffers.get(assistant.id);
|
package/src/components/panel.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createElement } from "../utils/dom";
|
|
2
|
+
import { DEFAULT_FLOATING_LAUNCHER_WIDTH } from "../defaults";
|
|
2
3
|
import { AgentWidgetConfig } from "../types";
|
|
3
4
|
import { positionMap } from "../utils/positioning";
|
|
4
5
|
import { isDockedMountMode } from "../utils/dock";
|
|
@@ -68,7 +69,7 @@ export const createWrapper = (config?: AgentWidgetConfig): PanelWrapper => {
|
|
|
68
69
|
"persona-widget-panel persona-relative persona-min-h-[320px]"
|
|
69
70
|
);
|
|
70
71
|
const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;
|
|
71
|
-
const width = launcherWidth ??
|
|
72
|
+
const width = launcherWidth ?? DEFAULT_FLOATING_LAUNCHER_WIDTH;
|
|
72
73
|
panel.style.width = width;
|
|
73
74
|
panel.style.maxWidth = width;
|
|
74
75
|
|
package/src/defaults.ts
CHANGED
|
@@ -2,6 +2,16 @@ import type { AgentWidgetConfig } from "./types";
|
|
|
2
2
|
import type { DeepPartial, PersonaTheme } from "./types/theme";
|
|
3
3
|
import { deepMerge } from "./utils/deep-merge";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Default width for the floating launcher panel (when not overridden).
|
|
7
|
+
* Benchmarks: many chat products use ~300–400px; 400px is a frequent “standard” default.
|
|
8
|
+
* We use 440px to better fit code/JSON and structured replies while staying responsive via `min(..., 100vw)`.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_FLOATING_LAUNCHER_WIDTH = "min(440px, calc(100vw - 24px))";
|
|
11
|
+
|
|
12
|
+
/** Max width cap paired with {@link DEFAULT_FLOATING_LAUNCHER_WIDTH} for theme defaults. */
|
|
13
|
+
export const DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH = "440px";
|
|
14
|
+
|
|
5
15
|
/**
|
|
6
16
|
* Default widget configuration
|
|
7
17
|
* Single source of truth for all default values
|
|
@@ -26,7 +36,7 @@ export const DEFAULT_WIDGET_CONFIG: Partial<AgentWidgetConfig> = {
|
|
|
26
36
|
agentIconName: "bot",
|
|
27
37
|
headerIconName: "bot",
|
|
28
38
|
position: "bottom-right",
|
|
29
|
-
width:
|
|
39
|
+
width: DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
30
40
|
heightOffset: 0,
|
|
31
41
|
autoExpand: false,
|
|
32
42
|
callToActionIconHidden: false,
|
package/src/index.ts
CHANGED
package/src/presets.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentWidgetConfig } from "./types";
|
|
2
2
|
import type { DeepPartial, PersonaTheme } from "./types/theme";
|
|
3
|
+
import { DEFAULT_FLOATING_LAUNCHER_WIDTH } from "./defaults";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* A named preset containing partial widget configuration.
|
|
@@ -65,7 +66,7 @@ export const PRESET_SHOP: WidgetPreset = {
|
|
|
65
66
|
subtitle: "Here to help you find what you need",
|
|
66
67
|
agentIconText: "🛍️",
|
|
67
68
|
position: "bottom-right",
|
|
68
|
-
width:
|
|
69
|
+
width: DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
69
70
|
},
|
|
70
71
|
copy: {
|
|
71
72
|
welcomeTitle: "Welcome to our shop!",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/** Declarative section/field definitions for the theme editor (pure data — no DOM, no render logic) */
|
|
2
2
|
|
|
3
3
|
import type { SectionDef, TabDef, SubGroupDef, FieldDef } from './types';
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH,
|
|
6
|
+
DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
7
|
+
} from '../defaults';
|
|
4
8
|
import { COLOR_FAMILIES } from './color-utils';
|
|
5
9
|
import {
|
|
6
10
|
ROLE_SURFACES,
|
|
@@ -273,8 +277,8 @@ const panelLayoutSectionDef: SectionDef = {
|
|
|
273
277
|
title: 'Panel',
|
|
274
278
|
collapsed: false,
|
|
275
279
|
fields: [
|
|
276
|
-
{ id: 'panel-width', label: 'Width', type: 'text', path: 'theme.components.panel.width', defaultValue:
|
|
277
|
-
{ id: 'panel-max-width', label: 'Max Width', type: 'text', path: 'theme.components.panel.maxWidth', defaultValue:
|
|
280
|
+
{ id: 'panel-width', label: 'Width', type: 'text', path: 'theme.components.panel.width', defaultValue: DEFAULT_FLOATING_LAUNCHER_WIDTH },
|
|
281
|
+
{ id: 'panel-max-width', label: 'Max Width', type: 'text', path: 'theme.components.panel.maxWidth', defaultValue: DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH },
|
|
278
282
|
{ id: 'panel-height', label: 'Height', type: 'text', path: 'theme.components.panel.height', defaultValue: '600px' },
|
|
279
283
|
{ id: 'panel-max-height', label: 'Max Height', type: 'text', path: 'theme.components.panel.maxHeight', defaultValue: 'calc(100vh - 80px)' },
|
|
280
284
|
{ id: 'panel-border-radius', label: 'Border Radius', type: 'select', path: 'theme.components.panel.borderRadius', defaultValue: 'palette.radius.xl', options: [
|
|
@@ -590,7 +594,7 @@ const launcherBasicsSectionDef: SectionDef = {
|
|
|
590
594
|
{ id: 'launch-enabled', label: 'Enabled', type: 'toggle', path: 'launcher.enabled', defaultValue: true },
|
|
591
595
|
{ id: 'launch-mount-mode', label: 'Mount Mode', type: 'select', path: 'launcher.mountMode', defaultValue: 'floating', options: [{ value: 'floating', label: 'Floating' }, { value: 'docked', label: 'Docked' }] },
|
|
592
596
|
{ id: 'launch-position', label: 'Position', type: 'select', path: 'launcher.position', defaultValue: 'bottom-right', options: [{ value: 'bottom-right', label: 'Bottom Right' }, { value: 'bottom-left', label: 'Bottom Left' }, { value: 'top-right', label: 'Top Right' }, { value: 'top-left', label: 'Top Left' }] },
|
|
593
|
-
{ id: 'launch-width', label: 'Width', type: 'text', path: 'launcher.width', defaultValue:
|
|
597
|
+
{ id: 'launch-width', label: 'Width', type: 'text', path: 'launcher.width', defaultValue: DEFAULT_FLOATING_LAUNCHER_WIDTH },
|
|
594
598
|
{ id: 'launch-auto-expand', label: 'Auto Expand', type: 'toggle', path: 'launcher.autoExpand', defaultValue: false },
|
|
595
599
|
{ id: 'launch-title', label: 'Title', type: 'text', path: 'launcher.title', defaultValue: 'Chat Assistant' },
|
|
596
600
|
{ id: 'launch-subtitle', label: 'Subtitle', type: 'text', path: 'launcher.subtitle', defaultValue: 'Here to help you get answers fast' },
|
package/src/theme-reference.ts
CHANGED
|
@@ -115,7 +115,7 @@ export const THEME_TOKEN_DOCS = {
|
|
|
115
115
|
panel: {
|
|
116
116
|
description: 'Chat panel container.',
|
|
117
117
|
properties:
|
|
118
|
-
'width, maxWidth (
|
|
118
|
+
'width, maxWidth (440px), height (600px), maxHeight, borderRadius, shadow.',
|
|
119
119
|
},
|
|
120
120
|
header: {
|
|
121
121
|
description: 'Chat panel header.',
|
package/src/ui.ts
CHANGED
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
import { readFlexGapPx, resolveArtifactPaneWidthPx } from "./utils/artifact-resize";
|
|
70
70
|
import { enhanceWithForms } from "./components/forms";
|
|
71
71
|
import { pluginRegistry } from "./plugins/registry";
|
|
72
|
-
import { mergeWithDefaults } from "./defaults";
|
|
72
|
+
import { mergeWithDefaults, DEFAULT_FLOATING_LAUNCHER_WIDTH } from "./defaults";
|
|
73
73
|
import { createEventBus } from "./utils/events";
|
|
74
74
|
import {
|
|
75
75
|
createActionManager,
|
|
@@ -883,6 +883,10 @@ export const createAgentExperience = (
|
|
|
883
883
|
selectedModelId: composerCfg?.selectedModelId,
|
|
884
884
|
onModelChange: (modelId: string) => {
|
|
885
885
|
config.composer = { ...config.composer, selectedModelId: modelId };
|
|
886
|
+
// Sync to agent config so the next request uses the selected model
|
|
887
|
+
if (config.agent) {
|
|
888
|
+
config.agent = { ...config.agent, model: modelId };
|
|
889
|
+
}
|
|
886
890
|
},
|
|
887
891
|
onVoiceToggle:
|
|
888
892
|
config.voiceRecognition?.enabled === true
|
|
@@ -929,13 +933,18 @@ export const createAgentExperience = (
|
|
|
929
933
|
ensureComposerAttachmentSurface(footer);
|
|
930
934
|
bindComposerRefsFromFooter(footer);
|
|
931
935
|
|
|
932
|
-
// Apply contentMaxWidth to composer form and attachment previews if configured
|
|
936
|
+
// Apply contentMaxWidth to composer form, suggestions, and attachment previews if configured
|
|
933
937
|
const contentMaxWidth = config.layout?.contentMaxWidth;
|
|
934
938
|
if (contentMaxWidth && composerForm) {
|
|
935
939
|
composerForm.style.maxWidth = contentMaxWidth;
|
|
936
940
|
composerForm.style.marginLeft = "auto";
|
|
937
941
|
composerForm.style.marginRight = "auto";
|
|
938
942
|
}
|
|
943
|
+
if (contentMaxWidth && suggestions) {
|
|
944
|
+
suggestions.style.maxWidth = contentMaxWidth;
|
|
945
|
+
suggestions.style.marginLeft = "auto";
|
|
946
|
+
suggestions.style.marginRight = "auto";
|
|
947
|
+
}
|
|
939
948
|
if (contentMaxWidth && attachmentPreviewsContainer) {
|
|
940
949
|
attachmentPreviewsContainer.style.maxWidth = contentMaxWidth;
|
|
941
950
|
attachmentPreviewsContainer.style.marginLeft = "auto";
|
|
@@ -1499,7 +1508,7 @@ export const createAgentExperience = (
|
|
|
1499
1508
|
if (mobileFullscreen && ownerWindow.innerWidth <= mobileBreakpoint) return;
|
|
1500
1509
|
if (!shouldExpandLauncherForArtifacts(config, launcherEnabled)) return;
|
|
1501
1510
|
|
|
1502
|
-
const base = config.launcher?.width ?? config.launcherWidth ??
|
|
1511
|
+
const base = config.launcher?.width ?? config.launcherWidth ?? DEFAULT_FLOATING_LAUNCHER_WIDTH;
|
|
1503
1512
|
const expanded =
|
|
1504
1513
|
config.features?.artifacts?.layout?.expandedPanelWidth ??
|
|
1505
1514
|
"min(720px, calc(100vw - 24px))";
|
|
@@ -1650,7 +1659,7 @@ export const createAgentExperience = (
|
|
|
1650
1659
|
|
|
1651
1660
|
// Re-apply panel width/maxWidth from initial setup
|
|
1652
1661
|
const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;
|
|
1653
|
-
const width = launcherWidth ??
|
|
1662
|
+
const width = launcherWidth ?? DEFAULT_FLOATING_LAUNCHER_WIDTH;
|
|
1654
1663
|
if (!sidebarMode && !dockedMode) {
|
|
1655
1664
|
if (isInlineEmbed && fullHeight) {
|
|
1656
1665
|
panel.style.width = "100%";
|
|
@@ -3600,7 +3609,7 @@ export const createAgentExperience = (
|
|
|
3600
3609
|
// In sidebar/fullHeight mode, don't override the width - it's handled by applyFullHeightStyles
|
|
3601
3610
|
if (!sidebarMode && !dockedMode) {
|
|
3602
3611
|
const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;
|
|
3603
|
-
const width = launcherWidth ??
|
|
3612
|
+
const width = launcherWidth ?? DEFAULT_FLOATING_LAUNCHER_WIDTH;
|
|
3604
3613
|
panel.style.width = width;
|
|
3605
3614
|
panel.style.maxWidth = width;
|
|
3606
3615
|
}
|
|
@@ -5021,6 +5030,11 @@ export const createAgentExperience = (
|
|
|
5021
5030
|
composerForm.style.marginLeft = "auto";
|
|
5022
5031
|
composerForm.style.marginRight = "auto";
|
|
5023
5032
|
}
|
|
5033
|
+
if (suggestions) {
|
|
5034
|
+
suggestions.style.maxWidth = updatedContentMaxWidth;
|
|
5035
|
+
suggestions.style.marginLeft = "auto";
|
|
5036
|
+
suggestions.style.marginRight = "auto";
|
|
5037
|
+
}
|
|
5024
5038
|
} else {
|
|
5025
5039
|
messagesWrapper.style.maxWidth = "";
|
|
5026
5040
|
messagesWrapper.style.marginLeft = "";
|
|
@@ -5031,6 +5045,11 @@ export const createAgentExperience = (
|
|
|
5031
5045
|
composerForm.style.marginLeft = "";
|
|
5032
5046
|
composerForm.style.marginRight = "";
|
|
5033
5047
|
}
|
|
5048
|
+
if (suggestions) {
|
|
5049
|
+
suggestions.style.maxWidth = "";
|
|
5050
|
+
suggestions.style.marginLeft = "";
|
|
5051
|
+
suggestions.style.marginRight = "";
|
|
5052
|
+
}
|
|
5034
5053
|
}
|
|
5035
5054
|
|
|
5036
5055
|
// Update status indicator visibility and text
|
package/src/utils/tokens.ts
CHANGED
|
@@ -8,6 +8,10 @@ import type {
|
|
|
8
8
|
ComponentTokens,
|
|
9
9
|
SemanticTokens,
|
|
10
10
|
} from '../types/theme';
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH,
|
|
13
|
+
DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
14
|
+
} from '../defaults';
|
|
11
15
|
|
|
12
16
|
export const DEFAULT_PALETTE = {
|
|
13
17
|
colors: {
|
|
@@ -277,8 +281,8 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
|
|
|
277
281
|
shadow: 'palette.shadows.lg',
|
|
278
282
|
},
|
|
279
283
|
panel: {
|
|
280
|
-
width:
|
|
281
|
-
maxWidth:
|
|
284
|
+
width: DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
285
|
+
maxWidth: DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH,
|
|
282
286
|
height: '600px',
|
|
283
287
|
maxHeight: 'calc(100vh - 80px)',
|
|
284
288
|
borderRadius: 'palette.radius.xl',
|