@runtypelabs/persona 1.36.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 +1080 -0
- package/dist/index.cjs +140 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2626 -0
- package/dist/index.d.ts +2626 -0
- package/dist/index.global.js +1843 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +140 -0
- package/dist/index.js.map +1 -0
- package/dist/install.global.js +2 -0
- package/dist/install.global.js.map +1 -0
- package/dist/widget.css +1627 -0
- package/package.json +79 -0
- package/src/@types/idiomorph.d.ts +37 -0
- package/src/client.test.ts +387 -0
- package/src/client.ts +1589 -0
- package/src/components/composer-builder.ts +530 -0
- package/src/components/feedback.ts +379 -0
- package/src/components/forms.ts +170 -0
- package/src/components/header-builder.ts +455 -0
- package/src/components/header-layouts.ts +303 -0
- package/src/components/launcher.ts +193 -0
- package/src/components/message-bubble.ts +528 -0
- package/src/components/messages.ts +54 -0
- package/src/components/panel.ts +204 -0
- package/src/components/reasoning-bubble.ts +144 -0
- package/src/components/registry.ts +87 -0
- package/src/components/suggestions.ts +97 -0
- package/src/components/tool-bubble.ts +288 -0
- package/src/defaults.ts +321 -0
- package/src/index.ts +175 -0
- package/src/install.ts +284 -0
- package/src/plugins/registry.ts +77 -0
- package/src/plugins/types.ts +95 -0
- package/src/postprocessors.ts +194 -0
- package/src/runtime/init.ts +162 -0
- package/src/session.ts +376 -0
- package/src/styles/tailwind.css +20 -0
- package/src/styles/widget.css +1627 -0
- package/src/types.ts +1635 -0
- package/src/ui.ts +3341 -0
- package/src/utils/actions.ts +227 -0
- package/src/utils/attachment-manager.ts +384 -0
- package/src/utils/code-generators.test.ts +500 -0
- package/src/utils/code-generators.ts +1806 -0
- package/src/utils/component-middleware.ts +137 -0
- package/src/utils/component-parser.ts +119 -0
- package/src/utils/constants.ts +16 -0
- package/src/utils/content.ts +306 -0
- package/src/utils/dom.ts +25 -0
- package/src/utils/events.ts +41 -0
- package/src/utils/formatting.test.ts +166 -0
- package/src/utils/formatting.ts +470 -0
- package/src/utils/icons.ts +92 -0
- package/src/utils/message-id.ts +37 -0
- package/src/utils/morph.ts +36 -0
- package/src/utils/positioning.ts +17 -0
- package/src/utils/storage.ts +72 -0
- package/src/utils/theme.ts +105 -0
- package/src/widget.css +1 -0
- package/widget.css +1 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
initAgentWidget as initAgentWidgetFn,
|
|
3
|
+
type AgentWidgetInitHandle
|
|
4
|
+
} from "./runtime/init";
|
|
5
|
+
|
|
6
|
+
export type {
|
|
7
|
+
AgentWidgetConfig,
|
|
8
|
+
AgentWidgetTheme,
|
|
9
|
+
AgentWidgetFeatureFlags,
|
|
10
|
+
AgentWidgetInitOptions,
|
|
11
|
+
AgentWidgetMessage,
|
|
12
|
+
AgentWidgetLauncherConfig,
|
|
13
|
+
AgentWidgetEvent,
|
|
14
|
+
AgentWidgetStreamParser,
|
|
15
|
+
AgentWidgetStreamParserResult,
|
|
16
|
+
AgentWidgetRequestPayload,
|
|
17
|
+
AgentWidgetCustomFetch,
|
|
18
|
+
AgentWidgetSSEEventParser,
|
|
19
|
+
AgentWidgetSSEEventResult,
|
|
20
|
+
AgentWidgetHeadersFunction,
|
|
21
|
+
// Multi-modal content types
|
|
22
|
+
TextContentPart,
|
|
23
|
+
ImageContentPart,
|
|
24
|
+
ContentPart,
|
|
25
|
+
MessageContent,
|
|
26
|
+
// Attachment config type
|
|
27
|
+
AgentWidgetAttachmentsConfig,
|
|
28
|
+
// Layout types
|
|
29
|
+
AgentWidgetLayoutConfig,
|
|
30
|
+
AgentWidgetHeaderLayoutConfig,
|
|
31
|
+
AgentWidgetMessageLayoutConfig,
|
|
32
|
+
AgentWidgetAvatarConfig,
|
|
33
|
+
AgentWidgetTimestampConfig,
|
|
34
|
+
WidgetLayoutSlot,
|
|
35
|
+
SlotRenderer,
|
|
36
|
+
SlotRenderContext,
|
|
37
|
+
HeaderRenderContext,
|
|
38
|
+
MessageRenderContext,
|
|
39
|
+
// Markdown types
|
|
40
|
+
AgentWidgetMarkdownConfig,
|
|
41
|
+
AgentWidgetMarkdownOptions,
|
|
42
|
+
AgentWidgetMarkdownRendererOverrides,
|
|
43
|
+
// Message actions types
|
|
44
|
+
AgentWidgetMessageActionsConfig,
|
|
45
|
+
AgentWidgetMessageFeedback,
|
|
46
|
+
// Client token types
|
|
47
|
+
ClientSession,
|
|
48
|
+
ClientInitResponse,
|
|
49
|
+
ClientChatRequest,
|
|
50
|
+
ClientFeedbackRequest,
|
|
51
|
+
ClientFeedbackType
|
|
52
|
+
} from "./types";
|
|
53
|
+
|
|
54
|
+
export { initAgentWidgetFn as initAgentWidget };
|
|
55
|
+
export {
|
|
56
|
+
createAgentExperience,
|
|
57
|
+
type AgentWidgetController
|
|
58
|
+
} from "./ui";
|
|
59
|
+
export {
|
|
60
|
+
AgentWidgetSession,
|
|
61
|
+
type AgentWidgetSessionStatus
|
|
62
|
+
} from "./session";
|
|
63
|
+
export { AgentWidgetClient } from "./client";
|
|
64
|
+
export { createLocalStorageAdapter } from "./utils/storage";
|
|
65
|
+
export {
|
|
66
|
+
createActionManager,
|
|
67
|
+
defaultActionHandlers,
|
|
68
|
+
defaultJsonActionParser
|
|
69
|
+
} from "./utils/actions";
|
|
70
|
+
export {
|
|
71
|
+
markdownPostprocessor,
|
|
72
|
+
escapeHtml,
|
|
73
|
+
directivePostprocessor,
|
|
74
|
+
createMarkdownProcessor,
|
|
75
|
+
createMarkdownProcessorFromConfig,
|
|
76
|
+
createDirectivePostprocessor
|
|
77
|
+
} from "./postprocessors";
|
|
78
|
+
export type { MarkdownProcessorOptions } from "./postprocessors";
|
|
79
|
+
export {
|
|
80
|
+
createPlainTextParser,
|
|
81
|
+
createJsonStreamParser,
|
|
82
|
+
createFlexibleJsonStreamParser,
|
|
83
|
+
createRegexJsonParser,
|
|
84
|
+
createXmlParser
|
|
85
|
+
} from "./utils/formatting";
|
|
86
|
+
export {
|
|
87
|
+
// Multi-modal content utilities
|
|
88
|
+
normalizeContent,
|
|
89
|
+
getDisplayText,
|
|
90
|
+
hasImages,
|
|
91
|
+
getImageParts,
|
|
92
|
+
createTextPart,
|
|
93
|
+
createImagePart,
|
|
94
|
+
fileToImagePart,
|
|
95
|
+
validateImageFile
|
|
96
|
+
} from "./utils/content";
|
|
97
|
+
export {
|
|
98
|
+
AttachmentManager,
|
|
99
|
+
type PendingAttachment,
|
|
100
|
+
type AttachmentManagerConfig
|
|
101
|
+
} from "./utils/attachment-manager";
|
|
102
|
+
export {
|
|
103
|
+
generateMessageId,
|
|
104
|
+
generateUserMessageId,
|
|
105
|
+
generateAssistantMessageId
|
|
106
|
+
} from "./utils/message-id";
|
|
107
|
+
export { generateCodeSnippet } from "./utils/code-generators";
|
|
108
|
+
export type { CodeFormat, CodeGeneratorHooks, CodeGeneratorOptions } from "./utils/code-generators";
|
|
109
|
+
export type { AgentWidgetInitHandle };
|
|
110
|
+
|
|
111
|
+
// Plugin system exports
|
|
112
|
+
export type { AgentWidgetPlugin } from "./plugins/types";
|
|
113
|
+
export { pluginRegistry } from "./plugins/registry";
|
|
114
|
+
|
|
115
|
+
// Component system exports
|
|
116
|
+
export { componentRegistry } from "./components/registry";
|
|
117
|
+
export type { ComponentRenderer, ComponentContext } from "./components/registry";
|
|
118
|
+
export {
|
|
119
|
+
createComponentStreamParser,
|
|
120
|
+
isComponentDirectiveType
|
|
121
|
+
} from "./utils/component-parser";
|
|
122
|
+
export type { ComponentDirective } from "./utils/component-parser";
|
|
123
|
+
export {
|
|
124
|
+
renderComponentDirective,
|
|
125
|
+
createComponentMiddleware,
|
|
126
|
+
hasComponentDirective,
|
|
127
|
+
extractComponentDirectiveFromMessage
|
|
128
|
+
} from "./utils/component-middleware";
|
|
129
|
+
|
|
130
|
+
// Default configuration exports
|
|
131
|
+
export {
|
|
132
|
+
DEFAULT_WIDGET_CONFIG,
|
|
133
|
+
DEFAULT_LIGHT_THEME,
|
|
134
|
+
DEFAULT_DARK_THEME,
|
|
135
|
+
mergeWithDefaults
|
|
136
|
+
} from "./defaults";
|
|
137
|
+
|
|
138
|
+
// Layout system exports
|
|
139
|
+
export {
|
|
140
|
+
buildHeader,
|
|
141
|
+
buildComposer,
|
|
142
|
+
attachHeaderToContainer
|
|
143
|
+
} from "./components/panel";
|
|
144
|
+
export type {
|
|
145
|
+
HeaderElements,
|
|
146
|
+
HeaderBuildContext,
|
|
147
|
+
ComposerElements,
|
|
148
|
+
ComposerBuildContext
|
|
149
|
+
} from "./components/panel";
|
|
150
|
+
export {
|
|
151
|
+
headerLayouts,
|
|
152
|
+
getHeaderLayout,
|
|
153
|
+
buildHeaderWithLayout,
|
|
154
|
+
buildDefaultHeader,
|
|
155
|
+
buildMinimalHeader,
|
|
156
|
+
buildExpandedHeader
|
|
157
|
+
} from "./components/header-layouts";
|
|
158
|
+
export type {
|
|
159
|
+
HeaderLayoutContext,
|
|
160
|
+
HeaderLayoutRenderer
|
|
161
|
+
} from "./components/header-layouts";
|
|
162
|
+
export {
|
|
163
|
+
createStandardBubble,
|
|
164
|
+
createBubbleWithLayout,
|
|
165
|
+
createTypingIndicator,
|
|
166
|
+
createMessageActions
|
|
167
|
+
} from "./components/message-bubble";
|
|
168
|
+
export type { MessageTransform, MessageActionCallbacks } from "./components/message-bubble";
|
|
169
|
+
export {
|
|
170
|
+
createCSATFeedback,
|
|
171
|
+
createNPSFeedback
|
|
172
|
+
} from "./components/feedback";
|
|
173
|
+
export type { CSATFeedbackOptions, NPSFeedbackOptions } from "./components/feedback";
|
|
174
|
+
|
|
175
|
+
export default initAgentWidgetFn;
|
package/src/install.ts
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone installer script for easy script tag installation
|
|
3
|
+
* This script automatically loads CSS and JS, then initializes the widget
|
|
4
|
+
* if configuration is provided via window.siteAgentConfig
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface SiteAgentInstallConfig {
|
|
8
|
+
version?: string;
|
|
9
|
+
cdn?: "unpkg" | "jsdelivr";
|
|
10
|
+
cssUrl?: string;
|
|
11
|
+
jsUrl?: string;
|
|
12
|
+
target?: string | HTMLElement;
|
|
13
|
+
config?: any;
|
|
14
|
+
autoInit?: boolean;
|
|
15
|
+
// Client token mode options (can also be set via data attributes)
|
|
16
|
+
clientToken?: string;
|
|
17
|
+
flowId?: string;
|
|
18
|
+
apiUrl?: string;
|
|
19
|
+
// Shadow DOM option (defaults to false for better CSS compatibility)
|
|
20
|
+
useShadowDom?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
declare global {
|
|
24
|
+
interface Window {
|
|
25
|
+
siteAgentConfig?: SiteAgentInstallConfig;
|
|
26
|
+
AgentWidget?: any;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
(function() {
|
|
31
|
+
"use strict";
|
|
32
|
+
|
|
33
|
+
// Prevent double installation
|
|
34
|
+
if ((window as any).__siteAgentInstallerLoaded) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
(window as any).__siteAgentInstallerLoaded = true;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Read configuration from data attributes on the current script tag.
|
|
41
|
+
* Supports: data-config (JSON), data-travrse-token, data-flow-id, data-api-url
|
|
42
|
+
*/
|
|
43
|
+
const getConfigFromScript = (): Partial<SiteAgentInstallConfig> => {
|
|
44
|
+
// Try to get the current script element
|
|
45
|
+
const script = document.currentScript as HTMLScriptElement | null;
|
|
46
|
+
if (!script) return {};
|
|
47
|
+
|
|
48
|
+
const scriptConfig: Partial<SiteAgentInstallConfig> = {};
|
|
49
|
+
|
|
50
|
+
// Full config from data-config attribute (JSON string)
|
|
51
|
+
const configJson = script.getAttribute('data-config');
|
|
52
|
+
if (configJson) {
|
|
53
|
+
try {
|
|
54
|
+
const parsedConfig = JSON.parse(configJson);
|
|
55
|
+
// If it has nested 'config' property, use it; otherwise treat as widget config
|
|
56
|
+
if (parsedConfig.config) {
|
|
57
|
+
Object.assign(scriptConfig, parsedConfig);
|
|
58
|
+
} else {
|
|
59
|
+
// Treat the entire object as widget config
|
|
60
|
+
scriptConfig.config = parsedConfig;
|
|
61
|
+
}
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.error("Failed to parse data-config JSON:", e);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Client token from data attribute (primary method for client token mode)
|
|
68
|
+
const token = script.getAttribute('data-travrse-token');
|
|
69
|
+
if (token) {
|
|
70
|
+
scriptConfig.clientToken = token;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Optional flow ID
|
|
74
|
+
const flowId = script.getAttribute('data-flow-id');
|
|
75
|
+
if (flowId) {
|
|
76
|
+
scriptConfig.flowId = flowId;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Optional API URL override
|
|
80
|
+
const apiUrl = script.getAttribute('data-api-url');
|
|
81
|
+
if (apiUrl) {
|
|
82
|
+
scriptConfig.apiUrl = apiUrl;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return scriptConfig;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Get config from script attributes (must be called synchronously during script execution)
|
|
89
|
+
const scriptConfig = getConfigFromScript();
|
|
90
|
+
|
|
91
|
+
// Merge script attributes with window config (script attributes take precedence)
|
|
92
|
+
const windowConfig: SiteAgentInstallConfig = window.siteAgentConfig || {};
|
|
93
|
+
const config: SiteAgentInstallConfig = { ...windowConfig, ...scriptConfig };
|
|
94
|
+
|
|
95
|
+
const version = config.version || "latest";
|
|
96
|
+
const cdn = config.cdn || "jsdelivr";
|
|
97
|
+
const autoInit = config.autoInit !== false; // Default to true
|
|
98
|
+
|
|
99
|
+
// Determine CDN base URL
|
|
100
|
+
const getCdnBase = () => {
|
|
101
|
+
if (config.cssUrl && config.jsUrl) {
|
|
102
|
+
return { cssUrl: config.cssUrl, jsUrl: config.jsUrl };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const packageName = "@runtypelabs/persona";
|
|
106
|
+
const basePath = `/npm/${packageName}@${version}/dist`;
|
|
107
|
+
|
|
108
|
+
if (cdn === "unpkg") {
|
|
109
|
+
return {
|
|
110
|
+
cssUrl: `https://unpkg.com${basePath}/widget.css`,
|
|
111
|
+
jsUrl: `https://unpkg.com${basePath}/index.global.js`
|
|
112
|
+
};
|
|
113
|
+
} else {
|
|
114
|
+
return {
|
|
115
|
+
cssUrl: `https://cdn.jsdelivr.net${basePath}/widget.css`,
|
|
116
|
+
jsUrl: `https://cdn.jsdelivr.net${basePath}/index.global.js`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const { cssUrl, jsUrl } = getCdnBase();
|
|
122
|
+
|
|
123
|
+
// Check if CSS is already loaded
|
|
124
|
+
const isCssLoaded = () => {
|
|
125
|
+
return !!document.head.querySelector('link[data-persona]') ||
|
|
126
|
+
!!document.head.querySelector(`link[href*="widget.css"]`);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Check if JS is already loaded
|
|
130
|
+
const isJsLoaded = () => {
|
|
131
|
+
return !!(window as any).AgentWidget;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Wait for framework hydration to complete (Next.js, Nuxt, etc.)
|
|
136
|
+
* This prevents the framework from removing dynamically added CSS during reconciliation.
|
|
137
|
+
* Uses requestIdleCallback + double requestAnimationFrame for reliable detection.
|
|
138
|
+
*/
|
|
139
|
+
const waitForHydration = (callback: () => void): void => {
|
|
140
|
+
let executed = false;
|
|
141
|
+
|
|
142
|
+
const execute = () => {
|
|
143
|
+
if (executed) return;
|
|
144
|
+
executed = true;
|
|
145
|
+
callback();
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const afterDom = () => {
|
|
149
|
+
// Strategy 1: Use requestIdleCallback if available (best for detecting idle after hydration)
|
|
150
|
+
if (typeof requestIdleCallback !== 'undefined') {
|
|
151
|
+
requestIdleCallback(() => {
|
|
152
|
+
// Double requestAnimationFrame ensures at least one full paint cycle completed
|
|
153
|
+
requestAnimationFrame(() => {
|
|
154
|
+
requestAnimationFrame(execute);
|
|
155
|
+
});
|
|
156
|
+
}, { timeout: 3000 }); // Max wait 3 seconds, then proceed anyway
|
|
157
|
+
} else {
|
|
158
|
+
// Strategy 2: Fallback for Safari (no requestIdleCallback)
|
|
159
|
+
// 300ms is typically enough for hydration on most pages
|
|
160
|
+
setTimeout(execute, 300);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
if (document.readyState === 'loading') {
|
|
165
|
+
document.addEventListener('DOMContentLoaded', afterDom);
|
|
166
|
+
} else {
|
|
167
|
+
// DOM already ready, but still wait for potential hydration
|
|
168
|
+
afterDom();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Load CSS
|
|
173
|
+
const loadCSS = (): Promise<void> => {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
if (isCssLoaded()) {
|
|
176
|
+
resolve();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const link = document.createElement("link");
|
|
181
|
+
link.rel = "stylesheet";
|
|
182
|
+
link.href = cssUrl;
|
|
183
|
+
link.setAttribute("data-persona", "true");
|
|
184
|
+
|
|
185
|
+
link.onload = () => resolve();
|
|
186
|
+
link.onerror = () => reject(new Error(`Failed to load CSS from ${cssUrl}`));
|
|
187
|
+
document.head.appendChild(link);
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Load JS
|
|
192
|
+
const loadJS = (): Promise<void> => {
|
|
193
|
+
return new Promise((resolve, reject) => {
|
|
194
|
+
if (isJsLoaded()) {
|
|
195
|
+
resolve();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const script = document.createElement("script");
|
|
200
|
+
script.src = jsUrl;
|
|
201
|
+
script.async = true;
|
|
202
|
+
script.onload = () => resolve();
|
|
203
|
+
script.onerror = () => reject(new Error(`Failed to load JS from ${jsUrl}`));
|
|
204
|
+
document.head.appendChild(script);
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Initialize widget
|
|
209
|
+
const initWidget = () => {
|
|
210
|
+
if (!window.AgentWidget || !window.AgentWidget.initAgentWidget) {
|
|
211
|
+
console.warn("AgentWidget not available. Make sure the script loaded successfully.");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const target = config.target || "body";
|
|
216
|
+
// Merge top-level config options into widget config
|
|
217
|
+
const widgetConfig = { ...config.config };
|
|
218
|
+
|
|
219
|
+
// Merge apiUrl from top-level config into widget config if present
|
|
220
|
+
if (config.apiUrl && !widgetConfig.apiUrl) {
|
|
221
|
+
widgetConfig.apiUrl = config.apiUrl;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Merge clientToken from top-level config into widget config if present
|
|
225
|
+
if (config.clientToken && !widgetConfig.clientToken) {
|
|
226
|
+
widgetConfig.clientToken = config.clientToken;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Merge flowId from top-level config into widget config if present
|
|
230
|
+
if (config.flowId && !widgetConfig.flowId) {
|
|
231
|
+
widgetConfig.flowId = config.flowId;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Only initialize if we have either apiUrl OR clientToken (or other config)
|
|
235
|
+
const hasApiConfig = widgetConfig.apiUrl || widgetConfig.clientToken;
|
|
236
|
+
if (!hasApiConfig && Object.keys(widgetConfig).length === 0) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Auto-apply markdown postprocessor if not explicitly set and available
|
|
241
|
+
if (!widgetConfig.postprocessMessage && window.AgentWidget.markdownPostprocessor) {
|
|
242
|
+
widgetConfig.postprocessMessage = ({ text }: { text: string }) =>
|
|
243
|
+
window.AgentWidget.markdownPostprocessor(text);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
window.AgentWidget.initAgentWidget({
|
|
248
|
+
target,
|
|
249
|
+
config: widgetConfig,
|
|
250
|
+
// Explicitly disable shadow DOM for better CSS compatibility with host page
|
|
251
|
+
useShadowDom: config.useShadowDom ?? false
|
|
252
|
+
});
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.error("Failed to initialize AgentWidget:", error);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// Main installation flow (called after hydration completes)
|
|
259
|
+
const install = async () => {
|
|
260
|
+
try {
|
|
261
|
+
await loadCSS();
|
|
262
|
+
await loadJS();
|
|
263
|
+
|
|
264
|
+
// Auto-init if we have config OR apiUrl OR clientToken
|
|
265
|
+
const shouldAutoInit = autoInit && (
|
|
266
|
+
config.config ||
|
|
267
|
+
config.apiUrl ||
|
|
268
|
+
config.clientToken
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
if (shouldAutoInit) {
|
|
272
|
+
// Wait a tick to ensure AgentWidget is fully initialized
|
|
273
|
+
setTimeout(initWidget, 0);
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error("Failed to install AgentWidget:", error);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// Start installation after hydration completes
|
|
281
|
+
// This prevents Next.js/Nuxt/etc. from removing dynamically added CSS
|
|
282
|
+
waitForHydration(install);
|
|
283
|
+
})();
|
|
284
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { AgentWidgetPlugin } from "./types";
|
|
2
|
+
|
|
3
|
+
class PluginRegistry {
|
|
4
|
+
private plugins: Map<string, AgentWidgetPlugin> = new Map();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register a plugin
|
|
8
|
+
*/
|
|
9
|
+
register(plugin: AgentWidgetPlugin): void {
|
|
10
|
+
if (this.plugins.has(plugin.id)) {
|
|
11
|
+
console.warn(`Plugin "${plugin.id}" is already registered. Overwriting.`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.plugins.set(plugin.id, plugin);
|
|
15
|
+
plugin.onRegister?.();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Unregister a plugin
|
|
20
|
+
*/
|
|
21
|
+
unregister(pluginId: string): void {
|
|
22
|
+
const plugin = this.plugins.get(pluginId);
|
|
23
|
+
if (plugin) {
|
|
24
|
+
plugin.onUnregister?.();
|
|
25
|
+
this.plugins.delete(pluginId);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get all plugins sorted by priority
|
|
31
|
+
*/
|
|
32
|
+
getAll(): AgentWidgetPlugin[] {
|
|
33
|
+
return Array.from(this.plugins.values()).sort(
|
|
34
|
+
(a, b) => (b.priority ?? 0) - (a.priority ?? 0)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get plugins for a specific instance (from config)
|
|
40
|
+
* Merges instance plugins with globally registered plugins
|
|
41
|
+
*/
|
|
42
|
+
getForInstance(instancePlugins?: AgentWidgetPlugin[]): AgentWidgetPlugin[] {
|
|
43
|
+
const allPlugins = this.getAll();
|
|
44
|
+
|
|
45
|
+
if (!instancePlugins || instancePlugins.length === 0) {
|
|
46
|
+
return allPlugins;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Merge instance plugins with global plugins
|
|
50
|
+
// Instance plugins override global plugins with the same ID
|
|
51
|
+
const instanceIds = new Set(instancePlugins.map(p => p.id));
|
|
52
|
+
const merged = [
|
|
53
|
+
...allPlugins.filter(p => !instanceIds.has(p.id)),
|
|
54
|
+
...instancePlugins
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
return merged.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Clear all plugins
|
|
62
|
+
*/
|
|
63
|
+
clear(): void {
|
|
64
|
+
this.plugins.forEach(plugin => plugin.onUnregister?.());
|
|
65
|
+
this.plugins.clear();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const pluginRegistry = new PluginRegistry();
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { AgentWidgetMessage, AgentWidgetConfig } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plugin interface for customizing widget components
|
|
5
|
+
*/
|
|
6
|
+
export interface AgentWidgetPlugin {
|
|
7
|
+
/**
|
|
8
|
+
* Unique identifier for the plugin
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Optional priority (higher = runs first). Default: 0
|
|
14
|
+
*/
|
|
15
|
+
priority?: number;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Custom renderer for message bubbles
|
|
19
|
+
* Return null to use default renderer
|
|
20
|
+
*/
|
|
21
|
+
renderMessage?: (context: {
|
|
22
|
+
message: AgentWidgetMessage;
|
|
23
|
+
defaultRenderer: () => HTMLElement;
|
|
24
|
+
config: AgentWidgetConfig;
|
|
25
|
+
}) => HTMLElement | null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Custom renderer for launcher button
|
|
29
|
+
* Return null to use default renderer
|
|
30
|
+
*/
|
|
31
|
+
renderLauncher?: (context: {
|
|
32
|
+
config: AgentWidgetConfig;
|
|
33
|
+
defaultRenderer: () => HTMLElement;
|
|
34
|
+
onToggle: () => void;
|
|
35
|
+
}) => HTMLElement | null;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Custom renderer for panel header
|
|
39
|
+
* Return null to use default renderer
|
|
40
|
+
*/
|
|
41
|
+
renderHeader?: (context: {
|
|
42
|
+
config: AgentWidgetConfig;
|
|
43
|
+
defaultRenderer: () => HTMLElement;
|
|
44
|
+
onClose?: () => void;
|
|
45
|
+
}) => HTMLElement | null;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Custom renderer for composer/input area
|
|
49
|
+
* Return null to use default renderer
|
|
50
|
+
*/
|
|
51
|
+
renderComposer?: (context: {
|
|
52
|
+
config: AgentWidgetConfig;
|
|
53
|
+
defaultRenderer: () => HTMLElement;
|
|
54
|
+
onSubmit: (text: string) => void;
|
|
55
|
+
disabled: boolean;
|
|
56
|
+
}) => HTMLElement | null;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Custom renderer for reasoning bubbles
|
|
60
|
+
* Return null to use default renderer
|
|
61
|
+
*/
|
|
62
|
+
renderReasoning?: (context: {
|
|
63
|
+
message: AgentWidgetMessage;
|
|
64
|
+
defaultRenderer: () => HTMLElement;
|
|
65
|
+
config: AgentWidgetConfig;
|
|
66
|
+
}) => HTMLElement | null;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Custom renderer for tool call bubbles
|
|
70
|
+
* Return null to use default renderer
|
|
71
|
+
*/
|
|
72
|
+
renderToolCall?: (context: {
|
|
73
|
+
message: AgentWidgetMessage;
|
|
74
|
+
defaultRenderer: () => HTMLElement;
|
|
75
|
+
config: AgentWidgetConfig;
|
|
76
|
+
}) => HTMLElement | null;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Called when plugin is registered
|
|
80
|
+
*/
|
|
81
|
+
onRegister?: () => void;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Called when plugin is unregistered
|
|
85
|
+
*/
|
|
86
|
+
onUnregister?: () => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|