@runtypelabs/persona 1.48.0 → 2.0.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 +140 -8
- package/dist/index.cjs +90 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1055 -24
- package/dist/index.d.ts +1055 -24
- package/dist/index.global.js +111 -60
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +90 -39
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/widget.css +836 -513
- package/package.json +1 -1
- package/src/artifacts-session.test.ts +80 -0
- package/src/client.test.ts +20 -21
- package/src/client.ts +153 -4
- package/src/components/approval-bubble.ts +45 -42
- package/src/components/artifact-card.ts +91 -0
- package/src/components/artifact-pane.ts +501 -0
- package/src/components/composer-builder.ts +32 -27
- package/src/components/event-stream-view.ts +40 -40
- package/src/components/feedback.ts +36 -36
- package/src/components/forms.ts +11 -11
- package/src/components/header-builder.test.ts +32 -0
- package/src/components/header-builder.ts +55 -36
- package/src/components/header-layouts.ts +58 -125
- package/src/components/launcher.ts +36 -21
- package/src/components/message-bubble.ts +92 -65
- package/src/components/messages.ts +2 -2
- package/src/components/panel.ts +42 -11
- package/src/components/reasoning-bubble.ts +23 -23
- package/src/components/registry.ts +4 -0
- package/src/components/suggestions.ts +1 -1
- package/src/components/tool-bubble.ts +32 -32
- package/src/defaults.ts +30 -4
- package/src/index.ts +80 -2
- package/src/install.ts +22 -0
- package/src/plugins/types.ts +23 -0
- package/src/postprocessors.ts +2 -2
- package/src/runtime/host-layout.ts +174 -0
- package/src/runtime/init.test.ts +236 -0
- package/src/runtime/init.ts +114 -55
- package/src/session.ts +135 -2
- package/src/styles/tailwind.css +1 -1
- package/src/styles/widget.css +836 -513
- package/src/types/theme.ts +354 -0
- package/src/types.ts +314 -15
- package/src/ui.docked.test.ts +104 -0
- package/src/ui.ts +940 -227
- package/src/utils/artifact-gate.test.ts +255 -0
- package/src/utils/artifact-gate.ts +142 -0
- package/src/utils/artifact-resize.test.ts +64 -0
- package/src/utils/artifact-resize.ts +67 -0
- package/src/utils/attachment-manager.ts +10 -10
- package/src/utils/code-generators.test.ts +52 -0
- package/src/utils/code-generators.ts +40 -36
- package/src/utils/dock.ts +17 -0
- package/src/utils/dom-context.test.ts +504 -0
- package/src/utils/dom-context.ts +896 -0
- package/src/utils/dom.ts +12 -1
- package/src/utils/message-fingerprint.test.ts +187 -0
- package/src/utils/message-fingerprint.ts +105 -0
- package/src/utils/migration.ts +179 -0
- package/src/utils/morph.ts +1 -1
- package/src/utils/plugins.ts +175 -0
- package/src/utils/positioning.ts +4 -4
- package/src/utils/theme.test.ts +125 -0
- package/src/utils/theme.ts +216 -60
- package/src/utils/tokens.ts +682 -0
|
@@ -11,15 +11,15 @@ export const updateToolBubbleUI = (messageId: string, bubble: HTMLElement, confi
|
|
|
11
11
|
const expanded = toolExpansionState.has(messageId);
|
|
12
12
|
const toolCallConfig = config?.toolCall ?? {};
|
|
13
13
|
const header = bubble.querySelector('button[data-expand-header="true"]') as HTMLElement;
|
|
14
|
-
const content = bubble.querySelector('.
|
|
14
|
+
const content = bubble.querySelector('.persona-border-t') as HTMLElement;
|
|
15
15
|
|
|
16
16
|
if (!header || !content) return;
|
|
17
17
|
|
|
18
18
|
header.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
19
19
|
|
|
20
|
-
// Find toggle icon container - it's the direct child div of headerMeta (which has
|
|
21
|
-
const headerMeta = header.querySelector('.
|
|
22
|
-
const toggleIcon = headerMeta?.querySelector(':scope > .
|
|
20
|
+
// Find toggle icon container - it's the direct child div of headerMeta (which has persona-ml-auto)
|
|
21
|
+
const headerMeta = header.querySelector('.persona-ml-auto') as HTMLElement;
|
|
22
|
+
const toggleIcon = headerMeta?.querySelector(':scope > .persona-flex.persona-items-center') as HTMLElement;
|
|
23
23
|
if (toggleIcon) {
|
|
24
24
|
toggleIcon.innerHTML = "";
|
|
25
25
|
const iconColor = toolCallConfig.toggleTextColor || toolCallConfig.headerTextColor || "currentColor";
|
|
@@ -43,17 +43,17 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
43
43
|
[
|
|
44
44
|
"vanilla-message-bubble",
|
|
45
45
|
"vanilla-tool-bubble",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
46
|
+
"persona-w-full",
|
|
47
|
+
"persona-max-w-[85%]",
|
|
48
|
+
"persona-rounded-2xl",
|
|
49
|
+
"persona-bg-persona-surface",
|
|
50
|
+
"persona-border",
|
|
51
|
+
"persona-border-persona-message-border",
|
|
52
|
+
"persona-text-persona-primary",
|
|
53
|
+
"persona-shadow-sm",
|
|
54
|
+
"persona-overflow-hidden",
|
|
55
|
+
"persona-px-0",
|
|
56
|
+
"persona-py-0"
|
|
57
57
|
].join(" ")
|
|
58
58
|
);
|
|
59
59
|
// Set id for idiomorph matching
|
|
@@ -81,7 +81,7 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
81
81
|
let expanded = toolExpansionState.has(message.id);
|
|
82
82
|
const header = createElement(
|
|
83
83
|
"button",
|
|
84
|
-
"
|
|
84
|
+
"persona-flex persona-w-full persona-items-center persona-justify-between persona-gap-3 persona-bg-transparent persona-px-4 persona-py-3 persona-text-left persona-cursor-pointer persona-border-none"
|
|
85
85
|
) as HTMLButtonElement;
|
|
86
86
|
header.type = "button";
|
|
87
87
|
header.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
@@ -101,15 +101,15 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
101
101
|
header.style.paddingBottom = toolCallConfig.headerPaddingY;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
const headerContent = createElement("div", "
|
|
105
|
-
const title = createElement("span", "
|
|
104
|
+
const headerContent = createElement("div", "persona-flex persona-flex-col persona-text-left");
|
|
105
|
+
const title = createElement("span", "persona-text-xs persona-text-persona-primary");
|
|
106
106
|
if (toolCallConfig.headerTextColor) {
|
|
107
107
|
title.style.color = toolCallConfig.headerTextColor;
|
|
108
108
|
}
|
|
109
109
|
title.textContent = describeToolTitle(tool);
|
|
110
110
|
headerContent.appendChild(title);
|
|
111
111
|
|
|
112
|
-
const toggleIcon = createElement("div", "
|
|
112
|
+
const toggleIcon = createElement("div", "persona-flex persona-items-center");
|
|
113
113
|
const iconColor = toolCallConfig.toggleTextColor || toolCallConfig.headerTextColor || "currentColor";
|
|
114
114
|
const chevronIcon = renderLucideIcon(expanded ? "chevron-up" : "chevron-down", 16, iconColor, 2);
|
|
115
115
|
if (chevronIcon) {
|
|
@@ -119,14 +119,14 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
119
119
|
toggleIcon.textContent = expanded ? "Hide" : "Show";
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
const headerMeta = createElement("div", "
|
|
122
|
+
const headerMeta = createElement("div", "persona-flex persona-items-center persona-gap-2 persona-ml-auto");
|
|
123
123
|
headerMeta.append(toggleIcon);
|
|
124
124
|
|
|
125
125
|
header.append(headerContent, headerMeta);
|
|
126
126
|
|
|
127
127
|
const content = createElement(
|
|
128
128
|
"div",
|
|
129
|
-
"
|
|
129
|
+
"persona-border-t persona-border-gray-200 persona-bg-gray-50 persona-space-y-3 persona-px-4 persona-py-3"
|
|
130
130
|
);
|
|
131
131
|
content.style.display = expanded ? "" : "none";
|
|
132
132
|
|
|
@@ -148,7 +148,7 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
148
148
|
|
|
149
149
|
// Add tool name at the top of content
|
|
150
150
|
if (tool.name) {
|
|
151
|
-
const toolName = createElement("div", "
|
|
151
|
+
const toolName = createElement("div", "persona-text-xs persona-text-persona-muted persona-italic");
|
|
152
152
|
if (toolCallConfig.contentTextColor) {
|
|
153
153
|
toolName.style.color = toolCallConfig.contentTextColor;
|
|
154
154
|
} else if (toolCallConfig.headerTextColor) {
|
|
@@ -159,10 +159,10 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
if (tool.args !== undefined) {
|
|
162
|
-
const argsBlock = createElement("div", "
|
|
162
|
+
const argsBlock = createElement("div", "persona-space-y-1");
|
|
163
163
|
const argsLabel = createElement(
|
|
164
164
|
"div",
|
|
165
|
-
"
|
|
165
|
+
"persona-text-xs persona-text-persona-muted"
|
|
166
166
|
);
|
|
167
167
|
if (toolCallConfig.labelTextColor) {
|
|
168
168
|
argsLabel.style.color = toolCallConfig.labelTextColor;
|
|
@@ -170,7 +170,7 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
170
170
|
argsLabel.textContent = "Arguments";
|
|
171
171
|
const argsPre = createElement(
|
|
172
172
|
"pre",
|
|
173
|
-
"
|
|
173
|
+
"persona-max-h-48 persona-overflow-auto persona-whitespace-pre-wrap persona-rounded-lg persona-border persona-border-gray-100 persona-bg-white persona-px-3 persona-py-2 persona-text-xs persona-text-persona-primary"
|
|
174
174
|
);
|
|
175
175
|
// Ensure font size matches header text (0.75rem / 12px)
|
|
176
176
|
argsPre.style.fontSize = "0.75rem";
|
|
@@ -190,10 +190,10 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
if (tool.chunks && tool.chunks.length) {
|
|
193
|
-
const logsBlock = createElement("div", "
|
|
193
|
+
const logsBlock = createElement("div", "persona-space-y-1");
|
|
194
194
|
const logsLabel = createElement(
|
|
195
195
|
"div",
|
|
196
|
-
"
|
|
196
|
+
"persona-text-xs persona-text-persona-muted"
|
|
197
197
|
);
|
|
198
198
|
if (toolCallConfig.labelTextColor) {
|
|
199
199
|
logsLabel.style.color = toolCallConfig.labelTextColor;
|
|
@@ -201,7 +201,7 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
201
201
|
logsLabel.textContent = "Activity";
|
|
202
202
|
const logsPre = createElement(
|
|
203
203
|
"pre",
|
|
204
|
-
"
|
|
204
|
+
"persona-max-h-48 persona-overflow-auto persona-whitespace-pre-wrap persona-rounded-lg persona-border persona-border-gray-100 persona-bg-white persona-px-3 persona-py-2 persona-text-xs persona-text-persona-primary"
|
|
205
205
|
);
|
|
206
206
|
// Ensure font size matches header text (0.75rem / 12px)
|
|
207
207
|
logsPre.style.fontSize = "0.75rem";
|
|
@@ -221,10 +221,10 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
if (tool.status === "complete" && tool.result !== undefined) {
|
|
224
|
-
const resultBlock = createElement("div", "
|
|
224
|
+
const resultBlock = createElement("div", "persona-space-y-1");
|
|
225
225
|
const resultLabel = createElement(
|
|
226
226
|
"div",
|
|
227
|
-
"
|
|
227
|
+
"persona-text-xs persona-text-persona-muted"
|
|
228
228
|
);
|
|
229
229
|
if (toolCallConfig.labelTextColor) {
|
|
230
230
|
resultLabel.style.color = toolCallConfig.labelTextColor;
|
|
@@ -232,7 +232,7 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
232
232
|
resultLabel.textContent = "Result";
|
|
233
233
|
const resultPre = createElement(
|
|
234
234
|
"pre",
|
|
235
|
-
"
|
|
235
|
+
"persona-max-h-48 persona-overflow-auto persona-whitespace-pre-wrap persona-rounded-lg persona-border persona-border-gray-100 persona-bg-white persona-px-3 persona-py-2 persona-text-xs persona-text-persona-primary"
|
|
236
236
|
);
|
|
237
237
|
// Ensure font size matches header text (0.75rem / 12px)
|
|
238
238
|
resultPre.style.fontSize = "0.75rem";
|
|
@@ -254,7 +254,7 @@ export const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidg
|
|
|
254
254
|
if (tool.status === "complete" && typeof tool.duration === "number") {
|
|
255
255
|
const duration = createElement(
|
|
256
256
|
"div",
|
|
257
|
-
"
|
|
257
|
+
"persona-text-xs persona-text-persona-muted"
|
|
258
258
|
);
|
|
259
259
|
if (toolCallConfig.contentTextColor) {
|
|
260
260
|
duration.style.color = toolCallConfig.contentTextColor;
|
package/src/defaults.ts
CHANGED
|
@@ -91,6 +91,12 @@ export const DEFAULT_WIDGET_CONFIG: Partial<AgentWidgetConfig> = {
|
|
|
91
91
|
colorScheme: "light",
|
|
92
92
|
launcher: {
|
|
93
93
|
enabled: true,
|
|
94
|
+
mountMode: "floating",
|
|
95
|
+
dock: {
|
|
96
|
+
side: "right",
|
|
97
|
+
width: "420px",
|
|
98
|
+
collapsedWidth: "72px",
|
|
99
|
+
},
|
|
94
100
|
title: "Chat Assistant",
|
|
95
101
|
subtitle: "Here to help you get answers fast",
|
|
96
102
|
agentIconText: "💬",
|
|
@@ -251,6 +257,10 @@ export function mergeWithDefaults(
|
|
|
251
257
|
launcher: {
|
|
252
258
|
...DEFAULT_WIDGET_CONFIG.launcher,
|
|
253
259
|
...config.launcher,
|
|
260
|
+
dock: {
|
|
261
|
+
...DEFAULT_WIDGET_CONFIG.launcher?.dock,
|
|
262
|
+
...config.launcher?.dock,
|
|
263
|
+
},
|
|
254
264
|
clearChat: {
|
|
255
265
|
...DEFAULT_WIDGET_CONFIG.launcher?.clearChat,
|
|
256
266
|
...config.launcher?.clearChat,
|
|
@@ -272,10 +282,26 @@ export function mergeWithDefaults(
|
|
|
272
282
|
...DEFAULT_WIDGET_CONFIG.voiceRecognition,
|
|
273
283
|
...config.voiceRecognition,
|
|
274
284
|
},
|
|
275
|
-
features: {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
285
|
+
features: (() => {
|
|
286
|
+
const da = DEFAULT_WIDGET_CONFIG.features?.artifacts;
|
|
287
|
+
const ca = config.features?.artifacts;
|
|
288
|
+
const mergedArtifacts =
|
|
289
|
+
da === undefined && ca === undefined
|
|
290
|
+
? undefined
|
|
291
|
+
: {
|
|
292
|
+
...da,
|
|
293
|
+
...ca,
|
|
294
|
+
layout: {
|
|
295
|
+
...da?.layout,
|
|
296
|
+
...ca?.layout,
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
return {
|
|
300
|
+
...DEFAULT_WIDGET_CONFIG.features,
|
|
301
|
+
...config.features,
|
|
302
|
+
...(mergedArtifacts !== undefined ? { artifacts: mergedArtifacts } : {}),
|
|
303
|
+
};
|
|
304
|
+
})(),
|
|
279
305
|
suggestionChips: config.suggestionChips ?? DEFAULT_WIDGET_CONFIG.suggestionChips,
|
|
280
306
|
suggestionChipsConfig: {
|
|
281
307
|
...DEFAULT_WIDGET_CONFIG.suggestionChipsConfig,
|
package/src/index.ts
CHANGED
|
@@ -7,9 +7,16 @@ export type {
|
|
|
7
7
|
AgentWidgetConfig,
|
|
8
8
|
AgentWidgetTheme,
|
|
9
9
|
AgentWidgetFeatureFlags,
|
|
10
|
+
AgentWidgetArtifactsFeature,
|
|
11
|
+
AgentWidgetArtifactsLayoutConfig,
|
|
12
|
+
PersonaArtifactKind,
|
|
13
|
+
PersonaArtifactRecord,
|
|
14
|
+
PersonaArtifactManualUpsert,
|
|
15
|
+
ArtifactConfigPayload,
|
|
10
16
|
AgentWidgetInitOptions,
|
|
11
17
|
AgentWidgetMessage,
|
|
12
18
|
AgentWidgetLauncherConfig,
|
|
19
|
+
AgentWidgetDockConfig,
|
|
13
20
|
AgentWidgetEvent,
|
|
14
21
|
AgentWidgetStreamParser,
|
|
15
22
|
AgentWidgetStreamParserResult,
|
|
@@ -25,6 +32,7 @@ export type {
|
|
|
25
32
|
MessageContent,
|
|
26
33
|
// Attachment config type
|
|
27
34
|
AgentWidgetAttachmentsConfig,
|
|
35
|
+
AgentWidgetComposerConfig,
|
|
28
36
|
// Layout types
|
|
29
37
|
AgentWidgetLayoutConfig,
|
|
30
38
|
AgentWidgetHeaderLayoutConfig,
|
|
@@ -62,6 +70,7 @@ export type {
|
|
|
62
70
|
// Agent execution types
|
|
63
71
|
AgentConfig,
|
|
64
72
|
AgentLoopConfig,
|
|
73
|
+
AgentToolsConfig,
|
|
65
74
|
AgentRequestOptions,
|
|
66
75
|
AgentExecutionState,
|
|
67
76
|
AgentMessageMetadata,
|
|
@@ -82,6 +91,11 @@ export type {
|
|
|
82
91
|
} from "./types";
|
|
83
92
|
|
|
84
93
|
export { initAgentWidgetFn as initAgentWidget };
|
|
94
|
+
export {
|
|
95
|
+
createWidgetHostLayout,
|
|
96
|
+
type WidgetHostLayout,
|
|
97
|
+
type WidgetHostLayoutMode
|
|
98
|
+
} from "./runtime/host-layout";
|
|
85
99
|
export {
|
|
86
100
|
createAgentExperience,
|
|
87
101
|
type AgentWidgetController
|
|
@@ -125,6 +139,21 @@ export {
|
|
|
125
139
|
fileToImagePart,
|
|
126
140
|
validateImageFile
|
|
127
141
|
} from "./utils/content";
|
|
142
|
+
export {
|
|
143
|
+
collectEnrichedPageContext,
|
|
144
|
+
formatEnrichedContext,
|
|
145
|
+
generateStableSelector,
|
|
146
|
+
defaultParseRules
|
|
147
|
+
} from "./utils/dom-context";
|
|
148
|
+
export type {
|
|
149
|
+
EnrichedPageElement,
|
|
150
|
+
DomContextOptions,
|
|
151
|
+
DomContextMode,
|
|
152
|
+
ParseOptionsConfig,
|
|
153
|
+
ParseRule,
|
|
154
|
+
RuleScoringContext,
|
|
155
|
+
FormatEnrichedContextOptions
|
|
156
|
+
} from "./utils/dom-context";
|
|
128
157
|
export {
|
|
129
158
|
AttachmentManager,
|
|
130
159
|
type PendingAttachment,
|
|
@@ -135,6 +164,7 @@ export {
|
|
|
135
164
|
generateUserMessageId,
|
|
136
165
|
generateAssistantMessageId
|
|
137
166
|
} from "./utils/message-id";
|
|
167
|
+
export { isDockedMountMode, resolveDockConfig } from "./utils/dock";
|
|
138
168
|
export { generateCodeSnippet } from "./utils/code-generators";
|
|
139
169
|
export type { CodeFormat, CodeGeneratorHooks, CodeGeneratorOptions } from "./utils/code-generators";
|
|
140
170
|
export { VERSION } from "./version";
|
|
@@ -144,6 +174,55 @@ export type { AgentWidgetInitHandle };
|
|
|
144
174
|
export type { AgentWidgetPlugin } from "./plugins/types";
|
|
145
175
|
export { pluginRegistry } from "./plugins/registry";
|
|
146
176
|
|
|
177
|
+
// Theme system exports
|
|
178
|
+
export {
|
|
179
|
+
createTheme,
|
|
180
|
+
resolveTokens,
|
|
181
|
+
themeToCssVariables,
|
|
182
|
+
applyThemeVariables,
|
|
183
|
+
getActiveTheme,
|
|
184
|
+
getColorScheme,
|
|
185
|
+
detectColorScheme,
|
|
186
|
+
createThemeObserver
|
|
187
|
+
} from "./utils/theme";
|
|
188
|
+
export {
|
|
189
|
+
DEFAULT_PALETTE,
|
|
190
|
+
DEFAULT_SEMANTIC,
|
|
191
|
+
DEFAULT_COMPONENTS,
|
|
192
|
+
validateTheme
|
|
193
|
+
} from "./utils/tokens";
|
|
194
|
+
export {
|
|
195
|
+
accessibilityPlugin,
|
|
196
|
+
animationsPlugin,
|
|
197
|
+
brandPlugin,
|
|
198
|
+
reducedMotionPlugin,
|
|
199
|
+
highContrastPlugin,
|
|
200
|
+
createPlugin
|
|
201
|
+
} from "./utils/plugins";
|
|
202
|
+
export {
|
|
203
|
+
migrateV1Theme,
|
|
204
|
+
validateV1Theme
|
|
205
|
+
} from "./utils/migration";
|
|
206
|
+
export type {
|
|
207
|
+
PersonaTheme,
|
|
208
|
+
PersonaThemePlugin,
|
|
209
|
+
CreateThemeOptions,
|
|
210
|
+
TokenReference,
|
|
211
|
+
ColorShade,
|
|
212
|
+
ColorPalette,
|
|
213
|
+
SpacingScale,
|
|
214
|
+
TypographyScale,
|
|
215
|
+
ShadowScale,
|
|
216
|
+
BorderScale,
|
|
217
|
+
RadiusScale,
|
|
218
|
+
SemanticColors,
|
|
219
|
+
SemanticSpacing,
|
|
220
|
+
SemanticTypography,
|
|
221
|
+
ComponentTokens,
|
|
222
|
+
ThemeValidationResult,
|
|
223
|
+
ThemeValidationError
|
|
224
|
+
} from "./types/theme";
|
|
225
|
+
|
|
147
226
|
// Component system exports
|
|
148
227
|
export { componentRegistry } from "./components/registry";
|
|
149
228
|
export type { ComponentRenderer, ComponentContext } from "./components/registry";
|
|
@@ -184,8 +263,7 @@ export {
|
|
|
184
263
|
getHeaderLayout,
|
|
185
264
|
buildHeaderWithLayout,
|
|
186
265
|
buildDefaultHeader,
|
|
187
|
-
buildMinimalHeader
|
|
188
|
-
buildExpandedHeader
|
|
266
|
+
buildMinimalHeader
|
|
189
267
|
} from "./components/header-layouts";
|
|
190
268
|
export type {
|
|
191
269
|
HeaderLayoutContext,
|
package/src/install.ts
CHANGED
|
@@ -18,6 +18,8 @@ interface SiteAgentInstallConfig {
|
|
|
18
18
|
clientToken?: string;
|
|
19
19
|
flowId?: string;
|
|
20
20
|
apiUrl?: string;
|
|
21
|
+
// Optional query param key that gates widget installation in preview mode
|
|
22
|
+
previewQueryParam?: string;
|
|
21
23
|
// Shadow DOM option (defaults to false for better CSS compatibility)
|
|
22
24
|
useShadowDom?: boolean;
|
|
23
25
|
}
|
|
@@ -84,6 +86,12 @@ declare global {
|
|
|
84
86
|
scriptConfig.apiUrl = apiUrl;
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
// Optional preview query param gate
|
|
90
|
+
const previewQueryParam = script.getAttribute('data-preview-param');
|
|
91
|
+
if (previewQueryParam) {
|
|
92
|
+
scriptConfig.previewQueryParam = previewQueryParam;
|
|
93
|
+
}
|
|
94
|
+
|
|
87
95
|
return scriptConfig;
|
|
88
96
|
};
|
|
89
97
|
|
|
@@ -93,6 +101,20 @@ declare global {
|
|
|
93
101
|
// Merge script attributes with window config (script attributes take precedence)
|
|
94
102
|
const windowConfig: SiteAgentInstallConfig = window.siteAgentConfig || {};
|
|
95
103
|
const config: SiteAgentInstallConfig = { ...windowConfig, ...scriptConfig };
|
|
104
|
+
|
|
105
|
+
const isPreviewModeEnabled = (): boolean => {
|
|
106
|
+
if (!config.previewQueryParam) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const params = new URLSearchParams(window.location.search);
|
|
111
|
+
const value = params.get(config.previewQueryParam);
|
|
112
|
+
return value !== null && value !== "" && value.toLowerCase() !== "false" && value !== "0";
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (!isPreviewModeEnabled()) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
96
118
|
|
|
97
119
|
const version = config.version || "latest";
|
|
98
120
|
const cdn = config.cdn || "jsdelivr";
|
package/src/plugins/types.ts
CHANGED
|
@@ -61,7 +61,30 @@ export interface AgentWidgetPlugin {
|
|
|
61
61
|
config: AgentWidgetConfig;
|
|
62
62
|
defaultRenderer: () => HTMLElement;
|
|
63
63
|
onSubmit: (text: string) => void;
|
|
64
|
+
/**
|
|
65
|
+
* When true, the assistant stream is active — same moment `session.isStreaming()` becomes true.
|
|
66
|
+
* Prefer wiring controls to `data-persona-composer-disable-when-streaming` plus `setComposerDisabled`
|
|
67
|
+
* in the host, or react to `footer.dataset.personaComposerStreaming === "true"`.
|
|
68
|
+
*/
|
|
69
|
+
streaming: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Legacy alias: host disables the primary submit control while `streaming` is true.
|
|
72
|
+
* @deprecated Use `streaming` for new plugins.
|
|
73
|
+
*/
|
|
64
74
|
disabled: boolean;
|
|
75
|
+
/** Opens the hidden file input when `config.attachments.enabled` is true (no-op otherwise). */
|
|
76
|
+
openAttachmentPicker: () => void;
|
|
77
|
+
/** From `config.composer.models` */
|
|
78
|
+
models?: Array<{ id: string; label: string }>;
|
|
79
|
+
/** From `config.composer.selectedModelId` */
|
|
80
|
+
selectedModelId?: string;
|
|
81
|
+
/** Updates `config.composer.selectedModelId` for the running widget instance. */
|
|
82
|
+
onModelChange?: (modelId: string) => void;
|
|
83
|
+
/**
|
|
84
|
+
* Same behavior as the built-in mic when voice is enabled.
|
|
85
|
+
* Omitted when `config.voiceRecognition.enabled` is not true.
|
|
86
|
+
*/
|
|
87
|
+
onVoiceToggle?: () => void;
|
|
65
88
|
}) => HTMLElement | null;
|
|
66
89
|
|
|
67
90
|
/**
|
package/src/postprocessors.ts
CHANGED
|
@@ -162,7 +162,7 @@ export const createDirectivePostprocessor = (markdownConfig?: AgentWidgetMarkdow
|
|
|
162
162
|
placeholders.forEach(({ token, type }) => {
|
|
163
163
|
const tokenRegex = new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
164
164
|
const safeType = escapeAttribute(type);
|
|
165
|
-
const replacement = `<div class="
|
|
165
|
+
const replacement = `<div class="persona-form-directive" data-tv-form="${safeType}"></div>`;
|
|
166
166
|
html = html.replace(tokenRegex, replacement);
|
|
167
167
|
});
|
|
168
168
|
|
|
@@ -186,7 +186,7 @@ export const directivePostprocessor = (text: string): string => {
|
|
|
186
186
|
placeholders.forEach(({ token, type }) => {
|
|
187
187
|
const tokenRegex = new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
188
188
|
const safeType = escapeAttribute(type);
|
|
189
|
-
const replacement = `<div class="
|
|
189
|
+
const replacement = `<div class="persona-form-directive" data-tv-form="${safeType}"></div>`;
|
|
190
190
|
html = html.replace(tokenRegex, replacement);
|
|
191
191
|
});
|
|
192
192
|
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentWidgetConfig,
|
|
3
|
+
AgentWidgetStateSnapshot,
|
|
4
|
+
} from "../types";
|
|
5
|
+
import { isDockedMountMode, resolveDockConfig } from "../utils/dock";
|
|
6
|
+
|
|
7
|
+
export type WidgetHostLayoutMode = "direct" | "docked";
|
|
8
|
+
|
|
9
|
+
export type WidgetHostLayout = {
|
|
10
|
+
mode: WidgetHostLayoutMode;
|
|
11
|
+
host: HTMLElement;
|
|
12
|
+
shell: HTMLElement | null;
|
|
13
|
+
syncWidgetState: (state: Pick<AgentWidgetStateSnapshot, "open" | "launcherEnabled">) => void;
|
|
14
|
+
updateConfig: (config?: AgentWidgetConfig) => void;
|
|
15
|
+
destroy: () => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const setDirectHostStyles = (host: HTMLElement, config?: AgentWidgetConfig): void => {
|
|
19
|
+
const launcherEnabled = config?.launcher?.enabled ?? true;
|
|
20
|
+
host.className = "persona-host";
|
|
21
|
+
host.style.height = launcherEnabled ? "" : "100%";
|
|
22
|
+
host.style.display = launcherEnabled ? "" : "flex";
|
|
23
|
+
host.style.flexDirection = launcherEnabled ? "" : "column";
|
|
24
|
+
host.style.flex = launcherEnabled ? "" : "1 1 auto";
|
|
25
|
+
host.style.minHeight = launcherEnabled ? "" : "0";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const applyDockStyles = (
|
|
29
|
+
shell: HTMLElement,
|
|
30
|
+
contentSlot: HTMLElement,
|
|
31
|
+
dockSlot: HTMLElement,
|
|
32
|
+
host: HTMLElement,
|
|
33
|
+
config: AgentWidgetConfig | undefined,
|
|
34
|
+
expanded: boolean
|
|
35
|
+
): void => {
|
|
36
|
+
const dock = resolveDockConfig(config);
|
|
37
|
+
const width = expanded ? dock.width : dock.collapsedWidth;
|
|
38
|
+
|
|
39
|
+
shell.dataset.personaHostLayout = "docked";
|
|
40
|
+
shell.dataset.personaDockSide = dock.side;
|
|
41
|
+
shell.dataset.personaDockOpen = expanded ? "true" : "false";
|
|
42
|
+
shell.style.display = "flex";
|
|
43
|
+
shell.style.flexDirection = "row";
|
|
44
|
+
shell.style.alignItems = "stretch";
|
|
45
|
+
shell.style.width = "100%";
|
|
46
|
+
shell.style.maxWidth = "100%";
|
|
47
|
+
shell.style.minWidth = "0";
|
|
48
|
+
shell.style.height = "100%";
|
|
49
|
+
shell.style.minHeight = "0";
|
|
50
|
+
shell.style.position = "relative";
|
|
51
|
+
|
|
52
|
+
contentSlot.style.display = "flex";
|
|
53
|
+
contentSlot.style.flexDirection = "column";
|
|
54
|
+
contentSlot.style.flex = "1 1 auto";
|
|
55
|
+
contentSlot.style.minWidth = "0";
|
|
56
|
+
contentSlot.style.minHeight = "0";
|
|
57
|
+
contentSlot.style.position = "relative";
|
|
58
|
+
|
|
59
|
+
dockSlot.style.display = "flex";
|
|
60
|
+
dockSlot.style.flexDirection = "column";
|
|
61
|
+
dockSlot.style.flex = `0 0 ${width}`;
|
|
62
|
+
dockSlot.style.width = width;
|
|
63
|
+
dockSlot.style.maxWidth = width;
|
|
64
|
+
dockSlot.style.minWidth = width;
|
|
65
|
+
dockSlot.style.minHeight = "0";
|
|
66
|
+
dockSlot.style.position = "relative";
|
|
67
|
+
dockSlot.style.overflow = "visible";
|
|
68
|
+
dockSlot.style.transition = "width 180ms ease, min-width 180ms ease, max-width 180ms ease, flex-basis 180ms ease";
|
|
69
|
+
|
|
70
|
+
host.className = "persona-host";
|
|
71
|
+
host.style.height = "100%";
|
|
72
|
+
host.style.minHeight = "0";
|
|
73
|
+
host.style.display = "flex";
|
|
74
|
+
host.style.flexDirection = "column";
|
|
75
|
+
host.style.flex = "1 1 auto";
|
|
76
|
+
|
|
77
|
+
if (dock.side === "left") {
|
|
78
|
+
if (shell.firstElementChild !== dockSlot) {
|
|
79
|
+
shell.replaceChildren(dockSlot, contentSlot);
|
|
80
|
+
}
|
|
81
|
+
} else if (shell.lastElementChild !== dockSlot) {
|
|
82
|
+
shell.replaceChildren(contentSlot, dockSlot);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const createDirectLayout = (target: HTMLElement, config?: AgentWidgetConfig): WidgetHostLayout => {
|
|
87
|
+
const host = target.ownerDocument.createElement("div");
|
|
88
|
+
setDirectHostStyles(host, config);
|
|
89
|
+
target.appendChild(host);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
mode: "direct",
|
|
93
|
+
host,
|
|
94
|
+
shell: null,
|
|
95
|
+
syncWidgetState: () => {},
|
|
96
|
+
updateConfig(nextConfig?: AgentWidgetConfig) {
|
|
97
|
+
setDirectHostStyles(host, nextConfig);
|
|
98
|
+
},
|
|
99
|
+
destroy() {
|
|
100
|
+
host.remove();
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const createDockedLayout = (target: HTMLElement, config?: AgentWidgetConfig): WidgetHostLayout => {
|
|
106
|
+
const { ownerDocument } = target;
|
|
107
|
+
const originalParent = target.parentElement;
|
|
108
|
+
|
|
109
|
+
if (!originalParent) {
|
|
110
|
+
throw new Error("Docked widget target must be attached to the DOM");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const tagName = target.tagName.toUpperCase();
|
|
114
|
+
if (tagName === "BODY" || tagName === "HTML") {
|
|
115
|
+
throw new Error('Docked widget target must be a concrete container element, not "body" or "html"');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const originalNextSibling = target.nextSibling;
|
|
119
|
+
const shell = ownerDocument.createElement("div");
|
|
120
|
+
const contentSlot = ownerDocument.createElement("div");
|
|
121
|
+
const dockSlot = ownerDocument.createElement("aside");
|
|
122
|
+
const host = ownerDocument.createElement("div");
|
|
123
|
+
let expanded = (config?.launcher?.enabled ?? true) ? (config?.launcher?.autoExpand ?? false) : true;
|
|
124
|
+
|
|
125
|
+
contentSlot.dataset.personaDockRole = "content";
|
|
126
|
+
dockSlot.dataset.personaDockRole = "panel";
|
|
127
|
+
host.dataset.personaDockRole = "host";
|
|
128
|
+
|
|
129
|
+
dockSlot.appendChild(host);
|
|
130
|
+
originalParent.insertBefore(shell, target);
|
|
131
|
+
contentSlot.appendChild(target);
|
|
132
|
+
shell.appendChild(contentSlot);
|
|
133
|
+
shell.appendChild(dockSlot);
|
|
134
|
+
applyDockStyles(shell, contentSlot, dockSlot, host, config, expanded);
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
mode: "docked",
|
|
138
|
+
host,
|
|
139
|
+
shell,
|
|
140
|
+
syncWidgetState(state) {
|
|
141
|
+
const nextExpanded = state.launcherEnabled ? state.open : true;
|
|
142
|
+
if (expanded === nextExpanded) return;
|
|
143
|
+
expanded = nextExpanded;
|
|
144
|
+
applyDockStyles(shell, contentSlot, dockSlot, host, config, expanded);
|
|
145
|
+
},
|
|
146
|
+
updateConfig(nextConfig?: AgentWidgetConfig) {
|
|
147
|
+
config = nextConfig;
|
|
148
|
+
if ((config?.launcher?.enabled ?? true) === false) {
|
|
149
|
+
expanded = true;
|
|
150
|
+
}
|
|
151
|
+
applyDockStyles(shell, contentSlot, dockSlot, host, config, expanded);
|
|
152
|
+
},
|
|
153
|
+
destroy() {
|
|
154
|
+
if (originalParent.isConnected) {
|
|
155
|
+
if (originalNextSibling && originalNextSibling.parentNode === originalParent) {
|
|
156
|
+
originalParent.insertBefore(target, originalNextSibling);
|
|
157
|
+
} else {
|
|
158
|
+
originalParent.appendChild(target);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
shell.remove();
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const createWidgetHostLayout = (
|
|
167
|
+
target: HTMLElement,
|
|
168
|
+
config?: AgentWidgetConfig
|
|
169
|
+
): WidgetHostLayout => {
|
|
170
|
+
if (isDockedMountMode(config)) {
|
|
171
|
+
return createDockedLayout(target, config);
|
|
172
|
+
}
|
|
173
|
+
return createDirectLayout(target, config);
|
|
174
|
+
};
|