@8wave/ai-elements 0.118.0 → 0.119.0-beta.1

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 (79) hide show
  1. package/dist/_chunks/{PkStreamingMarkdown--CcBvs19.js → PkStreamingMarkdown-kpEBZA4d.js} +789 -780
  2. package/dist/_chunks/PkStreamingMarkdown-kpEBZA4d.js.map +1 -0
  3. package/dist/_chunks/{PkToolShowArtifact-BVGN7hV2.js → PkToolShowArtifact-B8TOTS6A.js} +2 -2
  4. package/dist/_chunks/{PkToolShowArtifact-BVGN7hV2.js.map → PkToolShowArtifact-B8TOTS6A.js.map} +1 -1
  5. package/dist/_chunks/{PkToolShowContactForm-Blrwqpl0.js → PkToolShowContactForm-B89p05XM.js} +2 -2
  6. package/dist/_chunks/{PkToolShowContactForm-Blrwqpl0.js.map → PkToolShowContactForm-B89p05XM.js.map} +1 -1
  7. package/dist/_chunks/{PkToolShowForm-BpGHaN6x.js → PkToolShowForm-DTKQtFij.js} +4 -4
  8. package/dist/_chunks/{PkToolShowForm-BpGHaN6x.js.map → PkToolShowForm-DTKQtFij.js.map} +1 -1
  9. package/dist/_chunks/{PkToolShowSources-8c9RvGV1.js → PkToolShowSources-CbwixpOb.js} +2 -2
  10. package/dist/_chunks/{PkToolShowSources-8c9RvGV1.js.map → PkToolShowSources-CbwixpOb.js.map} +1 -1
  11. package/dist/ai-elements.es.js +1888 -1863
  12. package/dist/ai-elements.es.js.map +1 -1
  13. package/dist-vue/PkChatbot.js +1 -1
  14. package/dist-vue/PkChatbotEmptyState.js +1 -1
  15. package/dist-vue/PkChatbotInput.js +1 -1
  16. package/dist-vue/PkChatbotMessages.js +1 -1
  17. package/dist-vue/PkChatbotStepPayload.js +1 -1
  18. package/dist-vue/PkChatbotStepToolDetail.js +1 -1
  19. package/dist-vue/PkChatbotSteps.js +1 -1
  20. package/dist-vue/PkChatbotViewChat.js +1 -1
  21. package/dist-vue/PkChatbotViewProfile.js +1 -1
  22. package/dist-vue/_chunks/PkAvatar-BTqu8tn9.js +44 -0
  23. package/dist-vue/_chunks/PkAvatar-BTqu8tn9.js.map +1 -0
  24. package/dist-vue/_chunks/PkChatbot-C_OzxC0s.js +276 -0
  25. package/dist-vue/_chunks/PkChatbot-C_OzxC0s.js.map +1 -0
  26. package/dist-vue/_chunks/{PkChatbotEmptyState-D2dhInfG.js → PkChatbotEmptyState-BEQCnqui.js} +3 -3
  27. package/dist-vue/_chunks/{PkChatbotEmptyState-D2dhInfG.js.map → PkChatbotEmptyState-BEQCnqui.js.map} +1 -1
  28. package/dist-vue/_chunks/{PkChatbotInput-CQdxFbBR.js → PkChatbotInput-KJxWrDIT.js} +2 -2
  29. package/dist-vue/_chunks/{PkChatbotInput-CQdxFbBR.js.map → PkChatbotInput-KJxWrDIT.js.map} +1 -1
  30. package/dist-vue/_chunks/{PkChatbotMessages-coscJcpy.js → PkChatbotMessages-Zhuxom46.js} +283 -283
  31. package/dist-vue/_chunks/{PkChatbotMessages-coscJcpy.js.map → PkChatbotMessages-Zhuxom46.js.map} +1 -1
  32. package/dist-vue/_chunks/{PkChatbotStepPayload-DnqFluQZ.js → PkChatbotStepPayload-Da4N5CKa.js} +2 -2
  33. package/dist-vue/_chunks/{PkChatbotStepPayload-DnqFluQZ.js.map → PkChatbotStepPayload-Da4N5CKa.js.map} +1 -1
  34. package/dist-vue/_chunks/{PkChatbotStepToolDetail-BMslY_BG.js → PkChatbotStepToolDetail-rrtImvq5.js} +3 -3
  35. package/dist-vue/_chunks/{PkChatbotStepToolDetail-BMslY_BG.js.map → PkChatbotStepToolDetail-rrtImvq5.js.map} +1 -1
  36. package/dist-vue/_chunks/{PkChatbotSteps-7FT6Ju6_.js → PkChatbotSteps-LEfOolAc.js} +38 -38
  37. package/dist-vue/_chunks/{PkChatbotSteps-7FT6Ju6_.js.map → PkChatbotSteps-LEfOolAc.js.map} +1 -1
  38. package/dist-vue/_chunks/{PkChatbotViewChat-Cko-Cw6c.js → PkChatbotViewChat-BJ0YHCvA.js} +7 -7
  39. package/dist-vue/_chunks/PkChatbotViewChat-BJ0YHCvA.js.map +1 -0
  40. package/dist-vue/_chunks/{PkChatbotViewProfile-B57VRVaO.js → PkChatbotViewProfile-CyzRxE3D.js} +2 -2
  41. package/dist-vue/_chunks/{PkChatbotViewProfile-B57VRVaO.js.map → PkChatbotViewProfile-CyzRxE3D.js.map} +1 -1
  42. package/dist-vue/_chunks/{PkStreamingMarkdown-BtyWXY2k.js → PkStreamingMarkdown-BWEvuFZm.js} +2 -2
  43. package/dist-vue/_chunks/{PkStreamingMarkdown-BtyWXY2k.js.map → PkStreamingMarkdown-BWEvuFZm.js.map} +1 -1
  44. package/dist-vue/_chunks/{PkToolShowArtifact-Ritx54_X.js → PkToolShowArtifact-tNEG6-r_.js} +2 -2
  45. package/dist-vue/_chunks/{PkToolShowArtifact-Ritx54_X.js.map → PkToolShowArtifact-tNEG6-r_.js.map} +1 -1
  46. package/dist-vue/_chunks/{PkToolShowContactForm-D30IsZGy.js → PkToolShowContactForm-DBC6f9gR.js} +2 -2
  47. package/dist-vue/_chunks/{PkToolShowContactForm-D30IsZGy.js.map → PkToolShowContactForm-DBC6f9gR.js.map} +1 -1
  48. package/dist-vue/_chunks/{PkToolShowDiagram-PEozSAqU.js → PkToolShowDiagram-Bbq0ETt1.js} +2 -2
  49. package/dist-vue/_chunks/{PkToolShowDiagram-PEozSAqU.js.map → PkToolShowDiagram-Bbq0ETt1.js.map} +1 -1
  50. package/dist-vue/_chunks/{PkToolShowForm-ZmdM9J9x.js → PkToolShowForm-BG-8aTHy.js} +5 -5
  51. package/dist-vue/_chunks/{PkToolShowForm-ZmdM9J9x.js.map → PkToolShowForm-BG-8aTHy.js.map} +1 -1
  52. package/dist-vue/_chunks/{PkToolShowImageGallery-Dkjf7Tw_.js → PkToolShowImageGallery-DXn-bxHv.js} +2 -2
  53. package/dist-vue/_chunks/{PkToolShowImageGallery-Dkjf7Tw_.js.map → PkToolShowImageGallery-DXn-bxHv.js.map} +1 -1
  54. package/dist-vue/_chunks/{PkToolShowProductList-CsHYM8vl.js → PkToolShowProductList-BZ_fDRCq.js} +2 -2
  55. package/dist-vue/_chunks/{PkToolShowProductList-CsHYM8vl.js.map → PkToolShowProductList-BZ_fDRCq.js.map} +1 -1
  56. package/dist-vue/_chunks/{PkToolShowSources-CKZkcPLA.js → PkToolShowSources-CUURb0PG.js} +2 -2
  57. package/dist-vue/_chunks/{PkToolShowSources-CKZkcPLA.js.map → PkToolShowSources-CUURb0PG.js.map} +1 -1
  58. package/dist-vue/_chunks/{PkToolShowVideo-CKm1fVQs.js → PkToolShowVideo-FNlPjswi.js} +2 -2
  59. package/dist-vue/_chunks/{PkToolShowVideo-CKm1fVQs.js.map → PkToolShowVideo-FNlPjswi.js.map} +1 -1
  60. package/dist-vue/_chunks/{PkToolShowWebPages-DLEC9cuh.js → PkToolShowWebPages-CWV79tsb.js} +2 -2
  61. package/dist-vue/_chunks/{PkToolShowWebPages-DLEC9cuh.js.map → PkToolShowWebPages-CWV79tsb.js.map} +1 -1
  62. package/dist-vue/_chunks/{utils-fxHiCnzf.js → utils-Dk2jFXR_.js} +40 -31
  63. package/dist-vue/_chunks/utils-Dk2jFXR_.js.map +1 -0
  64. package/dist-vue/apps/web-component/src/composables/useChatbotAgent.d.ts +1 -4
  65. package/dist-vue/composables.js +132 -113
  66. package/dist-vue/composables.js.map +1 -1
  67. package/dist-vue/index.js +23 -23
  68. package/dist-vue/index.js.map +1 -1
  69. package/dist-vue/packages/components/src/chat/PkAvatar.d.ts +7 -0
  70. package/dist-vue/packages/components/src/chat/utils.d.ts +10 -0
  71. package/dist-vue/style.css +1 -1
  72. package/package.json +1 -1
  73. package/dist/_chunks/PkStreamingMarkdown--CcBvs19.js.map +0 -1
  74. package/dist-vue/_chunks/PkAvatar-ZgCsoO-p.js +0 -42
  75. package/dist-vue/_chunks/PkAvatar-ZgCsoO-p.js.map +0 -1
  76. package/dist-vue/_chunks/PkChatbot-Cm45hwYw.js +0 -275
  77. package/dist-vue/_chunks/PkChatbot-Cm45hwYw.js.map +0 -1
  78. package/dist-vue/_chunks/PkChatbotViewChat-Cko-Cw6c.js.map +0 -1
  79. package/dist-vue/_chunks/utils-fxHiCnzf.js.map +0 -1
@@ -1,11 +1,11 @@
1
- import { t as e } from "./PkStreamingMarkdown-BtyWXY2k.js";
1
+ import { t as e } from "./PkStreamingMarkdown-BWEvuFZm.js";
2
2
  import { l as t, s as n } from "./message-BOiF6Bt5.js";
3
- import { _ as r, d as i, f as a, g as o, h as ee, l as s, m as c, n as l } from "./utils-fxHiCnzf.js";
4
- import { t as u } from "./PkChatbotStepPayload-DnqFluQZ.js";
5
- import { t as d } from "./PkChatbotStepToolDetail-BMslY_BG.js";
3
+ import { _ as r, f as i, g as a, h as o, n as s, p as c, u as l, v as ee } from "./utils-Dk2jFXR_.js";
4
+ import { t as u } from "./PkChatbotStepPayload-Da4N5CKa.js";
5
+ import { t as d } from "./PkChatbotStepToolDetail-rrtImvq5.js";
6
6
  import { VvIcon as f } from "@volverjs/ui-vue/components";
7
- import { Fragment as p, computed as m, createBlock as h, createCommentVNode as g, createElementBlock as _, createElementVNode as v, createTextVNode as y, createVNode as b, defineComponent as x, normalizeClass as S, openBlock as C, reactive as w, ref as T, renderList as E, resolveDynamicComponent as D, toDisplayString as O, unref as k, watch as te, withCtx as ne } from "vue";
8
- import { useI18n as re } from "vue-i18n";
7
+ import { Fragment as p, computed as m, createBlock as h, createCommentVNode as g, createElementBlock as _, createElementVNode as v, createTextVNode as y, createVNode as b, defineComponent as x, normalizeClass as S, openBlock as C, reactive as w, ref as T, renderList as E, resolveDynamicComponent as te, toDisplayString as D, unref as O, watch as ne, withCtx as re } from "vue";
8
+ import { useI18n as k } from "vue-i18n";
9
9
  import { useResizeObserver as A } from "@vueuse/core";
10
10
  //#region ../../packages/components/src/chat/PkStreamingMarkdownAutoscroll.vue?vue&type=script&setup=true&lang.ts
11
11
  var j = 8, M = /* @__PURE__ */ x({
@@ -70,81 +70,81 @@ var j = 8, M = /* @__PURE__ */ x({
70
70
  isDark: { type: Boolean }
71
71
  },
72
72
  setup(x) {
73
- let A = x, { t: j, n: G } = re({ useScope: "global" }), K = (e) => a(e) === "output-error" && "errorText" in e && typeof e.errorText == "string" ? e.errorText : void 0, q = m(() => {
74
- let e = A.message.parts, a = A.message.metadata?.partTimings, s = [];
73
+ let A = x, { t: j, n: G } = k({ useScope: "global" }), K = (e) => c(e) === "output-error" && "errorText" in e && typeof e.errorText == "string" ? e.errorText : void 0, q = m(() => {
74
+ let e = A.message.parts, s = A.message.metadata?.partTimings, c = [];
75
75
  for (let [l, u] of e.entries()) {
76
76
  let d = n(u), f = t(u);
77
77
  if (!d && !f) continue;
78
- let p = "toolCallId" in u ? u.toolCallId : void 0, m = d && u.text.trim() ? u.text : void 0, h = K(u), g = r(u), _ = f && "input" in u ? u.input : void 0, v = f && "output" in u ? u.output : void 0;
79
- s.push({
78
+ let p = "toolCallId" in u ? u.toolCallId : void 0, m = d && u.text.trim() ? u.text : void 0, h = K(u), g = ee(u), _ = f && "input" in u ? u.input : void 0, v = f && "output" in u ? u.output : void 0;
79
+ c.push({
80
80
  key: p ?? `${u.type}-${l}`,
81
81
  part: u,
82
82
  icon: i(u),
83
- label: c(j, u),
84
- summary: ee(u),
85
- durationMs: p ? a?.[p] : void 0,
83
+ label: o(j, u),
84
+ summary: a(u),
85
+ durationMs: p ? s?.[p] : void 0,
86
86
  isActive: !!A.isStreaming && l === e.length - 1,
87
87
  text: m,
88
88
  error: h,
89
89
  hasRichDetail: g,
90
- hasBody: !!(m || h || (g ? _ !== void 0 || v !== void 0 : o(_) || o(v))),
90
+ hasBody: !!(m || h || (g ? _ !== void 0 || v !== void 0 : r(_) || r(v))),
91
91
  toolInput: _,
92
92
  toolOutput: v
93
93
  });
94
94
  }
95
- return s;
95
+ return c;
96
96
  }), J = m(() => {
97
97
  let { createdAt: e, completedAt: t } = A.message.metadata ?? {};
98
98
  if (e && t) return t - e;
99
- }), Y = (e) => s(G, e), X = w(/* @__PURE__ */ new Map()), Z = (e) => X.get(e.key) ?? !1, ie = (e) => {
99
+ }), Y = (e) => l(G, e), X = w(/* @__PURE__ */ new Map()), Z = (e) => X.get(e.key) ?? !1, ie = (e) => {
100
100
  X.set(e.key, !Z(e));
101
101
  }, Q = T(void 0), $ = m(() => Q.value ?? (!!A.isStreaming && !A.hasContent));
102
- return te(() => A.isStreaming, (e) => {
102
+ return ne(() => A.isStreaming, (e) => {
103
103
  e && (Q.value = void 0, X.clear());
104
104
  }), (t, n) => {
105
- let r = f;
105
+ let i = f;
106
106
  return q.value.length ? (C(), _("div", N, [v("button", {
107
107
  type: "button",
108
108
  class: "pk-chatbot-steps__header",
109
109
  "aria-expanded": $.value,
110
110
  onClick: n[0] ||= (e) => Q.value = !$.value
111
111
  }, [
112
- x.isStreaming ? (C(), h(r, {
112
+ x.isStreaming ? (C(), h(i, {
113
113
  key: 0,
114
114
  name: "loading-loop",
115
115
  class: "shrink-0"
116
- })) : (C(), h(r, {
116
+ })) : (C(), h(i, {
117
117
  key: 1,
118
118
  name: "ri:route-line",
119
119
  class: "shrink-0"
120
120
  })),
121
- v("span", F, O(k(j)("messagePart.steps")), 1),
122
- v("span", I, [y(O(k(j)("messagePart.stepCount", q.value.length)) + " ", 1), Y(J.value) ? (C(), _(p, { key: 0 }, [y(" · " + O(Y(J.value)), 1)], 64)) : g("", !0)]),
123
- b(r, {
121
+ v("span", F, D(O(j)("messagePart.steps")), 1),
122
+ v("span", I, [y(D(O(j)("messagePart.stepCount", q.value.length)) + " ", 1), Y(J.value) ? (C(), _(p, { key: 0 }, [y(" · " + D(Y(J.value)), 1)], 64)) : g("", !0)]),
123
+ b(i, {
124
124
  name: $.value ? "ri:arrow-up-s-line" : "ri:arrow-down-s-line",
125
125
  class: "pk-chatbot-steps__chevron shrink-0 ml-auto"
126
126
  }, null, 8, ["name"])
127
127
  ], 8, P), $.value ? (C(), _("ol", L, [(C(!0), _(p, null, E(q.value, (t) => (C(), _("li", {
128
128
  key: t.key,
129
129
  class: S(["pk-chatbot-steps__step", { "pk-chatbot-steps__step--active": t.isActive }])
130
- }, [(C(), h(D(t.hasBody ? "button" : "div"), {
130
+ }, [(C(), h(te(t.hasBody ? "button" : "div"), {
131
131
  type: t.hasBody ? "button" : void 0,
132
132
  class: "pk-chatbot-steps__step-header",
133
133
  "aria-expanded": t.hasBody ? Z(t) : void 0,
134
134
  onClick: (e) => t.hasBody ? ie(t) : void 0
135
135
  }, {
136
- default: ne(() => [
137
- v("span", { class: S(["pk-chatbot-steps-marker", { "pk-chatbot-steps-marker--active": t.isActive }]) }, [t.isActive ? (C(), h(r, {
136
+ default: re(() => [
137
+ v("span", { class: S(["pk-chatbot-steps-marker", { "pk-chatbot-steps-marker--active": t.isActive }]) }, [t.isActive ? (C(), h(i, {
138
138
  key: 0,
139
139
  name: "loading-loop"
140
- })) : t.icon ? (C(), h(r, {
140
+ })) : t.icon ? (C(), h(i, {
141
141
  key: 1,
142
142
  name: t.icon
143
143
  }, null, 8, ["name"])) : g("", !0)], 2),
144
- v("span", R, O(t.label), 1),
145
- v("span", z, O(t.summary), 1),
146
- Y(t.durationMs) ? (C(), _("span", B, O(Y(t.durationMs)), 1)) : g("", !0),
147
- t.hasBody ? (C(), h(r, {
144
+ v("span", R, D(t.label), 1),
145
+ v("span", z, D(t.summary), 1),
146
+ Y(t.durationMs) ? (C(), _("span", B, D(Y(t.durationMs)), 1)) : g("", !0),
147
+ t.hasBody ? (C(), h(i, {
148
148
  key: 1,
149
149
  name: Z(t) ? "ri:arrow-up-s-line" : "ri:arrow-down-s-line",
150
150
  class: "pk-chatbot-steps__chevron shrink-0 ml-auto"
@@ -159,7 +159,7 @@ var j = 8, M = /* @__PURE__ */ x({
159
159
  key: 0,
160
160
  markdown: t.text,
161
161
  "is-dark": x.isDark,
162
- "tail-length": k(l),
162
+ "tail-length": O(s),
163
163
  "inner-class": "wysiwyg",
164
164
  class: "pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll"
165
165
  }, null, 8, [
@@ -170,16 +170,16 @@ var j = 8, M = /* @__PURE__ */ x({
170
170
  markdown: t.text,
171
171
  "is-dark": x.isDark,
172
172
  class: "wysiwyg"
173
- }, null, 8, ["markdown", "is-dark"])])) : t.error ? (C(), _("div", U, [v("span", W, O(k(j)("messagePart.stepError")), 1), v("pre", null, O(t.error), 1)])) : t.hasRichDetail ? (C(), h(d, {
173
+ }, null, 8, ["markdown", "is-dark"])])) : t.error ? (C(), _("div", U, [v("span", W, D(O(j)("messagePart.stepError")), 1), v("pre", null, D(t.error), 1)])) : t.hasRichDetail ? (C(), h(d, {
174
174
  key: 3,
175
175
  part: t.part
176
- }, null, 8, ["part"])) : (C(), _(p, { key: 4 }, [k(o)(t.toolInput) ? (C(), h(u, {
176
+ }, null, 8, ["part"])) : (C(), _(p, { key: 4 }, [O(r)(t.toolInput) ? (C(), h(u, {
177
177
  key: 0,
178
- label: k(j)("messagePart.stepInput"),
178
+ label: O(j)("messagePart.stepInput"),
179
179
  value: t.toolInput
180
- }, null, 8, ["label", "value"])) : g("", !0), k(o)(t.toolOutput) ? (C(), h(u, {
180
+ }, null, 8, ["label", "value"])) : g("", !0), O(r)(t.toolOutput) ? (C(), h(u, {
181
181
  key: 1,
182
- label: k(j)("messagePart.stepOutput"),
182
+ label: O(j)("messagePart.stepOutput"),
183
183
  value: t.toolOutput
184
184
  }, null, 8, ["label", "value"])) : g("", !0)], 64))])) : g("", !0)], 2))), 128))])) : g("", !0)])) : g("", !0);
185
185
  };
@@ -188,4 +188,4 @@ var j = 8, M = /* @__PURE__ */ x({
188
188
  //#endregion
189
189
  export { M as n, G as t };
190
190
 
191
- //# sourceMappingURL=PkChatbotSteps-7FT6Ju6_.js.map
191
+ //# sourceMappingURL=PkChatbotSteps-LEfOolAc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkChatbotSteps-7FT6Ju6_.js","names":["_openBlock","_createElementBlock","_createVNode","PkStreamingMarkdown","_normalizeClass","_sfc_main","formatDuration","formatDurationWith","__unplugin_components_0","_openBlock","_createElementBlock","_createElementVNode","_createBlock","_toDisplayString","_unref","_createTextVNode","_Fragment","_createCommentVNode","_createVNode","_renderList","_normalizeClass","_resolveDynamicComponent","_withCtx","PkStreamingMarkdownAutoscroll","PkStreamingMarkdown","PkChatbotStepToolDetail","PkChatbotStepPayload","_sfc_main","PkStreamingMarkdown_default","isToolPart","isReasoningPart","hasRichToolDetail","getPartIcon","getPartState","hasPayload","getToolPartSummary","formatDuration","getToolPartLabel","LIVE_REASONING_TAIL_LENGTH","PkChatbotStepPayload_default","PkChatbotStepToolDetail_default","NEAR_BOTTOM_PX","PkStreamingMarkdownAutoscroll_default","defineComponent","scrollEl","ref","contentEl","stopAutoScroll","followedTop","handleScroll","el","useResizeObserver","openBlock","createElementBlock","createVNode","__props","normalizeClass","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","PkChatbotSteps_default","props","$t","$n","useI18n","getStepError","part","steps","computed","parts","partTimings","result","index","isReasoning","isTool","toolCallId","text","error","hasRichDetail","toolInput","toolOutput","totalDurationMs","createdAt","completedAt","formatDuration$1","ms","manualSteps","reactive","isStepExpanded","step","toggleStep","manualGroup","isGroupExpanded","watch","streaming","_component_VvIcon","VvIcon","createElementVNode","_cache","createBlock","toDisplayString","unref","createTextVNode","Fragment","createCommentVNode","renderList","resolveDynamicComponent","withCtx"],"sources":["../../../../packages/components/src/chat/PkStreamingMarkdownAutoscroll.vue","../../../../packages/components/src/chat/PkStreamingMarkdownAutoscroll.vue","../../../../packages/components/src/chat/PkChatbotSteps.vue","../../../../packages/components/src/chat/PkChatbotSteps.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { ref } from 'vue'\n import { useResizeObserver } from '@vueuse/core'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n defineProps<{\n markdown?: string\n innerClass?: string\n isDark?: boolean\n tailLength?: number\n }>()\n\n // Within this distance of the bottom the reader counts as being there.\n const NEAR_BOTTOM_PX = 8\n\n const scrollEl = ref<HTMLDivElement>()\n const contentEl = ref<InstanceType<typeof PkStreamingMarkdown>>()\n let stopAutoScroll = false\n // Where the last follow left the box: a scroll event reporting it is the\n // box's own, even when the content grew in the meantime.\n let followedTop: number | undefined\n\n // The follow stops when the reader leaves the bottom and resumes when they\n // are back. Measured from the bottom, not from the previous position: a\n // tail slides, so its height shrinks as often as it grows, and a shrink\n // moves the position up without the reader doing anything.\n const handleScroll = () => {\n const el = scrollEl.value\n if (!el || el.scrollTop === followedTop) {\n return\n }\n followedTop = undefined\n stopAutoScroll =\n el.scrollHeight - el.clientHeight - el.scrollTop > NEAR_BOTTOM_PX\n }\n\n // Driven by sizes, not by the prop: the markdown renders at most once per\n // interval, code, formulas and images grow after it, and the box itself\n // can be resized.\n const scrollToBottom = () => {\n const el = scrollEl.value\n if (!el) {\n return\n }\n // Content that fits leaves nothing to have scrolled away from, and no\n // scroll event would ever say the reader is back.\n if (el.scrollHeight <= el.clientHeight) {\n stopAutoScroll = false\n }\n if (stopAutoScroll) {\n return\n }\n el.scrollTop = el.scrollHeight\n followedTop = el.scrollTop\n }\n\n useResizeObserver([scrollEl, contentEl], scrollToBottom)\n</script>\n\n<template>\n <div\n ref=\"scrollEl\"\n class=\"overflow-auto light-scrollbar\"\n @scroll=\"handleScroll\">\n <PkStreamingMarkdown\n ref=\"contentEl\"\n :markdown\n :is-dark\n :tail-length\n :class=\"innerClass\" />\n </div>\n</template>\n","<script lang=\"ts\" setup>\n import { ref } from 'vue'\n import { useResizeObserver } from '@vueuse/core'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n defineProps<{\n markdown?: string\n innerClass?: string\n isDark?: boolean\n tailLength?: number\n }>()\n\n // Within this distance of the bottom the reader counts as being there.\n const NEAR_BOTTOM_PX = 8\n\n const scrollEl = ref<HTMLDivElement>()\n const contentEl = ref<InstanceType<typeof PkStreamingMarkdown>>()\n let stopAutoScroll = false\n // Where the last follow left the box: a scroll event reporting it is the\n // box's own, even when the content grew in the meantime.\n let followedTop: number | undefined\n\n // The follow stops when the reader leaves the bottom and resumes when they\n // are back. Measured from the bottom, not from the previous position: a\n // tail slides, so its height shrinks as often as it grows, and a shrink\n // moves the position up without the reader doing anything.\n const handleScroll = () => {\n const el = scrollEl.value\n if (!el || el.scrollTop === followedTop) {\n return\n }\n followedTop = undefined\n stopAutoScroll =\n el.scrollHeight - el.clientHeight - el.scrollTop > NEAR_BOTTOM_PX\n }\n\n // Driven by sizes, not by the prop: the markdown renders at most once per\n // interval, code, formulas and images grow after it, and the box itself\n // can be resized.\n const scrollToBottom = () => {\n const el = scrollEl.value\n if (!el) {\n return\n }\n // Content that fits leaves nothing to have scrolled away from, and no\n // scroll event would ever say the reader is back.\n if (el.scrollHeight <= el.clientHeight) {\n stopAutoScroll = false\n }\n if (stopAutoScroll) {\n return\n }\n el.scrollTop = el.scrollHeight\n followedTop = el.scrollTop\n }\n\n useResizeObserver([scrollEl, contentEl], scrollToBottom)\n</script>\n\n<template>\n <div\n ref=\"scrollEl\"\n class=\"overflow-auto light-scrollbar\"\n @scroll=\"handleScroll\">\n <PkStreamingMarkdown\n ref=\"contentEl\"\n :markdown\n :is-dark\n :tail-length\n :class=\"innerClass\" />\n </div>\n</template>\n","<script lang=\"ts\" setup>\n import { computed, reactive, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { UIChatMessage } from 'models'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkStreamingMarkdownAutoscroll from './PkStreamingMarkdownAutoscroll.vue'\n import PkChatbotStepToolDetail from './PkChatbotStepToolDetail.vue'\n import PkChatbotStepPayload from './PkChatbotStepPayload.vue'\n import {\n isReasoningPart,\n isToolPart,\n getPartIcon,\n getPartState,\n getToolPartLabel,\n getToolPartSummary,\n hasRichToolDetail,\n hasPayload,\n formatDuration as formatDurationWith,\n LIVE_REASONING_TAIL_LENGTH,\n } from './utils'\n\n const props = defineProps<{\n message: UIChatMessage\n isStreaming?: boolean\n // Something is drawn below the timeline (a text, a card, a tool).\n hasContent?: boolean\n isDark?: boolean\n }>()\n\n const { t: $t, n: $n } = useI18n({\n useScope: 'global',\n })\n\n type MessagePart = UIChatMessage['parts'][number]\n\n interface Step {\n key: string\n part: MessagePart\n icon?: string\n label?: string\n summary?: string\n durationMs?: number\n isActive: boolean\n text?: string\n error?: string\n hasRichDetail: boolean\n hasBody: boolean\n toolInput?: unknown\n toolOutput?: unknown\n }\n\n const getStepError = (part: MessagePart) =>\n getPartState(part) === 'output-error' &&\n 'errorText' in part &&\n typeof part.errorText === 'string'\n ? part.errorText\n : undefined\n\n // What the step header needs is decided here, once per recomputation.\n // `hasBody` was a function the template called six times per step, and\n // `steps` recomputes at every delta of the stream: on a tool answering\n // hundreds of kilobytes, where the answer comes from `hasPayload` and\n // `hasPayload` serializes the whole payload to find out, that is the same\n // JSON.stringify six times for one boolean.\n //\n // The two payload blocks keep asking for themselves, because they render\n // only inside an expanded step: deciding here whether to show the output\n // would serialize it for every step the reader never opens, which is all\n // of them by default.\n const steps = computed<Step[]>(() => {\n const parts = props.message.parts\n const partTimings = props.message.metadata?.partTimings\n const result: Step[] = []\n for (const [index, part] of parts.entries()) {\n const isReasoning = isReasoningPart(part)\n const isTool = isToolPart(part)\n if (!isReasoning && !isTool) {\n continue\n }\n const toolCallId =\n 'toolCallId' in part ? (part.toolCallId as string) : undefined\n const text = isReasoning && part.text.trim() ? part.text : undefined\n const error = getStepError(part)\n const hasRichDetail = hasRichToolDetail(part)\n const toolInput = isTool && 'input' in part ? part.input : undefined\n const toolOutput =\n isTool && 'output' in part ? part.output : undefined\n result.push({\n key: toolCallId ?? `${part.type}-${index}`,\n part,\n icon: getPartIcon(part),\n label: getToolPartLabel($t, part),\n summary: getToolPartSummary(part),\n durationMs: toolCallId ? partTimings?.[toolCallId] : undefined,\n isActive:\n Boolean(props.isStreaming) && index === parts.length - 1,\n text,\n error,\n hasRichDetail,\n // The `||` short-circuits as the old function did: a tool\n // with an input never serializes its output to answer this.\n hasBody: Boolean(\n text ||\n error ||\n (hasRichDetail\n ? toolInput !== undefined || toolOutput !== undefined\n : hasPayload(toolInput) || hasPayload(toolOutput)),\n ),\n toolInput,\n toolOutput,\n })\n }\n return result\n })\n\n const totalDurationMs = computed(() => {\n const { createdAt, completedAt } = props.message.metadata ?? {}\n if (!createdAt || !completedAt) {\n return undefined\n }\n return completedAt - createdAt\n })\n\n const formatDuration = (ms?: number) => formatDurationWith($n, ms)\n\n // --- Expand / collapse ---\n // Every step starts closed, the running one included: the timeline says\n // what the answer is doing, the body is opened only when asked for. The\n // reasoning of the active step still follows the stream once opened.\n const manualSteps = reactive(new Map<string, boolean>())\n const isStepExpanded = (step: Step) => manualSteps.get(step.key) ?? false\n const toggleStep = (step: Step) => {\n manualSteps.set(step.key, !isStepExpanded(step))\n }\n\n // The group is open while streaming and collapses to its header as soon\n // as the answer draws something below it, unless the user toggled it\n // manually: from then on the activity reads at the bottom of the answer,\n // and an open list growing above it would push the answer down.\n const manualGroup = ref<boolean | undefined>(undefined)\n const isGroupExpanded = computed(\n () =>\n manualGroup.value ??\n (Boolean(props.isStreaming) && !props.hasContent),\n )\n // Reset the manual override when a new stream starts so the timeline\n // opens again on regenerate.\n watch(\n () => props.isStreaming,\n (streaming) => {\n if (streaming) {\n manualGroup.value = undefined\n manualSteps.clear()\n }\n },\n )\n</script>\n\n<template>\n <div v-if=\"steps.length\" class=\"pk-chatbot-steps\">\n <button\n type=\"button\"\n class=\"pk-chatbot-steps__header\"\n :aria-expanded=\"isGroupExpanded\"\n @click=\"manualGroup = !isGroupExpanded\">\n <VvIcon v-if=\"isStreaming\" name=\"loading-loop\" class=\"shrink-0\" />\n <VvIcon v-else name=\"ri:route-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-steps__title\">\n {{ $t('messagePart.steps') }}\n </span>\n <span class=\"pk-chatbot-steps__meta\">\n {{ $t('messagePart.stepCount', steps.length) }}\n <template v-if=\"formatDuration(totalDurationMs)\">\n · {{ formatDuration(totalDurationMs) }}\n </template>\n </span>\n <VvIcon\n :name=\"\n isGroupExpanded\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"pk-chatbot-steps__chevron shrink-0 ml-auto\" />\n </button>\n <ol v-if=\"isGroupExpanded\" class=\"pk-chatbot-steps__list\">\n <li\n v-for=\"step in steps\"\n :key=\"step.key\"\n class=\"pk-chatbot-steps__step\"\n :class=\"{\n 'pk-chatbot-steps__step--active': step.isActive,\n }\">\n <component\n :is=\"step.hasBody ? 'button' : 'div'\"\n :type=\"step.hasBody ? 'button' : undefined\"\n class=\"pk-chatbot-steps__step-header\"\n :aria-expanded=\"\n step.hasBody ? isStepExpanded(step) : undefined\n \"\n @click=\"step.hasBody ? toggleStep(step) : undefined\">\n <span\n class=\"pk-chatbot-steps-marker\"\n :class=\"{\n 'pk-chatbot-steps-marker--active': step.isActive,\n }\">\n <VvIcon v-if=\"step.isActive\" name=\"loading-loop\" />\n <VvIcon v-else-if=\"step.icon\" :name=\"step.icon\" />\n </span>\n <span class=\"pk-chatbot-steps__step-label\">\n {{ step.label }}\n </span>\n <span class=\"pk-chatbot-steps__step-summary\">\n {{ step.summary }}\n </span>\n <span\n v-if=\"formatDuration(step.durationMs)\"\n class=\"pk-chatbot-steps__step-duration\">\n {{ formatDuration(step.durationMs) }}\n </span>\n <VvIcon\n v-if=\"step.hasBody\"\n :name=\"\n isStepExpanded(step)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"pk-chatbot-steps__chevron shrink-0 ml-auto\" />\n </component>\n <div\n v-if=\"step.hasBody && isStepExpanded(step)\"\n class=\"pk-chatbot-steps__step-body\">\n <PkStreamingMarkdownAutoscroll\n v-if=\"step.text && step.isActive\"\n :markdown=\"step.text\"\n :is-dark\n :tail-length=\"LIVE_REASONING_TAIL_LENGTH\"\n inner-class=\"wysiwyg\"\n class=\"pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll\" />\n <div\n v-else-if=\"step.text\"\n class=\"pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll light-scrollbar\">\n <PkStreamingMarkdown\n :markdown=\"step.text\"\n :is-dark\n class=\"wysiwyg\" />\n </div>\n <div\n v-else-if=\"step.error\"\n class=\"pk-chatbot-steps__step-payload pk-chatbot-steps__step-payload--error\">\n <span class=\"pk-chatbot-steps__step-payload-label\">\n {{ $t('messagePart.stepError') }}\n </span>\n <pre>{{ step.error }}</pre>\n </div>\n <PkChatbotStepToolDetail\n v-else-if=\"step.hasRichDetail\"\n :part=\"step.part\" />\n <template v-else>\n <PkChatbotStepPayload\n v-if=\"hasPayload(step.toolInput)\"\n :label=\"$t('messagePart.stepInput')\"\n :value=\"step.toolInput\" />\n <PkChatbotStepPayload\n v-if=\"hasPayload(step.toolOutput)\"\n :label=\"$t('messagePart.stepOutput')\"\n :value=\"step.toolOutput\" />\n </template>\n </div>\n </li>\n </ol>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-steps {\n width: 100%;\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded-lg);\n background-color: var(--color-surface-1);\n font-size: var(--text-12);\n color: var(--color-word-3);\n overflow: hidden;\n\n &__header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: 100%;\n min-height: var(--spacing-40);\n padding: var(--spacing-8) var(--spacing-sm);\n text-align: left;\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\n &__chevron {\n font-size: var(--text-16);\n }\n\n &__title {\n font-weight: 600;\n color: var(--color-word-2);\n flex-grow: 1;\n }\n\n &__meta {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__list {\n background-color: var(--color-surface);\n border-top: var(--spacing-px) solid var(--color-surface-3);\n padding: var(--spacing-8) var(--spacing-sm);\n display: flex;\n flex-direction: column;\n }\n\n &__step {\n position: relative;\n\n // Vertical connector between step markers\n &:not(:last-child)::before {\n content: '';\n position: absolute;\n left: var(--spacing-10);\n top: var(--spacing-28);\n bottom: calc(var(--spacing-8) * -1);\n width: var(--spacing-px);\n background-color: var(--color-surface-4);\n }\n }\n\n &__step-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: calc(100% + var(--spacing-16));\n min-height: var(--spacing-36);\n margin-inline: calc(var(--spacing-8) * -1);\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n\n &:is(button) {\n cursor: pointer;\n border-radius: var(--rounded);\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &-marker {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-20);\n height: var(--spacing-20);\n flex-shrink: 0;\n position: relative;\n z-index: 1;\n background-color: var(--color-surface-1);\n border-radius: var(--rounded-full);\n border: var(--spacing-px) solid var(--color-surface-4);\n\n &--active {\n background-color: var(\n --chatbot-main-color,\n var(--color-surface-4)\n );\n border-color: transparent;\n color: var(--chatbot-contrast-color, var(--color-word-1));\n }\n }\n\n &__step-label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-2);\n }\n\n &__step-summary {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-4);\n }\n\n &__step-duration {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__step--active &__step-label {\n color: var(--color-word-1);\n }\n\n &__step-body {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4);\n margin-left: var(--spacing-28);\n margin-block: var(--spacing-4) var(--spacing-8);\n }\n\n &__step-content {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n padding: var(--spacing-8);\n background-color: var(--color-surface-1);\n\n // The finished reasoning keeps the cap the streaming one had:\n // `.wysiwyg` hides its own overflow, so the scroll belongs to the\n // wrapper, exactly as in the autoscroll variant.\n &--scroll {\n max-height: var(--spacing-256);\n overflow: auto;\n }\n }\n\n &__step-payload {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2);\n\n pre {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n padding: var(--spacing-8);\n font-size: var(--text-10);\n max-height: var(--spacing-160);\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: var(--font-mono);\n background-color: var(--color-surface-1);\n }\n }\n\n &__step-payload-label {\n font-weight: 600;\n color: var(--color-word-3);\n text-transform: uppercase;\n font-size: var(--text-10);\n letter-spacing: 0.05em;\n }\n\n &__step-payload--error {\n .pk-chatbot-steps__step-payload-label,\n pre {\n color: var(--u-danger-text, var(--color-danger));\n }\n\n pre {\n border-color: var(--color-danger-lighten-5);\n }\n }\n }\n</style>\n","<script lang=\"ts\" setup>\n import { computed, reactive, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { UIChatMessage } from 'models'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkStreamingMarkdownAutoscroll from './PkStreamingMarkdownAutoscroll.vue'\n import PkChatbotStepToolDetail from './PkChatbotStepToolDetail.vue'\n import PkChatbotStepPayload from './PkChatbotStepPayload.vue'\n import {\n isReasoningPart,\n isToolPart,\n getPartIcon,\n getPartState,\n getToolPartLabel,\n getToolPartSummary,\n hasRichToolDetail,\n hasPayload,\n formatDuration as formatDurationWith,\n LIVE_REASONING_TAIL_LENGTH,\n } from './utils'\n\n const props = defineProps<{\n message: UIChatMessage\n isStreaming?: boolean\n // Something is drawn below the timeline (a text, a card, a tool).\n hasContent?: boolean\n isDark?: boolean\n }>()\n\n const { t: $t, n: $n } = useI18n({\n useScope: 'global',\n })\n\n type MessagePart = UIChatMessage['parts'][number]\n\n interface Step {\n key: string\n part: MessagePart\n icon?: string\n label?: string\n summary?: string\n durationMs?: number\n isActive: boolean\n text?: string\n error?: string\n hasRichDetail: boolean\n hasBody: boolean\n toolInput?: unknown\n toolOutput?: unknown\n }\n\n const getStepError = (part: MessagePart) =>\n getPartState(part) === 'output-error' &&\n 'errorText' in part &&\n typeof part.errorText === 'string'\n ? part.errorText\n : undefined\n\n // What the step header needs is decided here, once per recomputation.\n // `hasBody` was a function the template called six times per step, and\n // `steps` recomputes at every delta of the stream: on a tool answering\n // hundreds of kilobytes, where the answer comes from `hasPayload` and\n // `hasPayload` serializes the whole payload to find out, that is the same\n // JSON.stringify six times for one boolean.\n //\n // The two payload blocks keep asking for themselves, because they render\n // only inside an expanded step: deciding here whether to show the output\n // would serialize it for every step the reader never opens, which is all\n // of them by default.\n const steps = computed<Step[]>(() => {\n const parts = props.message.parts\n const partTimings = props.message.metadata?.partTimings\n const result: Step[] = []\n for (const [index, part] of parts.entries()) {\n const isReasoning = isReasoningPart(part)\n const isTool = isToolPart(part)\n if (!isReasoning && !isTool) {\n continue\n }\n const toolCallId =\n 'toolCallId' in part ? (part.toolCallId as string) : undefined\n const text = isReasoning && part.text.trim() ? part.text : undefined\n const error = getStepError(part)\n const hasRichDetail = hasRichToolDetail(part)\n const toolInput = isTool && 'input' in part ? part.input : undefined\n const toolOutput =\n isTool && 'output' in part ? part.output : undefined\n result.push({\n key: toolCallId ?? `${part.type}-${index}`,\n part,\n icon: getPartIcon(part),\n label: getToolPartLabel($t, part),\n summary: getToolPartSummary(part),\n durationMs: toolCallId ? partTimings?.[toolCallId] : undefined,\n isActive:\n Boolean(props.isStreaming) && index === parts.length - 1,\n text,\n error,\n hasRichDetail,\n // The `||` short-circuits as the old function did: a tool\n // with an input never serializes its output to answer this.\n hasBody: Boolean(\n text ||\n error ||\n (hasRichDetail\n ? toolInput !== undefined || toolOutput !== undefined\n : hasPayload(toolInput) || hasPayload(toolOutput)),\n ),\n toolInput,\n toolOutput,\n })\n }\n return result\n })\n\n const totalDurationMs = computed(() => {\n const { createdAt, completedAt } = props.message.metadata ?? {}\n if (!createdAt || !completedAt) {\n return undefined\n }\n return completedAt - createdAt\n })\n\n const formatDuration = (ms?: number) => formatDurationWith($n, ms)\n\n // --- Expand / collapse ---\n // Every step starts closed, the running one included: the timeline says\n // what the answer is doing, the body is opened only when asked for. The\n // reasoning of the active step still follows the stream once opened.\n const manualSteps = reactive(new Map<string, boolean>())\n const isStepExpanded = (step: Step) => manualSteps.get(step.key) ?? false\n const toggleStep = (step: Step) => {\n manualSteps.set(step.key, !isStepExpanded(step))\n }\n\n // The group is open while streaming and collapses to its header as soon\n // as the answer draws something below it, unless the user toggled it\n // manually: from then on the activity reads at the bottom of the answer,\n // and an open list growing above it would push the answer down.\n const manualGroup = ref<boolean | undefined>(undefined)\n const isGroupExpanded = computed(\n () =>\n manualGroup.value ??\n (Boolean(props.isStreaming) && !props.hasContent),\n )\n // Reset the manual override when a new stream starts so the timeline\n // opens again on regenerate.\n watch(\n () => props.isStreaming,\n (streaming) => {\n if (streaming) {\n manualGroup.value = undefined\n manualSteps.clear()\n }\n },\n )\n</script>\n\n<template>\n <div v-if=\"steps.length\" class=\"pk-chatbot-steps\">\n <button\n type=\"button\"\n class=\"pk-chatbot-steps__header\"\n :aria-expanded=\"isGroupExpanded\"\n @click=\"manualGroup = !isGroupExpanded\">\n <VvIcon v-if=\"isStreaming\" name=\"loading-loop\" class=\"shrink-0\" />\n <VvIcon v-else name=\"ri:route-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-steps__title\">\n {{ $t('messagePart.steps') }}\n </span>\n <span class=\"pk-chatbot-steps__meta\">\n {{ $t('messagePart.stepCount', steps.length) }}\n <template v-if=\"formatDuration(totalDurationMs)\">\n · {{ formatDuration(totalDurationMs) }}\n </template>\n </span>\n <VvIcon\n :name=\"\n isGroupExpanded\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"pk-chatbot-steps__chevron shrink-0 ml-auto\" />\n </button>\n <ol v-if=\"isGroupExpanded\" class=\"pk-chatbot-steps__list\">\n <li\n v-for=\"step in steps\"\n :key=\"step.key\"\n class=\"pk-chatbot-steps__step\"\n :class=\"{\n 'pk-chatbot-steps__step--active': step.isActive,\n }\">\n <component\n :is=\"step.hasBody ? 'button' : 'div'\"\n :type=\"step.hasBody ? 'button' : undefined\"\n class=\"pk-chatbot-steps__step-header\"\n :aria-expanded=\"\n step.hasBody ? isStepExpanded(step) : undefined\n \"\n @click=\"step.hasBody ? toggleStep(step) : undefined\">\n <span\n class=\"pk-chatbot-steps-marker\"\n :class=\"{\n 'pk-chatbot-steps-marker--active': step.isActive,\n }\">\n <VvIcon v-if=\"step.isActive\" name=\"loading-loop\" />\n <VvIcon v-else-if=\"step.icon\" :name=\"step.icon\" />\n </span>\n <span class=\"pk-chatbot-steps__step-label\">\n {{ step.label }}\n </span>\n <span class=\"pk-chatbot-steps__step-summary\">\n {{ step.summary }}\n </span>\n <span\n v-if=\"formatDuration(step.durationMs)\"\n class=\"pk-chatbot-steps__step-duration\">\n {{ formatDuration(step.durationMs) }}\n </span>\n <VvIcon\n v-if=\"step.hasBody\"\n :name=\"\n isStepExpanded(step)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"pk-chatbot-steps__chevron shrink-0 ml-auto\" />\n </component>\n <div\n v-if=\"step.hasBody && isStepExpanded(step)\"\n class=\"pk-chatbot-steps__step-body\">\n <PkStreamingMarkdownAutoscroll\n v-if=\"step.text && step.isActive\"\n :markdown=\"step.text\"\n :is-dark\n :tail-length=\"LIVE_REASONING_TAIL_LENGTH\"\n inner-class=\"wysiwyg\"\n class=\"pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll\" />\n <div\n v-else-if=\"step.text\"\n class=\"pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll light-scrollbar\">\n <PkStreamingMarkdown\n :markdown=\"step.text\"\n :is-dark\n class=\"wysiwyg\" />\n </div>\n <div\n v-else-if=\"step.error\"\n class=\"pk-chatbot-steps__step-payload pk-chatbot-steps__step-payload--error\">\n <span class=\"pk-chatbot-steps__step-payload-label\">\n {{ $t('messagePart.stepError') }}\n </span>\n <pre>{{ step.error }}</pre>\n </div>\n <PkChatbotStepToolDetail\n v-else-if=\"step.hasRichDetail\"\n :part=\"step.part\" />\n <template v-else>\n <PkChatbotStepPayload\n v-if=\"hasPayload(step.toolInput)\"\n :label=\"$t('messagePart.stepInput')\"\n :value=\"step.toolInput\" />\n <PkChatbotStepPayload\n v-if=\"hasPayload(step.toolOutput)\"\n :label=\"$t('messagePart.stepOutput')\"\n :value=\"step.toolOutput\" />\n </template>\n </div>\n </li>\n </ol>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-steps {\n width: 100%;\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded-lg);\n background-color: var(--color-surface-1);\n font-size: var(--text-12);\n color: var(--color-word-3);\n overflow: hidden;\n\n &__header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: 100%;\n min-height: var(--spacing-40);\n padding: var(--spacing-8) var(--spacing-sm);\n text-align: left;\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\n &__chevron {\n font-size: var(--text-16);\n }\n\n &__title {\n font-weight: 600;\n color: var(--color-word-2);\n flex-grow: 1;\n }\n\n &__meta {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__list {\n background-color: var(--color-surface);\n border-top: var(--spacing-px) solid var(--color-surface-3);\n padding: var(--spacing-8) var(--spacing-sm);\n display: flex;\n flex-direction: column;\n }\n\n &__step {\n position: relative;\n\n // Vertical connector between step markers\n &:not(:last-child)::before {\n content: '';\n position: absolute;\n left: var(--spacing-10);\n top: var(--spacing-28);\n bottom: calc(var(--spacing-8) * -1);\n width: var(--spacing-px);\n background-color: var(--color-surface-4);\n }\n }\n\n &__step-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: calc(100% + var(--spacing-16));\n min-height: var(--spacing-36);\n margin-inline: calc(var(--spacing-8) * -1);\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n\n &:is(button) {\n cursor: pointer;\n border-radius: var(--rounded);\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &-marker {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-20);\n height: var(--spacing-20);\n flex-shrink: 0;\n position: relative;\n z-index: 1;\n background-color: var(--color-surface-1);\n border-radius: var(--rounded-full);\n border: var(--spacing-px) solid var(--color-surface-4);\n\n &--active {\n background-color: var(\n --chatbot-main-color,\n var(--color-surface-4)\n );\n border-color: transparent;\n color: var(--chatbot-contrast-color, var(--color-word-1));\n }\n }\n\n &__step-label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-2);\n }\n\n &__step-summary {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-4);\n }\n\n &__step-duration {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__step--active &__step-label {\n color: var(--color-word-1);\n }\n\n &__step-body {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4);\n margin-left: var(--spacing-28);\n margin-block: var(--spacing-4) var(--spacing-8);\n }\n\n &__step-content {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n padding: var(--spacing-8);\n background-color: var(--color-surface-1);\n\n // The finished reasoning keeps the cap the streaming one had:\n // `.wysiwyg` hides its own overflow, so the scroll belongs to the\n // wrapper, exactly as in the autoscroll variant.\n &--scroll {\n max-height: var(--spacing-256);\n overflow: auto;\n }\n }\n\n &__step-payload {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2);\n\n pre {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n padding: var(--spacing-8);\n font-size: var(--text-10);\n max-height: var(--spacing-160);\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: var(--font-mono);\n background-color: var(--color-surface-1);\n }\n }\n\n &__step-payload-label {\n font-weight: 600;\n color: var(--color-word-3);\n text-transform: uppercase;\n font-size: var(--text-10);\n letter-spacing: 0.05em;\n }\n\n &__step-payload--error {\n .pk-chatbot-steps__step-payload-label,\n pre {\n color: var(--u-danger-text, var(--color-danger));\n }\n\n pre {\n border-color: var(--color-danger-lighten-5);\n }\n }\n }\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkChatbotSteps-LEfOolAc.js","names":["_openBlock","_createElementBlock","_createVNode","PkStreamingMarkdown","_normalizeClass","_sfc_main","formatDuration","formatDurationWith","__unplugin_components_0","_openBlock","_createElementBlock","_createElementVNode","_createBlock","_toDisplayString","_unref","_createTextVNode","_Fragment","_createCommentVNode","_createVNode","_renderList","_normalizeClass","_resolveDynamicComponent","_withCtx","PkStreamingMarkdownAutoscroll","PkStreamingMarkdown","PkChatbotStepToolDetail","PkChatbotStepPayload","_sfc_main","PkStreamingMarkdown_default","isToolPart","isReasoningPart","hasPayload","getPartIcon","getToolPartSummary","getToolPartLabel","LIVE_REASONING_TAIL_LENGTH","getPartState","formatDuration","hasRichToolDetail","PkChatbotStepPayload_default","PkChatbotStepToolDetail_default","NEAR_BOTTOM_PX","PkStreamingMarkdownAutoscroll_default","defineComponent","scrollEl","ref","contentEl","stopAutoScroll","followedTop","handleScroll","el","useResizeObserver","openBlock","createElementBlock","createVNode","__props","normalizeClass","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","PkChatbotSteps_default","props","$t","$n","useI18n","getStepError","part","steps","computed","parts","partTimings","result","index","isReasoning","isTool","toolCallId","text","error","hasRichDetail","toolInput","toolOutput","totalDurationMs","createdAt","completedAt","formatDuration$1","ms","manualSteps","reactive","isStepExpanded","step","toggleStep","manualGroup","isGroupExpanded","watch","streaming","_component_VvIcon","VvIcon","createElementVNode","_cache","createBlock","toDisplayString","unref","createTextVNode","Fragment","createCommentVNode","renderList","resolveDynamicComponent","withCtx"],"sources":["../../../../packages/components/src/chat/PkStreamingMarkdownAutoscroll.vue","../../../../packages/components/src/chat/PkStreamingMarkdownAutoscroll.vue","../../../../packages/components/src/chat/PkChatbotSteps.vue","../../../../packages/components/src/chat/PkChatbotSteps.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { ref } from 'vue'\n import { useResizeObserver } from '@vueuse/core'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n defineProps<{\n markdown?: string\n innerClass?: string\n isDark?: boolean\n tailLength?: number\n }>()\n\n // Within this distance of the bottom the reader counts as being there.\n const NEAR_BOTTOM_PX = 8\n\n const scrollEl = ref<HTMLDivElement>()\n const contentEl = ref<InstanceType<typeof PkStreamingMarkdown>>()\n let stopAutoScroll = false\n // Where the last follow left the box: a scroll event reporting it is the\n // box's own, even when the content grew in the meantime.\n let followedTop: number | undefined\n\n // The follow stops when the reader leaves the bottom and resumes when they\n // are back. Measured from the bottom, not from the previous position: a\n // tail slides, so its height shrinks as often as it grows, and a shrink\n // moves the position up without the reader doing anything.\n const handleScroll = () => {\n const el = scrollEl.value\n if (!el || el.scrollTop === followedTop) {\n return\n }\n followedTop = undefined\n stopAutoScroll =\n el.scrollHeight - el.clientHeight - el.scrollTop > NEAR_BOTTOM_PX\n }\n\n // Driven by sizes, not by the prop: the markdown renders at most once per\n // interval, code, formulas and images grow after it, and the box itself\n // can be resized.\n const scrollToBottom = () => {\n const el = scrollEl.value\n if (!el) {\n return\n }\n // Content that fits leaves nothing to have scrolled away from, and no\n // scroll event would ever say the reader is back.\n if (el.scrollHeight <= el.clientHeight) {\n stopAutoScroll = false\n }\n if (stopAutoScroll) {\n return\n }\n el.scrollTop = el.scrollHeight\n followedTop = el.scrollTop\n }\n\n useResizeObserver([scrollEl, contentEl], scrollToBottom)\n</script>\n\n<template>\n <div\n ref=\"scrollEl\"\n class=\"overflow-auto light-scrollbar\"\n @scroll=\"handleScroll\">\n <PkStreamingMarkdown\n ref=\"contentEl\"\n :markdown\n :is-dark\n :tail-length\n :class=\"innerClass\" />\n </div>\n</template>\n","<script lang=\"ts\" setup>\n import { ref } from 'vue'\n import { useResizeObserver } from '@vueuse/core'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n defineProps<{\n markdown?: string\n innerClass?: string\n isDark?: boolean\n tailLength?: number\n }>()\n\n // Within this distance of the bottom the reader counts as being there.\n const NEAR_BOTTOM_PX = 8\n\n const scrollEl = ref<HTMLDivElement>()\n const contentEl = ref<InstanceType<typeof PkStreamingMarkdown>>()\n let stopAutoScroll = false\n // Where the last follow left the box: a scroll event reporting it is the\n // box's own, even when the content grew in the meantime.\n let followedTop: number | undefined\n\n // The follow stops when the reader leaves the bottom and resumes when they\n // are back. Measured from the bottom, not from the previous position: a\n // tail slides, so its height shrinks as often as it grows, and a shrink\n // moves the position up without the reader doing anything.\n const handleScroll = () => {\n const el = scrollEl.value\n if (!el || el.scrollTop === followedTop) {\n return\n }\n followedTop = undefined\n stopAutoScroll =\n el.scrollHeight - el.clientHeight - el.scrollTop > NEAR_BOTTOM_PX\n }\n\n // Driven by sizes, not by the prop: the markdown renders at most once per\n // interval, code, formulas and images grow after it, and the box itself\n // can be resized.\n const scrollToBottom = () => {\n const el = scrollEl.value\n if (!el) {\n return\n }\n // Content that fits leaves nothing to have scrolled away from, and no\n // scroll event would ever say the reader is back.\n if (el.scrollHeight <= el.clientHeight) {\n stopAutoScroll = false\n }\n if (stopAutoScroll) {\n return\n }\n el.scrollTop = el.scrollHeight\n followedTop = el.scrollTop\n }\n\n useResizeObserver([scrollEl, contentEl], scrollToBottom)\n</script>\n\n<template>\n <div\n ref=\"scrollEl\"\n class=\"overflow-auto light-scrollbar\"\n @scroll=\"handleScroll\">\n <PkStreamingMarkdown\n ref=\"contentEl\"\n :markdown\n :is-dark\n :tail-length\n :class=\"innerClass\" />\n </div>\n</template>\n","<script lang=\"ts\" setup>\n import { computed, reactive, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { UIChatMessage } from 'models'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkStreamingMarkdownAutoscroll from './PkStreamingMarkdownAutoscroll.vue'\n import PkChatbotStepToolDetail from './PkChatbotStepToolDetail.vue'\n import PkChatbotStepPayload from './PkChatbotStepPayload.vue'\n import {\n isReasoningPart,\n isToolPart,\n getPartIcon,\n getPartState,\n getToolPartLabel,\n getToolPartSummary,\n hasRichToolDetail,\n hasPayload,\n formatDuration as formatDurationWith,\n LIVE_REASONING_TAIL_LENGTH,\n } from './utils'\n\n const props = defineProps<{\n message: UIChatMessage\n isStreaming?: boolean\n // Something is drawn below the timeline (a text, a card, a tool).\n hasContent?: boolean\n isDark?: boolean\n }>()\n\n const { t: $t, n: $n } = useI18n({\n useScope: 'global',\n })\n\n type MessagePart = UIChatMessage['parts'][number]\n\n interface Step {\n key: string\n part: MessagePart\n icon?: string\n label?: string\n summary?: string\n durationMs?: number\n isActive: boolean\n text?: string\n error?: string\n hasRichDetail: boolean\n hasBody: boolean\n toolInput?: unknown\n toolOutput?: unknown\n }\n\n const getStepError = (part: MessagePart) =>\n getPartState(part) === 'output-error' &&\n 'errorText' in part &&\n typeof part.errorText === 'string'\n ? part.errorText\n : undefined\n\n // What the step header needs is decided here, once per recomputation.\n // `hasBody` was a function the template called six times per step, and\n // `steps` recomputes at every delta of the stream: on a tool answering\n // hundreds of kilobytes, where the answer comes from `hasPayload` and\n // `hasPayload` serializes the whole payload to find out, that is the same\n // JSON.stringify six times for one boolean.\n //\n // The two payload blocks keep asking for themselves, because they render\n // only inside an expanded step: deciding here whether to show the output\n // would serialize it for every step the reader never opens, which is all\n // of them by default.\n const steps = computed<Step[]>(() => {\n const parts = props.message.parts\n const partTimings = props.message.metadata?.partTimings\n const result: Step[] = []\n for (const [index, part] of parts.entries()) {\n const isReasoning = isReasoningPart(part)\n const isTool = isToolPart(part)\n if (!isReasoning && !isTool) {\n continue\n }\n const toolCallId =\n 'toolCallId' in part ? (part.toolCallId as string) : undefined\n const text = isReasoning && part.text.trim() ? part.text : undefined\n const error = getStepError(part)\n const hasRichDetail = hasRichToolDetail(part)\n const toolInput = isTool && 'input' in part ? part.input : undefined\n const toolOutput =\n isTool && 'output' in part ? part.output : undefined\n result.push({\n key: toolCallId ?? `${part.type}-${index}`,\n part,\n icon: getPartIcon(part),\n label: getToolPartLabel($t, part),\n summary: getToolPartSummary(part),\n durationMs: toolCallId ? partTimings?.[toolCallId] : undefined,\n isActive:\n Boolean(props.isStreaming) && index === parts.length - 1,\n text,\n error,\n hasRichDetail,\n // The `||` short-circuits as the old function did: a tool\n // with an input never serializes its output to answer this.\n hasBody: Boolean(\n text ||\n error ||\n (hasRichDetail\n ? toolInput !== undefined || toolOutput !== undefined\n : hasPayload(toolInput) || hasPayload(toolOutput)),\n ),\n toolInput,\n toolOutput,\n })\n }\n return result\n })\n\n const totalDurationMs = computed(() => {\n const { createdAt, completedAt } = props.message.metadata ?? {}\n if (!createdAt || !completedAt) {\n return undefined\n }\n return completedAt - createdAt\n })\n\n const formatDuration = (ms?: number) => formatDurationWith($n, ms)\n\n // --- Expand / collapse ---\n // Every step starts closed, the running one included: the timeline says\n // what the answer is doing, the body is opened only when asked for. The\n // reasoning of the active step still follows the stream once opened.\n const manualSteps = reactive(new Map<string, boolean>())\n const isStepExpanded = (step: Step) => manualSteps.get(step.key) ?? false\n const toggleStep = (step: Step) => {\n manualSteps.set(step.key, !isStepExpanded(step))\n }\n\n // The group is open while streaming and collapses to its header as soon\n // as the answer draws something below it, unless the user toggled it\n // manually: from then on the activity reads at the bottom of the answer,\n // and an open list growing above it would push the answer down.\n const manualGroup = ref<boolean | undefined>(undefined)\n const isGroupExpanded = computed(\n () =>\n manualGroup.value ??\n (Boolean(props.isStreaming) && !props.hasContent),\n )\n // Reset the manual override when a new stream starts so the timeline\n // opens again on regenerate.\n watch(\n () => props.isStreaming,\n (streaming) => {\n if (streaming) {\n manualGroup.value = undefined\n manualSteps.clear()\n }\n },\n )\n</script>\n\n<template>\n <div v-if=\"steps.length\" class=\"pk-chatbot-steps\">\n <button\n type=\"button\"\n class=\"pk-chatbot-steps__header\"\n :aria-expanded=\"isGroupExpanded\"\n @click=\"manualGroup = !isGroupExpanded\">\n <VvIcon v-if=\"isStreaming\" name=\"loading-loop\" class=\"shrink-0\" />\n <VvIcon v-else name=\"ri:route-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-steps__title\">\n {{ $t('messagePart.steps') }}\n </span>\n <span class=\"pk-chatbot-steps__meta\">\n {{ $t('messagePart.stepCount', steps.length) }}\n <template v-if=\"formatDuration(totalDurationMs)\">\n · {{ formatDuration(totalDurationMs) }}\n </template>\n </span>\n <VvIcon\n :name=\"\n isGroupExpanded\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"pk-chatbot-steps__chevron shrink-0 ml-auto\" />\n </button>\n <ol v-if=\"isGroupExpanded\" class=\"pk-chatbot-steps__list\">\n <li\n v-for=\"step in steps\"\n :key=\"step.key\"\n class=\"pk-chatbot-steps__step\"\n :class=\"{\n 'pk-chatbot-steps__step--active': step.isActive,\n }\">\n <component\n :is=\"step.hasBody ? 'button' : 'div'\"\n :type=\"step.hasBody ? 'button' : undefined\"\n class=\"pk-chatbot-steps__step-header\"\n :aria-expanded=\"\n step.hasBody ? isStepExpanded(step) : undefined\n \"\n @click=\"step.hasBody ? toggleStep(step) : undefined\">\n <span\n class=\"pk-chatbot-steps-marker\"\n :class=\"{\n 'pk-chatbot-steps-marker--active': step.isActive,\n }\">\n <VvIcon v-if=\"step.isActive\" name=\"loading-loop\" />\n <VvIcon v-else-if=\"step.icon\" :name=\"step.icon\" />\n </span>\n <span class=\"pk-chatbot-steps__step-label\">\n {{ step.label }}\n </span>\n <span class=\"pk-chatbot-steps__step-summary\">\n {{ step.summary }}\n </span>\n <span\n v-if=\"formatDuration(step.durationMs)\"\n class=\"pk-chatbot-steps__step-duration\">\n {{ formatDuration(step.durationMs) }}\n </span>\n <VvIcon\n v-if=\"step.hasBody\"\n :name=\"\n isStepExpanded(step)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"pk-chatbot-steps__chevron shrink-0 ml-auto\" />\n </component>\n <div\n v-if=\"step.hasBody && isStepExpanded(step)\"\n class=\"pk-chatbot-steps__step-body\">\n <PkStreamingMarkdownAutoscroll\n v-if=\"step.text && step.isActive\"\n :markdown=\"step.text\"\n :is-dark\n :tail-length=\"LIVE_REASONING_TAIL_LENGTH\"\n inner-class=\"wysiwyg\"\n class=\"pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll\" />\n <div\n v-else-if=\"step.text\"\n class=\"pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll light-scrollbar\">\n <PkStreamingMarkdown\n :markdown=\"step.text\"\n :is-dark\n class=\"wysiwyg\" />\n </div>\n <div\n v-else-if=\"step.error\"\n class=\"pk-chatbot-steps__step-payload pk-chatbot-steps__step-payload--error\">\n <span class=\"pk-chatbot-steps__step-payload-label\">\n {{ $t('messagePart.stepError') }}\n </span>\n <pre>{{ step.error }}</pre>\n </div>\n <PkChatbotStepToolDetail\n v-else-if=\"step.hasRichDetail\"\n :part=\"step.part\" />\n <template v-else>\n <PkChatbotStepPayload\n v-if=\"hasPayload(step.toolInput)\"\n :label=\"$t('messagePart.stepInput')\"\n :value=\"step.toolInput\" />\n <PkChatbotStepPayload\n v-if=\"hasPayload(step.toolOutput)\"\n :label=\"$t('messagePart.stepOutput')\"\n :value=\"step.toolOutput\" />\n </template>\n </div>\n </li>\n </ol>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-steps {\n width: 100%;\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded-lg);\n background-color: var(--color-surface-1);\n font-size: var(--text-12);\n color: var(--color-word-3);\n overflow: hidden;\n\n &__header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: 100%;\n min-height: var(--spacing-40);\n padding: var(--spacing-8) var(--spacing-sm);\n text-align: left;\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\n &__chevron {\n font-size: var(--text-16);\n }\n\n &__title {\n font-weight: 600;\n color: var(--color-word-2);\n flex-grow: 1;\n }\n\n &__meta {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__list {\n background-color: var(--color-surface);\n border-top: var(--spacing-px) solid var(--color-surface-3);\n padding: var(--spacing-8) var(--spacing-sm);\n display: flex;\n flex-direction: column;\n }\n\n &__step {\n position: relative;\n\n // Vertical connector between step markers\n &:not(:last-child)::before {\n content: '';\n position: absolute;\n left: var(--spacing-10);\n top: var(--spacing-28);\n bottom: calc(var(--spacing-8) * -1);\n width: var(--spacing-px);\n background-color: var(--color-surface-4);\n }\n }\n\n &__step-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: calc(100% + var(--spacing-16));\n min-height: var(--spacing-36);\n margin-inline: calc(var(--spacing-8) * -1);\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n\n &:is(button) {\n cursor: pointer;\n border-radius: var(--rounded);\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &-marker {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-20);\n height: var(--spacing-20);\n flex-shrink: 0;\n position: relative;\n z-index: 1;\n background-color: var(--color-surface-1);\n border-radius: var(--rounded-full);\n border: var(--spacing-px) solid var(--color-surface-4);\n\n &--active {\n background-color: var(\n --chatbot-main-color,\n var(--color-surface-4)\n );\n border-color: transparent;\n color: var(--chatbot-contrast-color, var(--color-word-1));\n }\n }\n\n &__step-label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-2);\n }\n\n &__step-summary {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-4);\n }\n\n &__step-duration {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__step--active &__step-label {\n color: var(--color-word-1);\n }\n\n &__step-body {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4);\n margin-left: var(--spacing-28);\n margin-block: var(--spacing-4) var(--spacing-8);\n }\n\n &__step-content {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n padding: var(--spacing-8);\n background-color: var(--color-surface-1);\n\n // The finished reasoning keeps the cap the streaming one had:\n // `.wysiwyg` hides its own overflow, so the scroll belongs to the\n // wrapper, exactly as in the autoscroll variant.\n &--scroll {\n max-height: var(--spacing-256);\n overflow: auto;\n }\n }\n\n &__step-payload {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2);\n\n pre {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n padding: var(--spacing-8);\n font-size: var(--text-10);\n max-height: var(--spacing-160);\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: var(--font-mono);\n background-color: var(--color-surface-1);\n }\n }\n\n &__step-payload-label {\n font-weight: 600;\n color: var(--color-word-3);\n text-transform: uppercase;\n font-size: var(--text-10);\n letter-spacing: 0.05em;\n }\n\n &__step-payload--error {\n .pk-chatbot-steps__step-payload-label,\n pre {\n color: var(--u-danger-text, var(--color-danger));\n }\n\n pre {\n border-color: var(--color-danger-lighten-5);\n }\n }\n }\n</style>\n","<script lang=\"ts\" setup>\n import { computed, reactive, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { UIChatMessage } from 'models'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkStreamingMarkdownAutoscroll from './PkStreamingMarkdownAutoscroll.vue'\n import PkChatbotStepToolDetail from './PkChatbotStepToolDetail.vue'\n import PkChatbotStepPayload from './PkChatbotStepPayload.vue'\n import {\n isReasoningPart,\n isToolPart,\n getPartIcon,\n getPartState,\n getToolPartLabel,\n getToolPartSummary,\n hasRichToolDetail,\n hasPayload,\n formatDuration as formatDurationWith,\n LIVE_REASONING_TAIL_LENGTH,\n } from './utils'\n\n const props = defineProps<{\n message: UIChatMessage\n isStreaming?: boolean\n // Something is drawn below the timeline (a text, a card, a tool).\n hasContent?: boolean\n isDark?: boolean\n }>()\n\n const { t: $t, n: $n } = useI18n({\n useScope: 'global',\n })\n\n type MessagePart = UIChatMessage['parts'][number]\n\n interface Step {\n key: string\n part: MessagePart\n icon?: string\n label?: string\n summary?: string\n durationMs?: number\n isActive: boolean\n text?: string\n error?: string\n hasRichDetail: boolean\n hasBody: boolean\n toolInput?: unknown\n toolOutput?: unknown\n }\n\n const getStepError = (part: MessagePart) =>\n getPartState(part) === 'output-error' &&\n 'errorText' in part &&\n typeof part.errorText === 'string'\n ? part.errorText\n : undefined\n\n // What the step header needs is decided here, once per recomputation.\n // `hasBody` was a function the template called six times per step, and\n // `steps` recomputes at every delta of the stream: on a tool answering\n // hundreds of kilobytes, where the answer comes from `hasPayload` and\n // `hasPayload` serializes the whole payload to find out, that is the same\n // JSON.stringify six times for one boolean.\n //\n // The two payload blocks keep asking for themselves, because they render\n // only inside an expanded step: deciding here whether to show the output\n // would serialize it for every step the reader never opens, which is all\n // of them by default.\n const steps = computed<Step[]>(() => {\n const parts = props.message.parts\n const partTimings = props.message.metadata?.partTimings\n const result: Step[] = []\n for (const [index, part] of parts.entries()) {\n const isReasoning = isReasoningPart(part)\n const isTool = isToolPart(part)\n if (!isReasoning && !isTool) {\n continue\n }\n const toolCallId =\n 'toolCallId' in part ? (part.toolCallId as string) : undefined\n const text = isReasoning && part.text.trim() ? part.text : undefined\n const error = getStepError(part)\n const hasRichDetail = hasRichToolDetail(part)\n const toolInput = isTool && 'input' in part ? part.input : undefined\n const toolOutput =\n isTool && 'output' in part ? part.output : undefined\n result.push({\n key: toolCallId ?? `${part.type}-${index}`,\n part,\n icon: getPartIcon(part),\n label: getToolPartLabel($t, part),\n summary: getToolPartSummary(part),\n durationMs: toolCallId ? partTimings?.[toolCallId] : undefined,\n isActive:\n Boolean(props.isStreaming) && index === parts.length - 1,\n text,\n error,\n hasRichDetail,\n // The `||` short-circuits as the old function did: a tool\n // with an input never serializes its output to answer this.\n hasBody: Boolean(\n text ||\n error ||\n (hasRichDetail\n ? toolInput !== undefined || toolOutput !== undefined\n : hasPayload(toolInput) || hasPayload(toolOutput)),\n ),\n toolInput,\n toolOutput,\n })\n }\n return result\n })\n\n const totalDurationMs = computed(() => {\n const { createdAt, completedAt } = props.message.metadata ?? {}\n if (!createdAt || !completedAt) {\n return undefined\n }\n return completedAt - createdAt\n })\n\n const formatDuration = (ms?: number) => formatDurationWith($n, ms)\n\n // --- Expand / collapse ---\n // Every step starts closed, the running one included: the timeline says\n // what the answer is doing, the body is opened only when asked for. The\n // reasoning of the active step still follows the stream once opened.\n const manualSteps = reactive(new Map<string, boolean>())\n const isStepExpanded = (step: Step) => manualSteps.get(step.key) ?? false\n const toggleStep = (step: Step) => {\n manualSteps.set(step.key, !isStepExpanded(step))\n }\n\n // The group is open while streaming and collapses to its header as soon\n // as the answer draws something below it, unless the user toggled it\n // manually: from then on the activity reads at the bottom of the answer,\n // and an open list growing above it would push the answer down.\n const manualGroup = ref<boolean | undefined>(undefined)\n const isGroupExpanded = computed(\n () =>\n manualGroup.value ??\n (Boolean(props.isStreaming) && !props.hasContent),\n )\n // Reset the manual override when a new stream starts so the timeline\n // opens again on regenerate.\n watch(\n () => props.isStreaming,\n (streaming) => {\n if (streaming) {\n manualGroup.value = undefined\n manualSteps.clear()\n }\n },\n )\n</script>\n\n<template>\n <div v-if=\"steps.length\" class=\"pk-chatbot-steps\">\n <button\n type=\"button\"\n class=\"pk-chatbot-steps__header\"\n :aria-expanded=\"isGroupExpanded\"\n @click=\"manualGroup = !isGroupExpanded\">\n <VvIcon v-if=\"isStreaming\" name=\"loading-loop\" class=\"shrink-0\" />\n <VvIcon v-else name=\"ri:route-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-steps__title\">\n {{ $t('messagePart.steps') }}\n </span>\n <span class=\"pk-chatbot-steps__meta\">\n {{ $t('messagePart.stepCount', steps.length) }}\n <template v-if=\"formatDuration(totalDurationMs)\">\n · {{ formatDuration(totalDurationMs) }}\n </template>\n </span>\n <VvIcon\n :name=\"\n isGroupExpanded\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"pk-chatbot-steps__chevron shrink-0 ml-auto\" />\n </button>\n <ol v-if=\"isGroupExpanded\" class=\"pk-chatbot-steps__list\">\n <li\n v-for=\"step in steps\"\n :key=\"step.key\"\n class=\"pk-chatbot-steps__step\"\n :class=\"{\n 'pk-chatbot-steps__step--active': step.isActive,\n }\">\n <component\n :is=\"step.hasBody ? 'button' : 'div'\"\n :type=\"step.hasBody ? 'button' : undefined\"\n class=\"pk-chatbot-steps__step-header\"\n :aria-expanded=\"\n step.hasBody ? isStepExpanded(step) : undefined\n \"\n @click=\"step.hasBody ? toggleStep(step) : undefined\">\n <span\n class=\"pk-chatbot-steps-marker\"\n :class=\"{\n 'pk-chatbot-steps-marker--active': step.isActive,\n }\">\n <VvIcon v-if=\"step.isActive\" name=\"loading-loop\" />\n <VvIcon v-else-if=\"step.icon\" :name=\"step.icon\" />\n </span>\n <span class=\"pk-chatbot-steps__step-label\">\n {{ step.label }}\n </span>\n <span class=\"pk-chatbot-steps__step-summary\">\n {{ step.summary }}\n </span>\n <span\n v-if=\"formatDuration(step.durationMs)\"\n class=\"pk-chatbot-steps__step-duration\">\n {{ formatDuration(step.durationMs) }}\n </span>\n <VvIcon\n v-if=\"step.hasBody\"\n :name=\"\n isStepExpanded(step)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"pk-chatbot-steps__chevron shrink-0 ml-auto\" />\n </component>\n <div\n v-if=\"step.hasBody && isStepExpanded(step)\"\n class=\"pk-chatbot-steps__step-body\">\n <PkStreamingMarkdownAutoscroll\n v-if=\"step.text && step.isActive\"\n :markdown=\"step.text\"\n :is-dark\n :tail-length=\"LIVE_REASONING_TAIL_LENGTH\"\n inner-class=\"wysiwyg\"\n class=\"pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll\" />\n <div\n v-else-if=\"step.text\"\n class=\"pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll light-scrollbar\">\n <PkStreamingMarkdown\n :markdown=\"step.text\"\n :is-dark\n class=\"wysiwyg\" />\n </div>\n <div\n v-else-if=\"step.error\"\n class=\"pk-chatbot-steps__step-payload pk-chatbot-steps__step-payload--error\">\n <span class=\"pk-chatbot-steps__step-payload-label\">\n {{ $t('messagePart.stepError') }}\n </span>\n <pre>{{ step.error }}</pre>\n </div>\n <PkChatbotStepToolDetail\n v-else-if=\"step.hasRichDetail\"\n :part=\"step.part\" />\n <template v-else>\n <PkChatbotStepPayload\n v-if=\"hasPayload(step.toolInput)\"\n :label=\"$t('messagePart.stepInput')\"\n :value=\"step.toolInput\" />\n <PkChatbotStepPayload\n v-if=\"hasPayload(step.toolOutput)\"\n :label=\"$t('messagePart.stepOutput')\"\n :value=\"step.toolOutput\" />\n </template>\n </div>\n </li>\n </ol>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-steps {\n width: 100%;\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded-lg);\n background-color: var(--color-surface-1);\n font-size: var(--text-12);\n color: var(--color-word-3);\n overflow: hidden;\n\n &__header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: 100%;\n min-height: var(--spacing-40);\n padding: var(--spacing-8) var(--spacing-sm);\n text-align: left;\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\n &__chevron {\n font-size: var(--text-16);\n }\n\n &__title {\n font-weight: 600;\n color: var(--color-word-2);\n flex-grow: 1;\n }\n\n &__meta {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__list {\n background-color: var(--color-surface);\n border-top: var(--spacing-px) solid var(--color-surface-3);\n padding: var(--spacing-8) var(--spacing-sm);\n display: flex;\n flex-direction: column;\n }\n\n &__step {\n position: relative;\n\n // Vertical connector between step markers\n &:not(:last-child)::before {\n content: '';\n position: absolute;\n left: var(--spacing-10);\n top: var(--spacing-28);\n bottom: calc(var(--spacing-8) * -1);\n width: var(--spacing-px);\n background-color: var(--color-surface-4);\n }\n }\n\n &__step-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: calc(100% + var(--spacing-16));\n min-height: var(--spacing-36);\n margin-inline: calc(var(--spacing-8) * -1);\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n\n &:is(button) {\n cursor: pointer;\n border-radius: var(--rounded);\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &-marker {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-20);\n height: var(--spacing-20);\n flex-shrink: 0;\n position: relative;\n z-index: 1;\n background-color: var(--color-surface-1);\n border-radius: var(--rounded-full);\n border: var(--spacing-px) solid var(--color-surface-4);\n\n &--active {\n background-color: var(\n --chatbot-main-color,\n var(--color-surface-4)\n );\n border-color: transparent;\n color: var(--chatbot-contrast-color, var(--color-word-1));\n }\n }\n\n &__step-label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-2);\n }\n\n &__step-summary {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-4);\n }\n\n &__step-duration {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__step--active &__step-label {\n color: var(--color-word-1);\n }\n\n &__step-body {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-4);\n margin-left: var(--spacing-28);\n margin-block: var(--spacing-4) var(--spacing-8);\n }\n\n &__step-content {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n padding: var(--spacing-8);\n background-color: var(--color-surface-1);\n\n // The finished reasoning keeps the cap the streaming one had:\n // `.wysiwyg` hides its own overflow, so the scroll belongs to the\n // wrapper, exactly as in the autoscroll variant.\n &--scroll {\n max-height: var(--spacing-256);\n overflow: auto;\n }\n }\n\n &__step-payload {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-2);\n\n pre {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n padding: var(--spacing-8);\n font-size: var(--text-10);\n max-height: var(--spacing-160);\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: var(--font-mono);\n background-color: var(--color-surface-1);\n }\n }\n\n &__step-payload-label {\n font-weight: 600;\n color: var(--color-word-3);\n text-transform: uppercase;\n font-size: var(--text-10);\n letter-spacing: 0.05em;\n }\n\n &__step-payload--error {\n .pk-chatbot-steps__step-payload-label,\n pre {\n color: var(--u-danger-text, var(--color-danger));\n }\n\n pre {\n border-color: var(--color-danger-lighten-5);\n }\n }\n }\n</style>\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- import { t as e } from "./PkChatbotMessages-coscJcpy.js";
1
+ import { t as e } from "./PkChatbotMessages-Zhuxom46.js";
2
2
  import { a as t, n, t as r } from "./useChatbotStore-rtl-oKqU.js";
3
3
  import { t as i } from "./src-DCfvejyT.js";
4
4
  import { t as a } from "./useLocalizedString-PfaKjIQY.js";
5
- import { f as o, o as s } from "./utils-fxHiCnzf.js";
6
- import { t as c } from "./PkAvatar-ZgCsoO-p.js";
7
- import { n as l, t as u } from "./PkChatbotInput-CQdxFbBR.js";
8
- import { t as d } from "./PkChatbotEmptyState-D2dhInfG.js";
5
+ import { p as o, s } from "./utils-Dk2jFXR_.js";
6
+ import { t as c } from "./PkAvatar-BTqu8tn9.js";
7
+ import { n as l, t as u } from "./PkChatbotInput-KJxWrDIT.js";
8
+ import { t as d } from "./PkChatbotEmptyState-BEQCnqui.js";
9
9
  import { t as f } from "./PkChatbotWakeupBar-jBti21FQ.js";
10
10
  import { VvIcon as p } from "@volverjs/ui-vue/components";
11
11
  import { Fragment as m, Transition as h, computed as g, createBlock as _, createCommentVNode as v, createElementBlock as y, createElementVNode as b, createSlots as x, createTextVNode as ee, createVNode as S, defineAsyncComponent as C, defineComponent as w, isRef as te, nextTick as ne, normalizeClass as T, onBeforeUnmount as E, onMounted as re, onScopeDispose as ie, onUnmounted as ae, openBlock as D, ref as O, renderList as oe, shallowRef as se, toDisplayString as k, toValue as A, unref as j, useTemplateRef as ce, watch as M, withCtx as N } from "vue";
@@ -458,7 +458,7 @@ var Oe = { class: "pk-chatbot-view-chat__input-stage" }, ke = {
458
458
  },
459
459
  emits: ["show-info", "revise"],
460
460
  setup(i, { emit: l }) {
461
- let m = C(() => import("./PkToolShowForm-ZmdM9J9x.js").then((e) => e.n)), ee = C(() => import("./PkToolShowContactForm-D30IsZGy.js").then((e) => e.n)), w = C(() => import("./PkToolShowSuggestedReply-DjutyiEi.js").then((e) => e.n)), ne = C(() => import("./PkToolShowSources-CKZkcPLA.js").then((e) => e.n)), re = C(() => import("./PkToolRequestGeolocation-BE22qwxB.js")), ie = C(() => import("./PkToolRequestOAuthConnection-GfSalEJG.js")), ae = C(() => import("./PkToolShowLocation-Dyz6h_0n.js").then((e) => e.n)), oe = C(() => import("./PkToolShowDiagram-PEozSAqU.js")), se = C(() => import("./PkToolEditSpreadsheet-HAskd6d3.js")), A = i, pe = l, P = n(A.agentId), { agentInterface: F, agentFileUpload: I, agentVoice: me, actions: L, revisedAnswers: ge, messages: R, chat: z, messageFeedbacks: _e, feedbackDialogMessage: ve, isFeedbackSubmitting: ye, isFeedbackSubmitted: be, feedbackSubmitError: xe, isLeadSubmitted: we, isLoadingSubmitLead: Te, submitLeadError: Ee, input: B, inputMessagePlaceholder: Le, isConversationBlocked: Re, baseUrl: ze, pendingAttachments: Be, isAttachmentLimitReached: Ve, fileLimitError: He, apiClient: V, isDark: Ue } = de(P), We = (e) => V.value.getMediaDownloadUrl(A.agentId, P.localChatId, e), { handleSubmit: Ge, stopGeneration: Ke, regenerate: qe, onUpvote: Je, onDownvote: Ye, onFeedback: Xe, onFeedbackSubmit: Ze, onLeadSubmit: Qe, startNewChat: $e, addToolOutput: H, addToolApprovalResponse: et, handleFileSelect: tt, clearFileLimitError: nt, refreshMessages: rt, appendMessage: it } = P, { t: U } = le({ useScope: "global" }), at = g(() => {
461
+ let m = C(() => import("./PkToolShowForm-BG-8aTHy.js").then((e) => e.n)), ee = C(() => import("./PkToolShowContactForm-DBC6f9gR.js").then((e) => e.n)), w = C(() => import("./PkToolShowSuggestedReply-DjutyiEi.js").then((e) => e.n)), ne = C(() => import("./PkToolShowSources-CUURb0PG.js").then((e) => e.n)), re = C(() => import("./PkToolRequestGeolocation-BE22qwxB.js")), ie = C(() => import("./PkToolRequestOAuthConnection-GfSalEJG.js")), ae = C(() => import("./PkToolShowLocation-Dyz6h_0n.js").then((e) => e.n)), oe = C(() => import("./PkToolShowDiagram-Bbq0ETt1.js")), se = C(() => import("./PkToolEditSpreadsheet-HAskd6d3.js")), A = i, pe = l, P = n(A.agentId), { agentInterface: F, agentFileUpload: I, agentVoice: me, actions: L, revisedAnswers: ge, messages: R, chat: z, messageFeedbacks: _e, feedbackDialogMessage: ve, isFeedbackSubmitting: ye, isFeedbackSubmitted: be, feedbackSubmitError: xe, isLeadSubmitted: we, isLoadingSubmitLead: Te, submitLeadError: Ee, input: B, inputMessagePlaceholder: Le, isConversationBlocked: Re, baseUrl: ze, pendingAttachments: Be, isAttachmentLimitReached: Ve, fileLimitError: He, apiClient: V, isDark: Ue } = de(P), We = (e) => V.value.getMediaDownloadUrl(A.agentId, P.localChatId, e), { handleSubmit: Ge, stopGeneration: Ke, regenerate: qe, onUpvote: Je, onDownvote: Ye, onFeedback: Xe, onFeedbackSubmit: Ze, onLeadSubmit: Qe, startNewChat: $e, addToolOutput: H, addToolApprovalResponse: et, handleFileSelect: tt, clearFileLimitError: nt, refreshMessages: rt, appendMessage: it } = P, { t: U } = le({ useScope: "global" }), at = g(() => {
462
462
  let e = He.value;
463
463
  if (e) return e.reason === "per-chat" ? U("message.fileLimitPerChat", { max: e.max }) : U("message.fileLimitPerMessage", { max: e.max });
464
464
  }), ot = a(() => F.value?.dismissableNotice), st = g(() => R.value.some((e) => e.role === "user")), ct = g(() => A.modifier === "fullscreen" && !st.value), lt = fe(F, () => A.userName), ut = ce("chatViewEl"), dt = async (e) => (await V.value.expandSourceContext(A.agentId, e.documentId, e.chunkIndex)).content, ft = async (e) => {
@@ -873,4 +873,4 @@ var Oe = { class: "pk-chatbot-view-chat__input-stage" }, ke = {
873
873
  //#endregion
874
874
  export { he as i, Se as n, R as r, Le as t };
875
875
 
876
- //# sourceMappingURL=PkChatbotViewChat-Cko-Cw6c.js.map
876
+ //# sourceMappingURL=PkChatbotViewChat-BJ0YHCvA.js.map