@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
@@ -80,6 +80,7 @@ __export(theme_editor_exports, {
80
80
  buildPreviewConfigWithMessages: () => buildPreviewConfigWithMessages,
81
81
  buildShellCss: () => buildShellCss,
82
82
  buildSrcdoc: () => buildSrcdoc,
83
+ buildTranscriptStreamFrames: () => buildTranscriptStreamFrames,
83
84
  convertFromPx: () => convertFromPx,
84
85
  convertToPx: () => convertToPx,
85
86
  createPreviewMessages: () => createPreviewMessages,
@@ -99,6 +100,7 @@ __export(theme_editor_exports, {
99
100
  normalizeColorValue: () => normalizeColorValue,
100
101
  paletteColorPath: () => paletteColorPath,
101
102
  parseCssValue: () => parseCssValue,
103
+ presetStreamsText: () => presetStreamsText,
102
104
  resolveRoleAssignment: () => resolveRoleAssignment,
103
105
  resolveThemeColorPath: () => resolveThemeColorPath,
104
106
  scopeSection: () => scopeSection,
@@ -247,6 +249,12 @@ var DEFAULT_WIDGET_CONFIG = {
247
249
  previewMaxLines: 3,
248
250
  expandable: true,
249
251
  loadingAnimation: "none"
252
+ },
253
+ streamAnimation: {
254
+ type: "none",
255
+ placeholder: "none",
256
+ speed: 120,
257
+ duration: 1800
250
258
  }
251
259
  },
252
260
  suggestionChips: [
@@ -349,11 +357,13 @@ function mergeWithDefaults(config) {
349
357
  ...config.voiceRecognition
350
358
  },
351
359
  features: (() => {
352
- var _a2, _b2, _c2, _d2;
360
+ var _a2, _b2, _c2, _d2, _e2, _f2;
353
361
  const da = (_a2 = DEFAULT_WIDGET_CONFIG.features) == null ? void 0 : _a2.artifacts;
354
362
  const ca = (_b2 = config.features) == null ? void 0 : _b2.artifacts;
355
363
  const dsb = (_c2 = DEFAULT_WIDGET_CONFIG.features) == null ? void 0 : _c2.scrollToBottom;
356
364
  const csb = (_d2 = config.features) == null ? void 0 : _d2.scrollToBottom;
365
+ const dsa = (_e2 = DEFAULT_WIDGET_CONFIG.features) == null ? void 0 : _e2.streamAnimation;
366
+ const csa = (_f2 = config.features) == null ? void 0 : _f2.streamAnimation;
357
367
  const mergedArtifacts = da === void 0 && ca === void 0 ? void 0 : {
358
368
  ...da,
359
369
  ...ca,
@@ -366,11 +376,16 @@ function mergeWithDefaults(config) {
366
376
  ...dsb,
367
377
  ...csb
368
378
  };
379
+ const mergedStreamAnimation = dsa === void 0 && csa === void 0 ? void 0 : {
380
+ ...dsa,
381
+ ...csa
382
+ };
369
383
  return {
370
384
  ...DEFAULT_WIDGET_CONFIG.features,
371
385
  ...config.features,
372
386
  ...mergedScrollToBottom !== void 0 ? { scrollToBottom: mergedScrollToBottom } : {},
373
- ...mergedArtifacts !== void 0 ? { artifacts: mergedArtifacts } : {}
387
+ ...mergedArtifacts !== void 0 ? { artifacts: mergedArtifacts } : {},
388
+ ...mergedStreamAnimation !== void 0 ? { streamAnimation: mergedStreamAnimation } : {}
374
389
  };
375
390
  })(),
376
391
  suggestionChips: (_e = config.suggestionChips) != null ? _e : DEFAULT_WIDGET_CONFIG.suggestionChips,
@@ -2673,6 +2688,91 @@ var featuresSectionDef = {
2673
2688
  { id: "feat-scroll-bottom-label", label: "Scroll To Bottom Label", description: "Leave empty for icon-only mode", type: "text", path: "features.scrollToBottom.label", defaultValue: "" }
2674
2689
  ]
2675
2690
  };
2691
+ var streamAnimationSectionDef = {
2692
+ id: "stream-animation",
2693
+ title: "Stream Animation",
2694
+ description: "Control how assistant text appears while streaming.",
2695
+ collapsed: true,
2696
+ fields: [
2697
+ {
2698
+ id: "stream-anim-type",
2699
+ label: "Animation",
2700
+ description: "Reveal effect applied to each assistant reply as it streams.",
2701
+ type: "select",
2702
+ path: "features.streamAnimation.type",
2703
+ defaultValue: "none",
2704
+ options: [
2705
+ { value: "none", label: "None" },
2706
+ { value: "typewriter", label: "Typewriter" },
2707
+ { value: "word-fade", label: "Word fade" },
2708
+ { value: "letter-rise", label: "Letter rise" },
2709
+ { value: "glyph-cycle", label: "Glyph cycle" },
2710
+ { value: "wipe", label: "Wipe" },
2711
+ { value: "pop-bubble", label: "Pop bubble" }
2712
+ ]
2713
+ },
2714
+ {
2715
+ id: "stream-anim-placeholder",
2716
+ label: "Pre-first-token Placeholder",
2717
+ description: "What to show before the first token arrives.",
2718
+ type: "select",
2719
+ path: "features.streamAnimation.placeholder",
2720
+ defaultValue: "none",
2721
+ options: [
2722
+ { value: "none", label: "Typing indicator (default)" },
2723
+ { value: "skeleton", label: "Skeleton shimmer" }
2724
+ ]
2725
+ },
2726
+ {
2727
+ id: "stream-anim-buffer",
2728
+ label: "Content Buffering",
2729
+ description: "Trim in-progress units so only complete words/lines reveal.",
2730
+ type: "select",
2731
+ path: "features.streamAnimation.buffer",
2732
+ defaultValue: "none",
2733
+ options: [
2734
+ { value: "none", label: "None \u2014 stream every character" },
2735
+ { value: "word", label: "Word \u2014 hold until whitespace" },
2736
+ { value: "line", label: "Line \u2014 hold until newline" }
2737
+ ]
2738
+ },
2739
+ {
2740
+ id: "stream-anim-speed",
2741
+ label: "Per-unit Duration (ms)",
2742
+ description: "Animation length for each character or word.",
2743
+ type: "select",
2744
+ path: "features.streamAnimation.speed",
2745
+ defaultValue: 120,
2746
+ options: [
2747
+ { value: "40", label: "40ms \u2014 snappy" },
2748
+ { value: "80", label: "80ms" },
2749
+ { value: "120", label: "120ms (default)" },
2750
+ { value: "200", label: "200ms" },
2751
+ { value: "320", label: "320ms" },
2752
+ { value: "480", label: "480ms \u2014 slow" }
2753
+ ],
2754
+ formatValue: (v) => String(v != null ? v : 120),
2755
+ parseValue: (v) => Number(v)
2756
+ },
2757
+ {
2758
+ id: "stream-anim-duration",
2759
+ label: "Container Duration (ms)",
2760
+ description: "Length of container-level effects (pop-bubble, custom plugins).",
2761
+ type: "select",
2762
+ path: "features.streamAnimation.duration",
2763
+ defaultValue: 1800,
2764
+ options: [
2765
+ { value: "600", label: "600ms" },
2766
+ { value: "1200", label: "1200ms" },
2767
+ { value: "1800", label: "1800ms (default)" },
2768
+ { value: "2400", label: "2400ms" },
2769
+ { value: "3600", label: "3600ms \u2014 slow" }
2770
+ ],
2771
+ formatValue: (v) => String(v != null ? v : 1800),
2772
+ parseValue: (v) => Number(v)
2773
+ }
2774
+ ]
2775
+ };
2676
2776
  var attachmentsSectionDef = {
2677
2777
  id: "attachments-config",
2678
2778
  title: "Attachments",
@@ -2756,7 +2856,7 @@ var CONFIGURE_SUB_GROUPS = [
2756
2856
  { label: "Content", sections: [copySectionDef, suggestionsSectionDef] },
2757
2857
  { label: "Layout", sections: [generalLayoutSectionDef, headerLayoutSectionDef, messagesLayoutSectionDef, messageActionsSectionDef] },
2758
2858
  { label: "Widget", sections: [launcherBasicsSectionDef, launcherAdvancedSectionDef, sendButtonSectionDef, closeButtonSectionDef, clearChatSectionDef, statusIndicatorSectionDef] },
2759
- { label: "Features", sections: [featuresSectionDef, attachmentsSectionDef, artifactsSectionDef, artifactCustomizationSectionDef] },
2859
+ { label: "Features", sections: [featuresSectionDef, streamAnimationSectionDef, attachmentsSectionDef, artifactsSectionDef, artifactCustomizationSectionDef] },
2760
2860
  { label: "Developer", collapsedByDefault: true, sections: [apiIntegrationSectionDef, debugSectionDef, markdownSectionDef] }
2761
2861
  ];
2762
2862
  var CONFIGURE_SECTIONS = CONFIGURE_SUB_GROUPS.flatMap((g) => g.sections);
@@ -4806,7 +4906,7 @@ var AgentWidgetClient = class {
4806
4906
  const agentIterationMessages = /* @__PURE__ */ new Map();
4807
4907
  const iterationDisplay = (_a = this.config.iterationDisplay) != null ? _a : "separate";
4808
4908
  drainReadyQueue = () => {
4809
- 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;
4909
+ 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;
4810
4910
  for (let i = 0; i < seqReadyQueue.length; i++) {
4811
4911
  const payloadType = seqReadyQueue[i].payloadType;
4812
4912
  const payload = seqReadyQueue[i].payload;
@@ -5226,9 +5326,11 @@ var AgentWidgetClient = class {
5226
5326
  }
5227
5327
  continue;
5228
5328
  }
5329
+ const stepStopReason = payload.stopReason;
5229
5330
  if (didSplitByPartId) {
5230
5331
  if (assistantMessage !== null) {
5231
5332
  const msg = assistantMessage;
5333
+ if (stepStopReason) msg.stopReason = stepStopReason;
5232
5334
  streamParsers.delete(msg.id);
5233
5335
  rawContentBuffers.delete(msg.id);
5234
5336
  if (msg.streaming !== false) {
@@ -5239,6 +5341,7 @@ var AgentWidgetClient = class {
5239
5341
  const splitFinalContent = (_qa = payload.result) == null ? void 0 : _qa.response;
5240
5342
  const sealedForReconcile = lastSealedTextSegment;
5241
5343
  if (sealedForReconcile) {
5344
+ if (stepStopReason) sealedForReconcile.stopReason = stepStopReason;
5242
5345
  if (splitFinalContent !== void 0 && splitFinalContent !== null) {
5243
5346
  reconcileSealedAssistantWithFinalResponse(sealedForReconcile, splitFinalContent);
5244
5347
  } else {
@@ -5251,6 +5354,7 @@ var AgentWidgetClient = class {
5251
5354
  }
5252
5355
  const finalContent = (_ra = payload.result) == null ? void 0 : _ra.response;
5253
5356
  const assistant = ensureAssistantMessage();
5357
+ if (stepStopReason) assistant.stopReason = stepStopReason;
5254
5358
  if (finalContent !== void 0 && finalContent !== null) {
5255
5359
  const parser = streamParsers.get(assistant.id);
5256
5360
  let hasExtractedText = false;
@@ -5484,11 +5588,20 @@ var AgentWidgetClient = class {
5484
5588
  emitMessage(reasoningMessage);
5485
5589
  }
5486
5590
  }
5591
+ const turnStopReason = payload.stopReason;
5592
+ if (turnStopReason && assistantMessage !== null) {
5593
+ const turnId = payload.turnId;
5594
+ const matchesTurn = !turnId || ((_Ha = assistantMessage.agentMetadata) == null ? void 0 : _Ha.turnId) === turnId;
5595
+ if (matchesTurn) {
5596
+ assistantMessage.stopReason = turnStopReason;
5597
+ emitMessage(assistantMessage);
5598
+ }
5599
+ }
5487
5600
  } else if (payloadType === "agent_tool_start") {
5488
- const toolId = (_Ha = payload.toolCallId) != null ? _Ha : `agent-tool-${nextSequence()}`;
5601
+ const toolId = (_Ia = payload.toolCallId) != null ? _Ia : `agent-tool-${nextSequence()}`;
5489
5602
  trackToolId(getToolCallKey(payload), toolId);
5490
5603
  const toolMessage = ensureToolMessage(toolId);
5491
- const tool = (_Ia = toolMessage.toolCall) != null ? _Ia : {
5604
+ const tool = (_Ja = toolMessage.toolCall) != null ? _Ja : {
5492
5605
  id: toolId,
5493
5606
  status: "pending",
5494
5607
  name: void 0,
@@ -5500,12 +5613,12 @@ var AgentWidgetClient = class {
5500
5613
  completedAt: void 0,
5501
5614
  durationMs: void 0
5502
5615
  };
5503
- tool.name = (_Ka = (_Ja = payload.toolName) != null ? _Ja : payload.name) != null ? _Ka : tool.name;
5616
+ tool.name = (_La = (_Ka = payload.toolName) != null ? _Ka : payload.name) != null ? _La : tool.name;
5504
5617
  tool.status = "running";
5505
5618
  if (payload.parameters !== void 0) {
5506
5619
  tool.args = payload.parameters;
5507
5620
  }
5508
- tool.startedAt = resolveTimestamp((_La = payload.startedAt) != null ? _La : payload.timestamp);
5621
+ tool.startedAt = resolveTimestamp((_Ma = payload.startedAt) != null ? _Ma : payload.timestamp);
5509
5622
  toolMessage.toolCall = tool;
5510
5623
  toolMessage.streaming = true;
5511
5624
  toolMessage.agentMetadata = {
@@ -5514,21 +5627,21 @@ var AgentWidgetClient = class {
5514
5627
  };
5515
5628
  emitMessage(toolMessage);
5516
5629
  } else if (payloadType === "agent_tool_delta") {
5517
- const toolId = (_Ma = payload.toolCallId) != null ? _Ma : toolContext.lastId;
5630
+ const toolId = (_Na = payload.toolCallId) != null ? _Na : toolContext.lastId;
5518
5631
  if (toolId) {
5519
- const toolMessage = (_Na = toolMessages.get(toolId)) != null ? _Na : ensureToolMessage(toolId);
5632
+ const toolMessage = (_Oa = toolMessages.get(toolId)) != null ? _Oa : ensureToolMessage(toolId);
5520
5633
  if (toolMessage.toolCall) {
5521
- toolMessage.toolCall.chunks = (_Oa = toolMessage.toolCall.chunks) != null ? _Oa : [];
5522
- toolMessage.toolCall.chunks.push((_Pa = payload.delta) != null ? _Pa : "");
5634
+ toolMessage.toolCall.chunks = (_Pa = toolMessage.toolCall.chunks) != null ? _Pa : [];
5635
+ toolMessage.toolCall.chunks.push((_Qa = payload.delta) != null ? _Qa : "");
5523
5636
  toolMessage.toolCall.status = "running";
5524
5637
  toolMessage.streaming = true;
5525
5638
  emitMessage(toolMessage);
5526
5639
  }
5527
5640
  }
5528
5641
  } else if (payloadType === "agent_tool_complete") {
5529
- const toolId = (_Qa = payload.toolCallId) != null ? _Qa : toolContext.lastId;
5642
+ const toolId = (_Ra = payload.toolCallId) != null ? _Ra : toolContext.lastId;
5530
5643
  if (toolId) {
5531
- const toolMessage = (_Ra = toolMessages.get(toolId)) != null ? _Ra : ensureToolMessage(toolId);
5644
+ const toolMessage = (_Sa = toolMessages.get(toolId)) != null ? _Sa : ensureToolMessage(toolId);
5532
5645
  if (toolMessage.toolCall) {
5533
5646
  toolMessage.toolCall.status = "complete";
5534
5647
  if (payload.result !== void 0) {
@@ -5537,7 +5650,7 @@ var AgentWidgetClient = class {
5537
5650
  if (typeof payload.executionTime === "number") {
5538
5651
  toolMessage.toolCall.durationMs = payload.executionTime;
5539
5652
  }
5540
- toolMessage.toolCall.completedAt = resolveTimestamp((_Sa = payload.completedAt) != null ? _Sa : payload.timestamp);
5653
+ toolMessage.toolCall.completedAt = resolveTimestamp((_Ta = payload.completedAt) != null ? _Ta : payload.timestamp);
5541
5654
  toolMessage.streaming = false;
5542
5655
  emitMessage(toolMessage);
5543
5656
  const callKey = getToolCallKey(payload);
@@ -5552,7 +5665,7 @@ var AgentWidgetClient = class {
5552
5665
  const reflectionMessage = {
5553
5666
  id: reflectionId,
5554
5667
  role: "assistant",
5555
- content: (_Ta = payload.reflection) != null ? _Ta : "",
5668
+ content: (_Ua = payload.reflection) != null ? _Ua : "",
5556
5669
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
5557
5670
  streaming: false,
5558
5671
  variant: "reasoning",
@@ -5560,7 +5673,7 @@ var AgentWidgetClient = class {
5560
5673
  reasoning: {
5561
5674
  id: reflectionId,
5562
5675
  status: "complete",
5563
- chunks: [(_Ua = payload.reflection) != null ? _Ua : ""]
5676
+ chunks: [(_Va = payload.reflection) != null ? _Va : ""]
5564
5677
  },
5565
5678
  agentMetadata: {
5566
5679
  executionId: payload.executionId,
@@ -5581,7 +5694,7 @@ var AgentWidgetClient = class {
5581
5694
  }
5582
5695
  onEvent({ type: "status", status: "idle" });
5583
5696
  } else if (payloadType === "agent_error") {
5584
- const errorMessage = typeof payload.error === "string" ? payload.error : (_Wa = (_Va = payload.error) == null ? void 0 : _Va.message) != null ? _Wa : "Agent execution error";
5697
+ const errorMessage = typeof payload.error === "string" ? payload.error : (_Xa = (_Wa = payload.error) == null ? void 0 : _Wa.message) != null ? _Xa : "Agent execution error";
5585
5698
  if (payload.recoverable) {
5586
5699
  if (typeof console !== "undefined") {
5587
5700
  console.warn("[AgentWidget] Recoverable agent error:", errorMessage);
@@ -5594,7 +5707,7 @@ var AgentWidgetClient = class {
5594
5707
  }
5595
5708
  } else if (payloadType === "agent_ping") {
5596
5709
  } else if (payloadType === "agent_approval_start") {
5597
- const approvalId = (_Xa = payload.approvalId) != null ? _Xa : `approval-${nextSequence()}`;
5710
+ const approvalId = (_Ya = payload.approvalId) != null ? _Ya : `approval-${nextSequence()}`;
5598
5711
  const approvalMessage = {
5599
5712
  id: `approval-${approvalId}`,
5600
5713
  role: "assistant",
@@ -5606,17 +5719,17 @@ var AgentWidgetClient = class {
5606
5719
  approval: {
5607
5720
  id: approvalId,
5608
5721
  status: "pending",
5609
- agentId: (_Ya = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _Ya : "virtual",
5610
- executionId: (__a = (_Za = payload.executionId) != null ? _Za : agentExecution == null ? void 0 : agentExecution.executionId) != null ? __a : "",
5611
- toolName: (_$a = payload.toolName) != null ? _$a : "",
5722
+ agentId: (_Za = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _Za : "virtual",
5723
+ executionId: (_$a = (__a = payload.executionId) != null ? __a : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _$a : "",
5724
+ toolName: (_ab = payload.toolName) != null ? _ab : "",
5612
5725
  toolType: payload.toolType,
5613
- description: (_bb = payload.description) != null ? _bb : `Execute ${(_ab = payload.toolName) != null ? _ab : "tool"}`,
5726
+ description: (_cb = payload.description) != null ? _cb : `Execute ${(_bb = payload.toolName) != null ? _bb : "tool"}`,
5614
5727
  parameters: payload.parameters
5615
5728
  }
5616
5729
  };
5617
5730
  emitMessage(approvalMessage);
5618
5731
  } else if (payloadType === "step_await" && payload.awaitReason === "approval_required") {
5619
- const approvalId = (_cb = payload.approvalId) != null ? _cb : `approval-${nextSequence()}`;
5732
+ const approvalId = (_db = payload.approvalId) != null ? _db : `approval-${nextSequence()}`;
5620
5733
  const approvalMessage = {
5621
5734
  id: `approval-${approvalId}`,
5622
5735
  role: "assistant",
@@ -5628,11 +5741,11 @@ var AgentWidgetClient = class {
5628
5741
  approval: {
5629
5742
  id: approvalId,
5630
5743
  status: "pending",
5631
- agentId: (_db = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _db : "virtual",
5632
- executionId: (_fb = (_eb = payload.executionId) != null ? _eb : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _fb : "",
5633
- toolName: (_gb = payload.toolName) != null ? _gb : "",
5744
+ agentId: (_eb = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _eb : "virtual",
5745
+ executionId: (_gb = (_fb = payload.executionId) != null ? _fb : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _gb : "",
5746
+ toolName: (_hb = payload.toolName) != null ? _hb : "",
5634
5747
  toolType: payload.toolType,
5635
- description: (_ib = payload.description) != null ? _ib : `Execute ${(_hb = payload.toolName) != null ? _hb : "tool"}`,
5748
+ description: (_jb = payload.description) != null ? _jb : `Execute ${(_ib = payload.toolName) != null ? _ib : "tool"}`,
5636
5749
  parameters: payload.parameters
5637
5750
  }
5638
5751
  };
@@ -5651,11 +5764,11 @@ var AgentWidgetClient = class {
5651
5764
  sequence: nextSequence(),
5652
5765
  approval: {
5653
5766
  id: approvalId,
5654
- status: (_jb = payload.decision) != null ? _jb : "approved",
5655
- agentId: (_kb = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _kb : "virtual",
5656
- executionId: (_mb = (_lb = payload.executionId) != null ? _lb : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _mb : "",
5657
- toolName: (_nb = payload.toolName) != null ? _nb : "",
5658
- description: (_ob = payload.description) != null ? _ob : "",
5767
+ status: (_kb = payload.decision) != null ? _kb : "approved",
5768
+ agentId: (_lb = agentExecution == null ? void 0 : agentExecution.agentId) != null ? _lb : "virtual",
5769
+ executionId: (_nb = (_mb = payload.executionId) != null ? _mb : agentExecution == null ? void 0 : agentExecution.executionId) != null ? _nb : "",
5770
+ toolName: (_ob = payload.toolName) != null ? _ob : "",
5771
+ description: (_pb = payload.description) != null ? _pb : "",
5659
5772
  resolvedAt: Date.now()
5660
5773
  }
5661
5774
  };
@@ -5693,7 +5806,7 @@ var AgentWidgetClient = class {
5693
5806
  }
5694
5807
  } else if (payloadType === "artifact_delta") {
5695
5808
  const deltaId = String(payload.id);
5696
- const deltaText = typeof payload.delta === "string" ? payload.delta : String((_pb = payload.delta) != null ? _pb : "");
5809
+ const deltaText = typeof payload.delta === "string" ? payload.delta : String((_qb = payload.delta) != null ? _qb : "");
5697
5810
  onEvent({
5698
5811
  type: "artifact_delta",
5699
5812
  id: deltaId,
@@ -5716,7 +5829,7 @@ var AgentWidgetClient = class {
5716
5829
  if (refMsg) {
5717
5830
  refMsg.streaming = false;
5718
5831
  try {
5719
- const parsed = JSON.parse((_qb = refMsg.rawContent) != null ? _qb : "{}");
5832
+ const parsed = JSON.parse((_rb = refMsg.rawContent) != null ? _rb : "{}");
5720
5833
  if (parsed.props) {
5721
5834
  parsed.props.status = "complete";
5722
5835
  const acc = artifactContent.get(artCompleteId);
@@ -5737,7 +5850,7 @@ var AgentWidgetClient = class {
5737
5850
  if (!m || typeof m !== "object") {
5738
5851
  continue;
5739
5852
  }
5740
- const id = String((_rb = m.id) != null ? _rb : `msg-${nextSequence()}`);
5853
+ const id = String((_sb = m.id) != null ? _sb : `msg-${nextSequence()}`);
5741
5854
  const roleRaw = m.role;
5742
5855
  const role = roleRaw === "user" ? "user" : roleRaw === "system" ? "system" : "assistant";
5743
5856
  const msg = {
@@ -5756,7 +5869,7 @@ var AgentWidgetClient = class {
5756
5869
  if (msg.rawContent) {
5757
5870
  try {
5758
5871
  const parsed = JSON.parse(msg.rawContent);
5759
- const refArtId = (_sb = parsed == null ? void 0 : parsed.props) == null ? void 0 : _sb.artifactId;
5872
+ const refArtId = (_tb = parsed == null ? void 0 : parsed.props) == null ? void 0 : _tb.artifactId;
5760
5873
  if (typeof refArtId === "string") {
5761
5874
  artifactIdsWithCards.add(refArtId);
5762
5875
  }
@@ -5772,7 +5885,7 @@ var AgentWidgetClient = class {
5772
5885
  if (payload.error instanceof Error) {
5773
5886
  resolvedError = payload.error;
5774
5887
  } else if (payloadType === "dispatch_error") {
5775
- const msg = (_tb = payload.message) != null ? _tb : payload.error;
5888
+ const msg = (_ub = payload.message) != null ? _ub : payload.error;
5776
5889
  if (msg != null && msg !== "") {
5777
5890
  resolvedError = new Error(String(msg));
5778
5891
  }
@@ -5781,7 +5894,7 @@ var AgentWidgetClient = class {
5781
5894
  if (typeof e === "string" && e !== "") {
5782
5895
  resolvedError = new Error(e);
5783
5896
  } else if (e != null && typeof e === "object" && "message" in e) {
5784
- resolvedError = new Error(String((_ub = e.message) != null ? _ub : e));
5897
+ resolvedError = new Error(String((_vb = e.message) != null ? _vb : e));
5785
5898
  }
5786
5899
  } else if (payloadType === "error" && payload.error != null && payload.error !== "") {
5787
5900
  resolvedError = new Error(String(payload.error));
@@ -7626,6 +7739,8 @@ var AgentWidgetSession = class _AgentWidgetSession {
7626
7739
  var _a;
7627
7740
  (_a = this.abortController) == null ? void 0 : _a.abort();
7628
7741
  this.abortController = null;
7742
+ this.stopSpeaking();
7743
+ this.stopVoicePlayback();
7629
7744
  this.setStreaming(false);
7630
7745
  this.setStatus("idle");
7631
7746
  }
@@ -8258,6 +8373,9 @@ var morphMessages = (container, newContent, options = {}) => {
8258
8373
  if (oldNode.classList.contains("persona-animate-typing")) {
8259
8374
  return false;
8260
8375
  }
8376
+ if (oldNode.hasAttribute("data-preserve-runtime")) {
8377
+ return false;
8378
+ }
8261
8379
  if (oldNode.hasAttribute("data-preserve-animation")) {
8262
8380
  if (newNode instanceof HTMLElement && !newNode.hasAttribute("data-preserve-animation")) {
8263
8381
  return;
@@ -8279,7 +8397,7 @@ var morphMessages = (container, newContent, options = {}) => {
8279
8397
 
8280
8398
  // src/utils/message-fingerprint.ts
8281
8399
  function computeMessageFingerprint(message, configVersion) {
8282
- 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;
8400
+ 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;
8283
8401
  return [
8284
8402
  message.id,
8285
8403
  message.role,
@@ -8297,6 +8415,7 @@ function computeMessageFingerprint(message, configVersion) {
8297
8415
  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,
8298
8416
  (_A = (_z = (_y = message.reasoning) == null ? void 0 : _y.chunks) == null ? void 0 : _z.length) != null ? _A : 0,
8299
8417
  (_C = (_B = message.contentParts) == null ? void 0 : _B.length) != null ? _C : 0,
8418
+ (_D = message.stopReason) != null ? _D : "",
8300
8419
  configVersion
8301
8420
  ].join("\0");
8302
8421
  }
@@ -8397,6 +8516,241 @@ var statusCopy = {
8397
8516
  var DEFAULT_OVERLAY_Z_INDEX = 1e5;
8398
8517
  var PORTALED_OVERLAY_Z_INDEX = DEFAULT_OVERLAY_Z_INDEX + 1;
8399
8518
 
8519
+ // src/utils/stream-animation.ts
8520
+ var DEFAULT_STREAM_ANIMATION = {
8521
+ type: "none",
8522
+ placeholder: "none",
8523
+ speed: 120,
8524
+ duration: 1800,
8525
+ buffer: "none"
8526
+ };
8527
+ var DEFAULT_SKIP_TAGS = ["pre", "code", "a", "script", "style"];
8528
+ var resolveStreamAnimation = (feature) => {
8529
+ var _a, _b, _c, _d, _e;
8530
+ return {
8531
+ type: (_a = feature == null ? void 0 : feature.type) != null ? _a : DEFAULT_STREAM_ANIMATION.type,
8532
+ placeholder: (_b = feature == null ? void 0 : feature.placeholder) != null ? _b : DEFAULT_STREAM_ANIMATION.placeholder,
8533
+ speed: (_c = feature == null ? void 0 : feature.speed) != null ? _c : DEFAULT_STREAM_ANIMATION.speed,
8534
+ duration: (_d = feature == null ? void 0 : feature.duration) != null ? _d : DEFAULT_STREAM_ANIMATION.duration,
8535
+ buffer: (_e = feature == null ? void 0 : feature.buffer) != null ? _e : DEFAULT_STREAM_ANIMATION.buffer
8536
+ };
8537
+ };
8538
+ var BUILTIN_PLUGINS = [
8539
+ {
8540
+ name: "typewriter",
8541
+ containerClass: "persona-stream-typewriter",
8542
+ wrap: "char",
8543
+ useCaret: true
8544
+ },
8545
+ {
8546
+ name: "pop-bubble",
8547
+ bubbleClass: "persona-stream-pop",
8548
+ wrap: "none"
8549
+ },
8550
+ {
8551
+ name: "letter-rise",
8552
+ containerClass: "persona-stream-letter-rise",
8553
+ wrap: "char"
8554
+ },
8555
+ {
8556
+ name: "word-fade",
8557
+ containerClass: "persona-stream-word-fade",
8558
+ wrap: "word"
8559
+ }
8560
+ ];
8561
+ var globalRegistry = /* @__PURE__ */ new Map();
8562
+ for (const plugin of BUILTIN_PLUGINS) globalRegistry.set(plugin.name, plugin);
8563
+ var resolveStreamAnimationPlugin = (type, overrides) => {
8564
+ var _a, _b;
8565
+ if (type === "none") return null;
8566
+ if (overrides && Object.prototype.hasOwnProperty.call(overrides, type)) {
8567
+ return (_a = overrides[type]) != null ? _a : null;
8568
+ }
8569
+ return (_b = globalRegistry.get(type)) != null ? _b : null;
8570
+ };
8571
+ var applyStreamBuffer = (content, buffer, plugin, message, streaming) => {
8572
+ if (!streaming) return content;
8573
+ if (plugin == null ? void 0 : plugin.bufferContent) return plugin.bufferContent(content, message);
8574
+ if (!content) return content;
8575
+ if (buffer === "word") {
8576
+ const lastSpace = content.search(/\s(?=\S*$)/);
8577
+ if (lastSpace < 0) return "";
8578
+ return content.slice(0, lastSpace);
8579
+ }
8580
+ if (buffer === "line") {
8581
+ const lastNewline = content.lastIndexOf("\n");
8582
+ if (lastNewline < 0) return "";
8583
+ return content.slice(0, lastNewline);
8584
+ }
8585
+ return content;
8586
+ };
8587
+ var makeCharSpan = (doc, ch, messageId, index) => {
8588
+ const span = doc.createElement("span");
8589
+ span.className = "persona-stream-char";
8590
+ span.id = `stream-c-${messageId}-${index}`;
8591
+ span.style.setProperty("--char-index", String(index));
8592
+ span.textContent = ch;
8593
+ return span;
8594
+ };
8595
+ var makeWordSpan = (doc, word, messageId, index) => {
8596
+ const span = doc.createElement("span");
8597
+ span.className = "persona-stream-word";
8598
+ span.id = `stream-w-${messageId}-${index}`;
8599
+ span.style.setProperty("--word-index", String(index));
8600
+ span.textContent = word;
8601
+ return span;
8602
+ };
8603
+ var WHITESPACE_RE = /\s/;
8604
+ var shouldSkipSubtree = (node, skipTags) => {
8605
+ let current = node.parentNode;
8606
+ while (current) {
8607
+ if (current.nodeType === 1) {
8608
+ const el = current;
8609
+ if (skipTags.has(el.tagName.toLowerCase())) return true;
8610
+ }
8611
+ current = current.parentNode;
8612
+ }
8613
+ return false;
8614
+ };
8615
+ var wrapTextNodeChars = (textNode, messageId, counterRef) => {
8616
+ var _a;
8617
+ const doc = textNode.ownerDocument;
8618
+ const parent = textNode.parentNode;
8619
+ if (!doc || !parent) return;
8620
+ const text = (_a = textNode.nodeValue) != null ? _a : "";
8621
+ if (!text) return;
8622
+ const fragment = doc.createDocumentFragment();
8623
+ let i = 0;
8624
+ while (i < text.length) {
8625
+ if (WHITESPACE_RE.test(text[i])) {
8626
+ let j = i;
8627
+ while (j < text.length && WHITESPACE_RE.test(text[j])) j += 1;
8628
+ fragment.appendChild(doc.createTextNode(text.slice(i, j)));
8629
+ i = j;
8630
+ } else {
8631
+ const group = doc.createElement("span");
8632
+ group.className = "persona-stream-word-group";
8633
+ let j = i;
8634
+ while (j < text.length && !WHITESPACE_RE.test(text[j])) {
8635
+ group.appendChild(makeCharSpan(doc, text[j], messageId, counterRef.value));
8636
+ counterRef.value += 1;
8637
+ j += 1;
8638
+ }
8639
+ fragment.appendChild(group);
8640
+ i = j;
8641
+ }
8642
+ }
8643
+ parent.replaceChild(fragment, textNode);
8644
+ };
8645
+ var wrapTextNodeWords = (textNode, messageId, counterRef) => {
8646
+ var _a;
8647
+ const doc = textNode.ownerDocument;
8648
+ const parent = textNode.parentNode;
8649
+ if (!doc || !parent) return;
8650
+ const text = (_a = textNode.nodeValue) != null ? _a : "";
8651
+ if (!text) return;
8652
+ const fragment = doc.createDocumentFragment();
8653
+ const tokens = text.split(/(\s+)/);
8654
+ for (const token of tokens) {
8655
+ if (!token) continue;
8656
+ if (/^\s+$/.test(token)) {
8657
+ fragment.appendChild(doc.createTextNode(token));
8658
+ } else {
8659
+ fragment.appendChild(makeWordSpan(doc, token, messageId, counterRef.value));
8660
+ counterRef.value += 1;
8661
+ }
8662
+ }
8663
+ parent.replaceChild(fragment, textNode);
8664
+ };
8665
+ var wrapStreamAnimation = (html, mode, messageId, options) => {
8666
+ var _a;
8667
+ if (!html) return html;
8668
+ if (typeof document === "undefined") return html;
8669
+ const scratch = document.createElement("div");
8670
+ scratch.innerHTML = html;
8671
+ const skipTags = new Set(((_a = options == null ? void 0 : options.skipTags) != null ? _a : DEFAULT_SKIP_TAGS).map((t) => t.toLowerCase()));
8672
+ const walker = document.createTreeWalker(scratch, NodeFilter.SHOW_TEXT, null);
8673
+ const textNodes = [];
8674
+ let node = walker.nextNode();
8675
+ while (node) {
8676
+ if (!shouldSkipSubtree(node, skipTags)) {
8677
+ textNodes.push(node);
8678
+ }
8679
+ node = walker.nextNode();
8680
+ }
8681
+ const counterRef = { value: 0 };
8682
+ const wrap = mode === "char" ? wrapTextNodeChars : wrapTextNodeWords;
8683
+ for (const textNode of textNodes) {
8684
+ wrap(textNode, messageId, counterRef);
8685
+ }
8686
+ return scratch.innerHTML;
8687
+ };
8688
+ var createStreamCaret = (doc = document) => {
8689
+ const caret = doc.createElement("span");
8690
+ caret.className = "persona-stream-caret";
8691
+ caret.setAttribute("aria-hidden", "true");
8692
+ caret.setAttribute("data-preserve-animation", "stream-caret");
8693
+ return caret;
8694
+ };
8695
+ var createSkeletonPlaceholder = (doc = document) => {
8696
+ const wrapper = doc.createElement("div");
8697
+ wrapper.className = "persona-stream-skeleton";
8698
+ wrapper.setAttribute("data-preserve-animation", "stream-skeleton");
8699
+ wrapper.setAttribute("aria-hidden", "true");
8700
+ const line = doc.createElement("div");
8701
+ line.className = "persona-stream-skeleton-line";
8702
+ wrapper.appendChild(line);
8703
+ return wrapper;
8704
+ };
8705
+ var injectedStyleRoots = /* @__PURE__ */ new WeakMap();
8706
+ var injectPluginStyles = (plugin, root) => {
8707
+ var _a;
8708
+ if (!plugin.styles) return;
8709
+ let names = injectedStyleRoots.get(root);
8710
+ if (!names) {
8711
+ names = /* @__PURE__ */ new Set();
8712
+ injectedStyleRoots.set(root, names);
8713
+ }
8714
+ if (names.has(plugin.name)) {
8715
+ const escaped = plugin.name.replace(/["\\]/g, "\\$&");
8716
+ const existing = root.querySelector(
8717
+ `style[data-persona-animation="${escaped}"]`
8718
+ );
8719
+ if (existing) return;
8720
+ names.delete(plugin.name);
8721
+ }
8722
+ names.add(plugin.name);
8723
+ const doc = root instanceof ShadowRoot ? root.ownerDocument : (_a = root.ownerDocument) != null ? _a : document;
8724
+ const style = doc.createElement("style");
8725
+ style.setAttribute("data-persona-animation", plugin.name);
8726
+ style.textContent = plugin.styles;
8727
+ root.appendChild(style);
8728
+ };
8729
+ var attachedCleanups = /* @__PURE__ */ new WeakMap();
8730
+ var attachPlugin = (plugin, root) => {
8731
+ if (!plugin.onAttach) return;
8732
+ let cleanups = attachedCleanups.get(root);
8733
+ if (!cleanups) {
8734
+ cleanups = /* @__PURE__ */ new Map();
8735
+ attachedCleanups.set(root, cleanups);
8736
+ }
8737
+ if (cleanups.has(plugin.name)) return;
8738
+ const cleanup = plugin.onAttach(root);
8739
+ cleanups.set(plugin.name, cleanup);
8740
+ };
8741
+ var detachAllPlugins = (root) => {
8742
+ const cleanups = attachedCleanups.get(root);
8743
+ if (!cleanups) return;
8744
+ for (const cleanup of cleanups.values()) {
8745
+ if (typeof cleanup === "function") cleanup();
8746
+ }
8747
+ cleanups.clear();
8748
+ };
8749
+ var ensurePluginActive = (plugin, root) => {
8750
+ injectPluginStyles(plugin, root);
8751
+ attachPlugin(plugin, root);
8752
+ };
8753
+
8400
8754
  // src/utils/overlay-host-stacking.ts
8401
8755
  function syncOverlayHostStacking(host, zIndex = DEFAULT_OVERLAY_Z_INDEX) {
8402
8756
  const originalPosition = host.style.position;
@@ -9482,7 +9836,7 @@ var buildHeaderWithLayout = (config, layoutConfig, context) => {
9482
9836
 
9483
9837
  // src/components/composer-builder.ts
9484
9838
  var buildComposer = (context) => {
9485
- 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;
9839
+ 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;
9486
9840
  const { config } = context;
9487
9841
  const footer = createElement(
9488
9842
  "div",
@@ -9538,9 +9892,13 @@ var buildComposer = (context) => {
9538
9892
  const useIcon = (_d = sendButtonConfig.useIcon) != null ? _d : false;
9539
9893
  const iconText = (_e = sendButtonConfig.iconText) != null ? _e : "\u2191";
9540
9894
  const iconName = sendButtonConfig.iconName;
9541
- const tooltipText = (_f = sendButtonConfig.tooltipText) != null ? _f : "Send message";
9542
- const showTooltip = (_g = sendButtonConfig.showTooltip) != null ? _g : false;
9543
- const buttonSize = (_h = sendButtonConfig.size) != null ? _h : "40px";
9895
+ const stopIconName = (_f = sendButtonConfig.stopIconName) != null ? _f : "square";
9896
+ const tooltipText = (_g = sendButtonConfig.tooltipText) != null ? _g : "Send message";
9897
+ const stopTooltipText = (_h = sendButtonConfig.stopTooltipText) != null ? _h : "Stop generating";
9898
+ const sendLabel = (_j = (_i = config == null ? void 0 : config.copy) == null ? void 0 : _i.sendButtonLabel) != null ? _j : "Send";
9899
+ const stopLabel = (_l = (_k = config == null ? void 0 : config.copy) == null ? void 0 : _k.stopButtonLabel) != null ? _l : "Stop";
9900
+ const showTooltip = (_m = sendButtonConfig.showTooltip) != null ? _m : false;
9901
+ const buttonSize = (_n = sendButtonConfig.size) != null ? _n : "40px";
9544
9902
  const backgroundColor = sendButtonConfig.backgroundColor;
9545
9903
  const textColor = sendButtonConfig.textColor;
9546
9904
  const sendButtonWrapper = createElement("div", "persona-send-button-wrapper");
@@ -9550,6 +9908,8 @@ var buildComposer = (context) => {
9550
9908
  );
9551
9909
  sendButton.type = "submit";
9552
9910
  sendButton.setAttribute("data-persona-composer-submit", "");
9911
+ let sendIcon = null;
9912
+ let stopIcon = null;
9553
9913
  if (useIcon) {
9554
9914
  sendButton.style.width = buttonSize;
9555
9915
  sendButton.style.height = buttonSize;
@@ -9563,25 +9923,26 @@ var buildComposer = (context) => {
9563
9923
  } else {
9564
9924
  sendButton.style.color = "var(--persona-button-primary-fg, #ffffff)";
9565
9925
  }
9926
+ const iconSize = parseFloat(buttonSize) || 24;
9927
+ const iconColor = (textColor == null ? void 0 : textColor.trim()) || "currentColor";
9566
9928
  if (iconName) {
9567
- const iconSize = parseFloat(buttonSize) || 24;
9568
- const iconColor = (textColor == null ? void 0 : textColor.trim()) || "currentColor";
9569
- const iconSvg = renderLucideIcon(iconName, iconSize, iconColor, 2);
9570
- if (iconSvg) {
9571
- sendButton.appendChild(iconSvg);
9929
+ sendIcon = renderLucideIcon(iconName, iconSize, iconColor, 2);
9930
+ if (sendIcon) {
9931
+ sendButton.appendChild(sendIcon);
9572
9932
  } else {
9573
9933
  sendButton.textContent = iconText;
9574
9934
  }
9575
9935
  } else {
9576
9936
  sendButton.textContent = iconText;
9577
9937
  }
9938
+ stopIcon = renderLucideIcon(stopIconName, iconSize, iconColor, 2);
9578
9939
  if (backgroundColor) {
9579
9940
  sendButton.style.backgroundColor = backgroundColor;
9580
9941
  } else {
9581
9942
  sendButton.classList.add("persona-bg-persona-primary");
9582
9943
  }
9583
9944
  } else {
9584
- sendButton.textContent = (_j = (_i = config == null ? void 0 : config.copy) == null ? void 0 : _i.sendButtonLabel) != null ? _j : "Send";
9945
+ sendButton.textContent = sendLabel;
9585
9946
  if (textColor) {
9586
9947
  sendButton.style.color = textColor;
9587
9948
  } else {
@@ -9609,18 +9970,43 @@ var buildComposer = (context) => {
9609
9970
  sendButton.style.paddingTop = "";
9610
9971
  sendButton.style.paddingBottom = "";
9611
9972
  }
9973
+ let sendTooltip = null;
9612
9974
  if (showTooltip && tooltipText) {
9613
- const tooltip = createElement("div", "persona-send-button-tooltip");
9614
- tooltip.textContent = tooltipText;
9615
- sendButtonWrapper.appendChild(tooltip);
9975
+ sendTooltip = createElement("div", "persona-send-button-tooltip");
9976
+ sendTooltip.textContent = tooltipText;
9977
+ sendButtonWrapper.appendChild(sendTooltip);
9616
9978
  }
9979
+ sendButton.setAttribute("aria-label", tooltipText);
9617
9980
  sendButtonWrapper.appendChild(sendButton);
9618
- const voiceRecognitionConfig = (_k = config == null ? void 0 : config.voiceRecognition) != null ? _k : {};
9981
+ let currentMode = "send";
9982
+ const setSendButtonMode = (mode) => {
9983
+ if (mode === currentMode) return;
9984
+ currentMode = mode;
9985
+ const label = mode === "stop" ? stopTooltipText : tooltipText;
9986
+ sendButton.setAttribute("aria-label", label);
9987
+ if (sendTooltip) {
9988
+ sendTooltip.textContent = label;
9989
+ }
9990
+ if (useIcon) {
9991
+ if (sendIcon && stopIcon) {
9992
+ const next = mode === "stop" ? stopIcon : sendIcon;
9993
+ const prev = mode === "stop" ? sendIcon : stopIcon;
9994
+ if (prev.parentNode === sendButton) {
9995
+ sendButton.replaceChild(next, prev);
9996
+ } else {
9997
+ sendButton.appendChild(next);
9998
+ }
9999
+ }
10000
+ } else {
10001
+ sendButton.textContent = mode === "stop" ? stopLabel : sendLabel;
10002
+ }
10003
+ };
10004
+ const voiceRecognitionConfig = (_o = config == null ? void 0 : config.voiceRecognition) != null ? _o : {};
9619
10005
  const voiceRecognitionEnabled = voiceRecognitionConfig.enabled === true;
9620
10006
  let micButton = null;
9621
10007
  let micButtonWrapper = null;
9622
10008
  const hasSpeechRecognition = typeof window !== "undefined" && (typeof window.webkitSpeechRecognition !== "undefined" || typeof window.SpeechRecognition !== "undefined");
9623
- const hasRuntypeProvider = ((_l = voiceRecognitionConfig.provider) == null ? void 0 : _l.type) === "runtype";
10009
+ const hasRuntypeProvider = ((_p = voiceRecognitionConfig.provider) == null ? void 0 : _p.type) === "runtype";
9624
10010
  const hasVoiceInput = hasSpeechRecognition || hasRuntypeProvider;
9625
10011
  if (voiceRecognitionEnabled && hasVoiceInput) {
9626
10012
  micButtonWrapper = createElement("div", "persona-send-button-wrapper");
@@ -9631,11 +10017,11 @@ var buildComposer = (context) => {
9631
10017
  micButton.type = "button";
9632
10018
  micButton.setAttribute("data-persona-composer-mic", "");
9633
10019
  micButton.setAttribute("aria-label", "Start voice recognition");
9634
- const micIconName = (_m = voiceRecognitionConfig.iconName) != null ? _m : "mic";
9635
- const micIconSize = (_n = voiceRecognitionConfig.iconSize) != null ? _n : buttonSize;
10020
+ const micIconName = (_q = voiceRecognitionConfig.iconName) != null ? _q : "mic";
10021
+ const micIconSize = (_r = voiceRecognitionConfig.iconSize) != null ? _r : buttonSize;
9636
10022
  const micIconSizeNum = parseFloat(micIconSize) || 24;
9637
- const micBackgroundColor = (_o = voiceRecognitionConfig.backgroundColor) != null ? _o : backgroundColor;
9638
- const micIconColor = (_p = voiceRecognitionConfig.iconColor) != null ? _p : textColor;
10023
+ const micBackgroundColor = (_s = voiceRecognitionConfig.backgroundColor) != null ? _s : backgroundColor;
10024
+ const micIconColor = (_t = voiceRecognitionConfig.iconColor) != null ? _t : textColor;
9639
10025
  micButton.style.width = micIconSize;
9640
10026
  micButton.style.height = micIconSize;
9641
10027
  micButton.style.minWidth = micIconSize;
@@ -9678,15 +10064,15 @@ var buildComposer = (context) => {
9678
10064
  micButton.style.paddingBottom = voiceRecognitionConfig.paddingY;
9679
10065
  }
9680
10066
  micButtonWrapper.appendChild(micButton);
9681
- const micTooltipText = (_q = voiceRecognitionConfig.tooltipText) != null ? _q : "Start voice recognition";
9682
- const showMicTooltip = (_r = voiceRecognitionConfig.showTooltip) != null ? _r : false;
10067
+ const micTooltipText = (_u = voiceRecognitionConfig.tooltipText) != null ? _u : "Start voice recognition";
10068
+ const showMicTooltip = (_v = voiceRecognitionConfig.showTooltip) != null ? _v : false;
9683
10069
  if (showMicTooltip && micTooltipText) {
9684
10070
  const tooltip = createElement("div", "persona-send-button-tooltip");
9685
10071
  tooltip.textContent = micTooltipText;
9686
10072
  micButtonWrapper.appendChild(tooltip);
9687
10073
  }
9688
10074
  }
9689
- const attachmentsConfig = (_s = config == null ? void 0 : config.attachments) != null ? _s : {};
10075
+ const attachmentsConfig = (_w = config == null ? void 0 : config.attachments) != null ? _w : {};
9690
10076
  const attachmentsEnabled = attachmentsConfig.enabled === true;
9691
10077
  let attachmentButton = null;
9692
10078
  let attachmentButtonWrapper = null;
@@ -9700,8 +10086,8 @@ var buildComposer = (context) => {
9700
10086
  attachmentPreviewsContainer.style.display = "none";
9701
10087
  attachmentInput = createElement("input");
9702
10088
  attachmentInput.type = "file";
9703
- attachmentInput.accept = ((_t = attachmentsConfig.allowedTypes) != null ? _t : ALL_SUPPORTED_MIME_TYPES).join(",");
9704
- attachmentInput.multiple = ((_u = attachmentsConfig.maxFiles) != null ? _u : 4) > 1;
10089
+ attachmentInput.accept = ((_x = attachmentsConfig.allowedTypes) != null ? _x : ALL_SUPPORTED_MIME_TYPES).join(",");
10090
+ attachmentInput.multiple = ((_y = attachmentsConfig.maxFiles) != null ? _y : 4) > 1;
9705
10091
  attachmentInput.style.display = "none";
9706
10092
  attachmentInput.setAttribute("aria-label", "Attach files");
9707
10093
  attachmentButtonWrapper = createElement("div", "persona-send-button-wrapper");
@@ -9710,8 +10096,8 @@ var buildComposer = (context) => {
9710
10096
  "persona-rounded-button persona-flex persona-items-center persona-justify-center disabled:persona-opacity-50 persona-cursor-pointer persona-attachment-button"
9711
10097
  );
9712
10098
  attachmentButton.type = "button";
9713
- attachmentButton.setAttribute("aria-label", (_v = attachmentsConfig.buttonTooltipText) != null ? _v : "Attach file");
9714
- const attachIconName = (_w = attachmentsConfig.buttonIconName) != null ? _w : "paperclip";
10099
+ attachmentButton.setAttribute("aria-label", (_z = attachmentsConfig.buttonTooltipText) != null ? _z : "Attach file");
10100
+ const attachIconName = (_A = attachmentsConfig.buttonIconName) != null ? _A : "paperclip";
9715
10101
  const attachIconSize = buttonSize;
9716
10102
  const buttonSizeNum = parseFloat(attachIconSize) || 40;
9717
10103
  const attachIconSizeNum = Math.round(buttonSizeNum * 0.6);
@@ -9748,7 +10134,7 @@ var buildComposer = (context) => {
9748
10134
  attachmentInput == null ? void 0 : attachmentInput.click();
9749
10135
  });
9750
10136
  attachmentButtonWrapper.appendChild(attachmentButton);
9751
- const attachTooltipText = (_x = attachmentsConfig.buttonTooltipText) != null ? _x : "Attach file";
10137
+ const attachTooltipText = (_B = attachmentsConfig.buttonTooltipText) != null ? _B : "Attach file";
9752
10138
  const tooltip = createElement("div", "persona-send-button-tooltip");
9753
10139
  tooltip.textContent = attachTooltipText;
9754
10140
  attachmentButtonWrapper.appendChild(tooltip);
@@ -9778,16 +10164,16 @@ var buildComposer = (context) => {
9778
10164
  rightActions.append(sendButtonWrapper);
9779
10165
  actionsRow.append(leftActions, rightActions);
9780
10166
  composerForm.append(actionsRow);
9781
- const statusConfig = (_y = config == null ? void 0 : config.statusIndicator) != null ? _y : {};
10167
+ const statusConfig = (_C = config == null ? void 0 : config.statusIndicator) != null ? _C : {};
9782
10168
  const alignClass = statusConfig.align === "left" ? "persona-text-left" : statusConfig.align === "center" ? "persona-text-center" : "persona-text-right";
9783
10169
  const statusText = createElement(
9784
10170
  "div",
9785
10171
  `persona-mt-2 ${alignClass} persona-text-xs persona-text-persona-muted`
9786
10172
  );
9787
10173
  statusText.setAttribute("data-persona-composer-status", "");
9788
- const isVisible = (_z = statusConfig.visible) != null ? _z : true;
10174
+ const isVisible = (_D = statusConfig.visible) != null ? _D : true;
9789
10175
  statusText.style.display = isVisible ? "" : "none";
9790
- const idleLabel = (_A = statusConfig.idleText) != null ? _A : "Online";
10176
+ const idleLabel = (_E = statusConfig.idleText) != null ? _E : "Online";
9791
10177
  if (statusConfig.idleLink) {
9792
10178
  const link = createElement("a");
9793
10179
  link.href = statusConfig.idleLink;
@@ -9819,7 +10205,8 @@ var buildComposer = (context) => {
9819
10205
  // Actions row layout elements
9820
10206
  actionsRow,
9821
10207
  leftActions,
9822
- rightActions
10208
+ rightActions,
10209
+ setSendButtonMode
9823
10210
  };
9824
10211
  };
9825
10212
 
@@ -9967,11 +10354,48 @@ var buildPanel = (config, showClose = true) => {
9967
10354
  // Actions row layout elements
9968
10355
  actionsRow: composerElements.actionsRow,
9969
10356
  leftActions: composerElements.leftActions,
9970
- rightActions: composerElements.rightActions
10357
+ rightActions: composerElements.rightActions,
10358
+ setSendButtonMode: composerElements.setSendButtonMode
9971
10359
  };
9972
10360
  };
9973
10361
 
9974
10362
  // src/components/message-bubble.ts
10363
+ var getDefaultStopReasonNoticeCopy = (stopReason) => {
10364
+ switch (stopReason) {
10365
+ case "max_tool_calls":
10366
+ return "Stopped after calling a tool. Send a follow-up to continue.";
10367
+ case "length":
10368
+ return "Response cut off as max tokens reached. Ask for more to continue.";
10369
+ case "content_filter":
10370
+ return "The provider filtered this response.";
10371
+ case "error":
10372
+ return "Something went wrong generating this response.";
10373
+ case "end_turn":
10374
+ case "unknown":
10375
+ default:
10376
+ return null;
10377
+ }
10378
+ };
10379
+ var resolveStopReasonNoticeText = (stopReason, overrides) => {
10380
+ if (!stopReason) return null;
10381
+ const fallback = getDefaultStopReasonNoticeCopy(stopReason);
10382
+ if (fallback === null) return null;
10383
+ const override = overrides == null ? void 0 : overrides[stopReason];
10384
+ const text = override !== void 0 ? override : fallback;
10385
+ if (!text) return null;
10386
+ return text;
10387
+ };
10388
+ var createStopReasonNotice = (stopReason, text) => {
10389
+ const notice = createElement(
10390
+ "div",
10391
+ "persona-message-stop-reason persona-text-xs persona-mt-2 persona-italic"
10392
+ );
10393
+ notice.setAttribute("data-stop-reason", stopReason);
10394
+ notice.setAttribute("role", "note");
10395
+ notice.style.opacity = "0.75";
10396
+ notice.textContent = text;
10397
+ return notice;
10398
+ };
9975
10399
  var isSafeImageSrc = (src) => {
9976
10400
  const lower = src.toLowerCase();
9977
10401
  if (lower.startsWith("data:image/svg+xml")) return false;
@@ -10274,7 +10698,7 @@ var createMessageActions = (message, actionsConfig, _callbacks) => {
10274
10698
  return container;
10275
10699
  };
10276
10700
  var createStandardBubble = (message, transform, layoutConfig, actionsConfig, actionCallbacks, options) => {
10277
- var _a, _b, _c, _d, _e, _f, _g;
10701
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
10278
10702
  const config = layoutConfig != null ? layoutConfig : {};
10279
10703
  const layout = (_a = config.layout) != null ? _a : "bubble";
10280
10704
  const avatarConfig = config.avatar;
@@ -10299,22 +10723,73 @@ var createStandardBubble = (message, transform, layoutConfig, actionsConfig, act
10299
10723
  const messageContentText = (_g = (_f = message.content) == null ? void 0 : _f.trim()) != null ? _g : "";
10300
10724
  const isImageOnlyFallbackMessage = imageParts.length > 0 && messageContentText === IMAGE_ONLY_MESSAGE_FALLBACK_TEXT;
10301
10725
  const shouldHideTextUntilPreviewFails = isImageOnlyFallbackMessage;
10726
+ const streamAnimation = resolveStreamAnimation(
10727
+ (_i = (_h = options == null ? void 0 : options.widgetConfig) == null ? void 0 : _h.features) == null ? void 0 : _i.streamAnimation
10728
+ );
10729
+ 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;
10730
+ const streamPlugin = message.role === "assistant" && streamAnimation.type !== "none" ? resolveStreamAnimationPlugin(streamAnimation.type, streamPluginOverrides) : null;
10731
+ const pluginStillAnimating = message.role === "assistant" && ((_m = streamPlugin == null ? void 0 : streamPlugin.isAnimating) == null ? void 0 : _m.call(streamPlugin, message)) === true;
10732
+ const streamAnimationActive = message.role === "assistant" && streamPlugin !== null && (Boolean(message.streaming) || pluginStillAnimating);
10733
+ if (streamAnimationActive && (streamPlugin == null ? void 0 : streamPlugin.bubbleClass)) {
10734
+ bubble.classList.add(streamPlugin.bubbleClass);
10735
+ }
10302
10736
  const contentDiv = document.createElement("div");
10303
10737
  contentDiv.classList.add("persona-message-content");
10738
+ if (streamAnimationActive && streamPlugin) {
10739
+ if (streamPlugin.containerClass) {
10740
+ contentDiv.classList.add(streamPlugin.containerClass);
10741
+ }
10742
+ contentDiv.style.setProperty("--persona-stream-step", `${streamAnimation.speed}ms`);
10743
+ contentDiv.style.setProperty("--persona-stream-duration", `${streamAnimation.duration}ms`);
10744
+ }
10745
+ const bufferedContent = streamAnimationActive ? applyStreamBuffer(
10746
+ (_n = message.content) != null ? _n : "",
10747
+ streamAnimation.buffer,
10748
+ streamPlugin,
10749
+ message,
10750
+ Boolean(message.streaming)
10751
+ ) : (_o = message.content) != null ? _o : "";
10304
10752
  const transformedContent = transform({
10305
- text: message.content,
10753
+ text: bufferedContent,
10306
10754
  message,
10307
10755
  streaming: Boolean(message.streaming),
10308
10756
  raw: message.rawContent
10309
10757
  });
10758
+ let animatedContent = transformedContent;
10759
+ if (streamAnimationActive && (streamPlugin == null ? void 0 : streamPlugin.wrap) === "char") {
10760
+ animatedContent = wrapStreamAnimation(transformedContent, "char", message.id, {
10761
+ skipTags: streamPlugin.skipTags
10762
+ });
10763
+ } else if (streamAnimationActive && (streamPlugin == null ? void 0 : streamPlugin.wrap) === "word") {
10764
+ animatedContent = wrapStreamAnimation(transformedContent, "word", message.id, {
10765
+ skipTags: streamPlugin.skipTags
10766
+ });
10767
+ }
10310
10768
  let textContentDiv = null;
10311
10769
  if (shouldHideTextUntilPreviewFails) {
10312
10770
  textContentDiv = document.createElement("div");
10313
- textContentDiv.innerHTML = transformedContent;
10771
+ textContentDiv.innerHTML = animatedContent;
10314
10772
  textContentDiv.style.display = "none";
10315
10773
  contentDiv.appendChild(textContentDiv);
10316
10774
  } else {
10317
- contentDiv.innerHTML = transformedContent;
10775
+ contentDiv.innerHTML = animatedContent;
10776
+ }
10777
+ if (streamAnimationActive && (streamPlugin == null ? void 0 : streamPlugin.useCaret) && !shouldHideTextUntilPreviewFails && messageContentText) {
10778
+ const caret = createStreamCaret();
10779
+ const spans = contentDiv.querySelectorAll(
10780
+ ".persona-stream-char, .persona-stream-word"
10781
+ );
10782
+ const lastSpan = spans[spans.length - 1];
10783
+ if (lastSpan == null ? void 0 : lastSpan.parentNode) {
10784
+ lastSpan.parentNode.insertBefore(caret, lastSpan.nextSibling);
10785
+ } else {
10786
+ const lastChild = contentDiv.lastElementChild;
10787
+ if (lastChild) {
10788
+ lastChild.appendChild(caret);
10789
+ } else {
10790
+ contentDiv.appendChild(caret);
10791
+ }
10792
+ }
10318
10793
  }
10319
10794
  if (showTimestamp && timestampPosition === "inline" && message.createdAt) {
10320
10795
  const timestamp = createTimestamp(message, timestampConfig);
@@ -10343,18 +10818,37 @@ var createStandardBubble = (message, transform, layoutConfig, actionsConfig, act
10343
10818
  timestamp.classList.add("persona-mt-1");
10344
10819
  bubble.appendChild(timestamp);
10345
10820
  }
10821
+ const stopReasonNoticeText = message.role === "assistant" ? resolveStopReasonNoticeText(
10822
+ message.stopReason,
10823
+ (_q = (_p = options == null ? void 0 : options.widgetConfig) == null ? void 0 : _p.copy) == null ? void 0 : _q.stopReasonNotice
10824
+ ) : null;
10346
10825
  if (message.streaming && message.role === "assistant") {
10347
- if (!message.content || !message.content.trim()) {
10348
- const indicator = renderLoadingIndicatorWithFallback(
10349
- "inline",
10350
- options == null ? void 0 : options.loadingIndicatorRenderer,
10351
- options == null ? void 0 : options.widgetConfig
10352
- );
10353
- if (indicator) {
10354
- bubble.appendChild(indicator);
10826
+ const hasVisibleContent = Boolean(bufferedContent && bufferedContent.trim());
10827
+ const skeletonEnabled = streamAnimation.placeholder === "skeleton";
10828
+ const trailSkeleton = skeletonEnabled && streamAnimation.buffer === "line" && hasVisibleContent;
10829
+ if (!hasVisibleContent) {
10830
+ if (skeletonEnabled) {
10831
+ bubble.appendChild(createSkeletonPlaceholder());
10832
+ } else {
10833
+ const indicator = renderLoadingIndicatorWithFallback(
10834
+ "inline",
10835
+ options == null ? void 0 : options.loadingIndicatorRenderer,
10836
+ options == null ? void 0 : options.widgetConfig
10837
+ );
10838
+ if (indicator) {
10839
+ bubble.appendChild(indicator);
10840
+ }
10355
10841
  }
10842
+ } else if (trailSkeleton) {
10843
+ bubble.appendChild(createSkeletonPlaceholder());
10356
10844
  }
10357
10845
  }
10846
+ if (stopReasonNoticeText && message.stopReason && !message.streaming) {
10847
+ if (!messageContentText) {
10848
+ contentDiv.style.display = "none";
10849
+ }
10850
+ bubble.appendChild(createStopReasonNotice(message.stopReason, stopReasonNoticeText));
10851
+ }
10358
10852
  const shouldShowActions = message.role === "assistant" && !message.streaming && message.content && message.content.trim() && (actionsConfig == null ? void 0 : actionsConfig.enabled) !== false;
10359
10853
  if (shouldShowActions && actionsConfig) {
10360
10854
  const actions = createMessageActions(message, actionsConfig, actionCallbacks);
@@ -14025,7 +14519,7 @@ function buildDropOverlay(dropCfg) {
14025
14519
  return overlay;
14026
14520
  }
14027
14521
  var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
14028
- 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;
14522
+ 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;
14029
14523
  if (mount == null) {
14030
14524
  throw new Error(
14031
14525
  'createAgentExperience: mount must be a non-null HTMLElement (e.g. pass document.getElementById("my-root") after the node exists).'
@@ -14237,6 +14731,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
14237
14731
  leftActions,
14238
14732
  rightActions
14239
14733
  } = panelElements;
14734
+ let setSendButtonMode = panelElements.setSendButtonMode;
14240
14735
  let micButton = panelElements.micButton;
14241
14736
  let micButtonWrapper = panelElements.micButtonWrapper;
14242
14737
  let attachmentButton = panelElements.attachmentButton;
@@ -15069,12 +15564,24 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15069
15564
  const panelBorder = resolvePanelChrome(panelPartial == null ? void 0 : panelPartial.border, defaultPanelBorder);
15070
15565
  const panelShadow = resolvePanelChrome(panelPartial == null ? void 0 : panelPartial.shadow, defaultPanelShadow);
15071
15566
  const panelBorderRadius = resolvePanelChrome(panelPartial == null ? void 0 : panelPartial.borderRadius, defaultPanelBorderRadius);
15567
+ const prevBodyScrollTop = body.scrollTop;
15072
15568
  mount.style.cssText = "";
15073
15569
  wrapper.style.cssText = "";
15074
15570
  panel.style.cssText = "";
15075
15571
  container.style.cssText = "";
15076
15572
  body.style.cssText = "";
15077
15573
  footer.style.cssText = "";
15574
+ const restoreBodyScrollTop = () => {
15575
+ var _a3;
15576
+ if (prevBodyScrollTop <= 0) return;
15577
+ const ownerWindow3 = (_a3 = body.ownerDocument.defaultView) != null ? _a3 : window;
15578
+ ownerWindow3.requestAnimationFrame(() => {
15579
+ if (body.scrollTop === prevBodyScrollTop) return;
15580
+ const maxScrollTop = body.scrollHeight - body.clientHeight;
15581
+ if (maxScrollTop <= 0) return;
15582
+ body.scrollTop = Math.min(prevBodyScrollTop, maxScrollTop);
15583
+ });
15584
+ };
15078
15585
  if (shouldGoFullscreen) {
15079
15586
  wrapper.classList.remove(
15080
15587
  "persona-bottom-6",
@@ -15130,6 +15637,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15130
15637
  body.style.overflowY = "auto";
15131
15638
  footer.style.flexShrink = "0";
15132
15639
  wasMobileFullscreen = true;
15640
+ restoreBodyScrollTop();
15133
15641
  return;
15134
15642
  }
15135
15643
  const launcherWidth = (_r2 = (_q2 = config == null ? void 0 : config.launcher) == null ? void 0 : _q2.width) != null ? _r2 : config == null ? void 0 : config.launcherWidth;
@@ -15272,6 +15780,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15272
15780
  const zIndexStyles = !sidebarMode ? `z-index: ${(_w2 = (_v2 = config.launcher) == null ? void 0 : _v2.zIndex) != null ? _w2 : DEFAULT_OVERLAY_Z_INDEX} !important;` : "";
15273
15781
  wrapper.style.cssText += maxHeightStyles + paddingStyles + zIndexStyles;
15274
15782
  }
15783
+ restoreBodyScrollTop();
15275
15784
  };
15276
15785
  applyFullHeightStyles();
15277
15786
  applyThemeVariables(mount, config);
@@ -15335,6 +15844,17 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15335
15844
  cleanupThemeObserver = null;
15336
15845
  }
15337
15846
  });
15847
+ const streamAnimationConfig = (_D = config.features) == null ? void 0 : _D.streamAnimation;
15848
+ if ((streamAnimationConfig == null ? void 0 : streamAnimationConfig.type) && streamAnimationConfig.type !== "none") {
15849
+ const plugin = resolveStreamAnimationPlugin(
15850
+ streamAnimationConfig.type,
15851
+ streamAnimationConfig.plugins
15852
+ );
15853
+ if (plugin) {
15854
+ ensurePluginActive(plugin, mount);
15855
+ destroyCallbacks.push(() => detachAllPlugins(mount));
15856
+ }
15857
+ }
15338
15858
  const suggestionsManager = createSuggestions(suggestions);
15339
15859
  let closeHandler = null;
15340
15860
  let session;
@@ -15356,7 +15876,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
15356
15876
  lastUserMessageWasVoice: false,
15357
15877
  lastUserMessageId: null
15358
15878
  };
15359
- const voiceAutoResumeMode = (_E = (_D = config.voiceRecognition) == null ? void 0 : _D.autoResume) != null ? _E : false;
15879
+ const voiceAutoResumeMode = (_F = (_E = config.voiceRecognition) == null ? void 0 : _E.autoResume) != null ? _F : false;
15360
15880
  const emitVoiceState = (source) => {
15361
15881
  eventBus.emit("voice:state", {
15362
15882
  active: voiceState.active,
@@ -16044,7 +16564,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16044
16564
  });
16045
16565
  };
16046
16566
  const setComposerDisabled = (disabled) => {
16047
- sendButton.disabled = disabled;
16567
+ setSendButtonMode(disabled ? "stop" : "send");
16048
16568
  if (micButton) {
16049
16569
  micButton.disabled = disabled;
16050
16570
  }
@@ -16084,7 +16604,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16084
16604
  }
16085
16605
  }
16086
16606
  const useIcon = (_i2 = (_h2 = config.sendButton) == null ? void 0 : _h2.useIcon) != null ? _i2 : false;
16087
- if (!useIcon) {
16607
+ if (!useIcon && !(session == null ? void 0 : session.isStreaming())) {
16088
16608
  sendButton.textContent = (_k2 = (_j2 = config.copy) == null ? void 0 : _j2.sendButtonLabel) != null ? _k2 : "Send";
16089
16609
  }
16090
16610
  textarea.style.fontFamily = 'var(--persona-input-font-family, var(--persona-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif))';
@@ -16203,7 +16723,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16203
16723
  }
16204
16724
  });
16205
16725
  sessionRef.current = session;
16206
- if (((_G = (_F = config.voiceRecognition) == null ? void 0 : _F.provider) == null ? void 0 : _G.type) === "runtype") {
16726
+ if (((_H = (_G = config.voiceRecognition) == null ? void 0 : _G.provider) == null ? void 0 : _H.type) === "runtype") {
16207
16727
  try {
16208
16728
  session.setupVoice();
16209
16729
  } catch (err) {
@@ -16251,6 +16771,10 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16251
16771
  const handleSubmit = (event) => {
16252
16772
  var _a2;
16253
16773
  event.preventDefault();
16774
+ if (session.isStreaming()) {
16775
+ session.cancel();
16776
+ return;
16777
+ }
16254
16778
  const value = textarea.value.trim();
16255
16779
  const hasAttachments = (_a2 = attachmentManager == null ? void 0 : attachmentManager.hasAttachments()) != null ? _a2 : false;
16256
16780
  if (!value && !hasAttachments) return;
@@ -16805,7 +17329,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16805
17329
  }
16806
17330
  };
16807
17331
  recalcPanelHeight();
16808
- const ownerWindow = (_H = mount.ownerDocument.defaultView) != null ? _H : window;
17332
+ const ownerWindow = (_I = mount.ownerDocument.defaultView) != null ? _I : window;
16809
17333
  ownerWindow.addEventListener("resize", recalcPanelHeight);
16810
17334
  destroyCallbacks.push(() => ownerWindow.removeEventListener("resize", recalcPanelHeight));
16811
17335
  if (typeof ResizeObserver !== "undefined") {
@@ -16816,15 +17340,19 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
16816
17340
  destroyCallbacks.push(() => footerResizeObserver.disconnect());
16817
17341
  }
16818
17342
  lastScrollTop = body.scrollTop;
17343
+ let lastScrollHeight = body.scrollHeight;
16819
17344
  const handleScroll = () => {
16820
17345
  const scrollTop = body.scrollTop;
17346
+ const currentScrollHeight = body.scrollHeight;
17347
+ const scrollHeightShrank = currentScrollHeight < lastScrollHeight;
17348
+ lastScrollHeight = currentScrollHeight;
16821
17349
  const { action, nextLastScrollTop } = resolveFollowStateFromScroll({
16822
17350
  following: autoFollow.isFollowing(),
16823
17351
  currentScrollTop: scrollTop,
16824
17352
  lastScrollTop,
16825
17353
  nearBottom: isElementNearBottom(body, BOTTOM_THRESHOLD),
16826
17354
  userScrollThreshold: USER_SCROLL_THRESHOLD,
16827
- isAutoScrolling: isAutoScrolling || hasPendingAutoScroll,
17355
+ isAutoScrolling: isAutoScrolling || hasPendingAutoScroll || scrollHeightShrank,
16828
17356
  pauseOnUpwardScroll: true,
16829
17357
  pauseWhenAwayFromBottom: false,
16830
17358
  resumeRequiresDownwardScroll: true
@@ -17024,7 +17552,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
17024
17552
  }
17025
17553
  const controller = {
17026
17554
  update(nextConfig) {
17027
- 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;
17555
+ 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;
17028
17556
  const previousToolCallConfig = config.toolCall;
17029
17557
  const previousMessageActions = config.messageActions;
17030
17558
  const previousLayoutMessages = (_a2 = config.layout) == null ? void 0 : _a2.messages;
@@ -17235,7 +17763,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
17235
17763
  }
17236
17764
  const launcher = (_L2 = config.launcher) != null ? _L2 : {};
17237
17765
  const headerIconHidden = (_M2 = launcher.headerIconHidden) != null ? _M2 : false;
17238
- const layoutShowIcon = (_O = (_N2 = config.layout) == null ? void 0 : _N2.header) == null ? void 0 : _O.showIcon;
17766
+ const layoutShowIcon = (_O2 = (_N2 = config.layout) == null ? void 0 : _N2.header) == null ? void 0 : _O2.showIcon;
17239
17767
  const shouldHideIcon = headerIconHidden || layoutShowIcon === false;
17240
17768
  const headerIconName = launcher.headerIconName;
17241
17769
  const headerIconSize = (_P = launcher.headerIconSize) != null ? _P : "48px";
@@ -18306,7 +18834,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
18306
18834
  }
18307
18835
  }
18308
18836
  };
18309
- const shouldExposeDebugApi = ((_I = runtimeOptions == null ? void 0 : runtimeOptions.debugTools) != null ? _I : false) || Boolean(config.debug);
18837
+ const shouldExposeDebugApi = ((_J = runtimeOptions == null ? void 0 : runtimeOptions.debugTools) != null ? _J : false) || Boolean(config.debug);
18310
18838
  if (shouldExposeDebugApi && typeof window !== "undefined") {
18311
18839
  const previousDebug = window.AgentWidgetBrowser;
18312
18840
  const debugApi = {
@@ -18409,9 +18937,9 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
18409
18937
  const voiceKey = `${persistConfig.keyPrefix}widget-voice`;
18410
18938
  const voiceModeKey = `${persistConfig.keyPrefix}widget-voice-mode`;
18411
18939
  if (storage) {
18412
- const wasOpen = ((_J = persistConfig.persist) == null ? void 0 : _J.openState) && storage.getItem(openKey) === "true";
18413
- const wasVoiceActive = ((_K = persistConfig.persist) == null ? void 0 : _K.voiceState) && storage.getItem(voiceKey) === "true";
18414
- const wasInVoiceMode = ((_L = persistConfig.persist) == null ? void 0 : _L.voiceState) && storage.getItem(voiceModeKey) === "true";
18940
+ const wasOpen = ((_K = persistConfig.persist) == null ? void 0 : _K.openState) && storage.getItem(openKey) === "true";
18941
+ const wasVoiceActive = ((_L = persistConfig.persist) == null ? void 0 : _L.voiceState) && storage.getItem(voiceKey) === "true";
18942
+ const wasInVoiceMode = ((_M = persistConfig.persist) == null ? void 0 : _M.voiceState) && storage.getItem(voiceModeKey) === "true";
18415
18943
  if (wasOpen) {
18416
18944
  setTimeout(() => {
18417
18945
  controller.open();
@@ -18428,7 +18956,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
18428
18956
  }, 100);
18429
18957
  }, 0);
18430
18958
  }
18431
- if ((_M = persistConfig.persist) == null ? void 0 : _M.openState) {
18959
+ if ((_N = persistConfig.persist) == null ? void 0 : _N.openState) {
18432
18960
  eventBus.on("widget:opened", () => {
18433
18961
  storage.setItem(openKey, "true");
18434
18962
  });
@@ -18436,7 +18964,7 @@ var createAgentExperience = (mount, initialConfig, runtimeOptions) => {
18436
18964
  storage.setItem(openKey, "false");
18437
18965
  });
18438
18966
  }
18439
- if ((_N = persistConfig.persist) == null ? void 0 : _N.voiceState) {
18967
+ if ((_O = persistConfig.persist) == null ? void 0 : _O.voiceState) {
18440
18968
  eventBus.on("voice:state", (event) => {
18441
18969
  storage.setItem(voiceKey, event.active ? "true" : "false");
18442
18970
  });
@@ -19010,6 +19538,9 @@ function buildSrcdoc(mountId, shellMode, docked, widgetCssPath) {
19010
19538
  var PREVIEW_TRANSCRIPT_PRESET_LABELS = {
19011
19539
  "user-message": "User message",
19012
19540
  "assistant-message": "Assistant message",
19541
+ "assistant-code-block": "Assistant \u2014 code block",
19542
+ "assistant-markdown-table": "Assistant \u2014 markdown table",
19543
+ "assistant-image": "Assistant \u2014 image",
19013
19544
  "reasoning-streaming": "Reasoning (streaming)",
19014
19545
  "reasoning-complete": "Reasoning (complete)",
19015
19546
  "tool-running": "Tool call (running)",
@@ -19036,6 +19567,56 @@ function createPreviewTranscriptEntry(preset, index = 0) {
19036
19567
  content: "Absolutely. I can keep going and explain what happens next.",
19037
19568
  createdAt
19038
19569
  };
19570
+ case "assistant-code-block":
19571
+ return {
19572
+ id: `preview-seq-assistant-code-${suffix}`,
19573
+ role: "assistant",
19574
+ content: [
19575
+ "Here's how you'd wire up a streaming animation:",
19576
+ "",
19577
+ "```ts",
19578
+ "import { createAgentExperience } from '@runtypelabs/persona';",
19579
+ "",
19580
+ "createAgentExperience(el, {",
19581
+ " features: {",
19582
+ ' streamAnimation: { type: "letter-rise", speed: 120 },',
19583
+ " },",
19584
+ "});",
19585
+ "```",
19586
+ "",
19587
+ "Swap the `type` value to try the other presets."
19588
+ ].join("\n"),
19589
+ createdAt
19590
+ };
19591
+ case "assistant-markdown-table":
19592
+ return {
19593
+ id: `preview-seq-assistant-table-${suffix}`,
19594
+ role: "assistant",
19595
+ content: [
19596
+ "Here are the built-in streaming animations at a glance:",
19597
+ "",
19598
+ "| Preset | Wrap unit | Best for |",
19599
+ "| ------------ | --------- | --------------------------- |",
19600
+ "| Typewriter | Character | Classic terminal feel |",
19601
+ "| Letter rise | Character | Soft, staggered entrance |",
19602
+ "| Word fade | Word | Longer-form assistant replies |",
19603
+ "| Pop bubble | Bubble | Short, punchy affirmations |"
19604
+ ].join("\n"),
19605
+ createdAt
19606
+ };
19607
+ case "assistant-image":
19608
+ return {
19609
+ id: `preview-seq-assistant-image-${suffix}`,
19610
+ role: "assistant",
19611
+ content: [
19612
+ "Here's the reference diagram you asked for \u2014 let me know if you'd like a different view:",
19613
+ "",
19614
+ "![Stream animation reference](https://placehold.co/320x200/png?text=Stream+Animation)",
19615
+ "",
19616
+ "The gradient shows how per-unit delays stagger across the reply."
19617
+ ].join("\n"),
19618
+ createdAt
19619
+ };
19039
19620
  case "reasoning-streaming":
19040
19621
  return {
19041
19622
  id: `preview-seq-reasoning-stream-${suffix}`,
@@ -19103,6 +19684,38 @@ function createPreviewTranscriptEntry(preset, index = 0) {
19103
19684
  function appendPreviewTranscriptEntry(messages, preset) {
19104
19685
  return [...messages, createPreviewTranscriptEntry(preset, messages.length)];
19105
19686
  }
19687
+ function presetStreamsText(preset) {
19688
+ return preset === "assistant-message" || preset === "assistant-code-block" || preset === "assistant-markdown-table" || preset === "assistant-image";
19689
+ }
19690
+ function buildTranscriptStreamFrames(preset, suffix, options) {
19691
+ var _a, _b;
19692
+ const completed = createPreviewTranscriptEntry(preset, suffix);
19693
+ if (!presetStreamsText(preset) || typeof completed.content !== "string") {
19694
+ return [{ message: completed, delayMs: 0, done: true }];
19695
+ }
19696
+ const chunkSize = Math.max(1, (_a = options == null ? void 0 : options.chunkSize) != null ? _a : 24);
19697
+ const delayMs = Math.max(0, (_b = options == null ? void 0 : options.delayMs) != null ? _b : 42);
19698
+ const fullText = completed.content;
19699
+ const frames = [];
19700
+ frames.push({
19701
+ message: { ...completed, content: "", streaming: true },
19702
+ delayMs: 0,
19703
+ done: false
19704
+ });
19705
+ for (let i = chunkSize; i < fullText.length; i += chunkSize) {
19706
+ frames.push({
19707
+ message: { ...completed, content: fullText.slice(0, i), streaming: true },
19708
+ delayMs,
19709
+ done: false
19710
+ });
19711
+ }
19712
+ frames.push({
19713
+ message: { ...completed, content: fullText, streaming: false },
19714
+ delayMs,
19715
+ done: true
19716
+ });
19717
+ return frames;
19718
+ }
19106
19719
  var createAdvancedTranscriptPreviewMessages = () => [
19107
19720
  {
19108
19721
  id: "preview-adv-1",
@@ -19553,6 +20166,7 @@ function createThemePreview(container, initialOptions) {
19553
20166
  buildPreviewConfigWithMessages,
19554
20167
  buildShellCss,
19555
20168
  buildSrcdoc,
20169
+ buildTranscriptStreamFrames,
19556
20170
  convertFromPx,
19557
20171
  convertToPx,
19558
20172
  createPreviewMessages,
@@ -19572,6 +20186,7 @@ function createThemePreview(container, initialOptions) {
19572
20186
  normalizeColorValue,
19573
20187
  paletteColorPath,
19574
20188
  parseCssValue,
20189
+ presetStreamsText,
19575
20190
  resolveRoleAssignment,
19576
20191
  resolveThemeColorPath,
19577
20192
  scopeSection,