@8wave/ai-elements 0.114.0-beta.8 → 0.115.0-beta.13
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/Agent-DK76k05_.js +566 -0
- package/dist/_chunks/Agent-DK76k05_.js.map +1 -0
- package/dist/_chunks/{PkStreamingMarkdown-C1sANAr5.js → PkStreamingMarkdown-BKxA6Ntl.js} +688 -672
- package/dist/_chunks/PkStreamingMarkdown-BKxA6Ntl.js.map +1 -0
- package/dist/_chunks/{PkToolRequestGeolocation-BbxYKt-9.js → PkToolRequestGeolocation-OF1PWhMw.js} +2 -2
- package/{dist-vue/_chunks/PkToolRequestGeolocation-9vfYNaQo.js.map → dist/_chunks/PkToolRequestGeolocation-OF1PWhMw.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestOAuthConnection-CTp2w60F.js → PkToolRequestOAuthConnection-CgQePKKb.js} +4 -4
- package/dist/_chunks/PkToolRequestOAuthConnection-CgQePKKb.js.map +1 -0
- package/dist/_chunks/{PkToolShowArtifact-BfTjxGOT.js → PkToolShowArtifact-CHxeuKEU.js} +2 -2
- package/dist/_chunks/{PkToolShowArtifact-BfTjxGOT.js.map → PkToolShowArtifact-CHxeuKEU.js.map} +1 -1
- package/dist/_chunks/PkToolShowComparison-8d6fAhRp.js +72 -0
- package/dist/_chunks/PkToolShowComparison-8d6fAhRp.js.map +1 -0
- package/dist/_chunks/{PkToolShowContactForm-PFpY9dIp.js → PkToolShowContactForm-DqVLVwqJ.js} +2 -2
- package/dist/_chunks/{PkToolShowContactForm-PFpY9dIp.js.map → PkToolShowContactForm-DqVLVwqJ.js.map} +1 -1
- package/dist/_chunks/{PkToolShowForm-CMtSN0oX.js → PkToolShowForm-YJQPYfEe.js} +5 -5
- package/dist/_chunks/{PkToolShowForm-CMtSN0oX.js.map → PkToolShowForm-YJQPYfEe.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSources-C6cu66Ts.js → PkToolShowSources-kGQlIKaN.js} +3 -3
- package/dist/_chunks/{PkToolShowSources-C6cu66Ts.js.map → PkToolShowSources-kGQlIKaN.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSuggestedReply-CFO8wV1b.js → PkToolShowSuggestedReply-Dl4x_nEJ.js} +2 -2
- package/dist/_chunks/{PkToolShowSuggestedReply-CFO8wV1b.js.map → PkToolShowSuggestedReply-Dl4x_nEJ.js.map} +1 -1
- package/dist/_chunks/{markdown-BRD-hDrH.js → markdown-B4cYQGBo.js} +37 -21
- package/dist/_chunks/markdown-B4cYQGBo.js.map +1 -0
- package/dist/ai-elements.es.js +3177 -2748
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotAuth.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/PkChatbotViewConversations.js +1 -1
- package/dist-vue/PkChatbotViewFiles.js +1 -1
- package/dist-vue/PkChatbotViewProfile.js +1 -1
- package/dist-vue/_chunks/{Agent-BNlmK-uJ.js → Agent-tbKw8Mdt.js} +2 -2
- package/dist-vue/_chunks/{Agent-BNlmK-uJ.js.map → Agent-tbKw8Mdt.js.map} +1 -1
- package/dist-vue/_chunks/{Document-2X01-F4Z.js → Document-BWz2HySu.js} +37 -21
- package/dist-vue/_chunks/Document-BWz2HySu.js.map +1 -0
- package/dist-vue/_chunks/Media-_HfFLHGD.js.map +1 -1
- package/dist-vue/_chunks/{PkChatbot-HRayDr2d.js → PkChatbot-DtyZYAzj.js} +8 -8
- package/dist-vue/_chunks/{PkChatbot-HRayDr2d.js.map → PkChatbot-DtyZYAzj.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotAuth-jzoNJkmH.js +421 -0
- package/dist-vue/_chunks/PkChatbotAuth-jzoNJkmH.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotEmptyState-CPOcfoRC.js → PkChatbotEmptyState-Dicjr_Fv.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotEmptyState-CPOcfoRC.js.map → PkChatbotEmptyState-Dicjr_Fv.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotInput-pLJQIu7v.js → PkChatbotInput-DcLmOqA1.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotInput-pLJQIu7v.js.map → PkChatbotInput-DcLmOqA1.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-GZStJbx_.js → PkChatbotMessages-CrhRrcBs.js} +258 -256
- package/dist-vue/_chunks/PkChatbotMessages-CrhRrcBs.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotStepPayload-DU0W-eUp.js → PkChatbotStepPayload-rKaeDQDj.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotStepPayload-DU0W-eUp.js.map → PkChatbotStepPayload-rKaeDQDj.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-etlBryOg.js → PkChatbotStepToolDetail-5HdSV8Im.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-etlBryOg.js.map → PkChatbotStepToolDetail-5HdSV8Im.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotSteps-CPPjwTJN.js +191 -0
- package/dist-vue/_chunks/PkChatbotSteps-CPPjwTJN.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewChat-DIfBWFoG.js → PkChatbotViewChat-B13nCznB.js} +7 -7
- package/dist-vue/_chunks/{PkChatbotViewChat-DIfBWFoG.js.map → PkChatbotViewChat-B13nCznB.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewConversations-BCdolW0R.js → PkChatbotViewConversations-CHpN4vhm.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewConversations-BCdolW0R.js.map → PkChatbotViewConversations-CHpN4vhm.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewFiles-Cudao8ew.js → PkChatbotViewFiles-cRyG5X3P.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotViewFiles-Cudao8ew.js.map → PkChatbotViewFiles-cRyG5X3P.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewProfile-DDsKOkcC.js → PkChatbotViewProfile-CosaHA-g.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-DDsKOkcC.js.map → PkChatbotViewProfile-CosaHA-g.js.map} +1 -1
- package/dist-vue/_chunks/{PkStreamingMarkdown-BloMJaIW.js → PkStreamingMarkdown-DYy79C1P.js} +214 -205
- package/dist-vue/_chunks/{PkStreamingMarkdown-BloMJaIW.js.map → PkStreamingMarkdown-DYy79C1P.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolRequestGeolocation-9vfYNaQo.js → PkToolRequestGeolocation-BOVbaCzQ.js} +2 -2
- package/{dist/_chunks/PkToolRequestGeolocation-BbxYKt-9.js.map → dist-vue/_chunks/PkToolRequestGeolocation-BOVbaCzQ.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolRequestOAuthConnection-BorR5NmZ.js → PkToolRequestOAuthConnection-a2LegFoE.js} +4 -4
- package/dist-vue/_chunks/PkToolRequestOAuthConnection-a2LegFoE.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowArtifact-BuMY7GNj.js → PkToolShowArtifact-DUOnFdfa.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowArtifact-BuMY7GNj.js.map → PkToolShowArtifact-DUOnFdfa.js.map} +1 -1
- package/dist-vue/_chunks/PkToolShowComparison-DQs3E6-k.js +72 -0
- package/dist-vue/_chunks/PkToolShowComparison-DQs3E6-k.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowContactForm-0OQRqT8m.js → PkToolShowContactForm-1TFKvG_s.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowContactForm-0OQRqT8m.js.map → PkToolShowContactForm-1TFKvG_s.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowDiagram-BGL27iHG.js → PkToolShowDiagram-BnbkymPD.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowDiagram-BGL27iHG.js.map → PkToolShowDiagram-BnbkymPD.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowEmail-BJJpS69F.js → PkToolShowEmail-BDsBoETQ.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowEmail-BJJpS69F.js.map → PkToolShowEmail-BDsBoETQ.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowForm-DDcPpOZ8.js → PkToolShowForm-DwFKxMJd.js} +4 -4
- package/dist-vue/_chunks/{PkToolShowForm-DDcPpOZ8.js.map → PkToolShowForm-DwFKxMJd.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowMessage-BFGynf3p.js → PkToolShowMessage-Dau3agUy.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowMessage-BFGynf3p.js.map → PkToolShowMessage-Dau3agUy.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowProductList-6t1Ir5CW.js → PkToolShowProductList-CsupMsuy.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowProductList-6t1Ir5CW.js.map → PkToolShowProductList-CsupMsuy.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSources-cOF0ZxOj.js → PkToolShowSources-C8r1vKJk.js} +4 -4
- package/dist-vue/_chunks/{PkToolShowSources-cOF0ZxOj.js.map → PkToolShowSources-C8r1vKJk.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSuggestedReply-DA4NEjHB.js → PkToolShowSuggestedReply-qAWdCUhr.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowSuggestedReply-DA4NEjHB.js.map → PkToolShowSuggestedReply-qAWdCUhr.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowWebPages-OgaT1vYJ.js → PkToolShowWebPages-D4E3Xzg1.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowWebPages-OgaT1vYJ.js.map → PkToolShowWebPages-D4E3Xzg1.js.map} +1 -1
- package/dist-vue/_chunks/{PkUrl-Cd69XP5z.js → PkUrl-Cu3qKl4G.js} +2 -2
- package/dist-vue/_chunks/{PkUrl-Cd69XP5z.js.map → PkUrl-Cu3qKl4G.js.map} +1 -1
- package/dist-vue/_chunks/{createChatbotApiClient-CaVLm5W9.js → createChatbotApiClient-BlWs282E.js} +137 -168
- package/dist-vue/_chunks/createChatbotApiClient-BlWs282E.js.map +1 -0
- package/dist-vue/_chunks/{useChatbotStore-BwrBZJCq.js → useChatbotStore-CMo47fnS.js} +3 -3
- package/dist-vue/_chunks/{useChatbotStore-BwrBZJCq.js.map → useChatbotStore-CMo47fnS.js.map} +1 -1
- package/dist-vue/_chunks/utils-B45A2ONa.js +184 -0
- package/dist-vue/_chunks/utils-B45A2ONa.js.map +1 -0
- package/dist-vue/api.js +1 -1
- package/dist-vue/apps/web-component/src/components/EmbeddedChatWidget.ce.d.ts +4 -4
- package/dist-vue/apps/web-component/src/composables/useChatbotAuth.d.ts +53 -5
- package/dist-vue/apps/web-component/src/composables/useChatbotAuthForm.d.ts +14 -0
- package/dist-vue/composables.js +524 -39
- package/dist-vue/composables.js.map +1 -1
- package/dist-vue/index.js +3366 -3240
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/locales.js +46 -0
- package/dist-vue/packages/ability/src/index.d.ts +10 -14
- package/dist-vue/packages/auth/src/active-membership.d.ts +36 -0
- package/dist-vue/packages/auth/src/add-member-to-organization.d.ts +12 -2
- package/dist-vue/packages/auth/src/additional-fields.d.ts +48 -0
- package/dist-vue/packages/auth/src/organization-member-limit.d.ts +12 -0
- package/dist-vue/packages/auth/src/passkey-policy-client-plugin.d.ts +31 -0
- package/dist-vue/packages/auth/src/passkey-policy-plugin.d.ts +22 -0
- package/dist-vue/packages/auth/src/two-factor-admin-plugin.d.ts +2 -0
- package/dist-vue/packages/auth/src/two-factor-policy-client-plugin.d.ts +163 -0
- package/dist-vue/packages/auth/src/two-factor-policy-plugin.d.ts +106 -0
- package/dist-vue/packages/components/src/PkQrCode.d.ts +14 -0
- package/dist-vue/packages/components/src/chat/PkChatbotAuth.d.ts +19 -0
- package/dist-vue/packages/components/src/chat/PkChatbotMessages.d.ts +8 -4
- package/dist-vue/packages/components/src/chat/PkChatbotSteps.d.ts +1 -0
- package/dist-vue/packages/components/src/chat/PkTurnEvaluation.d.ts +8 -0
- package/dist-vue/packages/components/src/chat/utils.d.ts +9 -0
- package/dist-vue/packages/components/src/index.d.ts +2 -1
- package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +60 -0
- package/dist-vue/packages/models/src/schema/Chat.d.ts +42 -0
- package/dist-vue/packages/models/src/schema/Document.d.ts +494 -0
- package/dist-vue/packages/models/src/schema/DocumentFolder.d.ts +10 -0
- package/dist-vue/packages/models/src/schema/DocumentSyncSource.d.ts +71 -1
- package/dist-vue/packages/models/src/schema/Lead.d.ts +1 -1
- package/dist-vue/packages/models/src/schema/McpServer.d.ts +13 -0
- package/dist-vue/packages/models/src/schema/Media.d.ts +0 -1
- package/dist-vue/packages/models/src/schema/Member.d.ts +10 -0
- package/dist-vue/packages/models/src/schema/Notification.d.ts +31 -0
- package/dist-vue/packages/models/src/schema/Organization.d.ts +20 -0
- package/dist-vue/packages/models/src/schema/SignInSecurity.d.ts +168 -0
- package/dist-vue/packages/models/src/schema/User.d.ts +1 -0
- package/dist-vue/packages/models/src/schema/index.d.ts +1 -0
- package/dist-vue/style.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/Agent-DTLrWpzX.js +0 -460
- package/dist/_chunks/Agent-DTLrWpzX.js.map +0 -1
- package/dist/_chunks/Media-Cft7Pwrh.js +0 -108
- package/dist/_chunks/Media-Cft7Pwrh.js.map +0 -1
- package/dist/_chunks/PkStreamingMarkdown-C1sANAr5.js.map +0 -1
- package/dist/_chunks/PkToolRequestOAuthConnection-CTp2w60F.js.map +0 -1
- package/dist/_chunks/PkToolShowComparison-BJ7QBXtf.js +0 -60
- package/dist/_chunks/PkToolShowComparison-BJ7QBXtf.js.map +0 -1
- package/dist/_chunks/markdown-BRD-hDrH.js.map +0 -1
- package/dist-vue/_chunks/Document-2X01-F4Z.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotAuth-Bi508F1u.js +0 -349
- package/dist-vue/_chunks/PkChatbotAuth-Bi508F1u.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotMessages-GZStJbx_.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotSteps-Cl4pVtPJ.js +0 -190
- package/dist-vue/_chunks/PkChatbotSteps-Cl4pVtPJ.js.map +0 -1
- package/dist-vue/_chunks/PkToolRequestOAuthConnection-BorR5NmZ.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowComparison-BLn4pkno.js +0 -61
- package/dist-vue/_chunks/PkToolShowComparison-BLn4pkno.js.map +0 -1
- package/dist-vue/_chunks/createChatbotApiClient-CaVLm5W9.js.map +0 -1
- package/dist-vue/_chunks/utils-h4C7rRcu.js +0 -177
- package/dist-vue/_chunks/utils-h4C7rRcu.js.map +0 -1
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./PkStreamingMarkdown-BloMJaIW.js";
|
|
2
|
-
import { c as t, o as n } from "./message-RohfrdWS.js";
|
|
3
|
-
import { f as r, h as i, l as a, m as o, n as s, p as ee, s as c, u as l } from "./utils-h4C7rRcu.js";
|
|
4
|
-
import { t as u } from "./PkChatbotStepPayload-DU0W-eUp.js";
|
|
5
|
-
import { t as d } from "./PkChatbotStepToolDetail-etlBryOg.js";
|
|
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";
|
|
9
|
-
import { useResizeObserver as A } from "@vueuse/core";
|
|
10
|
-
//#region ../../packages/components/src/chat/PkStreamingMarkdownAutoscroll.vue?vue&type=script&setup=true&lang.ts
|
|
11
|
-
var j = 8, M = /* @__PURE__ */ x({
|
|
12
|
-
__name: "PkStreamingMarkdownAutoscroll",
|
|
13
|
-
props: {
|
|
14
|
-
markdown: {},
|
|
15
|
-
innerClass: {},
|
|
16
|
-
isDark: { type: Boolean },
|
|
17
|
-
tailLength: {}
|
|
18
|
-
},
|
|
19
|
-
setup(t) {
|
|
20
|
-
let n = T(), r = T(), i = !1, a, o = () => {
|
|
21
|
-
let e = n.value;
|
|
22
|
-
e && e.scrollTop !== a && (a = void 0, i = e.scrollHeight - e.clientHeight - e.scrollTop > j);
|
|
23
|
-
};
|
|
24
|
-
return A([n, r], () => {
|
|
25
|
-
let e = n.value;
|
|
26
|
-
e && (e.scrollHeight <= e.clientHeight && (i = !1), !i && (e.scrollTop = e.scrollHeight, a = e.scrollTop));
|
|
27
|
-
}), (i, a) => (C(), _("div", {
|
|
28
|
-
ref_key: "scrollEl",
|
|
29
|
-
ref: n,
|
|
30
|
-
class: "overflow-auto light-scrollbar",
|
|
31
|
-
onScroll: o
|
|
32
|
-
}, [b(e, {
|
|
33
|
-
ref_key: "contentEl",
|
|
34
|
-
ref: r,
|
|
35
|
-
markdown: t.markdown,
|
|
36
|
-
"is-dark": t.isDark,
|
|
37
|
-
"tail-length": t.tailLength,
|
|
38
|
-
class: S(t.innerClass)
|
|
39
|
-
}, null, 8, [
|
|
40
|
-
"markdown",
|
|
41
|
-
"is-dark",
|
|
42
|
-
"tail-length",
|
|
43
|
-
"class"
|
|
44
|
-
])], 544));
|
|
45
|
-
}
|
|
46
|
-
}), N = {
|
|
47
|
-
key: 0,
|
|
48
|
-
class: "pk-chatbot-steps"
|
|
49
|
-
}, P = ["aria-expanded"], F = { class: "pk-chatbot-steps__title" }, I = { class: "pk-chatbot-steps__meta" }, L = {
|
|
50
|
-
key: 0,
|
|
51
|
-
class: "pk-chatbot-steps__list"
|
|
52
|
-
}, R = { class: "pk-chatbot-steps__step-label" }, z = { class: "pk-chatbot-steps__step-summary" }, B = {
|
|
53
|
-
key: 0,
|
|
54
|
-
class: "pk-chatbot-steps__step-duration"
|
|
55
|
-
}, V = {
|
|
56
|
-
key: 0,
|
|
57
|
-
class: "pk-chatbot-steps__step-body"
|
|
58
|
-
}, H = {
|
|
59
|
-
key: 1,
|
|
60
|
-
class: "pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll light-scrollbar"
|
|
61
|
-
}, U = {
|
|
62
|
-
key: 2,
|
|
63
|
-
class: "pk-chatbot-steps__step-payload pk-chatbot-steps__step-payload--error"
|
|
64
|
-
}, W = { class: "pk-chatbot-steps__step-payload-label" }, G = /* @__PURE__ */ x({
|
|
65
|
-
__name: "PkChatbotSteps",
|
|
66
|
-
props: {
|
|
67
|
-
message: {},
|
|
68
|
-
isStreaming: { type: Boolean },
|
|
69
|
-
isDark: { type: Boolean }
|
|
70
|
-
},
|
|
71
|
-
setup(x) {
|
|
72
|
-
let A = x, { t: j, n: G } = re({ useScope: "global" }), K = (e) => l(e) === "output-error" && "errorText" in e && typeof e.errorText == "string" ? e.errorText : void 0, q = m(() => {
|
|
73
|
-
let e = A.message.parts, s = A.message.metadata?.partTimings, c = [];
|
|
74
|
-
for (let [l, u] of e.entries()) {
|
|
75
|
-
let d = n(u), f = t(u);
|
|
76
|
-
if (!d && !f) continue;
|
|
77
|
-
let p = "toolCallId" in u ? u.toolCallId : void 0, m = d && u.text.trim() ? u.text : void 0, h = K(u), g = i(u), _ = f && "input" in u ? u.input : void 0, v = f && "output" in u ? u.output : void 0;
|
|
78
|
-
c.push({
|
|
79
|
-
key: p ?? `${u.type}-${l}`,
|
|
80
|
-
part: u,
|
|
81
|
-
icon: a(u),
|
|
82
|
-
label: r(j, u),
|
|
83
|
-
summary: ee(u),
|
|
84
|
-
durationMs: p ? s?.[p] : void 0,
|
|
85
|
-
isActive: !!A.isStreaming && l === e.length - 1,
|
|
86
|
-
text: m,
|
|
87
|
-
error: h,
|
|
88
|
-
hasRichDetail: g,
|
|
89
|
-
hasBody: !!(m || h || (g ? _ !== void 0 || v !== void 0 : o(_) || o(v))),
|
|
90
|
-
toolInput: _,
|
|
91
|
-
toolOutput: v
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
return c;
|
|
95
|
-
}), J = m(() => {
|
|
96
|
-
let { createdAt: e, completedAt: t } = A.message.metadata ?? {};
|
|
97
|
-
if (e && t) return t - e;
|
|
98
|
-
}), Y = (e) => c(G, e), X = w(/* @__PURE__ */ new Map()), Z = (e) => X.get(e.key) ?? !1, ie = (e) => {
|
|
99
|
-
X.set(e.key, !Z(e));
|
|
100
|
-
}, Q = T(void 0), $ = m(() => Q.value ?? !!A.isStreaming);
|
|
101
|
-
return te(() => A.isStreaming, (e) => {
|
|
102
|
-
e && (Q.value = void 0, X.clear());
|
|
103
|
-
}), (t, n) => {
|
|
104
|
-
let r = f;
|
|
105
|
-
return q.value.length ? (C(), _("div", N, [v("button", {
|
|
106
|
-
type: "button",
|
|
107
|
-
class: "pk-chatbot-steps__header",
|
|
108
|
-
"aria-expanded": $.value,
|
|
109
|
-
onClick: n[0] ||= (e) => Q.value = !$.value
|
|
110
|
-
}, [
|
|
111
|
-
x.isStreaming ? (C(), h(r, {
|
|
112
|
-
key: 0,
|
|
113
|
-
name: "loading-loop",
|
|
114
|
-
class: "shrink-0"
|
|
115
|
-
})) : (C(), h(r, {
|
|
116
|
-
key: 1,
|
|
117
|
-
name: "ri:route-line",
|
|
118
|
-
class: "shrink-0"
|
|
119
|
-
})),
|
|
120
|
-
v("span", F, O(k(j)("messagePart.steps")), 1),
|
|
121
|
-
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)]),
|
|
122
|
-
b(r, {
|
|
123
|
-
name: $.value ? "ri:arrow-up-s-line" : "ri:arrow-down-s-line",
|
|
124
|
-
class: "pk-chatbot-steps__chevron shrink-0 ml-auto"
|
|
125
|
-
}, null, 8, ["name"])
|
|
126
|
-
], 8, P), $.value ? (C(), _("ol", L, [(C(!0), _(p, null, E(q.value, (t) => (C(), _("li", {
|
|
127
|
-
key: t.key,
|
|
128
|
-
class: S(["pk-chatbot-steps__step", { "pk-chatbot-steps__step--active": t.isActive }])
|
|
129
|
-
}, [(C(), h(D(t.hasBody ? "button" : "div"), {
|
|
130
|
-
type: t.hasBody ? "button" : void 0,
|
|
131
|
-
class: "pk-chatbot-steps__step-header",
|
|
132
|
-
"aria-expanded": t.hasBody ? Z(t) : void 0,
|
|
133
|
-
onClick: (e) => t.hasBody ? ie(t) : void 0
|
|
134
|
-
}, {
|
|
135
|
-
default: ne(() => [
|
|
136
|
-
v("span", { class: S(["pk-chatbot-steps-marker", { "pk-chatbot-steps-marker--active": t.isActive }]) }, [t.isActive ? (C(), h(r, {
|
|
137
|
-
key: 0,
|
|
138
|
-
name: "loading-loop"
|
|
139
|
-
})) : t.icon ? (C(), h(r, {
|
|
140
|
-
key: 1,
|
|
141
|
-
name: t.icon
|
|
142
|
-
}, null, 8, ["name"])) : g("", !0)], 2),
|
|
143
|
-
v("span", R, O(t.label), 1),
|
|
144
|
-
v("span", z, O(t.summary), 1),
|
|
145
|
-
Y(t.durationMs) ? (C(), _("span", B, O(Y(t.durationMs)), 1)) : g("", !0),
|
|
146
|
-
t.hasBody ? (C(), h(r, {
|
|
147
|
-
key: 1,
|
|
148
|
-
name: Z(t) ? "ri:arrow-up-s-line" : "ri:arrow-down-s-line",
|
|
149
|
-
class: "pk-chatbot-steps__chevron shrink-0 ml-auto"
|
|
150
|
-
}, null, 8, ["name"])) : g("", !0)
|
|
151
|
-
]),
|
|
152
|
-
_: 2
|
|
153
|
-
}, 1032, [
|
|
154
|
-
"type",
|
|
155
|
-
"aria-expanded",
|
|
156
|
-
"onClick"
|
|
157
|
-
])), t.hasBody && Z(t) ? (C(), _("div", V, [t.text && t.isActive ? (C(), h(M, {
|
|
158
|
-
key: 0,
|
|
159
|
-
markdown: t.text,
|
|
160
|
-
"is-dark": x.isDark,
|
|
161
|
-
"tail-length": k(s),
|
|
162
|
-
"inner-class": "wysiwyg",
|
|
163
|
-
class: "pk-chatbot-steps__step-content pk-chatbot-steps__step-content--scroll"
|
|
164
|
-
}, null, 8, [
|
|
165
|
-
"markdown",
|
|
166
|
-
"is-dark",
|
|
167
|
-
"tail-length"
|
|
168
|
-
])) : t.text ? (C(), _("div", H, [b(e, {
|
|
169
|
-
markdown: t.text,
|
|
170
|
-
"is-dark": x.isDark,
|
|
171
|
-
class: "wysiwyg"
|
|
172
|
-
}, 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
|
-
key: 3,
|
|
174
|
-
part: t.part
|
|
175
|
-
}, null, 8, ["part"])) : (C(), _(p, { key: 4 }, [k(o)(t.toolInput) ? (C(), h(u, {
|
|
176
|
-
key: 0,
|
|
177
|
-
label: k(j)("messagePart.stepInput"),
|
|
178
|
-
value: t.toolInput
|
|
179
|
-
}, null, 8, ["label", "value"])) : g("", !0), k(o)(t.toolOutput) ? (C(), h(u, {
|
|
180
|
-
key: 1,
|
|
181
|
-
label: k(j)("messagePart.stepOutput"),
|
|
182
|
-
value: t.toolOutput
|
|
183
|
-
}, null, 8, ["label", "value"])) : g("", !0)], 64))])) : g("", !0)], 2))), 128))])) : g("", !0)])) : g("", !0);
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
//#endregion
|
|
188
|
-
export { M as n, G as t };
|
|
189
|
-
|
|
190
|
-
//# sourceMappingURL=PkChatbotSteps-Cl4pVtPJ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbotSteps-Cl4pVtPJ.js","names":[],"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 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 once the\n // answer is complete, unless the user toggled it manually.\n const manualGroup = ref<boolean | undefined>(undefined)\n const isGroupExpanded = computed(\n () => manualGroup.value ?? Boolean(props.isStreaming),\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(--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 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 once the\n // answer is complete, unless the user toggled it manually.\n const manualGroup = ref<boolean | undefined>(undefined)\n const isGroupExpanded = computed(\n () => manualGroup.value ?? Boolean(props.isStreaming),\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(--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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PkToolRequestOAuthConnection-BorR5NmZ.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolRequestOAuthConnection.vue","../../../../packages/components/src/chat/PkToolRequestOAuthConnection.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { isVoiceRenderAck } from 'models'\n\n const props = defineProps<{\n part: unknown\n resolveConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; authorizeUrl: string }>\n checkConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; connected: boolean }>\n }>()\n\n const emit = defineEmits<{\n connected: [mcpServerId: string]\n }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n /** How often the connection is re-read while the sign-in window is out. */\n const POLL_INTERVAL_MS = 2000\n /**\n * How long we keep asking. The only end to a sign-in nobody finishes:\n * `popup.closed` cannot be that signal, because the browser disowns the\n * handle when the provider severs the opener (that is what\n * `Cross-Origin-Opener-Policy` does), so it reads as closed from the first\n * check of the very flow this watch exists for. Long enough for a password\n * manager and a second factor.\n */\n const POLL_TIMEOUT_MS = 5 * 60 * 1000\n\n const toolPart = computed(() => {\n return props.part as {\n state?: string\n toolCallId?: string\n input?: { serverName: string }\n output?: unknown\n }\n })\n\n const state = computed(() => toolPart.value.state ?? 'unknown')\n const serverName = computed(() => toolPart.value.input?.serverName)\n\n /**\n * A card drawn during a CALL arrives already \"answered\": the turn could\n * not wait for the connection, so the backend closed the tool with a note\n * saying the card was drawn (see `isVoiceRenderAck`, same rule as the\n * forms). That note is not a connection, and without this the card\n * rendered its inert fallback: the caller was told to connect the account\n * and given nothing to click (measured 2026-08-07).\n */\n const isRenderAck = computed(() => isVoiceRenderAck(toolPart.value.output))\n\n /** The output this card wrote itself once the connection went through. */\n const isConnectedByOutput = computed(\n () =>\n (toolPart.value.output as { connected?: unknown } | undefined)\n ?.connected === true,\n )\n\n const isAwaitingConnection = computed(\n () => state.value === 'input-available' || isRenderAck.value,\n )\n\n const mcpServerId = ref<string | null>(null)\n /**\n * Set once THIS card's sign-in went through, and never from a plain read of\n * the connection: a card still asking is the turn saying it could not use\n * that integration, and a token row can outlive its usefulness (a refresh\n * that failed transiently keeps the row, so the turn sees no usable token\n * while a status read still sees one). Trusting the read there would have\n * left the card claiming success, with no button, on a turn still waiting.\n * It doubles as the guard that resumes the turn exactly once, whichever\n * channel got there first.\n */\n const hasConnected = ref(false)\n /** A sign-in window we opened is still out there. */\n const isSigningIn = ref(false)\n const oauthError = ref<string | null>(null)\n\n const isDone = computed(\n () => hasConnected.value || isConnectedByOutput.value,\n )\n const canConnect = computed(\n () => Boolean(serverName.value) && Boolean(props.resolveConnection),\n )\n\n /**\n * Whether the server says this user has connected, asked without minting\n * anything: no OAuth `state` is burned by looking.\n */\n async function isConnectedNow(): Promise<boolean> {\n if (!serverName.value || !props.checkConnection) {\n return false\n }\n try {\n const status = await props.checkConnection(serverName.value)\n mcpServerId.value = status.mcpServerId\n return status.connected\n } catch {\n // Not knowing is not an error to show: the sign-in window is still\n // out there and the next read may well answer.\n return false\n }\n }\n\n let pollTimer: ReturnType<typeof setTimeout> | undefined\n /**\n * Which sign-in a running tick belongs to. A tick suspended on its read\n * outlives the attempt that scheduled it: without this it woke up inside\n * the NEXT attempt, restarted its own chain over that one's timer and then\n * stopped it on its own older deadline.\n */\n let signInAttempt = 0\n /** The window this attempt opened, the only sender we accept a report from. */\n let signInWindow: Window | null = null\n\n function stopWatching() {\n if (pollTimer !== undefined) {\n clearTimeout(pollTimer)\n pollTimer = undefined\n }\n signInAttempt += 1\n signInWindow = null\n isSigningIn.value = false\n }\n\n function settleConnected(connectedServerId: string) {\n stopWatching()\n if (hasConnected.value) {\n return\n }\n hasConnected.value = true\n mcpServerId.value = connectedServerId\n oauthError.value = null\n emit('connected', connectedServerId)\n }\n\n /**\n * Watches the connection from THIS window while the sign-in window is away.\n *\n * The callback page reports through `window.opener`, and a provider that\n * sends `Cross-Origin-Opener-Policy` anywhere in its redirect chain takes\n * that opener away: the connection then succeeds with nobody listening, the\n * card stays on its button and the suspended turn is never resumed\n * (reported 2026-08-27). This channel never goes through that window, so it\n * survives whatever the provider does to it. Chained rather than periodic:\n * a read slower than the interval would otherwise overlap the next one.\n */\n function watchForConnection(popup: Window) {\n stopWatching()\n const attempt = signInAttempt\n signInWindow = popup\n isSigningIn.value = true\n const deadline = Date.now() + POLL_TIMEOUT_MS\n const tick = async () => {\n if (attempt !== signInAttempt || Date.now() > deadline) {\n if (attempt === signInAttempt) {\n stopWatching()\n }\n return\n }\n const connected = await isConnectedNow()\n if (attempt !== signInAttempt) {\n return\n }\n if (connected && mcpServerId.value) {\n settleConnected(mcpServerId.value)\n return\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n\n /**\n * The callback page's own report, when the browser let it through: faster\n * than the poll, and the only channel carrying the failure reason.\n *\n * Accepted only from the window this card opened, and only while that\n * sign-in is out: the listener stays up for as long as the card is on\n * screen, and nothing else with a handle on this page may resume a turn by\n * posting a message at it.\n */\n function onMessage(event: MessageEvent) {\n if (\n event.data?.type !== 'mcp-oauth-complete' ||\n !isSigningIn.value ||\n event.source !== signInWindow\n ) {\n return\n }\n const reportedServerId = event.data.mcpServerId as string | undefined\n if (\n reportedServerId &&\n mcpServerId.value &&\n reportedServerId !== mcpServerId.value\n ) {\n return\n }\n if (!event.data.success) {\n // A failure carries no server id: the callback never got that far.\n stopWatching()\n oauthError.value = event.data.error ?? 'Unknown error'\n return\n }\n const connectedServerId = reportedServerId ?? mcpServerId.value\n if (connectedServerId) {\n settleConnected(connectedServerId)\n }\n }\n\n /**\n * Opens the provider's sign-in, on an authorize URL minted NOW.\n *\n * The URL carries an OAuth `state` that lives ten minutes server-side.\n * Minted when the card rendered, it was routinely expired by the time\n * anyone clicked, and the callback then failed on a sign-in the user had\n * just completed. The window is opened blank first, synchronously, or the\n * popup blocker kills it while we wait for the URL.\n */\n async function openAuthWindow() {\n if (\n !serverName.value ||\n !props.resolveConnection ||\n isSigningIn.value\n ) {\n return\n }\n oauthError.value = null\n const popup = window.open(\n '',\n 'mcp_oauth',\n 'width=600,height=700,menubar=no,toolbar=no',\n )\n if (!popup) {\n oauthError.value = $t('auth.popupBlocked')\n return\n }\n watchForConnection(popup)\n try {\n const connection = await props.resolveConnection(serverName.value)\n mcpServerId.value = connection.mcpServerId\n popup.location.href = connection.authorizeUrl\n } catch (err) {\n stopWatching()\n popup.close()\n oauthError.value =\n err instanceof Error ? err.message : 'Failed to resolve OAuth'\n }\n }\n\n // For the card's whole life, not just between the click and the answer: a\n // card that re-renders while the sign-in window is away used to lose the\n // listener with it, and the completion landed on nobody.\n onMounted(() => window.addEventListener('message', onMessage))\n onUnmounted(() => {\n window.removeEventListener('message', onMessage)\n stopWatching()\n })\n</script>\n\n<template>\n <!-- The same frame and header as the other tool cards: this was the only\n one drawing itself as a tinted box (DESIGN.md). -->\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface text-12\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:link-m\" class=\"text-16 shrink-0\" />\n <strong class=\"truncate\">{{ serverName }}</strong>\n </div>\n <div class=\"p-sm\">\n <!-- Connected: just now, or by this card on an earlier visit -->\n <template v-if=\"isDone\">\n <div class=\"flex items-center gap-8\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <strong class=\"font-medium text-word-1\">\n {{\n $t('messagePart.requestOAuthConnectionSuccess', {\n name: serverName,\n })\n }}\n </strong>\n </div>\n </template>\n\n <!-- Signing in: the window is out and we are watching for it -->\n <template v-else-if=\"isSigningIn\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"loading-loop\" />\n <span class=\"text-10\">{{\n $t('messagePart.requestOAuthConnection')\n }}</span>\n </div>\n </template>\n\n <!-- OAuth error -->\n <template v-else-if=\"oauthError\">\n <div class=\"flex flex-col gap-xs\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:close-line\" class=\"text-danger\" />\n <strong class=\"text-10\">\n {{ oauthError }}\n </strong>\n </div>\n <VvButton\n v-if=\"canConnect\"\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </div>\n </template>\n\n <!-- Waiting on the user: show the connect button -->\n <template v-else-if=\"isAwaitingConnection && canConnect\">\n <div class=\"flex items-center gap-6 text-word-3 mb-xs\">\n <span>\n {{\n $t('messagePart.requestOAuthConnectionPrompt', {\n name: serverName,\n })\n }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </template>\n <!-- Fallback for unknown state -->\n <template v-else>\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:link-m\" />\n <span class=\"text-10\">\n {{\n $t('messagePart.requestOAuthConnectionInfo', {\n name: serverName,\n })\n }}\n </span>\n </div>\n </template>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { isVoiceRenderAck } from 'models'\n\n const props = defineProps<{\n part: unknown\n resolveConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; authorizeUrl: string }>\n checkConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; connected: boolean }>\n }>()\n\n const emit = defineEmits<{\n connected: [mcpServerId: string]\n }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n /** How often the connection is re-read while the sign-in window is out. */\n const POLL_INTERVAL_MS = 2000\n /**\n * How long we keep asking. The only end to a sign-in nobody finishes:\n * `popup.closed` cannot be that signal, because the browser disowns the\n * handle when the provider severs the opener (that is what\n * `Cross-Origin-Opener-Policy` does), so it reads as closed from the first\n * check of the very flow this watch exists for. Long enough for a password\n * manager and a second factor.\n */\n const POLL_TIMEOUT_MS = 5 * 60 * 1000\n\n const toolPart = computed(() => {\n return props.part as {\n state?: string\n toolCallId?: string\n input?: { serverName: string }\n output?: unknown\n }\n })\n\n const state = computed(() => toolPart.value.state ?? 'unknown')\n const serverName = computed(() => toolPart.value.input?.serverName)\n\n /**\n * A card drawn during a CALL arrives already \"answered\": the turn could\n * not wait for the connection, so the backend closed the tool with a note\n * saying the card was drawn (see `isVoiceRenderAck`, same rule as the\n * forms). That note is not a connection, and without this the card\n * rendered its inert fallback: the caller was told to connect the account\n * and given nothing to click (measured 2026-08-07).\n */\n const isRenderAck = computed(() => isVoiceRenderAck(toolPart.value.output))\n\n /** The output this card wrote itself once the connection went through. */\n const isConnectedByOutput = computed(\n () =>\n (toolPart.value.output as { connected?: unknown } | undefined)\n ?.connected === true,\n )\n\n const isAwaitingConnection = computed(\n () => state.value === 'input-available' || isRenderAck.value,\n )\n\n const mcpServerId = ref<string | null>(null)\n /**\n * Set once THIS card's sign-in went through, and never from a plain read of\n * the connection: a card still asking is the turn saying it could not use\n * that integration, and a token row can outlive its usefulness (a refresh\n * that failed transiently keeps the row, so the turn sees no usable token\n * while a status read still sees one). Trusting the read there would have\n * left the card claiming success, with no button, on a turn still waiting.\n * It doubles as the guard that resumes the turn exactly once, whichever\n * channel got there first.\n */\n const hasConnected = ref(false)\n /** A sign-in window we opened is still out there. */\n const isSigningIn = ref(false)\n const oauthError = ref<string | null>(null)\n\n const isDone = computed(\n () => hasConnected.value || isConnectedByOutput.value,\n )\n const canConnect = computed(\n () => Boolean(serverName.value) && Boolean(props.resolveConnection),\n )\n\n /**\n * Whether the server says this user has connected, asked without minting\n * anything: no OAuth `state` is burned by looking.\n */\n async function isConnectedNow(): Promise<boolean> {\n if (!serverName.value || !props.checkConnection) {\n return false\n }\n try {\n const status = await props.checkConnection(serverName.value)\n mcpServerId.value = status.mcpServerId\n return status.connected\n } catch {\n // Not knowing is not an error to show: the sign-in window is still\n // out there and the next read may well answer.\n return false\n }\n }\n\n let pollTimer: ReturnType<typeof setTimeout> | undefined\n /**\n * Which sign-in a running tick belongs to. A tick suspended on its read\n * outlives the attempt that scheduled it: without this it woke up inside\n * the NEXT attempt, restarted its own chain over that one's timer and then\n * stopped it on its own older deadline.\n */\n let signInAttempt = 0\n /** The window this attempt opened, the only sender we accept a report from. */\n let signInWindow: Window | null = null\n\n function stopWatching() {\n if (pollTimer !== undefined) {\n clearTimeout(pollTimer)\n pollTimer = undefined\n }\n signInAttempt += 1\n signInWindow = null\n isSigningIn.value = false\n }\n\n function settleConnected(connectedServerId: string) {\n stopWatching()\n if (hasConnected.value) {\n return\n }\n hasConnected.value = true\n mcpServerId.value = connectedServerId\n oauthError.value = null\n emit('connected', connectedServerId)\n }\n\n /**\n * Watches the connection from THIS window while the sign-in window is away.\n *\n * The callback page reports through `window.opener`, and a provider that\n * sends `Cross-Origin-Opener-Policy` anywhere in its redirect chain takes\n * that opener away: the connection then succeeds with nobody listening, the\n * card stays on its button and the suspended turn is never resumed\n * (reported 2026-08-27). This channel never goes through that window, so it\n * survives whatever the provider does to it. Chained rather than periodic:\n * a read slower than the interval would otherwise overlap the next one.\n */\n function watchForConnection(popup: Window) {\n stopWatching()\n const attempt = signInAttempt\n signInWindow = popup\n isSigningIn.value = true\n const deadline = Date.now() + POLL_TIMEOUT_MS\n const tick = async () => {\n if (attempt !== signInAttempt || Date.now() > deadline) {\n if (attempt === signInAttempt) {\n stopWatching()\n }\n return\n }\n const connected = await isConnectedNow()\n if (attempt !== signInAttempt) {\n return\n }\n if (connected && mcpServerId.value) {\n settleConnected(mcpServerId.value)\n return\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n\n /**\n * The callback page's own report, when the browser let it through: faster\n * than the poll, and the only channel carrying the failure reason.\n *\n * Accepted only from the window this card opened, and only while that\n * sign-in is out: the listener stays up for as long as the card is on\n * screen, and nothing else with a handle on this page may resume a turn by\n * posting a message at it.\n */\n function onMessage(event: MessageEvent) {\n if (\n event.data?.type !== 'mcp-oauth-complete' ||\n !isSigningIn.value ||\n event.source !== signInWindow\n ) {\n return\n }\n const reportedServerId = event.data.mcpServerId as string | undefined\n if (\n reportedServerId &&\n mcpServerId.value &&\n reportedServerId !== mcpServerId.value\n ) {\n return\n }\n if (!event.data.success) {\n // A failure carries no server id: the callback never got that far.\n stopWatching()\n oauthError.value = event.data.error ?? 'Unknown error'\n return\n }\n const connectedServerId = reportedServerId ?? mcpServerId.value\n if (connectedServerId) {\n settleConnected(connectedServerId)\n }\n }\n\n /**\n * Opens the provider's sign-in, on an authorize URL minted NOW.\n *\n * The URL carries an OAuth `state` that lives ten minutes server-side.\n * Minted when the card rendered, it was routinely expired by the time\n * anyone clicked, and the callback then failed on a sign-in the user had\n * just completed. The window is opened blank first, synchronously, or the\n * popup blocker kills it while we wait for the URL.\n */\n async function openAuthWindow() {\n if (\n !serverName.value ||\n !props.resolveConnection ||\n isSigningIn.value\n ) {\n return\n }\n oauthError.value = null\n const popup = window.open(\n '',\n 'mcp_oauth',\n 'width=600,height=700,menubar=no,toolbar=no',\n )\n if (!popup) {\n oauthError.value = $t('auth.popupBlocked')\n return\n }\n watchForConnection(popup)\n try {\n const connection = await props.resolveConnection(serverName.value)\n mcpServerId.value = connection.mcpServerId\n popup.location.href = connection.authorizeUrl\n } catch (err) {\n stopWatching()\n popup.close()\n oauthError.value =\n err instanceof Error ? err.message : 'Failed to resolve OAuth'\n }\n }\n\n // For the card's whole life, not just between the click and the answer: a\n // card that re-renders while the sign-in window is away used to lose the\n // listener with it, and the completion landed on nobody.\n onMounted(() => window.addEventListener('message', onMessage))\n onUnmounted(() => {\n window.removeEventListener('message', onMessage)\n stopWatching()\n })\n</script>\n\n<template>\n <!-- The same frame and header as the other tool cards: this was the only\n one drawing itself as a tinted box (DESIGN.md). -->\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface text-12\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:link-m\" class=\"text-16 shrink-0\" />\n <strong class=\"truncate\">{{ serverName }}</strong>\n </div>\n <div class=\"p-sm\">\n <!-- Connected: just now, or by this card on an earlier visit -->\n <template v-if=\"isDone\">\n <div class=\"flex items-center gap-8\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <strong class=\"font-medium text-word-1\">\n {{\n $t('messagePart.requestOAuthConnectionSuccess', {\n name: serverName,\n })\n }}\n </strong>\n </div>\n </template>\n\n <!-- Signing in: the window is out and we are watching for it -->\n <template v-else-if=\"isSigningIn\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"loading-loop\" />\n <span class=\"text-10\">{{\n $t('messagePart.requestOAuthConnection')\n }}</span>\n </div>\n </template>\n\n <!-- OAuth error -->\n <template v-else-if=\"oauthError\">\n <div class=\"flex flex-col gap-xs\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:close-line\" class=\"text-danger\" />\n <strong class=\"text-10\">\n {{ oauthError }}\n </strong>\n </div>\n <VvButton\n v-if=\"canConnect\"\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </div>\n </template>\n\n <!-- Waiting on the user: show the connect button -->\n <template v-else-if=\"isAwaitingConnection && canConnect\">\n <div class=\"flex items-center gap-6 text-word-3 mb-xs\">\n <span>\n {{\n $t('messagePart.requestOAuthConnectionPrompt', {\n name: serverName,\n })\n }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </template>\n <!-- Fallback for unknown state -->\n <template v-else>\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:link-m\" />\n <span class=\"text-10\">\n {{\n $t('messagePart.requestOAuthConnectionInfo', {\n name: serverName,\n })\n }}\n </span>\n </div>\n </template>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;6BAsBU,IAAmB,KASnB,IAAkB;;;;;;;;;EA1BxB,IAAM,IAAQ,GAUR,IAAO,GAIP,EAAE,GAAG,MAAO,EAAQ,EAAE,UAAU,SAAS,CAAC,GAc1C,IAAW,QACN,EAAM,IAMhB,GAEK,IAAQ,QAAe,EAAS,MAAM,SAAS,SAAS,GACxD,IAAa,QAAe,EAAS,MAAM,OAAO,UAAU,GAU5D,IAAc,QAAe,EAAiB,EAAS,MAAM,MAAM,CAAC,GAGpE,IAAsB,QAEnB,EAAS,MAAM,QACV,cAAc,EAC5B,GAEM,IAAuB,QACnB,EAAM,UAAU,qBAAqB,EAAY,KAC3D,GAEM,IAAc,EAAmB,IAAI,GAWrC,IAAe,EAAI,EAAK,GAExB,IAAc,EAAI,EAAK,GACvB,IAAa,EAAmB,IAAI,GAEpC,IAAS,QACL,EAAa,SAAS,EAAoB,KACpD,GACM,IAAa,QACT,EAAQ,EAAW,SAAU,EAAQ,EAAM,iBACrD;EAMA,eAAe,IAAmC;GAC9C,IAAI,CAAC,EAAW,SAAS,CAAC,EAAM,iBAC5B,OAAO;GAEX,IAAI;IACA,IAAM,IAAS,MAAM,EAAM,gBAAgB,EAAW,KAAK;IAE3D,OADA,EAAY,QAAQ,EAAO,aACpB,EAAO;GAClB,QAAQ;IAGJ,OAAO;GACX;EACJ;EAEA,IAAI,GAOA,IAAgB,GAEhB,IAA8B;EAElC,SAAS,IAAe;GAOpB,AANI,MAAc,KAAA,MACd,aAAa,CAAS,GACtB,IAAY,KAAA,IAEhB,KAAiB,GACjB,IAAe,MACf,EAAY,QAAQ;EACxB;EAEA,SAAS,EAAgB,GAA2B;GAEhD,AADA,EAAa,GACT,GAAa,UAGjB,EAAa,QAAQ,IACrB,EAAY,QAAQ,GACpB,EAAW,QAAQ,MACnB,EAAK,aAAa,CAAiB;EACvC;EAaA,SAAS,GAAmB,GAAe;GACvC,EAAa;GACb,IAAM,IAAU;GAEhB,AADA,IAAe,GACf,EAAY,QAAQ;GACpB,IAAM,IAAW,KAAK,IAAI,IAAI,GACxB,IAAO,YAAY;IACrB,IAAI,MAAY,KAAiB,KAAK,IAAI,IAAI,GAAU;KACpD,AAAI,MAAY,KACZ,EAAa;KAEjB;IACJ;IACA,IAAM,IAAY,MAAM,EAAe;IACvC,IAAI,MAAY,GAGhB;SAAI,KAAa,EAAY,OAAO;MAChC,EAAgB,EAAY,KAAK;MACjC;KACJ;KACA,IAAY,WAAW,GAAM,CAAgB;IAD7C;GAEJ;GACA,IAAY,WAAW,GAAM,CAAgB;EACjD;EAWA,SAAS,EAAU,GAAqB;GACpC,IACI,EAAM,MAAM,SAAS,wBACrB,CAAC,EAAY,SACb,EAAM,WAAW,GAEjB;GAEJ,IAAM,IAAmB,EAAM,KAAK;GACpC,IACI,KACA,EAAY,SACZ,MAAqB,EAAY,OAEjC;GAEJ,IAAI,CAAC,EAAM,KAAK,SAAS;IAGrB,AADA,EAAa,GACb,EAAW,QAAQ,EAAM,KAAK,SAAS;IACvC;GACJ;GACA,IAAM,IAAoB,KAAoB,EAAY;GAC1D,AAAI,KACA,EAAgB,CAAiB;EAEzC;EAWA,eAAe,IAAiB;GAC5B,IACI,CAAC,EAAW,SACZ,CAAC,EAAM,qBACP,EAAY,OAEZ;GAEJ,EAAW,QAAQ;GACnB,IAAM,IAAQ,OAAO,KACjB,IACA,aACA,4CACJ;GACA,IAAI,CAAC,GAAO;IACR,EAAW,QAAQ,EAAG,mBAAmB;IACzC;GACJ;GACA,GAAmB,CAAK;GACxB,IAAI;IACA,IAAM,IAAa,MAAM,EAAM,kBAAkB,EAAW,KAAK;IAEjE,AADA,EAAY,QAAQ,EAAW,aAC/B,EAAM,SAAS,OAAO,EAAW;GACrC,SAAS,GAAK;IAGV,AAFA,EAAa,GACb,EAAM,MAAM,GACZ,EAAW,QACP,aAAe,QAAQ,EAAI,UAAU;GAC7C;EACJ;SAKA,QAAgB,OAAO,iBAAiB,WAAW,CAAS,CAAC,GAC7D,QAAkB;GAEd,AADA,OAAO,oBAAoB,WAAW,CAAS,GAC/C,EAAa;EACjB,CAAC;;GAMD,OAAA,EAAA,GAAA,EAyFM,OAzFN,GAyFM,CAvFF,EAIM,OAJN,GAIM,CAFF,EAAoD,GAAA;IAA5C,MAAK;IAAY,OAAM;GAC/B,CAAA,GAAA,EAAkD,UAAlD,GAAkD,EAAtB,EAAA,KAAU,GAAA,CAAA,CAAA,CAAA,GAE1C,EAiFM,OAjFN,GAiFM,CA/Ec,EAAA,SACZ,EAAA,GAAA,EASM,OATN,GASM,CARF,EAA4D,GAAA;IAApD,MAAK;IAAgB,OAAM;GACnC,CAAA,GAAA,EAMS,UANT,IAMS,EAJD,EAAA,CAAA,CAAE,CAAA,6CAAA,EAAsF,MAAA,EAAA,MAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAAA,KASnF,EAAA,SACjB,EAAA,GAAA,EAKM,OALN,IAKM,CAJF,EAA8B,GAAA,EAAtB,MAAK,eAAc,CAAA,GAC3B,EAES,QAFT,IAES,EADL,EAAA,CAAA,CAAE,CAAA,oCAAA,CAAA,GAAA,CAAA,CAAA,CAAA,KAMO,EAAA,SACjB,EAAA,GAAA,EAiBM,OAjBN,IAiBM,CAhBF,EAKM,OALN,GAKM,CAJF,EAAmD,GAAA;IAA3C,MAAK;IAAgB,OAAM;GACnC,CAAA,GAAA,EAES,UAFT,GAES,EADF,EAAA,KAAU,GAAA,CAAA,CAAA,CAAA,GAIX,EAAA,SADV,EAAA,GAAA,EAS8B,GAAA;;IAP1B,WAAU;IACV,MAAK;IACJ,OAAoC,EAAA,CAAA,CAAE,CAAA,4CAAA,EAAqF,MAAA,EAAA,MAAA,CAAA;IAK3H,SAAO;GAKC,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,KAAA,EAAA,SAAwB,EAAA,SAA7C,EAAA,GAAA,EAmBW,GAAA,EAAA,KAAA,EAAA,GAAA,CAlBP,EAQM,OARN,GAQM,CAPF,EAMO,QAAA,MAAA,EAJC,EAAA,CAAA,CAAE,CAAA,4CAAA,EAAqF,MAAA,EAAA,MAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAAA,GAMnG,EAQ8B,GAAA;IAP1B,WAAU;IACV,MAAK;IACJ,OAAgC,EAAA,CAAA,CAAE,CAAA,4CAAA,EAAiF,MAAA,EAAA,MAAA,CAAA;IAKnH,SAAO;GAIZ,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,EASM,OATN,GASM,CARF,EAA2B,GAAA,EAAnB,MAAK,YAAW,CAAA,GACxB,EAMO,QANP,GAMO,EAJC,EAAA,CAAA,CAAE,CAAA,0CAAA,EAAmF,MAAA,EAAA,MAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
-
import { VvButton as t, VvButtonGroup as n, VvIcon as r } from "@volverjs/ui-vue/components";
|
|
3
|
-
import { Fragment as i, computed as a, createCommentVNode as o, createElementBlock as s, createElementVNode as c, createVNode as l, defineComponent as u, normalizeClass as d, openBlock as f, renderList as p, toDisplayString as m, unref as h, withCtx as g } from "vue";
|
|
4
|
-
import { useClipboard as _ } from "@vueuse/core";
|
|
5
|
-
//#region ../../packages/components/src/chat/PkToolShowComparison.vue?vue&type=script&setup=true&lang.ts
|
|
6
|
-
var v = {
|
|
7
|
-
key: 0,
|
|
8
|
-
class: "border border-surface-3 rounded-xl w-full overflow-hidden"
|
|
9
|
-
}, y = { class: "px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8" }, b = { class: "font-bold" }, x = { class: "overflow-x-auto" }, S = { class: "w-full text-12 border-collapse bg-surface" }, C = /*@__PURE__*/ u({
|
|
10
|
-
__name: "PkToolShowComparison",
|
|
11
|
-
props: { part: {} },
|
|
12
|
-
setup(e) {
|
|
13
|
-
let u = e, { copy: C, copied: w } = _(), T = a(() => u.part), E = a(() => {
|
|
14
|
-
let e = T.value.input?.items;
|
|
15
|
-
return !e || e.length === 0 ? [] : Object.keys(e[0].values);
|
|
16
|
-
}), D = (e) => e === "✓" ? "text-success" : e === "✗" ? "text-danger" : "text-word-2", O = () => {
|
|
17
|
-
let e = T.value.input?.items;
|
|
18
|
-
if (!e) return;
|
|
19
|
-
let t = ["", ...e.map((e) => e.name)].join(" "), n = E.value.map((t) => [t.replace(/_/g, " "), ...e.map((e) => e.values[t] ?? "")].join(" "));
|
|
20
|
-
C([t, ...n].join("\n"));
|
|
21
|
-
};
|
|
22
|
-
return (e, a) => {
|
|
23
|
-
let u = r, _ = t, C = n;
|
|
24
|
-
return T.value?.input?.items?.length ? (f(), s("div", v, [c("div", y, [
|
|
25
|
-
l(u, {
|
|
26
|
-
name: "ri:scales-line",
|
|
27
|
-
class: "text-16"
|
|
28
|
-
}),
|
|
29
|
-
c("strong", b, m(T.value.input.title ?? e.$t("label.comparison")), 1),
|
|
30
|
-
l(C, {
|
|
31
|
-
modifiers: "compact",
|
|
32
|
-
class: "ml-auto shrink-0"
|
|
33
|
-
}, {
|
|
34
|
-
default: g(() => [l(_, {
|
|
35
|
-
modifiers: "action-quiet-small",
|
|
36
|
-
icon: h(w) ? "ri:check-line" : "ri:file-copy-line",
|
|
37
|
-
label: h(w) ? e.$t("action.copied") : e.$t("action.copy"),
|
|
38
|
-
onClick: O
|
|
39
|
-
}, null, 8, ["icon", "label"])]),
|
|
40
|
-
_: 1
|
|
41
|
-
})
|
|
42
|
-
]), c("div", x, [c("table", S, [c("thead", null, [c("tr", null, [a[0] ||= c("th", { class: "text-left text-word-3 font-medium p-8 border-b border-surface-3 min-w-32" }, null, -1), (f(!0), s(i, null, p(T.value.input.items, (e) => (f(), s("th", {
|
|
43
|
-
key: e.name,
|
|
44
|
-
class: d(["text-center text-word-1 p-8 border-b border-surface-3 min-w-24", e.highlighted ? "font-semibold bg-surface-1" : "font-medium"])
|
|
45
|
-
}, m(e.name), 3))), 128))])]), c("tbody", null, [(f(!0), s(i, null, p(E.value, (e, t) => (f(), s("tr", {
|
|
46
|
-
key: e,
|
|
47
|
-
class: d({ "bg-surface-1": t % 2 != 0 })
|
|
48
|
-
}, [c("td", { class: d(["text-word-3 font-medium p-8 whitespace-nowrap", { "border-b border-surface-3": t !== E.value.length - 1 }]) }, m(e.replace(/_/g, " ")), 3), (f(!0), s(i, null, p(T.value.input.items, (n) => (f(), s("td", {
|
|
49
|
-
key: n.name,
|
|
50
|
-
class: d(["text-center p-8", [D(n.values[e]), {
|
|
51
|
-
"border-b border-surface-3": t !== E.value.length - 1,
|
|
52
|
-
"bg-surface-1": n.highlighted
|
|
53
|
-
}]])
|
|
54
|
-
}, m(n.values[e] ?? "-"), 3))), 128))], 2))), 128))])])])])) : o("", !0);
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
}), w = /* @__PURE__ */ e({ default: () => T }), T = C;
|
|
58
|
-
//#endregion
|
|
59
|
-
export { w as n, T as t };
|
|
60
|
-
|
|
61
|
-
//# sourceMappingURL=PkToolShowComparison-BLn4pkno.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PkToolShowComparison-BLn4pkno.js","names":["$t"],"sources":["../../../../packages/components/src/chat/PkToolShowComparison.vue","../../../../packages/components/src/chat/PkToolShowComparison.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n // The tool contract asks for \"✓\" and \"✗\" on boolean features: the pair is\n // a verdict, so it wears one and the table reads by eye (DESIGN.md).\n const valueColorClass = (value?: string) => {\n if (value === '✓') {\n return 'text-success'\n }\n if (value === '✗') {\n return 'text-danger'\n }\n return 'text-word-2'\n }\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <div class=\"overflow-x-auto\">\n <table class=\"w-full text-12 border-collapse bg-surface\">\n <thead>\n <tr>\n <th\n class=\"text-left text-word-3 font-medium p-8 border-b border-surface-3 min-w-32\"></th>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n class=\"text-center text-word-1 p-8 border-b border-surface-3 min-w-24\"\n :class=\"\n item.highlighted\n ? 'font-semibold bg-surface-1'\n : 'font-medium'\n \">\n {{ item.name }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(feature, fIdx) in features\"\n :key=\"feature\"\n :class=\"{ 'bg-surface-1': fIdx % 2 !== 0 }\">\n <td\n class=\"text-word-3 font-medium p-8 whitespace-nowrap\"\n :class=\"{\n 'border-b border-surface-3':\n fIdx !== features.length - 1,\n }\">\n {{ feature.replace(/_/g, ' ') }}\n </td>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n class=\"text-center p-8\"\n :class=\"[\n valueColorClass(item.values[feature]),\n {\n 'border-b border-surface-3':\n fIdx !== features.length - 1,\n 'bg-surface-1': item.highlighted,\n },\n ]\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n // The tool contract asks for \"✓\" and \"✗\" on boolean features: the pair is\n // a verdict, so it wears one and the table reads by eye (DESIGN.md).\n const valueColorClass = (value?: string) => {\n if (value === '✓') {\n return 'text-success'\n }\n if (value === '✗') {\n return 'text-danger'\n }\n return 'text-word-2'\n }\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <div class=\"overflow-x-auto\">\n <table class=\"w-full text-12 border-collapse bg-surface\">\n <thead>\n <tr>\n <th\n class=\"text-left text-word-3 font-medium p-8 border-b border-surface-3 min-w-32\"></th>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n class=\"text-center text-word-1 p-8 border-b border-surface-3 min-w-24\"\n :class=\"\n item.highlighted\n ? 'font-semibold bg-surface-1'\n : 'font-medium'\n \">\n {{ item.name }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(feature, fIdx) in features\"\n :key=\"feature\"\n :class=\"{ 'bg-surface-1': fIdx % 2 !== 0 }\">\n <td\n class=\"text-word-3 font-medium p-8 whitespace-nowrap\"\n :class=\"{\n 'border-b border-surface-3':\n fIdx !== features.length - 1,\n }\">\n {{ feature.replace(/_/g, ' ') }}\n </td>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n class=\"text-center p-8\"\n :class=\"[\n valueColorClass(item.values[feature]),\n {\n 'border-b border-surface-3':\n fIdx !== features.length - 1,\n 'bg-surface-1': item.highlighted,\n },\n ]\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;EAII,IAAM,IAAQ,GAIR,EAAE,SAAM,cAAW,EAAa,GAEhC,IAAW,QACA,EAAM,IAWtB,GAEK,IAAW,QAAe;GAC5B,IAAM,IAAQ,EAAS,MAAM,OAAO;GAIpC,OAHI,CAAC,KAAS,EAAM,WAAW,IACpB,CAAC,IAEL,OAAO,KAAK,EAAM,EAAE,CAAC,MAAM;EACtC,CAAC,GAIK,KAAmB,MACjB,MAAU,MACH,iBAEP,MAAU,MACH,gBAEJ,eAGL,UAAkB;GACpB,IAAM,IAAQ,EAAS,MAAM,OAAO;GACpC,IAAI,CAAC,GACD;GAEJ,IAAM,IAAS,CAAC,IAAI,GAAG,EAAM,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,GAAI,GACpD,IAAO,EAAS,MAAM,KAAK,MAC7B,CAAC,EAAE,QAAQ,MAAM,GAAG,GAAG,GAAG,EAAM,KAAK,MAAM,EAAE,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,KAC3D,GACJ,CACJ;GACA,EAAK,CAAC,GAAQ,GAAG,CAAI,CAAC,CAAC,KAAK,IAAI,CAAC;EACrC;;;GAKU,OAAA,EAAA,OAAU,OAAO,OAAO,UADlC,EAAA,GAAA,EAmEM,OAnEN,GAmEM,CAhEF,EAaM,OAbN,GAaM;IAXF,EAAgD,GAAA;KAAxC,MAAK;KAAiB,OAAM;;IACpC,EAEW,UAFX,GAEW,EADP,EAAA,MAAS,MAAM,SAASA,EAAAA,GAAE,kBAAA,CAAA,GAAA,CAAA;IAE9B,EAMgB,GAAA;KAND,WAAU;KAAU,OAAM;;KACrC,SAAA,QAIyB,CAJzB,EAIyB,GAAA;MAHrB,WAAU;MACT,MAAM,EAAA,CAAA,IAAM,kBAAA;MACZ,OAAO,EAAA,CAAA,IAASA,EAAAA,GAAE,eAAA,IAAoBA,EAAAA,GAAE,aAAA;MACxC,SAAO;;;;GAGpB,CAAA,GAAA,EAiDM,OAjDN,GAiDM,CAhDF,EA+CQ,SA/CR,GA+CQ,CA9CJ,EAgBQ,SAAA,MAAA,CAfJ,EAcK,MAAA,MAAA,CAbD,AAAA,EAAA,OAAA,EAC0F,MAAA,EAAtF,OAAM,2EAA0E,GAAA,MAAA,EAAA,IACpF,EAAA,EAAA,GAAA,EAUK,GAAA,MAAA,EATc,EAAA,MAAS,MAAM,QAAvB,OADX,EAAA,GAAA,EAUK,MAAA;IARA,KAAK,EAAK;IACX,OAAK,EAAA,CAAC,kEACmC,EAAK,cAAA,+BAAA,aAAA,CAAA;GAK3C,GAAA,EAAA,EAAK,IAAI,GAAA,CAAA,EAIxB,GAAA,GAAA,EAAA,CAAA,CAAA,CAAA,GAAA,EA4BQ,SAAA,MAAA,EA3BJ,EAAA,EAAA,GAAA,EA0BK,GAAA,MAAA,EAzByB,EAAA,QAAlB,GAAS,OADrB,EAAA,GAAA,EA0BK,MAAA;IAxBA,KAAK;IACL,OAAK,EAAA,EAAA,gBAAoB,IAAI,KAAA,EAAA,CAAA;GAC9B,GAAA,CAAA,EAOK,MAAA,EAND,OAAK,EAAA,CAAC,iDAA+C,EAAA,6BACsD,MAAS,EAAA,MAAS,SAAM,EAAA,CAAA,CAAA,EAIhI,GAAA,EAAA,EAAQ,QAAO,MAAA,GAAA,CAAA,GAAA,CAAA,IAEtB,EAAA,EAAA,GAAA,EAaK,GAAA,MAAA,EAZc,EAAA,MAAS,MAAM,QAAvB,OADX,EAAA,GAAA,EAaK,MAAA;IAXA,KAAK,EAAK;IACX,OAAK,EAAA,CAAC,mBAAiB,CACmB,EAAgB,EAAK,OAAO,EAAO,GAAA;KAA+I,6BAAA,MAAS,EAAA,MAAS,SAAM;KAA0D,gBAAA,EAAK;;GAQhT,GAAA,EAAA,EAAK,OAAO,MAAO,GAAA,GAAA,CAAA"}
|