@runtypelabs/persona 3.16.0 → 3.17.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.
Files changed (58) hide show
  1. package/dist/animations/glyph-cycle.cjs +279 -0
  2. package/dist/animations/glyph-cycle.d.cts +5 -0
  3. package/dist/animations/glyph-cycle.d.ts +5 -0
  4. package/dist/animations/glyph-cycle.js +252 -0
  5. package/dist/animations/types-HPZY7oAI.d.cts +282 -0
  6. package/dist/animations/types-HPZY7oAI.d.ts +282 -0
  7. package/dist/animations/wipe.cjs +107 -0
  8. package/dist/animations/wipe.d.cts +5 -0
  9. package/dist/animations/wipe.d.ts +5 -0
  10. package/dist/animations/wipe.js +80 -0
  11. package/dist/index.cjs +48 -47
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +205 -1
  14. package/dist/index.d.ts +205 -1
  15. package/dist/index.global.js +136 -81
  16. package/dist/index.global.js.map +1 -1
  17. package/dist/index.js +48 -47
  18. package/dist/index.js.map +1 -1
  19. package/dist/testing.cjs +85 -0
  20. package/dist/testing.d.cts +39 -0
  21. package/dist/testing.d.ts +39 -0
  22. package/dist/testing.js +56 -0
  23. package/dist/theme-editor.cjs +714 -99
  24. package/dist/theme-editor.d.cts +214 -2
  25. package/dist/theme-editor.d.ts +214 -2
  26. package/dist/theme-editor.js +712 -99
  27. package/dist/widget.css +133 -0
  28. package/package.json +20 -3
  29. package/src/animations/glyph-cycle.ts +332 -0
  30. package/src/animations/wipe.ts +66 -0
  31. package/src/client.test.ts +141 -0
  32. package/src/client.ts +28 -0
  33. package/src/components/composer-builder.ts +61 -10
  34. package/src/components/message-bubble.test.ts +181 -2
  35. package/src/components/message-bubble.ts +209 -14
  36. package/src/components/panel.ts +4 -1
  37. package/src/defaults.ts +16 -0
  38. package/src/index-global.ts +31 -0
  39. package/src/index.ts +18 -0
  40. package/src/session.test.ts +93 -1
  41. package/src/session.ts +5 -0
  42. package/src/styles/widget.css +133 -0
  43. package/src/testing/index.ts +11 -0
  44. package/src/testing/mock-stream.test.ts +80 -0
  45. package/src/testing/mock-stream.ts +94 -0
  46. package/src/testing.ts +2 -0
  47. package/src/theme-editor/index.ts +4 -0
  48. package/src/theme-editor/preview-utils.test.ts +60 -0
  49. package/src/theme-editor/preview-utils.ts +129 -0
  50. package/src/theme-editor/sections.test.ts +19 -0
  51. package/src/theme-editor/sections.ts +84 -1
  52. package/src/types.ts +210 -0
  53. package/src/ui.stop-button.test.ts +165 -0
  54. package/src/ui.ts +75 -6
  55. package/src/utils/message-fingerprint.ts +2 -0
  56. package/src/utils/morph.ts +7 -0
  57. package/src/utils/stream-animation.test.ts +417 -0
  58. package/src/utils/stream-animation.ts +449 -0
@@ -138,6 +138,12 @@ var DEFAULT_WIDGET_CONFIG = {
138
138
  previewMaxLines: 3,
139
139
  expandable: true,
140
140
  loadingAnimation: "none"
141
+ },
142
+ streamAnimation: {
143
+ type: "none",
144
+ placeholder: "none",
145
+ speed: 120,
146
+ duration: 1800
141
147
  }
142
148
  },
143
149
  suggestionChips: [
@@ -240,11 +246,13 @@ function mergeWithDefaults(config) {
240
246
  ...config.voiceRecognition
241
247
  },
242
248
  features: (() => {
243
- var _a2, _b2, _c2, _d2;
249
+ var _a2, _b2, _c2, _d2, _e2, _f2;
244
250
  const da = (_a2 = DEFAULT_WIDGET_CONFIG.features) == null ? void 0 : _a2.artifacts;
245
251
  const ca = (_b2 = config.features) == null ? void 0 : _b2.artifacts;
246
252
  const dsb = (_c2 = DEFAULT_WIDGET_CONFIG.features) == null ? void 0 : _c2.scrollToBottom;
247
253
  const csb = (_d2 = config.features) == null ? void 0 : _d2.scrollToBottom;
254
+ const dsa = (_e2 = DEFAULT_WIDGET_CONFIG.features) == null ? void 0 : _e2.streamAnimation;
255
+ const csa = (_f2 = config.features) == null ? void 0 : _f2.streamAnimation;
248
256
  const mergedArtifacts = da === void 0 && ca === void 0 ? void 0 : {
249
257
  ...da,
250
258
  ...ca,
@@ -257,11 +265,16 @@ function mergeWithDefaults(config) {
257
265
  ...dsb,
258
266
  ...csb
259
267
  };
268
+ const mergedStreamAnimation = dsa === void 0 && csa === void 0 ? void 0 : {
269
+ ...dsa,
270
+ ...csa
271
+ };
260
272
  return {
261
273
  ...DEFAULT_WIDGET_CONFIG.features,
262
274
  ...config.features,
263
275
  ...mergedScrollToBottom !== void 0 ? { scrollToBottom: mergedScrollToBottom } : {},
264
- ...mergedArtifacts !== void 0 ? { artifacts: mergedArtifacts } : {}
276
+ ...mergedArtifacts !== void 0 ? { artifacts: mergedArtifacts } : {},
277
+ ...mergedStreamAnimation !== void 0 ? { streamAnimation: mergedStreamAnimation } : {}
265
278
  };
266
279
  })(),
267
280
  suggestionChips: (_e = config.suggestionChips) != null ? _e : DEFAULT_WIDGET_CONFIG.suggestionChips,
@@ -2564,6 +2577,91 @@ var featuresSectionDef = {
2564
2577
  { id: "feat-scroll-bottom-label", label: "Scroll To Bottom Label", description: "Leave empty for icon-only mode", type: "text", path: "features.scrollToBottom.label", defaultValue: "" }
2565
2578
  ]
2566
2579
  };
2580
+ var streamAnimationSectionDef = {
2581
+ id: "stream-animation",
2582
+ title: "Stream Animation",
2583
+ description: "Control how assistant text appears while streaming.",
2584
+ collapsed: true,
2585
+ fields: [
2586
+ {
2587
+ id: "stream-anim-type",
2588
+ label: "Animation",
2589
+ description: "Reveal effect applied to each assistant reply as it streams.",
2590
+ type: "select",
2591
+ path: "features.streamAnimation.type",
2592
+ defaultValue: "none",
2593
+ options: [
2594
+ { value: "none", label: "None" },
2595
+ { value: "typewriter", label: "Typewriter" },
2596
+ { value: "word-fade", label: "Word fade" },
2597
+ { value: "letter-rise", label: "Letter rise" },
2598
+ { value: "glyph-cycle", label: "Glyph cycle" },
2599
+ { value: "wipe", label: "Wipe" },
2600
+ { value: "pop-bubble", label: "Pop bubble" }
2601
+ ]
2602
+ },
2603
+ {
2604
+ id: "stream-anim-placeholder",
2605
+ label: "Pre-first-token Placeholder",
2606
+ description: "What to show before the first token arrives.",
2607
+ type: "select",
2608
+ path: "features.streamAnimation.placeholder",
2609
+ defaultValue: "none",
2610
+ options: [
2611
+ { value: "none", label: "Typing indicator (default)" },
2612
+ { value: "skeleton", label: "Skeleton shimmer" }
2613
+ ]
2614
+ },
2615
+ {
2616
+ id: "stream-anim-buffer",
2617
+ label: "Content Buffering",
2618
+ description: "Trim in-progress units so only complete words/lines reveal.",
2619
+ type: "select",
2620
+ path: "features.streamAnimation.buffer",
2621
+ defaultValue: "none",
2622
+ options: [
2623
+ { value: "none", label: "None \u2014 stream every character" },
2624
+ { value: "word", label: "Word \u2014 hold until whitespace" },
2625
+ { value: "line", label: "Line \u2014 hold until newline" }
2626
+ ]
2627
+ },
2628
+ {
2629
+ id: "stream-anim-speed",
2630
+ label: "Per-unit Duration (ms)",
2631
+ description: "Animation length for each character or word.",
2632
+ type: "select",
2633
+ path: "features.streamAnimation.speed",
2634
+ defaultValue: 120,
2635
+ options: [
2636
+ { value: "40", label: "40ms \u2014 snappy" },
2637
+ { value: "80", label: "80ms" },
2638
+ { value: "120", label: "120ms (default)" },
2639
+ { value: "200", label: "200ms" },
2640
+ { value: "320", label: "320ms" },
2641
+ { value: "480", label: "480ms \u2014 slow" }
2642
+ ],
2643
+ formatValue: (v) => String(v != null ? v : 120),
2644
+ parseValue: (v) => Number(v)
2645
+ },
2646
+ {
2647
+ id: "stream-anim-duration",
2648
+ label: "Container Duration (ms)",
2649
+ description: "Length of container-level effects (pop-bubble, custom plugins).",
2650
+ type: "select",
2651
+ path: "features.streamAnimation.duration",
2652
+ defaultValue: 1800,
2653
+ options: [
2654
+ { value: "600", label: "600ms" },
2655
+ { value: "1200", label: "1200ms" },
2656
+ { value: "1800", label: "1800ms (default)" },
2657
+ { value: "2400", label: "2400ms" },
2658
+ { value: "3600", label: "3600ms \u2014 slow" }
2659
+ ],
2660
+ formatValue: (v) => String(v != null ? v : 1800),
2661
+ parseValue: (v) => Number(v)
2662
+ }
2663
+ ]
2664
+ };
2567
2665
  var attachmentsSectionDef = {
2568
2666
  id: "attachments-config",
2569
2667
  title: "Attachments",
@@ -2647,7 +2745,7 @@ var CONFIGURE_SUB_GROUPS = [
2647
2745
  { label: "Content", sections: [copySectionDef, suggestionsSectionDef] },
2648
2746
  { label: "Layout", sections: [generalLayoutSectionDef, headerLayoutSectionDef, messagesLayoutSectionDef, messageActionsSectionDef] },
2649
2747
  { label: "Widget", sections: [launcherBasicsSectionDef, launcherAdvancedSectionDef, sendButtonSectionDef, closeButtonSectionDef, clearChatSectionDef, statusIndicatorSectionDef] },
2650
- { label: "Features", sections: [featuresSectionDef, attachmentsSectionDef, artifactsSectionDef, artifactCustomizationSectionDef] },
2748
+ { label: "Features", sections: [featuresSectionDef, streamAnimationSectionDef, attachmentsSectionDef, artifactsSectionDef, artifactCustomizationSectionDef] },
2651
2749
  { label: "Developer", collapsedByDefault: true, sections: [apiIntegrationSectionDef, debugSectionDef, markdownSectionDef] }
2652
2750
  ];
2653
2751
  var CONFIGURE_SECTIONS = CONFIGURE_SUB_GROUPS.flatMap((g) => g.sections);
@@ -4697,7 +4795,7 @@ var AgentWidgetClient = class {
4697
4795
  const agentIterationMessages = /* @__PURE__ */ new Map();
4698
4796
  const iterationDisplay = (_a = this.config.iterationDisplay) != null ? _a : "separate";
4699
4797
  drainReadyQueue = () => {
4700
- var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb, _hb, _ib, _jb, _kb, _lb, _mb, _nb, _ob, _pb, _qb, _rb, _sb, _tb, _ub;
4798
+ var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb, _hb, _ib, _jb, _kb, _lb, _mb, _nb, _ob, _pb, _qb, _rb, _sb, _tb, _ub, _vb;
4701
4799
  for (let i = 0; i < seqReadyQueue.length; i++) {
4702
4800
  const payloadType = seqReadyQueue[i].payloadType;
4703
4801
  const payload = seqReadyQueue[i].payload;
@@ -5117,9 +5215,11 @@ var AgentWidgetClient = class {
5117
5215
  }
5118
5216
  continue;
5119
5217
  }
5218
+ const stepStopReason = payload.stopReason;
5120
5219
  if (didSplitByPartId) {
5121
5220
  if (assistantMessage !== null) {
5122
5221
  const msg = assistantMessage;
5222
+ if (stepStopReason) msg.stopReason = stepStopReason;
5123
5223
  streamParsers.delete(msg.id);
5124
5224
  rawContentBuffers.delete(msg.id);
5125
5225
  if (msg.streaming !== false) {
@@ -5130,6 +5230,7 @@ var AgentWidgetClient = class {
5130
5230
  const splitFinalContent = (_qa = payload.result) == null ? void 0 : _qa.response;
5131
5231
  const sealedForReconcile = lastSealedTextSegment;
5132
5232
  if (sealedForReconcile) {
5233
+ if (stepStopReason) sealedForReconcile.stopReason = stepStopReason;
5133
5234
  if (splitFinalContent !== void 0 && splitFinalContent !== null) {
5134
5235
  reconcileSealedAssistantWithFinalResponse(sealedForReconcile, splitFinalContent);
5135
5236
  } else {
@@ -5142,6 +5243,7 @@ var AgentWidgetClient = class {
5142
5243
  }
5143
5244
  const finalContent = (_ra = payload.result) == null ? void 0 : _ra.response;
5144
5245
  const assistant = ensureAssistantMessage();
5246
+ if (stepStopReason) assistant.stopReason = stepStopReason;
5145
5247
  if (finalContent !== void 0 && finalContent !== null) {
5146
5248
  const parser = streamParsers.get(assistant.id);
5147
5249
  let hasExtractedText = false;
@@ -5375,11 +5477,20 @@ var AgentWidgetClient = class {
5375
5477
  emitMessage(reasoningMessage);
5376
5478
  }
5377
5479
  }
5480
+ const turnStopReason = payload.stopReason;
5481
+ if (turnStopReason && assistantMessage !== null) {
5482
+ const turnId = payload.turnId;
5483
+ const matchesTurn = !turnId || ((_Ha = assistantMessage.agentMetadata) == null ? void 0 : _Ha.turnId) === turnId;
5484
+ if (matchesTurn) {
5485
+ assistantMessage.stopReason = turnStopReason;
5486
+ emitMessage(assistantMessage);
5487
+ }
5488
+ }
5378
5489
  } else if (payloadType === "agent_tool_start") {
5379
- const toolId = (_Ha = payload.toolCallId) != null ? _Ha : `agent-tool-${nextSequence()}`;
5490
+ const toolId = (_Ia = payload.toolCallId) != null ? _Ia : `agent-tool-${nextSequence()}`;
5380
5491
  trackToolId(getToolCallKey(payload), toolId);
5381
5492
  const toolMessage = ensureToolMessage(toolId);
5382
- const tool = (_Ia = toolMessage.toolCall) != null ? _Ia : {
5493
+ const tool = (_Ja = toolMessage.toolCall) != null ? _Ja : {
5383
5494
  id: toolId,
5384
5495
  status: "pending",
5385
5496
  name: void 0,
@@ -5391,12 +5502,12 @@ var AgentWidgetClient = class {
5391
5502
  completedAt: void 0,
5392
5503
  durationMs: void 0
5393
5504
  };
5394
- tool.name = (_Ka = (_Ja = payload.toolName) != null ? _Ja : payload.name) != null ? _Ka : tool.name;
5505
+ tool.name = (_La = (_Ka = payload.toolName) != null ? _Ka : payload.name) != null ? _La : tool.name;
5395
5506
  tool.status = "running";
5396
5507
  if (payload.parameters !== void 0) {
5397
5508
  tool.args = payload.parameters;
5398
5509
  }
5399
- tool.startedAt = resolveTimestamp((_La = payload.startedAt) != null ? _La : payload.timestamp);
5510
+ tool.startedAt = resolveTimestamp((_Ma = payload.startedAt) != null ? _Ma : payload.timestamp);
5400
5511
  toolMessage.toolCall = tool;
5401
5512
  toolMessage.streaming = true;
5402
5513
  toolMessage.agentMetadata = {
@@ -5405,21 +5516,21 @@ var AgentWidgetClient = class {
5405
5516
  };
5406
5517
  emitMessage(toolMessage);
5407
5518
  } else if (payloadType === "agent_tool_delta") {
5408
- const toolId = (_Ma = payload.toolCallId) != null ? _Ma : toolContext.lastId;
5519
+ const toolId = (_Na = payload.toolCallId) != null ? _Na : toolContext.lastId;
5409
5520
  if (toolId) {
5410
- const toolMessage = (_Na = toolMessages.get(toolId)) != null ? _Na : ensureToolMessage(toolId);
5521
+ const toolMessage = (_Oa = toolMessages.get(toolId)) != null ? _Oa : ensureToolMessage(toolId);
5411
5522
  if (toolMessage.toolCall) {
5412
- toolMessage.toolCall.chunks = (_Oa = toolMessage.toolCall.chunks) != null ? _Oa : [];
5413
- toolMessage.toolCall.chunks.push((_Pa = payload.delta) != null ? _Pa : "");
5523
+ toolMessage.toolCall.chunks = (_Pa = toolMessage.toolCall.chunks) != null ? _Pa : [];
5524
+ toolMessage.toolCall.chunks.push((_Qa = payload.delta) != null ? _Qa : "");
5414
5525
  toolMessage.toolCall.status = "running";
5415
5526
  toolMessage.streaming = true;
5416
5527
  emitMessage(toolMessage);
5417
5528
  }
5418
5529
  }
5419
5530
  } else if (payloadType === "agent_tool_complete") {
5420
- const toolId = (_Qa = payload.toolCallId) != null ? _Qa : toolContext.lastId;
5531
+ const toolId = (_Ra = payload.toolCallId) != null ? _Ra : toolContext.lastId;
5421
5532
  if (toolId) {
5422
- const toolMessage = (_Ra = toolMessages.get(toolId)) != null ? _Ra : ensureToolMessage(toolId);
5533
+ const toolMessage = (_Sa = toolMessages.get(toolId)) != null ? _Sa : ensureToolMessage(toolId);
5423
5534
  if (toolMessage.toolCall) {
5424
5535
  toolMessage.toolCall.status = "complete";
5425
5536
  if (payload.result !== void 0) {
@@ -5428,7 +5539,7 @@ var AgentWidgetClient = class {
5428
5539
  if (typeof payload.executionTime === "number") {
5429
5540
  toolMessage.toolCall.durationMs = payload.executionTime;
5430
5541
  }
5431
- toolMessage.toolCall.completedAt = resolveTimestamp((_Sa = payload.completedAt) != null ? _Sa : payload.timestamp);
5542
+ toolMessage.toolCall.completedAt = resolveTimestamp((_Ta = payload.completedAt) != null ? _Ta : payload.timestamp);
5432
5543
  toolMessage.streaming = false;
5433
5544
  emitMessage(toolMessage);
5434
5545
  const callKey = getToolCallKey(payload);
@@ -5443,7 +5554,7 @@ var AgentWidgetClient = class {
5443
5554
  const reflectionMessage = {
5444
5555
  id: reflectionId,
5445
5556
  role: "assistant",
5446
- content: (_Ta = payload.reflection) != null ? _Ta : "",
5557
+ content: (_Ua = payload.reflection) != null ? _Ua : "",
5447
5558
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
5448
5559
  streaming: false,
5449
5560
  variant: "reasoning",
@@ -5451,7 +5562,7 @@ var AgentWidgetClient = class {
5451
5562
  reasoning: {
5452
5563
  id: reflectionId,
5453
5564
  status: "complete",
5454
- chunks: [(_Ua = payload.reflection) != null ? _Ua : ""]
5565
+ chunks: [(_Va = payload.reflection) != null ? _Va : ""]
5455
5566
  },
5456
5567
  agentMetadata: {
5457
5568
  executionId: payload.executionId,
@@ -5472,7 +5583,7 @@ var AgentWidgetClient = class {
5472
5583
  }
5473
5584
  onEvent({ type: "status", status: "idle" });
5474
5585
  } else if (payloadType === "agent_error") {
5475
- const errorMessage = typeof payload.error === "string" ? payload.error : (_Wa = (_Va = payload.error) == null ? void 0 : _Va.message) != null ? _Wa : "Agent execution error";
5586
+ const errorMessage = typeof payload.error === "string" ? payload.error : (_Xa = (_Wa = payload.error) == null ? void 0 : _Wa.message) != null ? _Xa : "Agent execution error";
5476
5587
  if (payload.recoverable) {
5477
5588
  if (typeof console !== "undefined") {
5478
5589
  console.warn("[AgentWidget] Recoverable agent error:", errorMessage);
@@ -5485,7 +5596,7 @@ var AgentWidgetClient = class {
5485
5596
  }
5486
5597
  } else if (payloadType === "agent_ping") {
5487
5598
  } else if (payloadType === "agent_approval_start") {
5488
- const approvalId = (_Xa = payload.approvalId) != null ? _Xa : `approval-${nextSequence()}`;
5599
+ const approvalId = (_Ya = payload.approvalId) != null ? _Ya : `approval-${nextSequence()}`;
5489
5600
  const approvalMessage = {
5490
5601
  id: `approval-${approvalId}`,
5491
5602
  role: "assistant",
@@ -5497,17 +5608,17 @@ var AgentWidgetClient = class {
5497
5608
  approval: {
5498
5609
  id: approvalId,
5499
5610
  status: "pending",
5500
- agentId: (_Ya = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _Ya : "virtual",
5501
- executionId: (__a = (_Za = payload.executionId) != null ? _Za : agentExecution == null ? void 0 : agentExecution.executionId) != null ? __a : "",
5502
- toolName: (_$a = payload.toolName) != null ? _$a : "",
5611
+ agentId: (_Za = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _Za : "virtual",
5612
+ executionId: (_$a = (__a = payload.executionId) != null ? __a : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _$a : "",
5613
+ toolName: (_ab = payload.toolName) != null ? _ab : "",
5503
5614
  toolType: payload.toolType,
5504
- description: (_bb = payload.description) != null ? _bb : `Execute ${(_ab = payload.toolName) != null ? _ab : "tool"}`,
5615
+ description: (_cb = payload.description) != null ? _cb : `Execute ${(_bb = payload.toolName) != null ? _bb : "tool"}`,
5505
5616
  parameters: payload.parameters
5506
5617
  }
5507
5618
  };
5508
5619
  emitMessage(approvalMessage);
5509
5620
  } else if (payloadType === "step_await" && payload.awaitReason === "approval_required") {
5510
- const approvalId = (_cb = payload.approvalId) != null ? _cb : `approval-${nextSequence()}`;
5621
+ const approvalId = (_db = payload.approvalId) != null ? _db : `approval-${nextSequence()}`;
5511
5622
  const approvalMessage = {
5512
5623
  id: `approval-${approvalId}`,
5513
5624
  role: "assistant",
@@ -5519,11 +5630,11 @@ var AgentWidgetClient = class {
5519
5630
  approval: {
5520
5631
  id: approvalId,
5521
5632
  status: "pending",
5522
- agentId: (_db = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _db : "virtual",
5523
- executionId: (_fb = (_eb = payload.executionId) != null ? _eb : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _fb : "",
5524
- toolName: (_gb = payload.toolName) != null ? _gb : "",
5633
+ agentId: (_eb = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _eb : "virtual",
5634
+ executionId: (_gb = (_fb = payload.executionId) != null ? _fb : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _gb : "",
5635
+ toolName: (_hb = payload.toolName) != null ? _hb : "",
5525
5636
  toolType: payload.toolType,
5526
- description: (_ib = payload.description) != null ? _ib : `Execute ${(_hb = payload.toolName) != null ? _hb : "tool"}`,
5637
+ description: (_jb = payload.description) != null ? _jb : `Execute ${(_ib = payload.toolName) != null ? _ib : "tool"}`,
5527
5638
  parameters: payload.parameters
5528
5639
  }
5529
5640
  };
@@ -5542,11 +5653,11 @@ var AgentWidgetClient = class {
5542
5653
  sequence: nextSequence(),
5543
5654
  approval: {
5544
5655
  id: approvalId,
5545
- status: (_jb = payload.decision) != null ? _jb : "approved",
5546
- agentId: (_kb = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _kb : "virtual",
5547
- executionId: (_mb = (_lb = payload.executionId) != null ? _lb : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _mb : "",
5548
- toolName: (_nb = payload.toolName) != null ? _nb : "",
5549
- description: (_ob = payload.description) != null ? _ob : "",
5656
+ status: (_kb = payload.decision) != null ? _kb : "approved",
5657
+ agentId: (_lb = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _lb : "virtual",
5658
+ executionId: (_nb = (_mb = payload.executionId) != null ? _mb : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _nb : "",
5659
+ toolName: (_ob = payload.toolName) != null ? _ob : "",
5660
+ description: (_pb = payload.description) != null ? _pb : "",
5550
5661
  resolvedAt: Date.now()
5551
5662
  }
5552
5663
  };
@@ -5584,7 +5695,7 @@ var AgentWidgetClient = class {
5584
5695
  }
5585
5696
  } else if (payloadType === "artifact_delta") {
5586
5697
  const deltaId = String(payload.id);
5587
- const deltaText = typeof payload.delta === "string" ? payload.delta : String((_pb = payload.delta) != null ? _pb : "");
5698
+ const deltaText = typeof payload.delta === "string" ? payload.delta : String((_qb = payload.delta) != null ? _qb : "");
5588
5699
  onEvent({
5589
5700
  type: "artifact_delta",
5590
5701
  id: deltaId,
@@ -5607,7 +5718,7 @@ var AgentWidgetClient = class {
5607
5718
  if (refMsg) {
5608
5719
  refMsg.streaming = false;
5609
5720
  try {
5610
- const parsed = JSON.parse((_qb = refMsg.rawContent) != null ? _qb : "{}");
5721
+ const parsed = JSON.parse((_rb = refMsg.rawContent) != null ? _rb : "{}");
5611
5722
  if (parsed.props) {
5612
5723
  parsed.props.status = "complete";
5613
5724
  const acc = artifactContent.get(artCompleteId);
@@ -5628,7 +5739,7 @@ var AgentWidgetClient = class {
5628
5739
  if (!m || typeof m !== "object") {
5629
5740
  continue;
5630
5741
  }
5631
- const id = String((_rb = m.id) != null ? _rb : `msg-${nextSequence()}`);
5742
+ const id = String((_sb = m.id) != null ? _sb : `msg-${nextSequence()}`);
5632
5743
  const roleRaw = m.role;
5633
5744
  const role = roleRaw === "user" ? "user" : roleRaw === "system" ? "system" : "assistant";
5634
5745
  const msg = {
@@ -5647,7 +5758,7 @@ var AgentWidgetClient = class {
5647
5758
  if (msg.rawContent) {
5648
5759
  try {
5649
5760
  const parsed = JSON.parse(msg.rawContent);
5650
- const refArtId = (_sb = parsed == null ? void 0 : parsed.props) == null ? void 0 : _sb.artifactId;
5761
+ const refArtId = (_tb = parsed == null ? void 0 : parsed.props) == null ? void 0 : _tb.artifactId;
5651
5762
  if (typeof refArtId === "string") {
5652
5763
  artifactIdsWithCards.add(refArtId);
5653
5764
  }
@@ -5663,7 +5774,7 @@ var AgentWidgetClient = class {
5663
5774
  if (payload.error instanceof Error) {
5664
5775
  resolvedError = payload.error;
5665
5776
  } else if (payloadType === "dispatch_error") {
5666
- const msg = (_tb = payload.message) != null ? _tb : payload.error;
5777
+ const msg = (_ub = payload.message) != null ? _ub : payload.error;
5667
5778
  if (msg != null && msg !== "") {
5668
5779
  resolvedError = new Error(String(msg));
5669
5780
  }
@@ -5672,7 +5783,7 @@ var AgentWidgetClient = class {
5672
5783
  if (typeof e === "string" && e !== "") {
5673
5784
  resolvedError = new Error(e);
5674
5785
  } else if (e != null && typeof e === "object" && "message" in e) {
5675
- resolvedError = new Error(String((_ub = e.message) != null ? _ub : e));
5786
+ resolvedError = new Error(String((_vb = e.message) != null ? _vb : e));
5676
5787
  }
5677
5788
  } else if (payloadType === "error" && payload.error != null && payload.error !== "") {
5678
5789
  resolvedError = new Error(String(payload.error));
@@ -7517,6 +7628,8 @@ var AgentWidgetSession = class _AgentWidgetSession {
7517
7628
  var _a;
7518
7629
  (_a = this.abortController) == null ? void 0 : _a.abort();
7519
7630
  this.abortController = null;
7631
+ this.stopSpeaking();
7632
+ this.stopVoicePlayback();
7520
7633
  this.setStreaming(false);
7521
7634
  this.setStatus("idle");
7522
7635
  }
@@ -8149,6 +8262,9 @@ var morphMessages = (container, newContent, options = {}) => {
8149
8262
  if (oldNode.classList.contains("persona-animate-typing")) {
8150
8263
  return false;
8151
8264
  }
8265
+ if (oldNode.hasAttribute("data-preserve-runtime")) {
8266
+ return false;
8267
+ }
8152
8268
  if (oldNode.hasAttribute("data-preserve-animation")) {
8153
8269
  if (newNode instanceof HTMLElement && !newNode.hasAttribute("data-preserve-animation")) {
8154
8270
  return;
@@ -8170,7 +8286,7 @@ var morphMessages = (container, newContent, options = {}) => {
8170
8286
 
8171
8287
  // src/utils/message-fingerprint.ts
8172
8288
  function computeMessageFingerprint(message, configVersion) {
8173
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C;
8289
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D;
8174
8290
  return [
8175
8291
  message.id,
8176
8292
  message.role,
@@ -8188,6 +8304,7 @@ function computeMessageFingerprint(message, configVersion) {
8188
8304
  typeof ((_w = message.toolCall) == null ? void 0 : _w.args) === "string" ? message.toolCall.args.length : ((_x = message.toolCall) == null ? void 0 : _x.args) ? JSON.stringify(message.toolCall.args).length : 0,
8189
8305
  (_A = (_z = (_y = message.reasoning) == null ? void 0 : _y.chunks) == null ? void 0 : _z.length) != null ? _A : 0,
8190
8306
  (_C = (_B = message.contentParts) == null ? void 0 : _B.length) != null ? _C : 0,
8307
+ (_D = message.stopReason) != null ? _D : "",
8191
8308
  configVersion
8192
8309
  ].join("\0");
8193
8310
  }
@@ -8288,6 +8405,241 @@ var statusCopy = {
8288
8405
  var DEFAULT_OVERLAY_Z_INDEX = 1e5;
8289
8406
  var PORTALED_OVERLAY_Z_INDEX = DEFAULT_OVERLAY_Z_INDEX + 1;
8290
8407
 
8408
+ // src/utils/stream-animation.ts
8409
+ var DEFAULT_STREAM_ANIMATION = {
8410
+ type: "none",
8411
+ placeholder: "none",
8412
+ speed: 120,
8413
+ duration: 1800,
8414
+ buffer: "none"
8415
+ };
8416
+ var DEFAULT_SKIP_TAGS = ["pre", "code", "a", "script", "style"];
8417
+ var resolveStreamAnimation = (feature) => {
8418
+ var _a, _b, _c, _d, _e;
8419
+ return {
8420
+ type: (_a = feature == null ? void 0 : feature.type) != null ? _a : DEFAULT_STREAM_ANIMATION.type,
8421
+ placeholder: (_b = feature == null ? void 0 : feature.placeholder) != null ? _b : DEFAULT_STREAM_ANIMATION.placeholder,
8422
+ speed: (_c = feature == null ? void 0 : feature.speed) != null ? _c : DEFAULT_STREAM_ANIMATION.speed,
8423
+ duration: (_d = feature == null ? void 0 : feature.duration) != null ? _d : DEFAULT_STREAM_ANIMATION.duration,
8424
+ buffer: (_e = feature == null ? void 0 : feature.buffer) != null ? _e : DEFAULT_STREAM_ANIMATION.buffer
8425
+ };
8426
+ };
8427
+ var BUILTIN_PLUGINS = [
8428
+ {
8429
+ name: "typewriter",
8430
+ containerClass: "persona-stream-typewriter",
8431
+ wrap: "char",
8432
+ useCaret: true
8433
+ },
8434
+ {
8435
+ name: "pop-bubble",
8436
+ bubbleClass: "persona-stream-pop",
8437
+ wrap: "none"
8438
+ },
8439
+ {
8440
+ name: "letter-rise",
8441
+ containerClass: "persona-stream-letter-rise",
8442
+ wrap: "char"
8443
+ },
8444
+ {
8445
+ name: "word-fade",
8446
+ containerClass: "persona-stream-word-fade",
8447
+ wrap: "word"
8448
+ }
8449
+ ];
8450
+ var globalRegistry = /* @__PURE__ */ new Map();
8451
+ for (const plugin of BUILTIN_PLUGINS) globalRegistry.set(plugin.name, plugin);
8452
+ var resolveStreamAnimationPlugin = (type, overrides) => {
8453
+ var _a, _b;
8454
+ if (type === "none") return null;
8455
+ if (overrides && Object.prototype.hasOwnProperty.call(overrides, type)) {
8456
+ return (_a = overrides[type]) != null ? _a : null;
8457
+ }
8458
+ return (_b = globalRegistry.get(type)) != null ? _b : null;
8459
+ };
8460
+ var applyStreamBuffer = (content, buffer, plugin, message, streaming) => {
8461
+ if (!streaming) return content;
8462
+ if (plugin == null ? void 0 : plugin.bufferContent) return plugin.bufferContent(content, message);
8463
+ if (!content) return content;
8464
+ if (buffer === "word") {
8465
+ const lastSpace = content.search(/\s(?=\S*$)/);
8466
+ if (lastSpace < 0) return "";
8467
+ return content.slice(0, lastSpace);
8468
+ }
8469
+ if (buffer === "line") {
8470
+ const lastNewline = content.lastIndexOf("\n");
8471
+ if (lastNewline < 0) return "";
8472
+ return content.slice(0, lastNewline);
8473
+ }
8474
+ return content;
8475
+ };
8476
+ var makeCharSpan = (doc, ch, messageId, index) => {
8477
+ const span = doc.createElement("span");
8478
+ span.className = "persona-stream-char";
8479
+ span.id = `stream-c-${messageId}-${index}`;
8480
+ span.style.setProperty("--char-index", String(index));
8481
+ span.textContent = ch;
8482
+ return span;
8483
+ };
8484
+ var makeWordSpan = (doc, word, messageId, index) => {
8485
+ const span = doc.createElement("span");
8486
+ span.className = "persona-stream-word";
8487
+ span.id = `stream-w-${messageId}-${index}`;
8488
+ span.style.setProperty("--word-index", String(index));
8489
+ span.textContent = word;
8490
+ return span;
8491
+ };
8492
+ var WHITESPACE_RE = /\s/;
8493
+ var shouldSkipSubtree = (node, skipTags) => {
8494
+ let current = node.parentNode;
8495
+ while (current) {
8496
+ if (current.nodeType === 1) {
8497
+ const el = current;
8498
+ if (skipTags.has(el.tagName.toLowerCase())) return true;
8499
+ }
8500
+ current = current.parentNode;
8501
+ }
8502
+ return false;
8503
+ };
8504
+ var wrapTextNodeChars = (textNode, messageId, counterRef) => {
8505
+ var _a;
8506
+ const doc = textNode.ownerDocument;
8507
+ const parent = textNode.parentNode;
8508
+ if (!doc || !parent) return;
8509
+ const text = (_a = textNode.nodeValue) != null ? _a : "";
8510
+ if (!text) return;
8511
+ const fragment = doc.createDocumentFragment();
8512
+ let i = 0;
8513
+ while (i < text.length) {
8514
+ if (WHITESPACE_RE.test(text[i])) {
8515
+ let j = i;
8516
+ while (j < text.length && WHITESPACE_RE.test(text[j])) j += 1;
8517
+ fragment.appendChild(doc.createTextNode(text.slice(i, j)));
8518
+ i = j;
8519
+ } else {
8520
+ const group = doc.createElement("span");
8521
+ group.className = "persona-stream-word-group";
8522
+ let j = i;
8523
+ while (j < text.length && !WHITESPACE_RE.test(text[j])) {
8524
+ group.appendChild(makeCharSpan(doc, text[j], messageId, counterRef.value));
8525
+ counterRef.value += 1;
8526
+ j += 1;
8527
+ }
8528
+ fragment.appendChild(group);
8529
+ i = j;
8530
+ }
8531
+ }
8532
+ parent.replaceChild(fragment, textNode);
8533
+ };
8534
+ var wrapTextNodeWords = (textNode, messageId, counterRef) => {
8535
+ var _a;
8536
+ const doc = textNode.ownerDocument;
8537
+ const parent = textNode.parentNode;
8538
+ if (!doc || !parent) return;
8539
+ const text = (_a = textNode.nodeValue) != null ? _a : "";
8540
+ if (!text) return;
8541
+ const fragment = doc.createDocumentFragment();
8542
+ const tokens = text.split(/(\s+)/);
8543
+ for (const token of tokens) {
8544
+ if (!token) continue;
8545
+ if (/^\s+$/.test(token)) {
8546
+ fragment.appendChild(doc.createTextNode(token));
8547
+ } else {
8548
+ fragment.appendChild(makeWordSpan(doc, token, messageId, counterRef.value));
8549
+ counterRef.value += 1;
8550
+ }
8551
+ }
8552
+ parent.replaceChild(fragment, textNode);
8553
+ };
8554
+ var wrapStreamAnimation = (html, mode, messageId, options) => {
8555
+ var _a;
8556
+ if (!html) return html;
8557
+ if (typeof document === "undefined") return html;
8558
+ const scratch = document.createElement("div");
8559
+ scratch.innerHTML = html;
8560
+ const skipTags = new Set(((_a = options == null ? void 0 : options.skipTags) != null ? _a : DEFAULT_SKIP_TAGS).map((t) => t.toLowerCase()));
8561
+ const walker = document.createTreeWalker(scratch, NodeFilter.SHOW_TEXT, null);
8562
+ const textNodes = [];
8563
+ let node = walker.nextNode();
8564
+ while (node) {
8565
+ if (!shouldSkipSubtree(node, skipTags)) {
8566
+ textNodes.push(node);
8567
+ }
8568
+ node = walker.nextNode();
8569
+ }
8570
+ const counterRef = { value: 0 };
8571
+ const wrap = mode === "char" ? wrapTextNodeChars : wrapTextNodeWords;
8572
+ for (const textNode of textNodes) {
8573
+ wrap(textNode, messageId, counterRef);
8574
+ }
8575
+ return scratch.innerHTML;
8576
+ };
8577
+ var createStreamCaret = (doc = document) => {
8578
+ const caret = doc.createElement("span");
8579
+ caret.className = "persona-stream-caret";
8580
+ caret.setAttribute("aria-hidden", "true");
8581
+ caret.setAttribute("data-preserve-animation", "stream-caret");
8582
+ return caret;
8583
+ };
8584
+ var createSkeletonPlaceholder = (doc = document) => {
8585
+ const wrapper = doc.createElement("div");
8586
+ wrapper.className = "persona-stream-skeleton";
8587
+ wrapper.setAttribute("data-preserve-animation", "stream-skeleton");
8588
+ wrapper.setAttribute("aria-hidden", "true");
8589
+ const line = doc.createElement("div");
8590
+ line.className = "persona-stream-skeleton-line";
8591
+ wrapper.appendChild(line);
8592
+ return wrapper;
8593
+ };
8594
+ var injectedStyleRoots = /* @__PURE__ */ new WeakMap();
8595
+ var injectPluginStyles = (plugin, root) => {
8596
+ var _a;
8597
+ if (!plugin.styles) return;
8598
+ let names = injectedStyleRoots.get(root);
8599
+ if (!names) {
8600
+ names = /* @__PURE__ */ new Set();
8601
+ injectedStyleRoots.set(root, names);
8602
+ }
8603
+ if (names.has(plugin.name)) {
8604
+ const escaped = plugin.name.replace(/["\\]/g, "\\$&");
8605
+ const existing = root.querySelector(
8606
+ `style[data-persona-animation="${escaped}"]`
8607
+ );
8608
+ if (existing) return;
8609
+ names.delete(plugin.name);
8610
+ }
8611
+ names.add(plugin.name);
8612
+ const doc = root instanceof ShadowRoot ? root.ownerDocument : (_a = root.ownerDocument) != null ? _a : document;
8613
+ const style = doc.createElement("style");
8614
+ style.setAttribute("data-persona-animation", plugin.name);
8615
+ style.textContent = plugin.styles;
8616
+ root.appendChild(style);
8617
+ };
8618
+ var attachedCleanups = /* @__PURE__ */ new WeakMap();
8619
+ var attachPlugin = (plugin, root) => {
8620
+ if (!plugin.onAttach) return;
8621
+ let cleanups = attachedCleanups.get(root);
8622
+ if (!cleanups) {
8623
+ cleanups = /* @__PURE__ */ new Map();
8624
+ attachedCleanups.set(root, cleanups);
8625
+ }
8626
+ if (cleanups.has(plugin.name)) return;
8627
+ const cleanup = plugin.onAttach(root);
8628
+ cleanups.set(plugin.name, cleanup);
8629
+ };
8630
+ var detachAllPlugins = (root) => {
8631
+ const cleanups = attachedCleanups.get(root);
8632
+ if (!cleanups) return;
8633
+ for (const cleanup of cleanups.values()) {
8634
+ if (typeof cleanup === "function") cleanup();
8635
+ }
8636
+ cleanups.clear();
8637
+ };
8638
+ var ensurePluginActive = (plugin, root) => {
8639
+ injectPluginStyles(plugin, root);
8640
+ attachPlugin(plugin, root);
8641
+ };
8642
+
8291
8643
  // src/utils/overlay-host-stacking.ts
8292
8644
  function syncOverlayHostStacking(host, zIndex = DEFAULT_OVERLAY_Z_INDEX) {
8293
8645
  const originalPosition = host.style.position;
@@ -9373,7 +9725,7 @@ var buildHeaderWithLayout = (config, layoutConfig, context) => {
9373
9725
 
9374
9726
  // src/components/composer-builder.ts
9375
9727
  var buildComposer = (context) => {
9376
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A;
9728
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E;
9377
9729
  const { config } = context;
9378
9730
  const footer = createElement(
9379
9731
  "div",
@@ -9429,9 +9781,13 @@ var buildComposer = (context) => {
9429
9781
  const useIcon = (_d = sendButtonConfig.useIcon) != null ? _d : false;
9430
9782
  const iconText = (_e = sendButtonConfig.iconText) != null ? _e : "\u2191";
9431
9783
  const iconName = sendButtonConfig.iconName;
9432
- const tooltipText = (_f = sendButtonConfig.tooltipText) != null ? _f : "Send message";
9433
- const showTooltip = (_g = sendButtonConfig.showTooltip) != null ? _g : false;
9434
- const buttonSize = (_h = sendButtonConfig.size) != null ? _h : "40px";
9784
+ const stopIconName = (_f = sendButtonConfig.stopIconName) != null ? _f : "square";
9785
+ const tooltipText = (_g = sendButtonConfig.tooltipText) != null ? _g : "Send message";
9786
+ const stopTooltipText = (_h = sendButtonConfig.stopTooltipText) != null ? _h : "Stop generating";
9787
+ const sendLabel = (_j = (_i = config == null ? void 0 : config.copy) == null ? void 0 : _i.sendButtonLabel) != null ? _j : "Send";
9788
+ const stopLabel = (_l = (_k = config == null ? void 0 : config.copy) == null ? void 0 : _k.stopButtonLabel) != null ? _l : "Stop";
9789
+ const showTooltip = (_m = sendButtonConfig.showTooltip) != null ? _m : false;
9790
+ const buttonSize = (_n = sendButtonConfig.size) != null ? _n : "40px";
9435
9791
  const backgroundColor = sendButtonConfig.backgroundColor;
9436
9792
  const textColor = sendButtonConfig.textColor;
9437
9793
  const sendButtonWrapper = createElement("div", "persona-send-button-wrapper");
@@ -9441,6 +9797,8 @@ var buildComposer = (context) => {
9441
9797
  );
9442
9798
  sendButton.type = "submit";
9443
9799
  sendButton.setAttribute("data-persona-composer-submit", "");
9800
+ let sendIcon = null;
9801
+ let stopIcon = null;
9444
9802
  if (useIcon) {
9445
9803
  sendButton.style.width = buttonSize;
9446
9804
  sendButton.style.height = buttonSize;
@@ -9454,25 +9812,26 @@ var buildComposer = (context) => {
9454
9812
  } else {
9455
9813
  sendButton.style.color = "var(--persona-button-primary-fg, #ffffff)";
9456
9814
  }
9815
+ const iconSize = parseFloat(buttonSize) || 24;
9816
+ const iconColor = (textColor == null ? void 0 : textColor.trim()) || "currentColor";
9457
9817
  if (iconName) {
9458
- const iconSize = parseFloat(buttonSize) || 24;
9459
- const iconColor = (textColor == null ? void 0 : textColor.trim()) || "currentColor";
9460
- const iconSvg = renderLucideIcon(iconName, iconSize, iconColor, 2);
9461
- if (iconSvg) {
9462
- sendButton.appendChild(iconSvg);
9818
+ sendIcon = renderLucideIcon(iconName, iconSize, iconColor, 2);
9819
+ if (sendIcon) {
9820
+ sendButton.appendChild(sendIcon);
9463
9821
  } else {
9464
9822
  sendButton.textContent = iconText;
9465
9823
  }
9466
9824
  } else {
9467
9825
  sendButton.textContent = iconText;
9468
9826
  }
9827
+ stopIcon = renderLucideIcon(stopIconName, iconSize, iconColor, 2);
9469
9828
  if (backgroundColor) {
9470
9829
  sendButton.style.backgroundColor = backgroundColor;
9471
9830
  } else {
9472
9831
  sendButton.classList.add("persona-bg-persona-primary");
9473
9832
  }
9474
9833
  } else {
9475
- sendButton.textContent = (_j = (_i = config == null ? void 0 : config.copy) == null ? void 0 : _i.sendButtonLabel) != null ? _j : "Send";
9834
+ sendButton.textContent = sendLabel;
9476
9835
  if (textColor) {
9477
9836
  sendButton.style.color = textColor;
9478
9837
  } else {
@@ -9500,18 +9859,43 @@ var buildComposer = (context) => {
9500
9859
  sendButton.style.paddingTop = "";
9501
9860
  sendButton.style.paddingBottom = "";
9502
9861
  }
9862
+ let sendTooltip = null;
9503
9863
  if (showTooltip && tooltipText) {
9504
- const tooltip = createElement("div", "persona-send-button-tooltip");
9505
- tooltip.textContent = tooltipText;
9506
- sendButtonWrapper.appendChild(tooltip);
9864
+ sendTooltip = createElement("div", "persona-send-button-tooltip");
9865
+ sendTooltip.textContent = tooltipText;
9866
+ sendButtonWrapper.appendChild(sendTooltip);
9507
9867
  }
9868
+ sendButton.setAttribute("aria-label", tooltipText);
9508
9869
  sendButtonWrapper.appendChild(sendButton);
9509
- const voiceRecognitionConfig = (_k = config == null ? void 0 : config.voiceRecognition) != null ? _k : {};
9870
+ let currentMode = "send";
9871
+ const setSendButtonMode = (mode) => {
9872
+ if (mode === currentMode) return;
9873
+ currentMode = mode;
9874
+ const label = mode === "stop" ? stopTooltipText : tooltipText;
9875
+ sendButton.setAttribute("aria-label", label);
9876
+ if (sendTooltip) {
9877
+ sendTooltip.textContent = label;
9878
+ }
9879
+ if (useIcon) {
9880
+ if (sendIcon && stopIcon) {
9881
+ const next = mode === "stop" ? stopIcon : sendIcon;
9882
+ const prev = mode === "stop" ? sendIcon : stopIcon;
9883
+ if (prev.parentNode === sendButton) {
9884
+ sendButton.replaceChild(next, prev);
9885
+ } else {
9886
+ sendButton.appendChild(next);
9887
+ }
9888
+ }
9889
+ } else {
9890
+ sendButton.textContent = mode === "stop" ? stopLabel : sendLabel;
9891
+ }
9892
+ };
9893
+ const voiceRecognitionConfig = (_o = config == null ? void 0 : config.voiceRecognition) != null ? _o : {};
9510
9894
  const voiceRecognitionEnabled = voiceRecognitionConfig.enabled === true;
9511
9895
  let micButton = null;
9512
9896
  let micButtonWrapper = null;
9513
9897
  const hasSpeechRecognition = typeof window !== "undefined" && (typeof window.webkitSpeechRecognition !== "undefined" || typeof window.SpeechRecognition !== "undefined");
9514
- const hasRuntypeProvider = ((_l = voiceRecognitionConfig.provider) == null ? void 0 : _l.type) === "runtype";
9898
+ const hasRuntypeProvider = ((_p = voiceRecognitionConfig.provider) == null ? void 0 : _p.type) === "runtype";
9515
9899
  const hasVoiceInput = hasSpeechRecognition || hasRuntypeProvider;
9516
9900
  if (voiceRecognitionEnabled && hasVoiceInput) {
9517
9901
  micButtonWrapper = createElement("div", "persona-send-button-wrapper");
@@ -9522,11 +9906,11 @@ var buildComposer = (context) => {
9522
9906
  micButton.type = "button";
9523
9907
  micButton.setAttribute("data-persona-composer-mic", "");
9524
9908
  micButton.setAttribute("aria-label", "Start voice recognition");
9525
- const micIconName = (_m = voiceRecognitionConfig.iconName) != null ? _m : "mic";
9526
- const micIconSize = (_n = voiceRecognitionConfig.iconSize) != null ? _n : buttonSize;
9909
+ const micIconName = (_q = voiceRecognitionConfig.iconName) != null ? _q : "mic";
9910
+ const micIconSize = (_r = voiceRecognitionConfig.iconSize) != null ? _r : buttonSize;
9527
9911
  const micIconSizeNum = parseFloat(micIconSize) || 24;
9528
- const micBackgroundColor = (_o = voiceRecognitionConfig.backgroundColor) != null ? _o : backgroundColor;
9529
- const micIconColor = (_p = voiceRecognitionConfig.iconColor) != null ? _p : textColor;
9912
+ const micBackgroundColor = (_s = voiceRecognitionConfig.backgroundColor) != null ? _s : backgroundColor;
9913
+ const micIconColor = (_t = voiceRecognitionConfig.iconColor) != null ? _t : textColor;
9530
9914
  micButton.style.width = micIconSize;
9531
9915
  micButton.style.height = micIconSize;
9532
9916
  micButton.style.minWidth = micIconSize;
@@ -9569,15 +9953,15 @@ var buildComposer = (context) => {
9569
9953
  micButton.style.paddingBottom = voiceRecognitionConfig.paddingY;
9570
9954
  }
9571
9955
  micButtonWrapper.appendChild(micButton);
9572
- const micTooltipText = (_q = voiceRecognitionConfig.tooltipText) != null ? _q : "Start voice recognition";
9573
- const showMicTooltip = (_r = voiceRecognitionConfig.showTooltip) != null ? _r : false;
9956
+ const micTooltipText = (_u = voiceRecognitionConfig.tooltipText) != null ? _u : "Start voice recognition";
9957
+ const showMicTooltip = (_v = voiceRecognitionConfig.showTooltip) != null ? _v : false;
9574
9958
  if (showMicTooltip && micTooltipText) {
9575
9959
  const tooltip = createElement("div", "persona-send-button-tooltip");
9576
9960
  tooltip.textContent = micTooltipText;
9577
9961
  micButtonWrapper.appendChild(tooltip);
9578
9962
  }
9579
9963
  }
9580
- const attachmentsConfig = (_s = config == null ? void 0 : config.attachments) != null ? _s : {};
9964
+ const attachmentsConfig = (_w = config == null ? void 0 : config.attachments) != null ? _w : {};
9581
9965
  const attachmentsEnabled = attachmentsConfig.enabled === true;
9582
9966
  let attachmentButton = null;
9583
9967
  let attachmentButtonWrapper = null;
@@ -9591,8 +9975,8 @@ var buildComposer = (context) => {
9591
9975
  attachmentPreviewsContainer.style.display = "none";
9592
9976
  attachmentInput = createElement("input");
9593
9977
  attachmentInput.type = "file";
9594
- attachmentInput.accept = ((_t = attachmentsConfig.allowedTypes) != null ? _t : ALL_SUPPORTED_MIME_TYPES).join(",");
9595
- attachmentInput.multiple = ((_u = attachmentsConfig.maxFiles) != null ? _u : 4) > 1;
9978
+ attachmentInput.accept = ((_x = attachmentsConfig.allowedTypes) != null ? _x : ALL_SUPPORTED_MIME_TYPES).join(",");
9979
+ attachmentInput.multiple = ((_y = attachmentsConfig.maxFiles) != null ? _y : 4) > 1;
9596
9980
  attachmentInput.style.display = "none";
9597
9981
  attachmentInput.setAttribute("aria-label", "Attach files");
9598
9982
  attachmentButtonWrapper = createElement("div", "persona-send-button-wrapper");
@@ -9601,8 +9985,8 @@ var buildComposer = (context) => {
9601
9985
  "persona-rounded-button persona-flex persona-items-center persona-justify-center disabled:persona-opacity-50 persona-cursor-pointer persona-attachment-button"
9602
9986
  );
9603
9987
  attachmentButton.type = "button";
9604
- attachmentButton.setAttribute("aria-label", (_v = attachmentsConfig.buttonTooltipText) != null ? _v : "Attach file");
9605
- const attachIconName = (_w = attachmentsConfig.buttonIconName) != null ? _w : "paperclip";
9988
+ attachmentButton.setAttribute("aria-label", (_z = attachmentsConfig.buttonTooltipText) != null ? _z : "Attach file");
9989
+ const attachIconName = (_A = attachmentsConfig.buttonIconName) != null ? _A : "paperclip";
9606
9990
  const attachIconSize = buttonSize;
9607
9991
  const buttonSizeNum = parseFloat(attachIconSize) || 40;
9608
9992
  const attachIconSizeNum = Math.round(buttonSizeNum * 0.6);
@@ -9639,7 +10023,7 @@ var buildComposer = (context) => {
9639
10023
  attachmentInput == null ? void 0 : attachmentInput.click();
9640
10024
  });
9641
10025
  attachmentButtonWrapper.appendChild(attachmentButton);
9642
- const attachTooltipText = (_x = attachmentsConfig.buttonTooltipText) != null ? _x : "Attach file";
10026
+ const attachTooltipText = (_B = attachmentsConfig.buttonTooltipText) != null ? _B : "Attach file";
9643
10027
  const tooltip = createElement("div", "persona-send-button-tooltip");
9644
10028
  tooltip.textContent = attachTooltipText;
9645
10029
  attachmentButtonWrapper.appendChild(tooltip);
@@ -9669,16 +10053,16 @@ var buildComposer = (context) => {
9669
10053
  rightActions.append(sendButtonWrapper);
9670
10054
  actionsRow.append(leftActions, rightActions);
9671
10055
  composerForm.append(actionsRow);
9672
- const statusConfig = (_y = config == null ? void 0 : config.statusIndicator) != null ? _y : {};
10056
+ const statusConfig = (_C = config == null ? void 0 : config.statusIndicator) != null ? _C : {};
9673
10057
  const alignClass = statusConfig.align === "left" ? "persona-text-left" : statusConfig.align === "center" ? "persona-text-center" : "persona-text-right";
9674
10058
  const statusText = createElement(
9675
10059
  "div",
9676
10060
  `persona-mt-2 ${alignClass} persona-text-xs persona-text-persona-muted`
9677
10061
  );
9678
10062
  statusText.setAttribute("data-persona-composer-status", "");
9679
- const isVisible = (_z = statusConfig.visible) != null ? _z : true;
10063
+ const isVisible = (_D = statusConfig.visible) != null ? _D : true;
9680
10064
  statusText.style.display = isVisible ? "" : "none";
9681
- const idleLabel = (_A = statusConfig.idleText) != null ? _A : "Online";
10065
+ const idleLabel = (_E = statusConfig.idleText) != null ? _E : "Online";
9682
10066
  if (statusConfig.idleLink) {
9683
10067
  const link = createElement("a");
9684
10068
  link.href = statusConfig.idleLink;
@@ -9710,7 +10094,8 @@ var buildComposer = (context) => {
9710
10094
  // Actions row layout elements
9711
10095
  actionsRow,
9712
10096
  leftActions,
9713
- rightActions
10097
+ rightActions,
10098
+ setSendButtonMode
9714
10099
  };
9715
10100
  };
9716
10101
 
@@ -9858,11 +10243,48 @@ var buildPanel = (config, showClose = true) => {
9858
10243
  // Actions row layout elements
9859
10244
  actionsRow: composerElements.actionsRow,
9860
10245
  leftActions: composerElements.leftActions,
9861
- rightActions: composerElements.rightActions
10246
+ rightActions: composerElements.rightActions,
10247
+ setSendButtonMode: composerElements.setSendButtonMode
9862
10248
  };
9863
10249
  };
9864
10250
 
9865
10251
  // src/components/message-bubble.ts
10252
+ var getDefaultStopReasonNoticeCopy = (stopReason) => {
10253
+ switch (stopReason) {
10254
+ case "max_tool_calls":
10255
+ return "Stopped after calling a tool. Send a follow-up to continue.";
10256
+ case "length":
10257
+ return "Response cut off as max tokens reached. Ask for more to continue.";
10258
+ case "content_filter":
10259
+ return "The provider filtered this response.";
10260
+ case "error":
10261
+ return "Something went wrong generating this response.";
10262
+ case "end_turn":
10263
+ case "unknown":
10264
+ default:
10265
+ return null;
10266
+ }
10267
+ };
10268
+ var resolveStopReasonNoticeText = (stopReason, overrides) => {
10269
+ if (!stopReason) return null;
10270
+ const fallback = getDefaultStopReasonNoticeCopy(stopReason);
10271
+ if (fallback === null) return null;
10272
+ const override = overrides == null ? void 0 : overrides[stopReason];
10273
+ const text = override !== void 0 ? override : fallback;
10274
+ if (!text) return null;
10275
+ return text;
10276
+ };
10277
+ var createStopReasonNotice = (stopReason, text) => {
10278
+ const notice = createElement(
10279
+ "div",
10280
+ "persona-message-stop-reason persona-text-xs persona-mt-2 persona-italic"
10281
+ );
10282
+ notice.setAttribute("data-stop-reason", stopReason);
10283
+ notice.setAttribute("role", "note");
10284
+ notice.style.opacity = "0.75";
10285
+ notice.textContent = text;
10286
+ return notice;
10287
+ };
9866
10288
  var isSafeImageSrc = (src) => {
9867
10289
  const lower = src.toLowerCase();
9868
10290
  if (lower.startsWith("data:image/svg+xml")) return false;
@@ -10165,7 +10587,7 @@ var createMessageActions = (message, actionsConfig, _callbacks) => {
10165
10587
  return container;
10166
10588
  };
10167
10589
  var createStandardBubble = (message, transform, layoutConfig, actionsConfig, actionCallbacks, options) => {
10168
- var _a, _b, _c, _d, _e, _f, _g;
10590
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
10169
10591
  const config = layoutConfig != null ? layoutConfig : {};
10170
10592
  const layout = (_a = config.layout) != null ? _a : "bubble";
10171
10593
  const avatarConfig = config.avatar;
@@ -10190,22 +10612,73 @@ var createStandardBubble = (message, transform, layoutConfig, actionsConfig, act
10190
10612
  const messageContentText = (_g = (_f = message.content) == null ? void 0 : _f.trim()) != null ? _g : "";
10191
10613
  const isImageOnlyFallbackMessage = imageParts.length > 0 && messageContentText === IMAGE_ONLY_MESSAGE_FALLBACK_TEXT;
10192
10614
  const shouldHideTextUntilPreviewFails = isImageOnlyFallbackMessage;
10615
+ const streamAnimation = resolveStreamAnimation(
10616
+ (_i = (_h = options == null ? void 0 : options.widgetConfig) == null ? void 0 : _h.features) == null ? void 0 : _i.streamAnimation
10617
+ );
10618
+ const streamPluginOverrides = (_l = (_k = (_j = options == null ? void 0 : options.widgetConfig) == null ? void 0 : _j.features) == null ? void 0 : _k.streamAnimation) == null ? void 0 : _l.plugins;
10619
+ const streamPlugin = message.role === "assistant" && streamAnimation.type !== "none" ? resolveStreamAnimationPlugin(streamAnimation.type, streamPluginOverrides) : null;
10620
+ const pluginStillAnimating = message.role === "assistant" && ((_m = streamPlugin == null ? void 0 : streamPlugin.isAnimating) == null ? void 0 : _m.call(streamPlugin, message)) === true;
10621
+ const streamAnimationActive = message.role === "assistant" && streamPlugin !== null && (Boolean(message.streaming) || pluginStillAnimating);
10622
+ if (streamAnimationActive && (streamPlugin == null ? void 0 : streamPlugin.bubbleClass)) {
10623
+ bubble.classList.add(streamPlugin.bubbleClass);
10624
+ }
10193
10625
  const contentDiv = document.createElement("div");
10194
10626
  contentDiv.classList.add("persona-message-content");
10627
+ if (streamAnimationActive && streamPlugin) {
10628
+ if (streamPlugin.containerClass) {
10629
+ contentDiv.classList.add(streamPlugin.containerClass);
10630
+ }
10631
+ contentDiv.style.setProperty("--persona-stream-step", `${streamAnimation.speed}ms`);
10632
+ contentDiv.style.setProperty("--persona-stream-duration", `${streamAnimation.duration}ms`);
10633
+ }
10634
+ const bufferedContent = streamAnimationActive ? applyStreamBuffer(
10635
+ (_n = message.content) != null ? _n : "",
10636
+ streamAnimation.buffer,
10637
+ streamPlugin,
10638
+ message,
10639
+ Boolean(message.streaming)
10640
+ ) : (_o = message.content) != null ? _o : "";
10195
10641
  const transformedContent = transform({
10196
- text: message.content,
10642
+ text: bufferedContent,
10197
10643
  message,
10198
10644
  streaming: Boolean(message.streaming),
10199
10645
  raw: message.rawContent
10200
10646
  });
10647
+ let animatedContent = transformedContent;
10648
+ if (streamAnimationActive && (streamPlugin == null ? void 0 : streamPlugin.wrap) === "char") {
10649
+ animatedContent = wrapStreamAnimation(transformedContent, "char", message.id, {
10650
+ skipTags: streamPlugin.skipTags
10651
+ });
10652
+ } else if (streamAnimationActive && (streamPlugin == null ? void 0 : streamPlugin.wrap) === "word") {
10653
+ animatedContent = wrapStreamAnimation(transformedContent, "word", message.id, {
10654
+ skipTags: streamPlugin.skipTags
10655
+ });
10656
+ }
10201
10657
  let textContentDiv = null;
10202
10658
  if (shouldHideTextUntilPreviewFails) {
10203
10659
  textContentDiv = document.createElement("div");
10204
- textContentDiv.innerHTML = transformedContent;
10660
+ textContentDiv.innerHTML = animatedContent;
10205
10661
  textContentDiv.style.display = "none";
10206
10662
  contentDiv.appendChild(textContentDiv);
10207
10663
  } else {
10208
- contentDiv.innerHTML = transformedContent;
10664
+ contentDiv.innerHTML = animatedContent;
10665
+ }
10666
+ if (streamAnimationActive && (streamPlugin == null ? void 0 : streamPlugin.useCaret) && !shouldHideTextUntilPreviewFails && messageContentText) {
10667
+ const caret = createStreamCaret();
10668
+ const spans = contentDiv.querySelectorAll(
10669
+ ".persona-stream-char, .persona-stream-word"
10670
+ );
10671
+ const lastSpan = spans[spans.length - 1];
10672
+ if (lastSpan == null ? void 0 : lastSpan.parentNode) {
10673
+ lastSpan.parentNode.insertBefore(caret, lastSpan.nextSibling);
10674
+ } else {
10675
+ const lastChild = contentDiv.lastElementChild;
10676
+ if (lastChild) {
10677
+ lastChild.appendChild(caret);
10678
+ } else {
10679
+ contentDiv.appendChild(caret);
10680
+ }
10681
+ }
10209
10682
  }
10210
10683
  if (showTimestamp && timestampPosition === "inline" && message.createdAt) {
10211
10684
  const timestamp = createTimestamp(message, timestampConfig);
@@ -10234,18 +10707,37 @@ var createStandardBubble = (message, transform, layoutConfig, actionsConfig, act
10234
10707
  timestamp.classList.add("persona-mt-1");
10235
10708
  bubble.appendChild(timestamp);
10236
10709
  }
10710
+ const stopReasonNoticeText = message.role === "assistant" ? resolveStopReasonNoticeText(
10711
+ message.stopReason,
10712
+ (_q = (_p = options == null ? void 0 : options.widgetConfig) == null ? void 0 : _p.copy) == null ? void 0 : _q.stopReasonNotice
10713
+ ) : null;
10237
10714
  if (message.streaming && message.role === "assistant") {
10238
- if (!message.content || !message.content.trim()) {
10239
- const indicator = renderLoadingIndicatorWithFallback(
10240
- "inline",
10241
- options == null ? void 0 : options.loadingIndicatorRenderer,
10242
- options == null ? void 0 : options.widgetConfig
10243
- );
10244
- if (indicator) {
10245
- bubble.appendChild(indicator);
10715
+ const hasVisibleContent = Boolean(bufferedContent && bufferedContent.trim());
10716
+ const skeletonEnabled = streamAnimation.placeholder === "skeleton";
10717
+ const trailSkeleton = skeletonEnabled && streamAnimation.buffer === "line" && hasVisibleContent;
10718
+ if (!hasVisibleContent) {
10719
+ if (skeletonEnabled) {
10720
+ bubble.appendChild(createSkeletonPlaceholder());
10721
+ } else {
10722
+ const indicator = renderLoadingIndicatorWithFallback(
10723
+ "inline",
10724
+ options == null ? void 0 : options.loadingIndicatorRenderer,
10725
+ options == null ? void 0 : options.widgetConfig
10726
+ );
10727
+ if (indicator) {
10728
+ bubble.appendChild(indicator);
10729
+ }
10246
10730
  }
10731
+ } else if (trailSkeleton) {
10732
+ bubble.appendChild(createSkeletonPlaceholder());
10247
10733
  }
10248
10734
  }
10735
+ if (stopReasonNoticeText && message.stopReason && !message.streaming) {
10736
+ if (!messageContentText) {
10737
+ contentDiv.style.display = "none";
10738
+ }
10739
+ bubble.appendChild(createStopReasonNotice(message.stopReason, stopReasonNoticeText));
10740
+ }
10249
10741
  const shouldShowActions = message.role === "assistant" && !message.streaming && message.content && message.content.trim() && (actionsConfig == null ? void 0 : actionsConfig.enabled) !== false;
10250
10742
  if (shouldShowActions && actionsConfig) {
10251
10743
  const actions = createMessageActions(message, actionsConfig, actionCallbacks);
@@ -13916,7 +14408,7 @@ function buildDropOverlay(dropCfg) {
13916
14408
  return overlay;
13917
14409
  }
13918
14410
  var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
13919
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N;
14411
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O;
13920
14412
  if (mount == null) {
13921
14413
  throw new Error(
13922
14414
  'createAgentExperience: mount must be a non-null HTMLElement (e.g. pass document.getElementById("my-root") after the node exists).'
@@ -14128,6 +14620,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
14128
14620
  leftActions,
14129
14621
  rightActions
14130
14622
  } = panelElements;
14623
+ let setSendButtonMode = panelElements.setSendButtonMode;
14131
14624
  let micButton = panelElements.micButton;
14132
14625
  let micButtonWrapper = panelElements.micButtonWrapper;
14133
14626
  let attachmentButton = panelElements.attachmentButton;
@@ -14960,12 +15453,24 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
14960
15453
  const panelBorder = resolvePanelChrome(panelPartial == null ? void 0 : panelPartial.border, defaultPanelBorder);
14961
15454
  const panelShadow = resolvePanelChrome(panelPartial == null ? void 0 : panelPartial.shadow, defaultPanelShadow);
14962
15455
  const panelBorderRadius = resolvePanelChrome(panelPartial == null ? void 0 : panelPartial.borderRadius, defaultPanelBorderRadius);
15456
+ const prevBodyScrollTop = body.scrollTop;
14963
15457
  mount.style.cssText = "";
14964
15458
  wrapper.style.cssText = "";
14965
15459
  panel.style.cssText = "";
14966
15460
  container.style.cssText = "";
14967
15461
  body.style.cssText = "";
14968
15462
  footer.style.cssText = "";
15463
+ const restoreBodyScrollTop = () => {
15464
+ var _a3;
15465
+ if (prevBodyScrollTop <= 0) return;
15466
+ const ownerWindow3 = (_a3 = body.ownerDocument.defaultView) != null ? _a3 : window;
15467
+ ownerWindow3.requestAnimationFrame(() => {
15468
+ if (body.scrollTop === prevBodyScrollTop) return;
15469
+ const maxScrollTop = body.scrollHeight - body.clientHeight;
15470
+ if (maxScrollTop <= 0) return;
15471
+ body.scrollTop = Math.min(prevBodyScrollTop, maxScrollTop);
15472
+ });
15473
+ };
14969
15474
  if (shouldGoFullscreen) {
14970
15475
  wrapper.classList.remove(
14971
15476
  "persona-bottom-6",
@@ -15021,6 +15526,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15021
15526
  body.style.overflowY = "auto";
15022
15527
  footer.style.flexShrink = "0";
15023
15528
  wasMobileFullscreen = true;
15529
+ restoreBodyScrollTop();
15024
15530
  return;
15025
15531
  }
15026
15532
  const launcherWidth = (_r2 = (_q2 = config == null ? void 0 : config.launcher) == null ? void 0 : _q2.width) != null ? _r2 : config == null ? void 0 : config.launcherWidth;
@@ -15163,6 +15669,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15163
15669
  const zIndexStyles = !sidebarMode ? `z-index: ${(_w2 = (_v2 = config.launcher) == null ? void 0 : _v2.zIndex) != null ? _w2 : DEFAULT_OVERLAY_Z_INDEX} !important;` : "";
15164
15670
  wrapper.style.cssText += maxHeightStyles + paddingStyles + zIndexStyles;
15165
15671
  }
15672
+ restoreBodyScrollTop();
15166
15673
  };
15167
15674
  applyFullHeightStyles();
15168
15675
  applyThemeVariables(mount, config);
@@ -15226,6 +15733,17 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15226
15733
  cleanupThemeObserver = null;
15227
15734
  }
15228
15735
  });
15736
+ const streamAnimationConfig = (_D = config.features) == null ? void 0 : _D.streamAnimation;
15737
+ if ((streamAnimationConfig == null ? void 0 : streamAnimationConfig.type) && streamAnimationConfig.type !== "none") {
15738
+ const plugin = resolveStreamAnimationPlugin(
15739
+ streamAnimationConfig.type,
15740
+ streamAnimationConfig.plugins
15741
+ );
15742
+ if (plugin) {
15743
+ ensurePluginActive(plugin, mount);
15744
+ destroyCallbacks.push(() => detachAllPlugins(mount));
15745
+ }
15746
+ }
15229
15747
  const suggestionsManager = createSuggestions(suggestions);
15230
15748
  let closeHandler = null;
15231
15749
  let session;
@@ -15247,7 +15765,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15247
15765
  lastUserMessageWasVoice: false,
15248
15766
  lastUserMessageId: null
15249
15767
  };
15250
- const voiceAutoResumeMode = (_E = (_D = config.voiceRecognition) == null ? void 0 : _D.autoResume) != null ? _E : false;
15768
+ const voiceAutoResumeMode = (_F = (_E = config.voiceRecognition) == null ? void 0 : _E.autoResume) != null ? _F : false;
15251
15769
  const emitVoiceState = (source) => {
15252
15770
  eventBus.emit("voice:state", {
15253
15771
  active: voiceState.active,
@@ -15935,7 +16453,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15935
16453
  });
15936
16454
  };
15937
16455
  const setComposerDisabled = (disabled) => {
15938
- sendButton.disabled = disabled;
16456
+ setSendButtonMode(disabled ? "stop" : "send");
15939
16457
  if (micButton) {
15940
16458
  micButton.disabled = disabled;
15941
16459
  }
@@ -15975,7 +16493,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15975
16493
  }
15976
16494
  }
15977
16495
  const useIcon = (_i2 = (_h2 = config.sendButton) == null ? void 0 : _h2.useIcon) != null ? _i2 : false;
15978
- if (!useIcon) {
16496
+ if (!useIcon && !(session == null ? void 0 : session.isStreaming())) {
15979
16497
  sendButton.textContent = (_k2 = (_j2 = config.copy) == null ? void 0 : _j2.sendButtonLabel) != null ? _k2 : "Send";
15980
16498
  }
15981
16499
  textarea.style.fontFamily = 'var(--persona-input-font-family, var(--persona-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif))';
@@ -16094,7 +16612,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16094
16612
  }
16095
16613
  });
16096
16614
  sessionRef.current = session;
16097
- if (((_G = (_F = config.voiceRecognition) == null ? void 0 : _F.provider) == null ? void 0 : _G.type) === "runtype") {
16615
+ if (((_H = (_G = config.voiceRecognition) == null ? void 0 : _G.provider) == null ? void 0 : _H.type) === "runtype") {
16098
16616
  try {
16099
16617
  session.setupVoice();
16100
16618
  } catch (err) {
@@ -16142,6 +16660,10 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16142
16660
  const handleSubmit = (event) => {
16143
16661
  var _a2;
16144
16662
  event.preventDefault();
16663
+ if (session.isStreaming()) {
16664
+ session.cancel();
16665
+ return;
16666
+ }
16145
16667
  const value = textarea.value.trim();
16146
16668
  const hasAttachments = (_a2 = attachmentManager == null ? void 0 : attachmentManager.hasAttachments()) != null ? _a2 : false;
16147
16669
  if (!value && !hasAttachments) return;
@@ -16696,7 +17218,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16696
17218
  }
16697
17219
  };
16698
17220
  recalcPanelHeight();
16699
- const ownerWindow = (_H = mount.ownerDocument.defaultView) != null ? _H : window;
17221
+ const ownerWindow = (_I = mount.ownerDocument.defaultView) != null ? _I : window;
16700
17222
  ownerWindow.addEventListener("resize", recalcPanelHeight);
16701
17223
  destroyCallbacks.push(() => ownerWindow.removeEventListener("resize", recalcPanelHeight));
16702
17224
  if (typeof ResizeObserver !== "undefined") {
@@ -16707,15 +17229,19 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16707
17229
  destroyCallbacks.push(() => footerResizeObserver.disconnect());
16708
17230
  }
16709
17231
  lastScrollTop = body.scrollTop;
17232
+ let lastScrollHeight = body.scrollHeight;
16710
17233
  const handleScroll = () => {
16711
17234
  const scrollTop = body.scrollTop;
17235
+ const currentScrollHeight = body.scrollHeight;
17236
+ const scrollHeightShrank = currentScrollHeight < lastScrollHeight;
17237
+ lastScrollHeight = currentScrollHeight;
16712
17238
  const { action, nextLastScrollTop } = resolveFollowStateFromScroll({
16713
17239
  following: autoFollow.isFollowing(),
16714
17240
  currentScrollTop: scrollTop,
16715
17241
  lastScrollTop,
16716
17242
  nearBottom: isElementNearBottom(body, BOTTOM_THRESHOLD),
16717
17243
  userScrollThreshold: USER_SCROLL_THRESHOLD,
16718
- isAutoScrolling: isAutoScrolling || hasPendingAutoScroll,
17244
+ isAutoScrolling: isAutoScrolling || hasPendingAutoScroll || scrollHeightShrank,
16719
17245
  pauseOnUpwardScroll: true,
16720
17246
  pauseWhenAwayFromBottom: false,
16721
17247
  resumeRequiresDownwardScroll: true
@@ -16915,7 +17441,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16915
17441
  }
16916
17442
  const controller = {
16917
17443
  update(nextConfig) {
16918
- var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2, _j2, _k2, _l2, _m2, _n2, _o2, _p2, _q2, _r2, _s2, _t2, _u2, _v2, _w2, _x2, _y2, _z2, _A2, _B2, _C2, _D2, _E2, _F2, _G2, _H2, _I2, _J2, _K2, _L2, _M2, _N2, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab;
17444
+ var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2, _j2, _k2, _l2, _m2, _n2, _o2, _p2, _q2, _r2, _s2, _t2, _u2, _v2, _w2, _x2, _y2, _z2, _A2, _B2, _C2, _D2, _E2, _F2, _G2, _H2, _I2, _J2, _K2, _L2, _M2, _N2, _O2, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab;
16919
17445
  const previousToolCallConfig = config.toolCall;
16920
17446
  const previousMessageActions = config.messageActions;
16921
17447
  const previousLayoutMessages = (_a2 = config.layout) == null ? void 0 : _a2.messages;
@@ -17126,7 +17652,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
17126
17652
  }
17127
17653
  const launcher = (_L2 = config.launcher) != null ? _L2 : {};
17128
17654
  const headerIconHidden = (_M2 = launcher.headerIconHidden) != null ? _M2 : false;
17129
- const layoutShowIcon = (_O = (_N2 = config.layout) == null ? void 0 : _N2.header) == null ? void 0 : _O.showIcon;
17655
+ const layoutShowIcon = (_O2 = (_N2 = config.layout) == null ? void 0 : _N2.header) == null ? void 0 : _O2.showIcon;
17130
17656
  const shouldHideIcon = headerIconHidden || layoutShowIcon === false;
17131
17657
  const headerIconName = launcher.headerIconName;
17132
17658
  const headerIconSize = (_P = launcher.headerIconSize) != null ? _P : "48px";
@@ -18197,7 +18723,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
18197
18723
  }
18198
18724
  }
18199
18725
  };
18200
- const shouldExposeDebugApi = ((_I = runtimeOptions == null ? void 0 : runtimeOptions.debugTools) != null ? _I : false) || Boolean(config.debug);
18726
+ const shouldExposeDebugApi = ((_J = runtimeOptions == null ? void 0 : runtimeOptions.debugTools) != null ? _J : false) || Boolean(config.debug);
18201
18727
  if (shouldExposeDebugApi && typeof window !== "undefined") {
18202
18728
  const previousDebug = window.AgentWidgetBrowser;
18203
18729
  const debugApi = {
@@ -18300,9 +18826,9 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
18300
18826
  const voiceKey = `${persistConfig.keyPrefix}widget-voice`;
18301
18827
  const voiceModeKey = `${persistConfig.keyPrefix}widget-voice-mode`;
18302
18828
  if (storage) {
18303
- const wasOpen = ((_J = persistConfig.persist) == null ? void 0 : _J.openState) && storage.getItem(openKey) === "true";
18304
- const wasVoiceActive = ((_K = persistConfig.persist) == null ? void 0 : _K.voiceState) && storage.getItem(voiceKey) === "true";
18305
- const wasInVoiceMode = ((_L = persistConfig.persist) == null ? void 0 : _L.voiceState) && storage.getItem(voiceModeKey) === "true";
18829
+ const wasOpen = ((_K = persistConfig.persist) == null ? void 0 : _K.openState) && storage.getItem(openKey) === "true";
18830
+ const wasVoiceActive = ((_L = persistConfig.persist) == null ? void 0 : _L.voiceState) && storage.getItem(voiceKey) === "true";
18831
+ const wasInVoiceMode = ((_M = persistConfig.persist) == null ? void 0 : _M.voiceState) && storage.getItem(voiceModeKey) === "true";
18306
18832
  if (wasOpen) {
18307
18833
  setTimeout(() => {
18308
18834
  controller.open();
@@ -18319,7 +18845,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
18319
18845
  }, 100);
18320
18846
  }, 0);
18321
18847
  }
18322
- if ((_M = persistConfig.persist) == null ? void 0 : _M.openState) {
18848
+ if ((_N = persistConfig.persist) == null ? void 0 : _N.openState) {
18323
18849
  eventBus.on("widget:opened", () => {
18324
18850
  storage.setItem(openKey, "true");
18325
18851
  });
@@ -18327,7 +18853,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
18327
18853
  storage.setItem(openKey, "false");
18328
18854
  });
18329
18855
  }
18330
- if ((_N = persistConfig.persist) == null ? void 0 : _N.voiceState) {
18856
+ if ((_O = persistConfig.persist) == null ? void 0 : _O.voiceState) {
18331
18857
  eventBus.on("voice:state", (event) => {
18332
18858
  storage.setItem(voiceKey, event.active ? "true" : "false");
18333
18859
  });
@@ -18901,6 +19427,9 @@ function buildSrcdoc(mountId, shellMode, docked, widgetCssPath) {
18901
19427
  var PREVIEW_TRANSCRIPT_PRESET_LABELS = {
18902
19428
  "user-message": "User message",
18903
19429
  "assistant-message": "Assistant message",
19430
+ "assistant-code-block": "Assistant \u2014 code block",
19431
+ "assistant-markdown-table": "Assistant \u2014 markdown table",
19432
+ "assistant-image": "Assistant \u2014 image",
18904
19433
  "reasoning-streaming": "Reasoning (streaming)",
18905
19434
  "reasoning-complete": "Reasoning (complete)",
18906
19435
  "tool-running": "Tool call (running)",
@@ -18927,6 +19456,56 @@ function createPreviewTranscriptEntry(preset, index = 0) {
18927
19456
  content: "Absolutely. I can keep going and explain what happens next.",
18928
19457
  createdAt
18929
19458
  };
19459
+ case "assistant-code-block":
19460
+ return {
19461
+ id: `preview-seq-assistant-code-${suffix}`,
19462
+ role: "assistant",
19463
+ content: [
19464
+ "Here's how you'd wire up a streaming animation:",
19465
+ "",
19466
+ "```ts",
19467
+ "import { createAgentExperience } from '@runtypelabs/persona';",
19468
+ "",
19469
+ "createAgentExperience(el, {",
19470
+ " features: {",
19471
+ ' streamAnimation: { type: "letter-rise", speed: 120 },',
19472
+ " },",
19473
+ "});",
19474
+ "```",
19475
+ "",
19476
+ "Swap the `type` value to try the other presets."
19477
+ ].join("\n"),
19478
+ createdAt
19479
+ };
19480
+ case "assistant-markdown-table":
19481
+ return {
19482
+ id: `preview-seq-assistant-table-${suffix}`,
19483
+ role: "assistant",
19484
+ content: [
19485
+ "Here are the built-in streaming animations at a glance:",
19486
+ "",
19487
+ "| Preset | Wrap unit | Best for |",
19488
+ "| ------------ | --------- | --------------------------- |",
19489
+ "| Typewriter | Character | Classic terminal feel |",
19490
+ "| Letter rise | Character | Soft, staggered entrance |",
19491
+ "| Word fade | Word | Longer-form assistant replies |",
19492
+ "| Pop bubble | Bubble | Short, punchy affirmations |"
19493
+ ].join("\n"),
19494
+ createdAt
19495
+ };
19496
+ case "assistant-image":
19497
+ return {
19498
+ id: `preview-seq-assistant-image-${suffix}`,
19499
+ role: "assistant",
19500
+ content: [
19501
+ "Here's the reference diagram you asked for \u2014 let me know if you'd like a different view:",
19502
+ "",
19503
+ "![Stream animation reference](https://placehold.co/320x200/png?text=Stream+Animation)",
19504
+ "",
19505
+ "The gradient shows how per-unit delays stagger across the reply."
19506
+ ].join("\n"),
19507
+ createdAt
19508
+ };
18930
19509
  case "reasoning-streaming":
18931
19510
  return {
18932
19511
  id: `preview-seq-reasoning-stream-${suffix}`,
@@ -18994,6 +19573,38 @@ function createPreviewTranscriptEntry(preset, index = 0) {
18994
19573
  function appendPreviewTranscriptEntry(messages, preset) {
18995
19574
  return [...messages, createPreviewTranscriptEntry(preset, messages.length)];
18996
19575
  }
19576
+ function presetStreamsText(preset) {
19577
+ return preset === "assistant-message" || preset === "assistant-code-block" || preset === "assistant-markdown-table" || preset === "assistant-image";
19578
+ }
19579
+ function buildTranscriptStreamFrames(preset, suffix, options) {
19580
+ var _a, _b;
19581
+ const completed = createPreviewTranscriptEntry(preset, suffix);
19582
+ if (!presetStreamsText(preset) || typeof completed.content !== "string") {
19583
+ return [{ message: completed, delayMs: 0, done: true }];
19584
+ }
19585
+ const chunkSize = Math.max(1, (_a = options == null ? void 0 : options.chunkSize) != null ? _a : 24);
19586
+ const delayMs = Math.max(0, (_b = options == null ? void 0 : options.delayMs) != null ? _b : 42);
19587
+ const fullText = completed.content;
19588
+ const frames = [];
19589
+ frames.push({
19590
+ message: { ...completed, content: "", streaming: true },
19591
+ delayMs: 0,
19592
+ done: false
19593
+ });
19594
+ for (let i = chunkSize; i < fullText.length; i += chunkSize) {
19595
+ frames.push({
19596
+ message: { ...completed, content: fullText.slice(0, i), streaming: true },
19597
+ delayMs,
19598
+ done: false
19599
+ });
19600
+ }
19601
+ frames.push({
19602
+ message: { ...completed, content: fullText, streaming: false },
19603
+ delayMs,
19604
+ done: true
19605
+ });
19606
+ return frames;
19607
+ }
18997
19608
  var createAdvancedTranscriptPreviewMessages = () => [
18998
19609
  {
18999
19610
  id: "preview-adv-1",
@@ -19443,6 +20054,7 @@ export {
19443
20054
  buildPreviewConfigWithMessages,
19444
20055
  buildShellCss,
19445
20056
  buildSrcdoc,
20057
+ buildTranscriptStreamFrames,
19446
20058
  convertFromPx,
19447
20059
  convertToPx,
19448
20060
  createPreviewMessages,
@@ -19462,6 +20074,7 @@ export {
19462
20074
  normalizeColorValue,
19463
20075
  paletteColorPath,
19464
20076
  parseCssValue,
20077
+ presetStreamsText,
19465
20078
  resolveRoleAssignment,
19466
20079
  resolveThemeColorPath,
19467
20080
  scopeSection,