@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
|
@@ -52,7 +52,7 @@ const createMessageImagePreviews = (
|
|
|
52
52
|
try {
|
|
53
53
|
const container = createElement(
|
|
54
54
|
"div",
|
|
55
|
-
"
|
|
55
|
+
"persona-flex persona-flex-col persona-gap-2"
|
|
56
56
|
);
|
|
57
57
|
container.setAttribute("data-message-attachments", "images");
|
|
58
58
|
if (hasVisibleText) {
|
|
@@ -82,8 +82,8 @@ const createMessageImagePreviews = (
|
|
|
82
82
|
imageElement.style.height = "auto";
|
|
83
83
|
imageElement.style.objectFit = "contain";
|
|
84
84
|
imageElement.style.borderRadius = "10px";
|
|
85
|
-
imageElement.style.backgroundColor = "var(--
|
|
86
|
-
imageElement.style.border = "1px solid var(--
|
|
85
|
+
imageElement.style.backgroundColor = "var(--persona-attachment-image-bg, var(--persona-container, #f3f4f6))";
|
|
86
|
+
imageElement.style.border = "1px solid var(--persona-attachment-image-border, var(--persona-border, #e5e7eb))";
|
|
87
87
|
|
|
88
88
|
let settled = false;
|
|
89
89
|
visiblePreviewCount += 1;
|
|
@@ -119,22 +119,22 @@ const createMessageImagePreviews = (
|
|
|
119
119
|
// Create typing indicator element
|
|
120
120
|
export const createTypingIndicator = (): HTMLElement => {
|
|
121
121
|
const container = document.createElement("div");
|
|
122
|
-
container.className = "
|
|
122
|
+
container.className = "persona-flex persona-items-center persona-space-x-1 persona-h-5 persona-mt-2";
|
|
123
123
|
|
|
124
124
|
const dot1 = document.createElement("div");
|
|
125
|
-
dot1.className = "
|
|
125
|
+
dot1.className = "persona-bg-persona-primary persona-animate-typing persona-rounded-full persona-h-1.5 persona-w-1.5";
|
|
126
126
|
dot1.style.animationDelay = "0ms";
|
|
127
127
|
|
|
128
128
|
const dot2 = document.createElement("div");
|
|
129
|
-
dot2.className = "
|
|
129
|
+
dot2.className = "persona-bg-persona-primary persona-animate-typing persona-rounded-full persona-h-1.5 persona-w-1.5";
|
|
130
130
|
dot2.style.animationDelay = "250ms";
|
|
131
131
|
|
|
132
132
|
const dot3 = document.createElement("div");
|
|
133
|
-
dot3.className = "
|
|
133
|
+
dot3.className = "persona-bg-persona-primary persona-animate-typing persona-rounded-full persona-h-1.5 persona-w-1.5";
|
|
134
134
|
dot3.style.animationDelay = "500ms";
|
|
135
135
|
|
|
136
136
|
const srOnly = document.createElement("span");
|
|
137
|
-
srOnly.className = "
|
|
137
|
+
srOnly.className = "persona-sr-only";
|
|
138
138
|
srOnly.textContent = "Loading";
|
|
139
139
|
|
|
140
140
|
container.appendChild(dot1);
|
|
@@ -183,7 +183,7 @@ const createAvatar = (
|
|
|
183
183
|
): HTMLElement => {
|
|
184
184
|
const avatar = createElement(
|
|
185
185
|
"div",
|
|
186
|
-
"
|
|
186
|
+
"persona-flex-shrink-0 persona-w-8 persona-h-8 persona-rounded-full persona-flex persona-items-center persona-justify-center persona-text-sm"
|
|
187
187
|
);
|
|
188
188
|
|
|
189
189
|
const avatarContent = role === "user"
|
|
@@ -196,22 +196,22 @@ const createAvatar = (
|
|
|
196
196
|
const img = createElement("img") as HTMLImageElement;
|
|
197
197
|
img.src = avatarContent;
|
|
198
198
|
img.alt = role === "user" ? "User" : "Assistant";
|
|
199
|
-
img.className = "
|
|
199
|
+
img.className = "persona-w-full persona-h-full persona-rounded-full persona-object-cover";
|
|
200
200
|
avatar.appendChild(img);
|
|
201
201
|
} else {
|
|
202
202
|
// Emoji or text
|
|
203
203
|
avatar.textContent = avatarContent;
|
|
204
204
|
avatar.classList.add(
|
|
205
|
-
role === "user" ? "
|
|
206
|
-
"
|
|
205
|
+
role === "user" ? "persona-bg-persona-accent" : "persona-bg-persona-primary",
|
|
206
|
+
"persona-text-white"
|
|
207
207
|
);
|
|
208
208
|
}
|
|
209
209
|
} else {
|
|
210
210
|
// Default avatar
|
|
211
211
|
avatar.textContent = role === "user" ? "U" : "A";
|
|
212
212
|
avatar.classList.add(
|
|
213
|
-
role === "user" ? "
|
|
214
|
-
"
|
|
213
|
+
role === "user" ? "persona-bg-persona-accent" : "persona-bg-persona-primary",
|
|
214
|
+
"persona-text-white"
|
|
215
215
|
);
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -227,7 +227,7 @@ const createTimestamp = (
|
|
|
227
227
|
): HTMLElement => {
|
|
228
228
|
const timestamp = createElement(
|
|
229
229
|
"div",
|
|
230
|
-
"
|
|
230
|
+
"persona-text-xs persona-text-persona-muted"
|
|
231
231
|
);
|
|
232
232
|
|
|
233
233
|
const date = new Date(message.createdAt);
|
|
@@ -252,7 +252,7 @@ const getBubbleClasses = (
|
|
|
252
252
|
role: "user" | "assistant" | "system",
|
|
253
253
|
layout: AgentWidgetMessageLayoutConfig["layout"] = "bubble"
|
|
254
254
|
): string[] => {
|
|
255
|
-
const baseClasses = ["vanilla-message-bubble", "
|
|
255
|
+
const baseClasses = ["vanilla-message-bubble", "persona-max-w-[85%]"];
|
|
256
256
|
|
|
257
257
|
switch (layout) {
|
|
258
258
|
case "flat":
|
|
@@ -260,15 +260,15 @@ const getBubbleClasses = (
|
|
|
260
260
|
if (role === "user") {
|
|
261
261
|
baseClasses.push(
|
|
262
262
|
"vanilla-message-user-bubble",
|
|
263
|
-
"
|
|
264
|
-
"
|
|
265
|
-
"
|
|
263
|
+
"persona-ml-auto",
|
|
264
|
+
"persona-text-persona-primary",
|
|
265
|
+
"persona-py-2"
|
|
266
266
|
);
|
|
267
267
|
} else {
|
|
268
268
|
baseClasses.push(
|
|
269
269
|
"vanilla-message-assistant-bubble",
|
|
270
|
-
"
|
|
271
|
-
"
|
|
270
|
+
"persona-text-persona-primary",
|
|
271
|
+
"persona-py-2"
|
|
272
272
|
);
|
|
273
273
|
}
|
|
274
274
|
break;
|
|
@@ -276,27 +276,27 @@ const getBubbleClasses = (
|
|
|
276
276
|
case "minimal":
|
|
277
277
|
// Minimal layout: reduced padding and styling
|
|
278
278
|
baseClasses.push(
|
|
279
|
-
"
|
|
280
|
-
"
|
|
279
|
+
"persona-text-sm",
|
|
280
|
+
"persona-leading-relaxed"
|
|
281
281
|
);
|
|
282
282
|
if (role === "user") {
|
|
283
283
|
baseClasses.push(
|
|
284
284
|
"vanilla-message-user-bubble",
|
|
285
|
-
"
|
|
286
|
-
"
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
285
|
+
"persona-ml-auto",
|
|
286
|
+
"persona-bg-persona-accent",
|
|
287
|
+
"persona-text-white",
|
|
288
|
+
"persona-px-3",
|
|
289
|
+
"persona-py-2",
|
|
290
|
+
"persona-rounded-lg"
|
|
291
291
|
);
|
|
292
292
|
} else {
|
|
293
293
|
baseClasses.push(
|
|
294
294
|
"vanilla-message-assistant-bubble",
|
|
295
|
-
"
|
|
296
|
-
"
|
|
297
|
-
"
|
|
298
|
-
"
|
|
299
|
-
"
|
|
295
|
+
"persona-bg-persona-surface",
|
|
296
|
+
"persona-text-persona-primary",
|
|
297
|
+
"persona-px-3",
|
|
298
|
+
"persona-py-2",
|
|
299
|
+
"persona-rounded-lg"
|
|
300
300
|
);
|
|
301
301
|
}
|
|
302
302
|
break;
|
|
@@ -305,29 +305,29 @@ const getBubbleClasses = (
|
|
|
305
305
|
default:
|
|
306
306
|
// Default bubble layout
|
|
307
307
|
baseClasses.push(
|
|
308
|
-
"
|
|
309
|
-
"
|
|
310
|
-
"
|
|
311
|
-
"
|
|
308
|
+
"persona-rounded-2xl",
|
|
309
|
+
"persona-text-sm",
|
|
310
|
+
"persona-leading-relaxed",
|
|
311
|
+
"persona-shadow-sm"
|
|
312
312
|
);
|
|
313
313
|
if (role === "user") {
|
|
314
314
|
baseClasses.push(
|
|
315
315
|
"vanilla-message-user-bubble",
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
"
|
|
319
|
-
"
|
|
320
|
-
"
|
|
316
|
+
"persona-ml-auto",
|
|
317
|
+
"persona-bg-persona-accent",
|
|
318
|
+
"persona-text-white",
|
|
319
|
+
"persona-px-5",
|
|
320
|
+
"persona-py-3"
|
|
321
321
|
);
|
|
322
322
|
} else {
|
|
323
323
|
baseClasses.push(
|
|
324
324
|
"vanilla-message-assistant-bubble",
|
|
325
|
-
"
|
|
326
|
-
"
|
|
327
|
-
"
|
|
328
|
-
"
|
|
329
|
-
"
|
|
330
|
-
"
|
|
325
|
+
"persona-bg-persona-surface",
|
|
326
|
+
"persona-border",
|
|
327
|
+
"persona-border-persona-message-border",
|
|
328
|
+
"persona-text-persona-primary",
|
|
329
|
+
"persona-px-5",
|
|
330
|
+
"persona-py-3"
|
|
331
331
|
);
|
|
332
332
|
}
|
|
333
333
|
break;
|
|
@@ -338,36 +338,53 @@ const getBubbleClasses = (
|
|
|
338
338
|
|
|
339
339
|
/**
|
|
340
340
|
* Create message action buttons (copy, upvote, downvote)
|
|
341
|
+
*
|
|
342
|
+
* This is a pure rendering function. It creates button elements with the
|
|
343
|
+
* correct `data-action` attributes, icons, and CSS classes. All click
|
|
344
|
+
* handling, vote state management, clipboard logic, and callback dispatch
|
|
345
|
+
* is handled via event delegation in `ui.ts` so that handlers survive
|
|
346
|
+
* idiomorph DOM morphing.
|
|
341
347
|
*/
|
|
342
348
|
export const createMessageActions = (
|
|
343
349
|
message: AgentWidgetMessage,
|
|
344
350
|
actionsConfig: AgentWidgetMessageActionsConfig,
|
|
351
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
345
352
|
_callbacks?: MessageActionCallbacks
|
|
346
353
|
): HTMLElement => {
|
|
347
354
|
const showCopy = actionsConfig.showCopy ?? true;
|
|
348
355
|
const showUpvote = actionsConfig.showUpvote ?? true;
|
|
349
356
|
const showDownvote = actionsConfig.showDownvote ?? true;
|
|
357
|
+
|
|
358
|
+
// Don't render the container at all when no actions are visible
|
|
359
|
+
if (!showCopy && !showUpvote && !showDownvote) {
|
|
360
|
+
const empty = createElement("div");
|
|
361
|
+
empty.style.display = "none";
|
|
362
|
+
empty.id = `actions-${message.id}`;
|
|
363
|
+
empty.setAttribute("data-actions-for", message.id);
|
|
364
|
+
return empty;
|
|
365
|
+
}
|
|
366
|
+
|
|
350
367
|
const visibility = actionsConfig.visibility ?? "hover";
|
|
351
368
|
const align = actionsConfig.align ?? "right";
|
|
352
369
|
const layout = actionsConfig.layout ?? "pill-inside";
|
|
353
370
|
|
|
354
371
|
// Map alignment to CSS class
|
|
355
372
|
const alignClass = {
|
|
356
|
-
left: "
|
|
357
|
-
center: "
|
|
358
|
-
right: "
|
|
373
|
+
left: "persona-message-actions-left",
|
|
374
|
+
center: "persona-message-actions-center",
|
|
375
|
+
right: "persona-message-actions-right",
|
|
359
376
|
}[align];
|
|
360
377
|
|
|
361
378
|
// Map layout to CSS class
|
|
362
379
|
const layoutClass = {
|
|
363
|
-
"pill-inside": "
|
|
364
|
-
"row-inside": "
|
|
380
|
+
"pill-inside": "persona-message-actions-pill",
|
|
381
|
+
"row-inside": "persona-message-actions-row",
|
|
365
382
|
}[layout];
|
|
366
383
|
|
|
367
384
|
const container = createElement(
|
|
368
385
|
"div",
|
|
369
|
-
`
|
|
370
|
-
visibility === "hover" ? "
|
|
386
|
+
`persona-message-actions persona-flex persona-items-center persona-gap-1 persona-mt-2 ${alignClass} ${layoutClass} ${
|
|
387
|
+
visibility === "hover" ? "persona-message-actions-hover" : ""
|
|
371
388
|
}`
|
|
372
389
|
);
|
|
373
390
|
// Set id for idiomorph matching (prevents recreation on morph)
|
|
@@ -380,7 +397,7 @@ export const createMessageActions = (
|
|
|
380
397
|
dataAction: string
|
|
381
398
|
): HTMLButtonElement => {
|
|
382
399
|
const button = document.createElement("button");
|
|
383
|
-
button.className = "
|
|
400
|
+
button.className = "persona-message-action-btn";
|
|
384
401
|
button.setAttribute("aria-label", label);
|
|
385
402
|
button.setAttribute("title", label);
|
|
386
403
|
button.setAttribute("data-action", dataAction);
|
|
@@ -393,17 +410,17 @@ export const createMessageActions = (
|
|
|
393
410
|
return button;
|
|
394
411
|
};
|
|
395
412
|
|
|
396
|
-
// Copy button
|
|
413
|
+
// Copy button
|
|
397
414
|
if (showCopy) {
|
|
398
415
|
container.appendChild(createActionButton("copy", "Copy message", "copy"));
|
|
399
416
|
}
|
|
400
417
|
|
|
401
|
-
// Upvote button
|
|
418
|
+
// Upvote button
|
|
402
419
|
if (showUpvote) {
|
|
403
420
|
container.appendChild(createActionButton("thumbs-up", "Upvote", "upvote"));
|
|
404
421
|
}
|
|
405
422
|
|
|
406
|
-
// Downvote button
|
|
423
|
+
// Downvote button
|
|
407
424
|
if (showDownvote) {
|
|
408
425
|
container.appendChild(createActionButton("thumbs-down", "Downvote", "downvote"));
|
|
409
426
|
}
|
|
@@ -453,6 +470,15 @@ export const createStandardBubble = (
|
|
|
453
470
|
bubble.id = `bubble-${message.id}`;
|
|
454
471
|
bubble.setAttribute("data-message-id", message.id);
|
|
455
472
|
|
|
473
|
+
// Apply component-level color overrides via CSS variables
|
|
474
|
+
if (message.role === "user") {
|
|
475
|
+
bubble.style.backgroundColor = 'var(--persona-message-user-bg, var(--persona-accent))';
|
|
476
|
+
bubble.style.color = 'var(--persona-message-user-text, white)';
|
|
477
|
+
} else if (message.role === "assistant") {
|
|
478
|
+
bubble.style.backgroundColor = 'var(--persona-message-assistant-bg, var(--persona-surface))';
|
|
479
|
+
bubble.style.color = 'var(--persona-message-assistant-text, var(--persona-text))';
|
|
480
|
+
}
|
|
481
|
+
|
|
456
482
|
const imageParts = getMessageImageParts(message);
|
|
457
483
|
const messageContentText = message.content?.trim() ?? "";
|
|
458
484
|
const isImageOnlyFallbackMessage =
|
|
@@ -461,6 +487,7 @@ export const createStandardBubble = (
|
|
|
461
487
|
|
|
462
488
|
// Add message content
|
|
463
489
|
const contentDiv = document.createElement("div");
|
|
490
|
+
contentDiv.classList.add("persona-message-content");
|
|
464
491
|
const transformedContent = transform({
|
|
465
492
|
text: message.content,
|
|
466
493
|
message,
|
|
@@ -481,7 +508,7 @@ export const createStandardBubble = (
|
|
|
481
508
|
// Add inline timestamp if configured
|
|
482
509
|
if (showTimestamp && timestampPosition === "inline" && message.createdAt) {
|
|
483
510
|
const timestamp = createTimestamp(message, timestampConfig!);
|
|
484
|
-
timestamp.classList.add("
|
|
511
|
+
timestamp.classList.add("persona-ml-2", "persona-inline");
|
|
485
512
|
contentDiv.appendChild(timestamp);
|
|
486
513
|
}
|
|
487
514
|
|
|
@@ -508,7 +535,7 @@ export const createStandardBubble = (
|
|
|
508
535
|
// Add timestamp below if configured
|
|
509
536
|
if (showTimestamp && timestampPosition === "below" && message.createdAt) {
|
|
510
537
|
const timestamp = createTimestamp(message, timestampConfig!);
|
|
511
|
-
timestamp.classList.add("
|
|
538
|
+
timestamp.classList.add("persona-mt-1");
|
|
512
539
|
bubble.appendChild(timestamp);
|
|
513
540
|
}
|
|
514
541
|
|
|
@@ -548,7 +575,7 @@ export const createStandardBubble = (
|
|
|
548
575
|
// Create wrapper with avatar
|
|
549
576
|
const wrapper = createElement(
|
|
550
577
|
"div",
|
|
551
|
-
`
|
|
578
|
+
`persona-flex persona-gap-2 ${message.role === "user" ? "persona-flex-row-reverse" : ""}`
|
|
552
579
|
);
|
|
553
580
|
|
|
554
581
|
const avatar = createAvatar(avatarConfig!, message.role);
|
|
@@ -560,8 +587,8 @@ export const createStandardBubble = (
|
|
|
560
587
|
}
|
|
561
588
|
|
|
562
589
|
// Adjust bubble max-width when avatar is present
|
|
563
|
-
bubble.classList.remove("
|
|
564
|
-
bubble.classList.add("
|
|
590
|
+
bubble.classList.remove("persona-max-w-[85%]");
|
|
591
|
+
bubble.classList.add("persona-max-w-[calc(85%-2.5rem)]");
|
|
565
592
|
|
|
566
593
|
return wrapper;
|
|
567
594
|
};
|
|
@@ -35,9 +35,9 @@ export const renderMessages = (
|
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const wrapper = createElement("div", "
|
|
38
|
+
const wrapper = createElement("div", "persona-flex");
|
|
39
39
|
if (message.role === "user") {
|
|
40
|
-
wrapper.classList.add("
|
|
40
|
+
wrapper.classList.add("persona-justify-end");
|
|
41
41
|
}
|
|
42
42
|
wrapper.appendChild(bubble);
|
|
43
43
|
fragment.appendChild(wrapper);
|
package/src/components/panel.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createElement } from "../utils/dom";
|
|
2
2
|
import { AgentWidgetConfig } from "../types";
|
|
3
3
|
import { positionMap } from "../utils/positioning";
|
|
4
|
+
import { isDockedMountMode } from "../utils/dock";
|
|
4
5
|
import { buildHeader, attachHeaderToContainer, HeaderElements } from "./header-builder";
|
|
5
6
|
import { buildHeaderWithLayout } from "./header-layouts";
|
|
6
7
|
import { buildComposer, ComposerElements } from "./composer-builder";
|
|
@@ -12,17 +13,32 @@ export interface PanelWrapper {
|
|
|
12
13
|
|
|
13
14
|
export const createWrapper = (config?: AgentWidgetConfig): PanelWrapper => {
|
|
14
15
|
const launcherEnabled = config?.launcher?.enabled ?? true;
|
|
16
|
+
const dockedMode = isDockedMountMode(config);
|
|
17
|
+
|
|
18
|
+
if (dockedMode) {
|
|
19
|
+
const wrapper = createElement(
|
|
20
|
+
"div",
|
|
21
|
+
"persona-relative persona-h-full persona-w-full persona-flex persona-flex-1 persona-min-h-0 persona-flex-col"
|
|
22
|
+
);
|
|
23
|
+
const panel = createElement(
|
|
24
|
+
"div",
|
|
25
|
+
"persona-relative persona-h-full persona-w-full persona-flex persona-flex-1 persona-min-h-0 persona-flex-col"
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
wrapper.appendChild(panel);
|
|
29
|
+
return { wrapper, panel };
|
|
30
|
+
}
|
|
15
31
|
|
|
16
32
|
if (!launcherEnabled) {
|
|
17
33
|
// For inline embed mode, use flex layout to ensure the widget fills its container
|
|
18
34
|
// and only the chat messages area scrolls
|
|
19
35
|
const wrapper = createElement(
|
|
20
36
|
"div",
|
|
21
|
-
"
|
|
37
|
+
"persona-relative persona-h-full persona-flex persona-flex-col persona-flex-1 persona-min-h-0"
|
|
22
38
|
);
|
|
23
39
|
const panel = createElement(
|
|
24
40
|
"div",
|
|
25
|
-
"
|
|
41
|
+
"persona-relative persona-flex-1 persona-flex persona-flex-col persona-min-h-0"
|
|
26
42
|
);
|
|
27
43
|
|
|
28
44
|
// Apply width from config, defaulting to 100% for inline embed mode
|
|
@@ -42,12 +58,12 @@ export const createWrapper = (config?: AgentWidgetConfig): PanelWrapper => {
|
|
|
42
58
|
|
|
43
59
|
const wrapper = createElement(
|
|
44
60
|
"div",
|
|
45
|
-
`
|
|
61
|
+
`persona-widget-wrapper persona-fixed ${position} persona-z-50 persona-transition`
|
|
46
62
|
);
|
|
47
63
|
|
|
48
64
|
const panel = createElement(
|
|
49
65
|
"div",
|
|
50
|
-
"
|
|
66
|
+
"persona-widget-panel persona-relative persona-min-h-[320px]"
|
|
51
67
|
);
|
|
52
68
|
const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;
|
|
53
69
|
const width = launcherWidth ?? "min(400px, calc(100vw - 24px))";
|
|
@@ -98,7 +114,7 @@ export const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelE
|
|
|
98
114
|
// the body (chat messages area) to scroll while header/footer stay fixed
|
|
99
115
|
const container = createElement(
|
|
100
116
|
"div",
|
|
101
|
-
"
|
|
117
|
+
"persona-widget-container persona-flex persona-h-full persona-w-full persona-flex-1 persona-min-h-0 persona-flex-col persona-bg-persona-surface persona-text-persona-primary persona-rounded-2xl persona-overflow-hidden persona-border persona-border-persona-border"
|
|
102
118
|
);
|
|
103
119
|
|
|
104
120
|
// Build header using layout config if available, otherwise use standard builder
|
|
@@ -111,22 +127,22 @@ export const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelE
|
|
|
111
127
|
// Build body with intro card and messages wrapper
|
|
112
128
|
const body = createElement(
|
|
113
129
|
"div",
|
|
114
|
-
"
|
|
130
|
+
"persona-widget-body persona-flex persona-flex-1 persona-min-h-0 persona-flex-col persona-gap-6 persona-overflow-y-auto persona-bg-persona-container persona-px-6 persona-py-6"
|
|
115
131
|
);
|
|
116
132
|
body.id = "persona-scroll-container";
|
|
117
133
|
|
|
118
134
|
const introCard = createElement(
|
|
119
135
|
"div",
|
|
120
|
-
"
|
|
136
|
+
"persona-rounded-2xl persona-bg-persona-surface persona-p-6 persona-shadow-sm"
|
|
121
137
|
);
|
|
122
138
|
const introTitle = createElement(
|
|
123
139
|
"h2",
|
|
124
|
-
"
|
|
140
|
+
"persona-text-lg persona-font-semibold persona-text-persona-primary"
|
|
125
141
|
);
|
|
126
142
|
introTitle.textContent = config?.copy?.welcomeTitle ?? "Hello 👋";
|
|
127
143
|
const introSubtitle = createElement(
|
|
128
144
|
"p",
|
|
129
|
-
"
|
|
145
|
+
"persona-mt-2 persona-text-sm persona-text-persona-muted"
|
|
130
146
|
);
|
|
131
147
|
introSubtitle.textContent =
|
|
132
148
|
config?.copy?.welcomeSubtitle ??
|
|
@@ -135,10 +151,25 @@ export const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelE
|
|
|
135
151
|
|
|
136
152
|
const messagesWrapper = createElement(
|
|
137
153
|
"div",
|
|
138
|
-
"
|
|
154
|
+
"persona-flex persona-flex-col persona-gap-3"
|
|
139
155
|
);
|
|
140
156
|
|
|
141
|
-
|
|
157
|
+
const contentMaxWidth = config?.layout?.contentMaxWidth;
|
|
158
|
+
if (contentMaxWidth) {
|
|
159
|
+
messagesWrapper.style.maxWidth = contentMaxWidth;
|
|
160
|
+
messagesWrapper.style.marginLeft = "auto";
|
|
161
|
+
messagesWrapper.style.marginRight = "auto";
|
|
162
|
+
messagesWrapper.style.width = "100%";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const showWelcomeCard = config?.copy?.showWelcomeCard !== false;
|
|
166
|
+
if (!showWelcomeCard) {
|
|
167
|
+
body.classList.remove("persona-gap-6");
|
|
168
|
+
body.classList.add("persona-gap-3");
|
|
169
|
+
body.append(messagesWrapper);
|
|
170
|
+
} else {
|
|
171
|
+
body.append(introCard, messagesWrapper);
|
|
172
|
+
}
|
|
142
173
|
|
|
143
174
|
// Build composer/footer using extracted builder
|
|
144
175
|
const composerElements: ComposerElements = buildComposer({ config });
|
|
@@ -10,15 +10,15 @@ export const reasoningExpansionState = new Set<string>();
|
|
|
10
10
|
export const updateReasoningBubbleUI = (messageId: string, bubble: HTMLElement): void => {
|
|
11
11
|
const expanded = reasoningExpansionState.has(messageId);
|
|
12
12
|
const header = bubble.querySelector('button[data-expand-header="true"]') as HTMLElement;
|
|
13
|
-
const content = bubble.querySelector('.
|
|
13
|
+
const content = bubble.querySelector('.persona-border-t') as HTMLElement;
|
|
14
14
|
|
|
15
15
|
if (!header || !content) return;
|
|
16
16
|
|
|
17
17
|
header.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
18
18
|
|
|
19
|
-
// Find toggle icon container - it's the direct child div of headerMeta (which has
|
|
20
|
-
const headerMeta = header.querySelector('.
|
|
21
|
-
const toggleIcon = headerMeta?.querySelector(':scope > .
|
|
19
|
+
// Find toggle icon container - it's the direct child div of headerMeta (which has persona-ml-auto)
|
|
20
|
+
const headerMeta = header.querySelector('.persona-ml-auto') as HTMLElement;
|
|
21
|
+
const toggleIcon = headerMeta?.querySelector(':scope > .persona-flex.persona-items-center') as HTMLElement;
|
|
22
22
|
if (toggleIcon) {
|
|
23
23
|
toggleIcon.innerHTML = "";
|
|
24
24
|
const iconColor = "currentColor";
|
|
@@ -40,17 +40,17 @@ export const createReasoningBubble = (message: AgentWidgetMessage): HTMLElement
|
|
|
40
40
|
[
|
|
41
41
|
"vanilla-message-bubble",
|
|
42
42
|
"vanilla-reasoning-bubble",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
43
|
+
"persona-w-full",
|
|
44
|
+
"persona-max-w-[85%]",
|
|
45
|
+
"persona-rounded-2xl",
|
|
46
|
+
"persona-bg-persona-surface",
|
|
47
|
+
"persona-border",
|
|
48
|
+
"persona-border-persona-message-border",
|
|
49
|
+
"persona-text-persona-primary",
|
|
50
|
+
"persona-shadow-sm",
|
|
51
|
+
"persona-overflow-hidden",
|
|
52
|
+
"persona-px-0",
|
|
53
|
+
"persona-py-0"
|
|
54
54
|
].join(" ")
|
|
55
55
|
);
|
|
56
56
|
// Set id for idiomorph matching
|
|
@@ -64,19 +64,19 @@ export const createReasoningBubble = (message: AgentWidgetMessage): HTMLElement
|
|
|
64
64
|
let expanded = reasoningExpansionState.has(message.id);
|
|
65
65
|
const header = createElement(
|
|
66
66
|
"button",
|
|
67
|
-
"
|
|
67
|
+
"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"
|
|
68
68
|
) as HTMLButtonElement;
|
|
69
69
|
header.type = "button";
|
|
70
70
|
header.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
71
71
|
header.setAttribute("data-expand-header", "true");
|
|
72
72
|
header.setAttribute("data-bubble-type", "reasoning");
|
|
73
73
|
|
|
74
|
-
const headerContent = createElement("div", "
|
|
75
|
-
const title = createElement("span", "
|
|
74
|
+
const headerContent = createElement("div", "persona-flex persona-flex-col persona-text-left");
|
|
75
|
+
const title = createElement("span", "persona-text-xs persona-text-persona-primary");
|
|
76
76
|
title.textContent = "Thinking...";
|
|
77
77
|
headerContent.appendChild(title);
|
|
78
78
|
|
|
79
|
-
const status = createElement("span", "
|
|
79
|
+
const status = createElement("span", "persona-text-xs persona-text-persona-primary");
|
|
80
80
|
status.textContent = describeReasonStatus(reasoning);
|
|
81
81
|
headerContent.appendChild(status);
|
|
82
82
|
|
|
@@ -86,7 +86,7 @@ export const createReasoningBubble = (message: AgentWidgetMessage): HTMLElement
|
|
|
86
86
|
title.style.display = "";
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const toggleIcon = createElement("div", "
|
|
89
|
+
const toggleIcon = createElement("div", "persona-flex persona-items-center");
|
|
90
90
|
const iconColor = "currentColor";
|
|
91
91
|
const chevronIcon = renderLucideIcon(expanded ? "chevron-up" : "chevron-down", 16, iconColor, 2);
|
|
92
92
|
if (chevronIcon) {
|
|
@@ -96,21 +96,21 @@ export const createReasoningBubble = (message: AgentWidgetMessage): HTMLElement
|
|
|
96
96
|
toggleIcon.textContent = expanded ? "Hide" : "Show";
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
const headerMeta = createElement("div", "
|
|
99
|
+
const headerMeta = createElement("div", "persona-flex persona-items-center persona-ml-auto");
|
|
100
100
|
headerMeta.append(toggleIcon);
|
|
101
101
|
|
|
102
102
|
header.append(headerContent, headerMeta);
|
|
103
103
|
|
|
104
104
|
const content = createElement(
|
|
105
105
|
"div",
|
|
106
|
-
"
|
|
106
|
+
"persona-border-t persona-border-gray-200 persona-bg-gray-50 persona-px-4 persona-py-3"
|
|
107
107
|
);
|
|
108
108
|
content.style.display = expanded ? "" : "none";
|
|
109
109
|
|
|
110
110
|
const text = reasoning.chunks.join("");
|
|
111
111
|
const body = createElement(
|
|
112
112
|
"div",
|
|
113
|
-
"
|
|
113
|
+
"persona-whitespace-pre-wrap persona-text-xs persona-leading-snug persona-text-persona-muted"
|
|
114
114
|
);
|
|
115
115
|
body.textContent =
|
|
116
116
|
text ||
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AgentWidgetConfig, AgentWidgetMessage } from "../types";
|
|
2
|
+
import { PersonaArtifactCard } from "./artifact-card";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Context provided to component renderers
|
|
@@ -85,3 +86,6 @@ class ComponentRegistry {
|
|
|
85
86
|
* Global component registry instance
|
|
86
87
|
*/
|
|
87
88
|
export const componentRegistry = new ComponentRegistry();
|
|
89
|
+
|
|
90
|
+
// Register built-in components
|
|
91
|
+
componentRegistry.register("PersonaArtifactCard", PersonaArtifactCard);
|
|
@@ -52,7 +52,7 @@ export const createSuggestions = (container: HTMLElement): SuggestionButtons =>
|
|
|
52
52
|
chips.forEach((chip) => {
|
|
53
53
|
const btn = createElement(
|
|
54
54
|
"button",
|
|
55
|
-
"
|
|
55
|
+
"persona-rounded-button persona-bg-persona-surface persona-px-3 persona-py-1.5 persona-text-xs persona-font-medium persona-text-persona-muted hover:persona-opacity-90 persona-cursor-pointer persona-border persona-border-gray-200"
|
|
56
56
|
) as HTMLButtonElement;
|
|
57
57
|
btn.type = "button";
|
|
58
58
|
btn.textContent = chip;
|