@8wave/ai-elements 0.118.0 → 0.119.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{PkStreamingMarkdown--CcBvs19.js → PkStreamingMarkdown-Bry3y6Ru.js} +789 -780
- package/dist/_chunks/PkStreamingMarkdown-Bry3y6Ru.js.map +1 -0
- package/dist/_chunks/{PkToolShowArtifact-BVGN7hV2.js → PkToolShowArtifact-CfJLe8vD.js} +2 -2
- package/dist/_chunks/{PkToolShowArtifact-BVGN7hV2.js.map → PkToolShowArtifact-CfJLe8vD.js.map} +1 -1
- package/dist/_chunks/{PkToolShowContactForm-Blrwqpl0.js → PkToolShowContactForm-CQQ6p4bJ.js} +2 -2
- package/dist/_chunks/{PkToolShowContactForm-Blrwqpl0.js.map → PkToolShowContactForm-CQQ6p4bJ.js.map} +1 -1
- package/dist/_chunks/{PkToolShowForm-BpGHaN6x.js → PkToolShowForm-CGAc1O6K.js} +4 -4
- package/dist/_chunks/{PkToolShowForm-BpGHaN6x.js.map → PkToolShowForm-CGAc1O6K.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSources-8c9RvGV1.js → PkToolShowSources-BJ-XFrLD.js} +3 -3
- package/dist/_chunks/{PkToolShowSources-8c9RvGV1.js.map → PkToolShowSources-BJ-XFrLD.js.map} +1 -1
- package/dist/_chunks/{markdown-c59ir6tw.js → markdown-DpoAzJSk.js} +64 -61
- package/dist/_chunks/markdown-DpoAzJSk.js.map +1 -0
- package/dist/ai-elements.es.js +1944 -1865
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotEmptyState.js +1 -1
- package/dist-vue/PkChatbotInput.js +1 -1
- package/dist-vue/PkChatbotMessages.js +1 -1
- package/dist-vue/PkChatbotStepPayload.js +1 -1
- package/dist-vue/PkChatbotStepToolDetail.js +1 -1
- package/dist-vue/PkChatbotSteps.js +1 -1
- package/dist-vue/PkChatbotViewChat.js +1 -1
- package/dist-vue/PkChatbotViewFiles.js +1 -1
- package/dist-vue/PkChatbotViewProfile.js +1 -1
- package/dist-vue/_chunks/{Document-B-oyVh7v.js → Document-CV-RJu_p.js} +64 -61
- package/dist-vue/_chunks/Document-CV-RJu_p.js.map +1 -0
- package/dist-vue/_chunks/PkAvatar-BTqu8tn9.js +44 -0
- package/dist-vue/_chunks/PkAvatar-BTqu8tn9.js.map +1 -0
- package/dist-vue/_chunks/PkChatbot-CcNC2r_E.js +276 -0
- package/dist-vue/_chunks/PkChatbot-CcNC2r_E.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotEmptyState-D2dhInfG.js → PkChatbotEmptyState-Toj4CqXV.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotEmptyState-D2dhInfG.js.map → PkChatbotEmptyState-Toj4CqXV.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotInput-CQdxFbBR.js → PkChatbotInput-BfZqRpQ2.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotInput-CQdxFbBR.js.map → PkChatbotInput-BfZqRpQ2.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-coscJcpy.js → PkChatbotMessages-B6qYZsMZ.js} +283 -283
- package/dist-vue/_chunks/{PkChatbotMessages-coscJcpy.js.map → PkChatbotMessages-B6qYZsMZ.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotStepPayload-DnqFluQZ.js → PkChatbotStepPayload-D0eoPBQ8.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotStepPayload-DnqFluQZ.js.map → PkChatbotStepPayload-D0eoPBQ8.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-BMslY_BG.js → PkChatbotStepToolDetail-CMx4cw1D.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-BMslY_BG.js.map → PkChatbotStepToolDetail-CMx4cw1D.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotSteps-7FT6Ju6_.js → PkChatbotSteps-DeZQhGRU.js} +38 -38
- package/dist-vue/_chunks/{PkChatbotSteps-7FT6Ju6_.js.map → PkChatbotSteps-DeZQhGRU.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-Cko-Cw6c.js → PkChatbotViewChat--Q90F_Kg.js} +7 -7
- package/dist-vue/_chunks/PkChatbotViewChat--Q90F_Kg.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewFiles-gqCumfod.js → PkChatbotViewFiles-CA20TBFY.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewFiles-gqCumfod.js.map → PkChatbotViewFiles-CA20TBFY.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewProfile-B57VRVaO.js → PkChatbotViewProfile-CyzRxE3D.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-B57VRVaO.js.map → PkChatbotViewProfile-CyzRxE3D.js.map} +1 -1
- package/dist-vue/_chunks/{PkStreamingMarkdown-BtyWXY2k.js → PkStreamingMarkdown-C55Fi0Pd.js} +2 -2
- package/dist-vue/_chunks/{PkStreamingMarkdown-BtyWXY2k.js.map → PkStreamingMarkdown-C55Fi0Pd.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowArtifact-Ritx54_X.js → PkToolShowArtifact-z1qTnFkA.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowArtifact-Ritx54_X.js.map → PkToolShowArtifact-z1qTnFkA.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowContactForm-D30IsZGy.js → PkToolShowContactForm-BjNpnv6y.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowContactForm-D30IsZGy.js.map → PkToolShowContactForm-BjNpnv6y.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowDiagram-PEozSAqU.js → PkToolShowDiagram-CqkabP0e.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowDiagram-PEozSAqU.js.map → PkToolShowDiagram-CqkabP0e.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowForm-ZmdM9J9x.js → PkToolShowForm-CZFWZub7.js} +5 -5
- package/dist-vue/_chunks/{PkToolShowForm-ZmdM9J9x.js.map → PkToolShowForm-CZFWZub7.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowImageGallery-Dkjf7Tw_.js → PkToolShowImageGallery-CkfXP_lD.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowImageGallery-Dkjf7Tw_.js.map → PkToolShowImageGallery-CkfXP_lD.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowProductList-CsHYM8vl.js → PkToolShowProductList-De-PtmD-.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowProductList-CsHYM8vl.js.map → PkToolShowProductList-De-PtmD-.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSources-CKZkcPLA.js → PkToolShowSources-CSN7S0zo.js} +3 -3
- package/dist-vue/_chunks/{PkToolShowSources-CKZkcPLA.js.map → PkToolShowSources-CSN7S0zo.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowVideo-CKm1fVQs.js → PkToolShowVideo-B_Q93E2y.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowVideo-CKm1fVQs.js.map → PkToolShowVideo-B_Q93E2y.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowWebPages-DLEC9cuh.js → PkToolShowWebPages-D3CdjJUc.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowWebPages-DLEC9cuh.js.map → PkToolShowWebPages-D3CdjJUc.js.map} +1 -1
- package/dist-vue/_chunks/{utils-fxHiCnzf.js → utils-CwIMEuAv.js} +41 -32
- package/dist-vue/_chunks/utils-CwIMEuAv.js.map +1 -0
- package/dist-vue/apps/web-component/src/composables/useChatbotAgent.d.ts +1 -4
- package/dist-vue/composables.js +188 -115
- package/dist-vue/composables.js.map +1 -1
- package/dist-vue/index.js +4005 -3980
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/locales.js +4 -0
- package/dist-vue/packages/components/src/chat/PkAvatar.d.ts +7 -0
- package/dist-vue/packages/components/src/chat/utils.d.ts +10 -0
- package/dist-vue/packages/models/src/schema/Document.d.ts +25 -0
- package/dist-vue/packages/models/src/schema/Notification.d.ts +2 -0
- package/dist-vue/packages/models/src/schema/Platform.d.ts +72 -0
- package/dist-vue/style.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/PkStreamingMarkdown--CcBvs19.js.map +0 -1
- package/dist/_chunks/markdown-c59ir6tw.js.map +0 -1
- package/dist-vue/_chunks/Document-B-oyVh7v.js.map +0 -1
- package/dist-vue/_chunks/PkAvatar-ZgCsoO-p.js +0 -42
- package/dist-vue/_chunks/PkAvatar-ZgCsoO-p.js.map +0 -1
- package/dist-vue/_chunks/PkChatbot-Cm45hwYw.js +0 -275
- package/dist-vue/_chunks/PkChatbot-Cm45hwYw.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotViewChat-Cko-Cw6c.js.map +0 -1
- package/dist-vue/_chunks/utils-fxHiCnzf.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { t as e } from "./PkStreamingMarkdown-
|
|
1
|
+
import { t as e } from "./PkStreamingMarkdown-C55Fi0Pd.js";
|
|
2
2
|
import { l as t, s as n } from "./message-BOiF6Bt5.js";
|
|
3
|
-
import { _ as r,
|
|
4
|
-
import { t as u } from "./PkChatbotStepPayload-
|
|
5
|
-
import { t as d } from "./PkChatbotStepToolDetail-
|
|
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-CwIMEuAv.js";
|
|
4
|
+
import { t as u } from "./PkChatbotStepPayload-D0eoPBQ8.js";
|
|
5
|
+
import { t as d } from "./PkChatbotStepToolDetail-CMx4cw1D.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
|
|
8
|
-
import { useI18n as
|
|
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 } =
|
|
74
|
-
let e = A.message.parts,
|
|
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 =
|
|
79
|
-
|
|
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:
|
|
84
|
-
summary:
|
|
85
|
-
durationMs: p ?
|
|
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 :
|
|
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
|
|
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) =>
|
|
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
|
|
102
|
+
return ne(() => A.isStreaming, (e) => {
|
|
103
103
|
e && (Q.value = void 0, X.clear());
|
|
104
104
|
}), (t, n) => {
|
|
105
|
-
let
|
|
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(
|
|
112
|
+
x.isStreaming ? (C(), h(i, {
|
|
113
113
|
key: 0,
|
|
114
114
|
name: "loading-loop",
|
|
115
115
|
class: "shrink-0"
|
|
116
|
-
})) : (C(), h(
|
|
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(
|
|
122
|
-
v("span", I, [y(O(
|
|
123
|
-
b(
|
|
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(
|
|
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:
|
|
137
|
-
v("span", { class: S(["pk-chatbot-steps-marker", { "pk-chatbot-steps-marker--active": t.isActive }]) }, [t.isActive ? (C(), h(
|
|
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(
|
|
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,
|
|
145
|
-
v("span", z,
|
|
146
|
-
Y(t.durationMs) ? (C(), _("span", B,
|
|
147
|
-
t.hasBody ? (C(), h(
|
|
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":
|
|
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(
|
|
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 }, [
|
|
176
|
+
}, null, 8, ["part"])) : (C(), _(p, { key: 4 }, [O(r)(t.toolInput) ? (C(), h(u, {
|
|
177
177
|
key: 0,
|
|
178
|
-
label:
|
|
178
|
+
label: O(j)("messagePart.stepInput"),
|
|
179
179
|
value: t.toolInput
|
|
180
|
-
}, null, 8, ["label", "value"])) : g("", !0),
|
|
180
|
+
}, null, 8, ["label", "value"])) : g("", !0), O(r)(t.toolOutput) ? (C(), h(u, {
|
|
181
181
|
key: 1,
|
|
182
|
-
label:
|
|
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-
|
|
191
|
+
//# sourceMappingURL=PkChatbotSteps-DeZQhGRU.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-DeZQhGRU.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-
|
|
1
|
+
import { t as e } from "./PkChatbotMessages-B6qYZsMZ.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 {
|
|
6
|
-
import { t as c } from "./PkAvatar-
|
|
7
|
-
import { n as l, t as u } from "./PkChatbotInput-
|
|
8
|
-
import { t as d } from "./PkChatbotEmptyState-
|
|
5
|
+
import { p as o, s } from "./utils-CwIMEuAv.js";
|
|
6
|
+
import { t as c } from "./PkAvatar-BTqu8tn9.js";
|
|
7
|
+
import { n as l, t as u } from "./PkChatbotInput-BfZqRpQ2.js";
|
|
8
|
+
import { t as d } from "./PkChatbotEmptyState-Toj4CqXV.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-
|
|
461
|
+
let m = C(() => import("./PkToolShowForm-CZFWZub7.js").then((e) => e.n)), ee = C(() => import("./PkToolShowContactForm-BjNpnv6y.js").then((e) => e.n)), w = C(() => import("./PkToolShowSuggestedReply-DjutyiEi.js").then((e) => e.n)), ne = C(() => import("./PkToolShowSources-CSN7S0zo.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-CqkabP0e.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
|
|
876
|
+
//# sourceMappingURL=PkChatbotViewChat--Q90F_Kg.js.map
|