@runtypelabs/persona 1.40.0 → 1.41.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 +151 -0
- package/dist/index.cjs +26 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +211 -3
- package/dist/index.d.ts +211 -3
- package/dist/index.global.js +51 -51
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +28 -28
- package/dist/index.js.map +1 -1
- package/dist/widget.css +7 -0
- package/package.json +1 -1
- package/src/components/composer-builder.ts +1 -1
- package/src/components/message-bubble.ts +63 -8
- package/src/index.ts +14 -3
- package/src/plugins/types.ts +38 -1
- package/src/styles/widget.css +7 -0
- package/src/types.ts +163 -0
- package/src/ui.ts +172 -44
- package/src/utils/morph.ts +3 -1
package/dist/widget.css
CHANGED
|
@@ -687,6 +687,13 @@
|
|
|
687
687
|
box-shadow: none !important;
|
|
688
688
|
}
|
|
689
689
|
|
|
690
|
+
/* Prevent iOS Safari from zooming on input focus (requires 16px minimum) */
|
|
691
|
+
@media (hover: none) and (pointer: coarse) {
|
|
692
|
+
.tvw-composer-textarea {
|
|
693
|
+
font-size: 1rem !important;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
690
697
|
/* Prevent form container from showing focus styles */
|
|
691
698
|
.tvw-widget-composer:focus-within {
|
|
692
699
|
outline: none !important;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/persona",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.41.0",
|
|
4
4
|
"description": "Themeable, plugable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -85,7 +85,7 @@ export const buildComposer = (context: ComposerBuildContext): ComposerElements =
|
|
|
85
85
|
const textarea = createElement("textarea") as HTMLTextAreaElement;
|
|
86
86
|
textarea.placeholder = config?.copy?.inputPlaceholder ?? "Type your message…";
|
|
87
87
|
textarea.className =
|
|
88
|
-
"tvw-w-full tvw-min-h-[24px] tvw-resize-none tvw-border-none tvw-bg-transparent tvw-text-sm tvw-text-cw-primary focus:tvw-outline-none focus:tvw-border-none";
|
|
88
|
+
"tvw-w-full tvw-min-h-[24px] tvw-resize-none tvw-border-none tvw-bg-transparent tvw-text-sm tvw-text-cw-primary focus:tvw-outline-none focus:tvw-border-none tvw-composer-textarea";
|
|
89
89
|
textarea.rows = 1;
|
|
90
90
|
|
|
91
91
|
// Apply font family and weight from config
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { createElement } from "../utils/dom";
|
|
2
|
-
import {
|
|
3
|
-
AgentWidgetMessage,
|
|
2
|
+
import {
|
|
3
|
+
AgentWidgetMessage,
|
|
4
4
|
AgentWidgetMessageLayoutConfig,
|
|
5
5
|
AgentWidgetAvatarConfig,
|
|
6
6
|
AgentWidgetTimestampConfig,
|
|
7
7
|
AgentWidgetMessageActionsConfig,
|
|
8
|
-
AgentWidgetMessageFeedback
|
|
8
|
+
AgentWidgetMessageFeedback,
|
|
9
|
+
LoadingIndicatorRenderContext
|
|
9
10
|
} from "../types";
|
|
10
11
|
import { renderLucideIcon } from "../utils/icons";
|
|
11
12
|
|
|
13
|
+
export type LoadingIndicatorRenderer = (context: LoadingIndicatorRenderContext) => HTMLElement | null;
|
|
14
|
+
|
|
12
15
|
export type MessageTransform = (context: {
|
|
13
16
|
text: string;
|
|
14
17
|
message: AgentWidgetMessage;
|
|
@@ -50,6 +53,35 @@ export const createTypingIndicator = (): HTMLElement => {
|
|
|
50
53
|
return container;
|
|
51
54
|
};
|
|
52
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Render loading indicator with fallback chain:
|
|
58
|
+
* 1. Custom renderer (if provided and returns non-null)
|
|
59
|
+
* 2. Default typing indicator
|
|
60
|
+
*/
|
|
61
|
+
export const renderLoadingIndicatorWithFallback = (
|
|
62
|
+
location: 'inline' | 'standalone',
|
|
63
|
+
customRenderer?: LoadingIndicatorRenderer,
|
|
64
|
+
widgetConfig?: import("../types").AgentWidgetConfig
|
|
65
|
+
): HTMLElement | null => {
|
|
66
|
+
const context: LoadingIndicatorRenderContext = {
|
|
67
|
+
config: widgetConfig ?? ({} as import("../types").AgentWidgetConfig),
|
|
68
|
+
streaming: true,
|
|
69
|
+
location,
|
|
70
|
+
defaultRenderer: createTypingIndicator
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Try custom renderer first
|
|
74
|
+
if (customRenderer) {
|
|
75
|
+
const result = customRenderer(context);
|
|
76
|
+
if (result !== null) {
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Fall back to default
|
|
82
|
+
return createTypingIndicator();
|
|
83
|
+
};
|
|
84
|
+
|
|
53
85
|
/**
|
|
54
86
|
* Create an avatar element
|
|
55
87
|
*/
|
|
@@ -394,6 +426,20 @@ export const createMessageActions = (
|
|
|
394
426
|
return container;
|
|
395
427
|
};
|
|
396
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Options for creating a standard message bubble
|
|
431
|
+
*/
|
|
432
|
+
export type CreateStandardBubbleOptions = {
|
|
433
|
+
/**
|
|
434
|
+
* Custom loading indicator renderer for inline location
|
|
435
|
+
*/
|
|
436
|
+
loadingIndicatorRenderer?: LoadingIndicatorRenderer;
|
|
437
|
+
/**
|
|
438
|
+
* Full widget config (needed for loading indicator context)
|
|
439
|
+
*/
|
|
440
|
+
widgetConfig?: import("../types").AgentWidgetConfig;
|
|
441
|
+
};
|
|
442
|
+
|
|
397
443
|
/**
|
|
398
444
|
* Create standard message bubble
|
|
399
445
|
* Supports layout configuration for avatars, timestamps, and visual presets
|
|
@@ -403,7 +449,8 @@ export const createStandardBubble = (
|
|
|
403
449
|
transform: MessageTransform,
|
|
404
450
|
layoutConfig?: AgentWidgetMessageLayoutConfig,
|
|
405
451
|
actionsConfig?: AgentWidgetMessageActionsConfig,
|
|
406
|
-
actionCallbacks?: MessageActionCallbacks
|
|
452
|
+
actionCallbacks?: MessageActionCallbacks,
|
|
453
|
+
options?: CreateStandardBubbleOptions
|
|
407
454
|
): HTMLElement => {
|
|
408
455
|
const config = layoutConfig ?? {};
|
|
409
456
|
const layout = config.layout ?? "bubble";
|
|
@@ -449,8 +496,15 @@ export const createStandardBubble = (
|
|
|
449
496
|
// Add typing indicator if this is a streaming assistant message
|
|
450
497
|
if (message.streaming && message.role === "assistant") {
|
|
451
498
|
if (!message.content || !message.content.trim()) {
|
|
452
|
-
|
|
453
|
-
|
|
499
|
+
// Use custom renderer if provided, otherwise default
|
|
500
|
+
const indicator = renderLoadingIndicatorWithFallback(
|
|
501
|
+
'inline',
|
|
502
|
+
options?.loadingIndicatorRenderer,
|
|
503
|
+
options?.widgetConfig
|
|
504
|
+
);
|
|
505
|
+
if (indicator) {
|
|
506
|
+
bubble.appendChild(indicator);
|
|
507
|
+
}
|
|
454
508
|
}
|
|
455
509
|
}
|
|
456
510
|
|
|
@@ -502,7 +556,8 @@ export const createBubbleWithLayout = (
|
|
|
502
556
|
transform: MessageTransform,
|
|
503
557
|
layoutConfig?: AgentWidgetMessageLayoutConfig,
|
|
504
558
|
actionsConfig?: AgentWidgetMessageActionsConfig,
|
|
505
|
-
actionCallbacks?: MessageActionCallbacks
|
|
559
|
+
actionCallbacks?: MessageActionCallbacks,
|
|
560
|
+
options?: CreateStandardBubbleOptions
|
|
506
561
|
): HTMLElement => {
|
|
507
562
|
const config = layoutConfig ?? {};
|
|
508
563
|
|
|
@@ -524,5 +579,5 @@ export const createBubbleWithLayout = (
|
|
|
524
579
|
}
|
|
525
580
|
|
|
526
581
|
// Fall back to standard bubble
|
|
527
|
-
return createStandardBubble(message, transform, layoutConfig, actionsConfig, actionCallbacks);
|
|
582
|
+
return createStandardBubble(message, transform, layoutConfig, actionsConfig, actionCallbacks, options);
|
|
528
583
|
};
|
package/src/index.ts
CHANGED
|
@@ -53,7 +53,12 @@ export type {
|
|
|
53
53
|
InjectMessageOptions,
|
|
54
54
|
InjectAssistantMessageOptions,
|
|
55
55
|
InjectUserMessageOptions,
|
|
56
|
-
InjectSystemMessageOptions
|
|
56
|
+
InjectSystemMessageOptions,
|
|
57
|
+
// Loading indicator types
|
|
58
|
+
LoadingIndicatorRenderContext,
|
|
59
|
+
AgentWidgetLoadingIndicatorConfig,
|
|
60
|
+
// Idle indicator types
|
|
61
|
+
IdleIndicatorRenderContext
|
|
57
62
|
} from "./types";
|
|
58
63
|
|
|
59
64
|
export { initAgentWidgetFn as initAgentWidget };
|
|
@@ -169,9 +174,15 @@ export {
|
|
|
169
174
|
createStandardBubble,
|
|
170
175
|
createBubbleWithLayout,
|
|
171
176
|
createTypingIndicator,
|
|
172
|
-
createMessageActions
|
|
177
|
+
createMessageActions,
|
|
178
|
+
renderLoadingIndicatorWithFallback
|
|
179
|
+
} from "./components/message-bubble";
|
|
180
|
+
export type {
|
|
181
|
+
MessageTransform,
|
|
182
|
+
MessageActionCallbacks,
|
|
183
|
+
LoadingIndicatorRenderer,
|
|
184
|
+
CreateStandardBubbleOptions
|
|
173
185
|
} from "./components/message-bubble";
|
|
174
|
-
export type { MessageTransform, MessageActionCallbacks } from "./components/message-bubble";
|
|
175
186
|
export {
|
|
176
187
|
createCSATFeedback,
|
|
177
188
|
createNPSFeedback
|
package/src/plugins/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentWidgetMessage, AgentWidgetConfig } from "../types";
|
|
1
|
+
import { AgentWidgetMessage, AgentWidgetConfig, LoadingIndicatorRenderContext, IdleIndicatorRenderContext } from "../types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Plugin interface for customizing widget components
|
|
@@ -75,6 +75,43 @@ export interface AgentWidgetPlugin {
|
|
|
75
75
|
config: AgentWidgetConfig;
|
|
76
76
|
}) => HTMLElement | null;
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Custom renderer for loading indicator
|
|
80
|
+
* Return null to use default renderer (or config-based renderer)
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* renderLoadingIndicator: ({ location, defaultRenderer }) => {
|
|
85
|
+
* if (location === 'standalone') {
|
|
86
|
+
* const el = document.createElement('div');
|
|
87
|
+
* el.textContent = 'Thinking...';
|
|
88
|
+
* return el;
|
|
89
|
+
* }
|
|
90
|
+
* return defaultRenderer();
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
renderLoadingIndicator?: (context: LoadingIndicatorRenderContext) => HTMLElement | null;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Custom renderer for idle state indicator.
|
|
98
|
+
* Called when the widget is idle (not streaming) and has at least one message.
|
|
99
|
+
* Return an HTMLElement to display, or null to hide (default).
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* renderIdleIndicator: ({ lastMessage, messageCount }) => {
|
|
104
|
+
* if (messageCount === 0) return null;
|
|
105
|
+
* if (lastMessage?.role !== 'assistant') return null;
|
|
106
|
+
* const el = document.createElement('div');
|
|
107
|
+
* el.className = 'idle-pulse';
|
|
108
|
+
* el.setAttribute('data-preserve-animation', 'true');
|
|
109
|
+
* return el;
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
renderIdleIndicator?: (context: IdleIndicatorRenderContext) => HTMLElement | null;
|
|
114
|
+
|
|
78
115
|
/**
|
|
79
116
|
* Called when plugin is registered
|
|
80
117
|
*/
|
package/src/styles/widget.css
CHANGED
|
@@ -687,6 +687,13 @@
|
|
|
687
687
|
box-shadow: none !important;
|
|
688
688
|
}
|
|
689
689
|
|
|
690
|
+
/* Prevent iOS Safari from zooming on input focus (requires 16px minimum) */
|
|
691
|
+
@media (hover: none) and (pointer: coarse) {
|
|
692
|
+
.tvw-composer-textarea {
|
|
693
|
+
font-size: 1rem !important;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
690
697
|
/* Prevent form container from showing focus styles */
|
|
691
698
|
.tvw-widget-composer:focus-within {
|
|
692
699
|
outline: none !important;
|
package/src/types.ts
CHANGED
|
@@ -1256,6 +1256,146 @@ export type AgentWidgetPersistStateConfig = {
|
|
|
1256
1256
|
clearOnChatClear?: boolean;
|
|
1257
1257
|
};
|
|
1258
1258
|
|
|
1259
|
+
// ============================================================================
|
|
1260
|
+
// Loading Indicator Types
|
|
1261
|
+
// ============================================================================
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* Context provided to loading indicator render functions.
|
|
1265
|
+
* Used for customizing the loading indicator appearance.
|
|
1266
|
+
*/
|
|
1267
|
+
export type LoadingIndicatorRenderContext = {
|
|
1268
|
+
/**
|
|
1269
|
+
* Full widget configuration for accessing theme, etc.
|
|
1270
|
+
*/
|
|
1271
|
+
config: AgentWidgetConfig;
|
|
1272
|
+
/**
|
|
1273
|
+
* Current streaming state (always true when indicator is shown)
|
|
1274
|
+
*/
|
|
1275
|
+
streaming: boolean;
|
|
1276
|
+
/**
|
|
1277
|
+
* Location where the indicator is rendered:
|
|
1278
|
+
* - 'inline': Inside a streaming assistant message bubble (when content is empty)
|
|
1279
|
+
* - 'standalone': Separate bubble while waiting for stream to start
|
|
1280
|
+
*/
|
|
1281
|
+
location: 'inline' | 'standalone';
|
|
1282
|
+
/**
|
|
1283
|
+
* Function to render the default 3-dot bouncing indicator.
|
|
1284
|
+
* Call this if you want to use the default for certain cases.
|
|
1285
|
+
*/
|
|
1286
|
+
defaultRenderer: () => HTMLElement;
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* Context provided to idle indicator render functions.
|
|
1291
|
+
* Used for customizing the idle state indicator appearance.
|
|
1292
|
+
*/
|
|
1293
|
+
export type IdleIndicatorRenderContext = {
|
|
1294
|
+
/**
|
|
1295
|
+
* Full widget configuration for accessing theme, etc.
|
|
1296
|
+
*/
|
|
1297
|
+
config: AgentWidgetConfig;
|
|
1298
|
+
/**
|
|
1299
|
+
* The last message in the conversation (if any).
|
|
1300
|
+
* Useful for conditional rendering based on who spoke last.
|
|
1301
|
+
*/
|
|
1302
|
+
lastMessage: AgentWidgetMessage | undefined;
|
|
1303
|
+
/**
|
|
1304
|
+
* Total number of messages in the conversation.
|
|
1305
|
+
*/
|
|
1306
|
+
messageCount: number;
|
|
1307
|
+
};
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* Configuration for customizing the loading indicator.
|
|
1311
|
+
* The loading indicator is shown while waiting for a response or
|
|
1312
|
+
* when an assistant message is streaming but has no content yet.
|
|
1313
|
+
*
|
|
1314
|
+
* @example
|
|
1315
|
+
* ```typescript
|
|
1316
|
+
* // Custom animated spinner
|
|
1317
|
+
* config: {
|
|
1318
|
+
* loadingIndicator: {
|
|
1319
|
+
* render: ({ location }) => {
|
|
1320
|
+
* const el = document.createElement('div');
|
|
1321
|
+
* el.innerHTML = '<svg class="spinner">...</svg>';
|
|
1322
|
+
* el.setAttribute('data-preserve-animation', 'true');
|
|
1323
|
+
* return el;
|
|
1324
|
+
* }
|
|
1325
|
+
* }
|
|
1326
|
+
* }
|
|
1327
|
+
* ```
|
|
1328
|
+
*
|
|
1329
|
+
* @example
|
|
1330
|
+
* ```typescript
|
|
1331
|
+
* // Different indicators by location
|
|
1332
|
+
* config: {
|
|
1333
|
+
* loadingIndicator: {
|
|
1334
|
+
* render: ({ location, defaultRenderer }) => {
|
|
1335
|
+
* if (location === 'inline') {
|
|
1336
|
+
* return defaultRenderer(); // Use default for inline
|
|
1337
|
+
* }
|
|
1338
|
+
* // Custom for standalone
|
|
1339
|
+
* const el = document.createElement('div');
|
|
1340
|
+
* el.textContent = 'Thinking...';
|
|
1341
|
+
* return el;
|
|
1342
|
+
* }
|
|
1343
|
+
* }
|
|
1344
|
+
* }
|
|
1345
|
+
* ```
|
|
1346
|
+
*
|
|
1347
|
+
* @example
|
|
1348
|
+
* ```typescript
|
|
1349
|
+
* // Hide loading indicator entirely
|
|
1350
|
+
* config: {
|
|
1351
|
+
* loadingIndicator: {
|
|
1352
|
+
* render: () => null
|
|
1353
|
+
* }
|
|
1354
|
+
* }
|
|
1355
|
+
* ```
|
|
1356
|
+
*/
|
|
1357
|
+
export type AgentWidgetLoadingIndicatorConfig = {
|
|
1358
|
+
/**
|
|
1359
|
+
* Whether to show the bubble background and border around the standalone loading indicator.
|
|
1360
|
+
* Set to false to render the loading indicator without any bubble styling.
|
|
1361
|
+
* @default true
|
|
1362
|
+
*/
|
|
1363
|
+
showBubble?: boolean;
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Custom render function for the loading indicator.
|
|
1367
|
+
* Return an HTMLElement to display, or null to hide the indicator.
|
|
1368
|
+
*
|
|
1369
|
+
* For custom animations, add `data-preserve-animation="true"` attribute
|
|
1370
|
+
* to prevent the DOM morpher from interrupting the animation.
|
|
1371
|
+
*/
|
|
1372
|
+
render?: (context: LoadingIndicatorRenderContext) => HTMLElement | null;
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* Render function for the idle state indicator.
|
|
1376
|
+
* Called when the widget is idle (not streaming) and has at least one message.
|
|
1377
|
+
* Return an HTMLElement to display, or null to hide (default).
|
|
1378
|
+
*
|
|
1379
|
+
* For animations, add `data-preserve-animation="true"` attribute
|
|
1380
|
+
* to prevent the DOM morpher from interrupting the animation.
|
|
1381
|
+
*
|
|
1382
|
+
* @example
|
|
1383
|
+
* ```typescript
|
|
1384
|
+
* loadingIndicator: {
|
|
1385
|
+
* renderIdle: ({ lastMessage }) => {
|
|
1386
|
+
* // Only show idle indicator after assistant messages
|
|
1387
|
+
* if (lastMessage?.role !== 'assistant') return null;
|
|
1388
|
+
* const el = document.createElement('div');
|
|
1389
|
+
* el.className = 'pulse-dot';
|
|
1390
|
+
* el.setAttribute('data-preserve-animation', 'true');
|
|
1391
|
+
* return el;
|
|
1392
|
+
* }
|
|
1393
|
+
* }
|
|
1394
|
+
* ```
|
|
1395
|
+
*/
|
|
1396
|
+
renderIdle?: (context: IdleIndicatorRenderContext) => HTMLElement | null;
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1259
1399
|
export type AgentWidgetConfig = {
|
|
1260
1400
|
apiUrl?: string;
|
|
1261
1401
|
flowId?: string;
|
|
@@ -1693,6 +1833,29 @@ export type AgentWidgetConfig = {
|
|
|
1693
1833
|
* ```
|
|
1694
1834
|
*/
|
|
1695
1835
|
persistState?: boolean | AgentWidgetPersistStateConfig;
|
|
1836
|
+
|
|
1837
|
+
/**
|
|
1838
|
+
* Configuration for customizing the loading indicator.
|
|
1839
|
+
* The loading indicator is shown while waiting for a response or
|
|
1840
|
+
* when an assistant message is streaming but has no content yet.
|
|
1841
|
+
*
|
|
1842
|
+
* @example
|
|
1843
|
+
* ```typescript
|
|
1844
|
+
* config: {
|
|
1845
|
+
* loadingIndicator: {
|
|
1846
|
+
* render: ({ location, defaultRenderer }) => {
|
|
1847
|
+
* if (location === 'standalone') {
|
|
1848
|
+
* const el = document.createElement('div');
|
|
1849
|
+
* el.textContent = 'Thinking...';
|
|
1850
|
+
* return el;
|
|
1851
|
+
* }
|
|
1852
|
+
* return defaultRenderer();
|
|
1853
|
+
* }
|
|
1854
|
+
* }
|
|
1855
|
+
* }
|
|
1856
|
+
* ```
|
|
1857
|
+
*/
|
|
1858
|
+
loadingIndicator?: AgentWidgetLoadingIndicatorConfig;
|
|
1696
1859
|
};
|
|
1697
1860
|
|
|
1698
1861
|
export type AgentWidgetMessageRole = "user" | "assistant" | "system";
|