@8wave/ai-elements 0.119.0-beta.26 → 0.119.0-beta.27
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-D8YrsP52.js → Agent-Gdgpd3LA.js} +66 -66
- package/dist/_chunks/Agent-Gdgpd3LA.js.map +1 -0
- package/dist/_chunks/{PkStreamingMarkdown-Bry3y6Ru.js → PkStreamingMarkdown-BK9O_NLx.js} +889 -852
- package/dist/_chunks/{PkStreamingMarkdown-Bry3y6Ru.js.map → PkStreamingMarkdown-BK9O_NLx.js.map} +1 -1
- package/dist/_chunks/{PkToolEditSpreadsheet-5vHzEy4P.js → PkToolEditSpreadsheet-D-RCfnrN.js} +2 -2
- package/dist/_chunks/{PkToolEditSpreadsheet-5vHzEy4P.js.map → PkToolEditSpreadsheet-D-RCfnrN.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestGeolocation-Cj8hTfNF.js → PkToolRequestGeolocation-PxhQxMP1.js} +2 -2
- package/dist/_chunks/{PkToolRequestGeolocation-Cj8hTfNF.js.map → PkToolRequestGeolocation-PxhQxMP1.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestOAuthConnection-BWxR1hV4.js → PkToolRequestOAuthConnection-QYDJPUFF.js} +5 -4
- package/dist/_chunks/PkToolRequestOAuthConnection-QYDJPUFF.js.map +1 -0
- package/dist/_chunks/{PkToolShowArtifact-CfJLe8vD.js → PkToolShowArtifact-DGUj8f1k.js} +3 -3
- package/dist/_chunks/PkToolShowArtifact-DGUj8f1k.js.map +1 -0
- package/dist/_chunks/{PkToolShowCalendarEvent-Bp_yv8S7.js → PkToolShowCalendarEvent-BEjPZC1F.js} +4 -3
- package/dist/_chunks/PkToolShowCalendarEvent-BEjPZC1F.js.map +1 -0
- package/dist/_chunks/{PkToolShowComparison-Bvd4f4tJ.js → PkToolShowComparison-DLC22e1W.js} +4 -3
- package/dist/_chunks/PkToolShowComparison-DLC22e1W.js.map +1 -0
- package/dist/_chunks/{PkToolShowContactForm--9XOVqNB.js → PkToolShowContactForm-DXJI0MPZ.js} +5 -4
- package/dist/_chunks/PkToolShowContactForm-DXJI0MPZ.js.map +1 -0
- package/dist/_chunks/{PkToolShowDiagram-C-pNbC9n.js → PkToolShowDiagram-C5mSqZDp.js} +9 -8
- package/dist/_chunks/PkToolShowDiagram-C5mSqZDp.js.map +1 -0
- package/dist/_chunks/{PkToolShowEmail-BKel_O29.js → PkToolShowEmail-C7mUCqGL.js} +4 -3
- package/dist/_chunks/PkToolShowEmail-C7mUCqGL.js.map +1 -0
- package/dist/_chunks/{PkToolShowForm-CGAc1O6K.js → PkToolShowForm-UQo0Xdiq.js} +6 -5
- package/dist/_chunks/PkToolShowForm-UQo0Xdiq.js.map +1 -0
- package/dist/_chunks/{PkToolShowImageGallery-CIhIrIcY.js → PkToolShowImageGallery-rZ3AtIEP.js} +2 -2
- package/dist/_chunks/{PkToolShowImageGallery-CIhIrIcY.js.map → PkToolShowImageGallery-rZ3AtIEP.js.map} +1 -1
- package/dist/_chunks/{PkToolShowLocation-78C7jFiV.js → PkToolShowLocation-BOmxHZKd.js} +4 -3
- package/dist/_chunks/PkToolShowLocation-BOmxHZKd.js.map +1 -0
- package/dist/_chunks/{PkToolShowMessage-BScy-w4J.js → PkToolShowMessage-vw4XS4TR.js} +4 -3
- package/dist/_chunks/PkToolShowMessage-vw4XS4TR.js.map +1 -0
- package/dist/_chunks/{PkToolShowProductList-BjWKnbyD.js → PkToolShowProductList-ByeSoIwq.js} +2 -2
- package/dist/_chunks/{PkToolShowProductList-BjWKnbyD.js.map → PkToolShowProductList-ByeSoIwq.js.map} +1 -1
- package/dist/_chunks/{PkToolShowQrCode-B70jPHDw.js → PkToolShowQrCode-BhcAUXSZ.js} +4 -3
- package/dist/_chunks/PkToolShowQrCode-BhcAUXSZ.js.map +1 -0
- package/dist/_chunks/{PkToolShowSources-BJ-XFrLD.js → PkToolShowSources-q4XBOwJl.js} +51 -50
- package/dist/_chunks/PkToolShowSources-q4XBOwJl.js.map +1 -0
- package/dist/_chunks/{PkToolShowSuggestedReply-0KahUIlw.js → PkToolShowSuggestedReply-BISQY1ce.js} +2 -2
- package/dist/_chunks/{PkToolShowSuggestedReply-0KahUIlw.js.map → PkToolShowSuggestedReply-BISQY1ce.js.map} +1 -1
- package/dist/_chunks/{PkToolShowVideo-_V_d-PDr.js → PkToolShowVideo-BmHHW4Ep.js} +2 -2
- package/dist/_chunks/{PkToolShowVideo-_V_d-PDr.js.map → PkToolShowVideo-BmHHW4Ep.js.map} +1 -1
- package/dist/_chunks/{PkToolShowWebPages-D55VEPjK.js → PkToolShowWebPages-BaapF0CC.js} +2 -2
- package/dist/_chunks/{PkToolShowWebPages-D55VEPjK.js.map → PkToolShowWebPages-BaapF0CC.js.map} +1 -1
- package/dist/_chunks/VvButtonLocalized-DV-zbH9b.js +21 -0
- package/dist/_chunks/VvButtonLocalized-DV-zbH9b.js.map +1 -0
- package/dist/_chunks/{chatImagePolicy-DXkhb-N_.js → chatImagePolicy-CDNkMKdT.js} +2 -2
- package/dist/_chunks/{chatImagePolicy-DXkhb-N_.js.map → chatImagePolicy-CDNkMKdT.js.map} +1 -1
- package/dist/_chunks/{markdown-DpoAzJSk.js → markdown-BbaLt57-.js} +8 -8
- package/dist/_chunks/{markdown-DpoAzJSk.js.map → markdown-BbaLt57-.js.map} +1 -1
- package/dist/_chunks/text-BHCGblDQ.js +6 -0
- package/dist/_chunks/text-BHCGblDQ.js.map +1 -0
- package/dist/ai-elements.es.js +2789 -2526
- 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/PkChatbotError.js +1 -1
- package/dist-vue/PkChatbotFeedbackForm.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/PkChatbotWakeupBar.js +1 -1
- package/dist-vue/_chunks/{Agent-dGF8mkG9.js → Agent-DU1WWaHR.js} +87 -87
- package/dist-vue/_chunks/Agent-DU1WWaHR.js.map +1 -0
- package/dist-vue/_chunks/{Document-CV-RJu_p.js → Document-Cto-Qws0.js} +2 -2
- package/dist-vue/_chunks/{Document-CV-RJu_p.js.map → Document-Cto-Qws0.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbot-536vsOfp.js → PkChatbot-D0BQ3VnX.js} +86 -85
- package/dist-vue/_chunks/PkChatbot-D0BQ3VnX.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotAuth-BMLLTSwK.js → PkChatbotAuth-BVZzjbK9.js} +4 -3
- package/dist-vue/_chunks/PkChatbotAuth-BVZzjbK9.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotEmptyState-Toj4CqXV.js → PkChatbotEmptyState-CGjFcwwB.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotEmptyState-Toj4CqXV.js.map → PkChatbotEmptyState-CGjFcwwB.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotError-CRGbK09-.js → PkChatbotError-BV5ga7hk.js} +4 -3
- package/dist-vue/_chunks/PkChatbotError-BV5ga7hk.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotFeedbackForm-CMFXjoKR.js → PkChatbotFeedbackForm-B5cYsWBr.js} +8 -7
- package/dist-vue/_chunks/PkChatbotFeedbackForm-B5cYsWBr.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotInput-BfZqRpQ2.js → PkChatbotInput-BVBQY0V5.js} +8 -7
- package/dist-vue/_chunks/PkChatbotInput-BVBQY0V5.js.map +1 -0
- package/dist-vue/_chunks/PkChatbotMessages-CK0lhcDj.js +876 -0
- package/dist-vue/_chunks/PkChatbotMessages-CK0lhcDj.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotStepPayload-D0eoPBQ8.js → PkChatbotStepPayload-l57qHqVf.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotStepPayload-D0eoPBQ8.js.map → PkChatbotStepPayload-l57qHqVf.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-CMx4cw1D.js → PkChatbotStepToolDetail-ChrOa4W4.js} +16 -16
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-CMx4cw1D.js.map → PkChatbotStepToolDetail-ChrOa4W4.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotSteps-DeZQhGRU.js → PkChatbotSteps-DnU9r4XT.js} +40 -40
- package/dist-vue/_chunks/{PkChatbotSteps-DeZQhGRU.js.map → PkChatbotSteps-DnU9r4XT.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-CVT6OUIv.js → PkChatbotViewChat-DIx3nwEg.js} +12 -12
- package/dist-vue/_chunks/{PkChatbotViewChat-CVT6OUIv.js.map → PkChatbotViewChat-DIx3nwEg.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotViewConversations-DDWO1Ci_.js +199 -0
- package/dist-vue/_chunks/PkChatbotViewConversations-DDWO1Ci_.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewFiles-CA20TBFY.js → PkChatbotViewFiles-O9YjrGP0.js} +18 -17
- package/dist-vue/_chunks/PkChatbotViewFiles-O9YjrGP0.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewProfile-CyzRxE3D.js → PkChatbotViewProfile-BoImMMj2.js} +11 -10
- package/dist-vue/_chunks/PkChatbotViewProfile-BoImMMj2.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotWakeupBar-jBti21FQ.js → PkChatbotWakeupBar-Cjgarxnw.js} +3 -2
- package/dist-vue/_chunks/PkChatbotWakeupBar-Cjgarxnw.js.map +1 -0
- package/dist-vue/_chunks/{PkStreamingMarkdown-C55Fi0Pd.js → PkStreamingMarkdown-JEkbkY5k.js} +23 -23
- package/dist-vue/_chunks/{PkStreamingMarkdown-C55Fi0Pd.js.map → PkStreamingMarkdown-JEkbkY5k.js.map} +1 -1
- package/dist-vue/_chunks/{PkSvgViewer-Ts3gQ1hB.js → PkSvgViewer-DrA4yi8Q.js} +7 -6
- package/dist-vue/_chunks/PkSvgViewer-DrA4yi8Q.js.map +1 -0
- package/dist-vue/_chunks/{PkToolEditSpreadsheet-HAskd6d3.js → PkToolEditSpreadsheet-BX_h8Hut.js} +2 -2
- package/dist-vue/_chunks/{PkToolEditSpreadsheet-HAskd6d3.js.map → PkToolEditSpreadsheet-BX_h8Hut.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolRequestGeolocation-BE22qwxB.js → PkToolRequestGeolocation-JZYgc-2X.js} +2 -2
- package/dist-vue/_chunks/{PkToolRequestGeolocation-BE22qwxB.js.map → PkToolRequestGeolocation-JZYgc-2X.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolRequestOAuthConnection-GfSalEJG.js → PkToolRequestOAuthConnection-Bwj84qHq.js} +11 -10
- package/dist-vue/_chunks/PkToolRequestOAuthConnection-Bwj84qHq.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowArtifact-z1qTnFkA.js → PkToolShowArtifact-BIvl5I2I.js} +5 -5
- package/dist-vue/_chunks/PkToolShowArtifact-BIvl5I2I.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowCalendarEvent-Dv_rss26.js → PkToolShowCalendarEvent-ve1I0xaV.js} +7 -6
- package/dist-vue/_chunks/PkToolShowCalendarEvent-ve1I0xaV.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowComparison-DQs3E6-k.js → PkToolShowComparison-c2e239T4.js} +3 -2
- package/dist-vue/_chunks/PkToolShowComparison-c2e239T4.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowContactForm-Y0lrHXhr.js → PkToolShowContactForm-CddxrLBZ.js} +13 -12
- package/dist-vue/_chunks/PkToolShowContactForm-CddxrLBZ.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowDiagram-7WxDObZ6.js → PkToolShowDiagram-DdR4x6pP.js} +10 -9
- package/dist-vue/_chunks/PkToolShowDiagram-DdR4x6pP.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowEmail-C591LR5N.js → PkToolShowEmail-CUgZN0J5.js} +8 -7
- package/dist-vue/_chunks/PkToolShowEmail-CUgZN0J5.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowForm-CZFWZub7.js → PkToolShowForm-DiXcYws_.js} +14 -13
- package/dist-vue/_chunks/PkToolShowForm-DiXcYws_.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowImageGallery-CkfXP_lD.js → PkToolShowImageGallery-COrJEjSv.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowImageGallery-CkfXP_lD.js.map → PkToolShowImageGallery-COrJEjSv.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowLocation-Dyz6h_0n.js → PkToolShowLocation-CHi_cONJ.js} +13 -12
- package/dist-vue/_chunks/PkToolShowLocation-CHi_cONJ.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowMessage-CbXERPHI.js → PkToolShowMessage-ATTN5Hac.js} +8 -7
- package/dist-vue/_chunks/PkToolShowMessage-ATTN5Hac.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowProductList-De-PtmD-.js → PkToolShowProductList-BIvJlpMT.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowProductList-De-PtmD-.js.map → PkToolShowProductList-BIvJlpMT.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowQrCode-DEAvfLFu.js → PkToolShowQrCode-D_5VUtEB.js} +3 -2
- package/dist-vue/_chunks/PkToolShowQrCode-D_5VUtEB.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowSources-CSN7S0zo.js → PkToolShowSources-CC-0Oh4K.js} +57 -56
- package/dist-vue/_chunks/PkToolShowSources-CC-0Oh4K.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowSuggestedReply-DjutyiEi.js → PkToolShowSuggestedReply-I6FBos-F.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowSuggestedReply-DjutyiEi.js.map → PkToolShowSuggestedReply-I6FBos-F.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowVideo-B_Q93E2y.js → PkToolShowVideo-UUNkUlrg.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowVideo-B_Q93E2y.js.map → PkToolShowVideo-UUNkUlrg.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowWebPages-D3CdjJUc.js → PkToolShowWebPages-CLYx_PgL.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowWebPages-D3CdjJUc.js.map → PkToolShowWebPages-CLYx_PgL.js.map} +1 -1
- package/dist-vue/_chunks/VvButtonLocalized-BuzLsXp3.js +20 -0
- package/dist-vue/_chunks/VvButtonLocalized-BuzLsXp3.js.map +1 -0
- package/dist-vue/_chunks/{createChatbotApiClient-DSLxswr4.js → createChatbotApiClient-BdmGCAQR.js} +17 -17
- package/dist-vue/_chunks/{createChatbotApiClient-DSLxswr4.js.map → createChatbotApiClient-BdmGCAQR.js.map} +1 -1
- package/dist-vue/_chunks/{useChatbotStore-rtl-oKqU.js → useChatbotStore-BAv5K91p.js} +3 -3
- package/dist-vue/_chunks/{useChatbotStore-rtl-oKqU.js.map → useChatbotStore-BAv5K91p.js.map} +1 -1
- package/dist-vue/_chunks/{utils-CwIMEuAv.js → utils-BqPJ6DJT.js} +142 -78
- package/dist-vue/_chunks/utils-BqPJ6DJT.js.map +1 -0
- package/dist-vue/api.js +1 -1
- package/dist-vue/composables.js +276 -24
- package/dist-vue/composables.js.map +1 -1
- package/dist-vue/index.js +4214 -4186
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/locales.js +42 -6
- package/dist-vue/packages/components/src/PkEditorMarkdown.d.ts +3 -2
- package/dist-vue/packages/components/src/VvButtonLocalized.d.ts +11 -0
- package/dist-vue/packages/components/src/chat/index.d.ts +1 -0
- package/dist-vue/packages/components/src/chat/utils.d.ts +30 -0
- package/dist-vue/packages/components/src/index.d.ts +1 -0
- package/package.json +1 -1
- package/dist/_chunks/Agent-D8YrsP52.js.map +0 -1
- package/dist/_chunks/PkToolRequestOAuthConnection-BWxR1hV4.js.map +0 -1
- package/dist/_chunks/PkToolShowArtifact-CfJLe8vD.js.map +0 -1
- package/dist/_chunks/PkToolShowCalendarEvent-Bp_yv8S7.js.map +0 -1
- package/dist/_chunks/PkToolShowComparison-Bvd4f4tJ.js.map +0 -1
- package/dist/_chunks/PkToolShowContactForm--9XOVqNB.js.map +0 -1
- package/dist/_chunks/PkToolShowDiagram-C-pNbC9n.js.map +0 -1
- package/dist/_chunks/PkToolShowEmail-BKel_O29.js.map +0 -1
- package/dist/_chunks/PkToolShowForm-CGAc1O6K.js.map +0 -1
- package/dist/_chunks/PkToolShowLocation-78C7jFiV.js.map +0 -1
- package/dist/_chunks/PkToolShowMessage-BScy-w4J.js.map +0 -1
- package/dist/_chunks/PkToolShowQrCode-B70jPHDw.js.map +0 -1
- package/dist/_chunks/PkToolShowSources-BJ-XFrLD.js.map +0 -1
- package/dist/_chunks/text-DbSEK8si.js +0 -6
- package/dist/_chunks/text-DbSEK8si.js.map +0 -1
- package/dist-vue/_chunks/Agent-dGF8mkG9.js.map +0 -1
- package/dist-vue/_chunks/PkChatbot-536vsOfp.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotAuth-BMLLTSwK.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotError-CRGbK09-.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotFeedbackForm-CMFXjoKR.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotInput-BfZqRpQ2.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotMessages-CnI3e9wF.js +0 -869
- package/dist-vue/_chunks/PkChatbotMessages-CnI3e9wF.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotViewConversations-DHeFtAfN.js +0 -197
- package/dist-vue/_chunks/PkChatbotViewConversations-DHeFtAfN.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotViewFiles-CA20TBFY.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotViewProfile-CyzRxE3D.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotWakeupBar-jBti21FQ.js.map +0 -1
- package/dist-vue/_chunks/PkSvgViewer-Ts3gQ1hB.js.map +0 -1
- package/dist-vue/_chunks/PkToolRequestOAuthConnection-GfSalEJG.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowArtifact-z1qTnFkA.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowCalendarEvent-Dv_rss26.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowComparison-DQs3E6-k.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowContactForm-Y0lrHXhr.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowDiagram-7WxDObZ6.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowEmail-C591LR5N.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowForm-CZFWZub7.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowLocation-Dyz6h_0n.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowMessage-CbXERPHI.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowQrCode-DEAvfLFu.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowSources-CSN7S0zo.js.map +0 -1
- package/dist-vue/_chunks/utils-CwIMEuAv.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbotStepPayload-D0eoPBQ8.js","names":["__unplugin_components_0","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_withCtx","_createCommentVNode","_sfc_main","stringifyPayload","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","PREVIEW_LENGTH","PkChatbotStepPayload_default","defineComponent","props","__props","$t","useI18n","stringify","value","indent","toPreview","single","entries","computed","index","item","own","rows","key","preview","full","text","summary","list","row","isOpen","ref","expandedRows","reactive","toggleRow","_component_VvIcon","VvIcon","openBlock","createElementBlock","createElementVNode","_cache","toDisplayString","createVNode","Fragment","renderList","createBlock","resolveDynamicComponent","withCtx","createCommentVNode"],"sources":["../../../../packages/components/src/chat/PkChatbotStepPayload.vue","../../../../packages/components/src/chat/PkChatbotStepPayload.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, reactive, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { stringifyPayload } from './utils'\n\n interface PayloadRow {\n key: string\n preview: string\n full?: string\n }\n\n const props = defineProps<{\n label: string\n value: unknown\n }>()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n // A preview stays on a single line: long enough to recognize the value,\n // short enough to keep the payload readable without expanding anything.\n const PREVIEW_LENGTH = 120\n\n const stringify = (value: unknown, indent = 0): string | undefined => {\n try {\n return JSON.stringify(value, null, indent)\n } catch {\n return undefined\n }\n }\n\n const toPreview = (value: unknown): string => {\n if (value === undefined) {\n return ''\n }\n const text =\n typeof value === 'string' ? value : (stringify(value) ?? '')\n const single = text.replace(/\\s+/g, ' ').trim()\n return single.length > PREVIEW_LENGTH\n ? `${single.slice(0, PREVIEW_LENGTH)}…`\n : single\n }\n\n // Objects are listed by key, arrays by position: either way the payload\n // becomes a list of short lines instead of one wall of JSON.\n const entries = computed<[string, unknown][] | undefined>(() => {\n const value = props.value\n if (Array.isArray(value)) {\n return value.length\n ? value.map(\n (item, index) =>\n [`${index + 1}`, item] as [string, unknown],\n )\n : undefined\n }\n if (value && typeof value === 'object') {\n const own = Object.entries(value as Record<string, unknown>)\n return own.length ? own : undefined\n }\n return undefined\n })\n\n const rows = computed<PayloadRow[] | undefined>(() =>\n entries.value?.map(([key, value]) => {\n const preview = toPreview(value)\n const full = typeof value === 'string' ? value : stringify(value, 2)\n return {\n key,\n preview: preview || '-',\n // Only a value the preview had to cut (long, multiline or\n // nested) is worth its own expandable block.\n full: full && full.trim() !== preview ? full : undefined,\n }\n }),\n )\n\n // Scalars and strings have no rows: they fall back to the plain payload.\n const text = computed(() =>\n rows.value ? undefined : stringifyPayload(props.value),\n )\n\n const summary = computed(() => {\n const list = rows.value\n if (!list) {\n return toPreview(props.value)\n }\n if (Array.isArray(props.value)) {\n return $t('messagePart.stepItems', list.length)\n }\n if (list.length === 1) {\n return `${list[0].key}: ${list[0].preview}`\n }\n return list.map((row) => row.key).join(' · ')\n })\n\n const isOpen = ref(false)\n const expandedRows = reactive(new Set<string>())\n const toggleRow = (key: string) => {\n if (expandedRows.has(key)) {\n expandedRows.delete(key)\n } else {\n expandedRows.add(key)\n }\n }\n</script>\n\n<template>\n <div v-if=\"rows || text\" class=\"pk-chatbot-step-payload\">\n <button\n type=\"button\"\n class=\"pk-chatbot-step-payload__header\"\n :aria-expanded=\"isOpen\"\n @click=\"isOpen = !isOpen\">\n <span class=\"pk-chatbot-step-payload__label\">{{ label }}</span>\n <span class=\"pk-chatbot-step-payload__summary\">\n {{ summary }}\n </span>\n <VvIcon\n :name=\"isOpen ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"shrink-0\" />\n </button>\n <ul v-if=\"isOpen && rows\" class=\"pk-chatbot-step-payload__rows\">\n <li v-for=\"row in rows\" :key=\"row.key\">\n <component\n :is=\"row.full ? 'button' : 'div'\"\n :type=\"row.full ? 'button' : undefined\"\n class=\"pk-chatbot-step-payload__row\"\n :aria-expanded=\"\n row.full ? expandedRows.has(row.key) : undefined\n \"\n @click=\"row.full ? toggleRow(row.key) : undefined\">\n <span class=\"pk-chatbot-step-payload__key\">\n {{ row.key }}\n </span>\n <span class=\"pk-chatbot-step-payload__value\">\n {{ row.preview }}\n </span>\n <VvIcon\n v-if=\"row.full\"\n :name=\"\n expandedRows.has(row.key)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"shrink-0\" />\n </component>\n <pre\n v-if=\"row.full && expandedRows.has(row.key)\"\n class=\"pk-chatbot-step-payload__full\"\n >{{ row.full }}</pre>\n </li>\n </ul>\n <pre v-else-if=\"isOpen\" class=\"pk-chatbot-step-payload__full\">{{\n text\n }}</pre>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-step-payload {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\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-28);\n padding: var(--spacing-4) var(--spacing-8);\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 &__label {\n flex-shrink: 0;\n font-weight: 600;\n font-size: var(--text-10);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--color-word-3);\n }\n\n &__summary {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--font-mono);\n font-size: var(--text-10);\n color: var(--color-word-4);\n }\n\n &__rows > li {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n\n &__row {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-8);\n width: 100%;\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n font-size: var(--text-10);\n\n &:is(button) {\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &__key {\n flex-shrink: 0;\n max-width: 40%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--font-mono);\n color: var(--color-word-4);\n }\n\n &__value {\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-2);\n }\n\n &__full {\n padding: var(--spacing-8);\n border-top: var(--spacing-px) solid var(--color-surface-3);\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 font-size: var(--text-10);\n color: var(--color-word-3);\n }\n }\n</style>\n","<script lang=\"ts\" setup>\n import { computed, reactive, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { stringifyPayload } from './utils'\n\n interface PayloadRow {\n key: string\n preview: string\n full?: string\n }\n\n const props = defineProps<{\n label: string\n value: unknown\n }>()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n // A preview stays on a single line: long enough to recognize the value,\n // short enough to keep the payload readable without expanding anything.\n const PREVIEW_LENGTH = 120\n\n const stringify = (value: unknown, indent = 0): string | undefined => {\n try {\n return JSON.stringify(value, null, indent)\n } catch {\n return undefined\n }\n }\n\n const toPreview = (value: unknown): string => {\n if (value === undefined) {\n return ''\n }\n const text =\n typeof value === 'string' ? value : (stringify(value) ?? '')\n const single = text.replace(/\\s+/g, ' ').trim()\n return single.length > PREVIEW_LENGTH\n ? `${single.slice(0, PREVIEW_LENGTH)}…`\n : single\n }\n\n // Objects are listed by key, arrays by position: either way the payload\n // becomes a list of short lines instead of one wall of JSON.\n const entries = computed<[string, unknown][] | undefined>(() => {\n const value = props.value\n if (Array.isArray(value)) {\n return value.length\n ? value.map(\n (item, index) =>\n [`${index + 1}`, item] as [string, unknown],\n )\n : undefined\n }\n if (value && typeof value === 'object') {\n const own = Object.entries(value as Record<string, unknown>)\n return own.length ? own : undefined\n }\n return undefined\n })\n\n const rows = computed<PayloadRow[] | undefined>(() =>\n entries.value?.map(([key, value]) => {\n const preview = toPreview(value)\n const full = typeof value === 'string' ? value : stringify(value, 2)\n return {\n key,\n preview: preview || '-',\n // Only a value the preview had to cut (long, multiline or\n // nested) is worth its own expandable block.\n full: full && full.trim() !== preview ? full : undefined,\n }\n }),\n )\n\n // Scalars and strings have no rows: they fall back to the plain payload.\n const text = computed(() =>\n rows.value ? undefined : stringifyPayload(props.value),\n )\n\n const summary = computed(() => {\n const list = rows.value\n if (!list) {\n return toPreview(props.value)\n }\n if (Array.isArray(props.value)) {\n return $t('messagePart.stepItems', list.length)\n }\n if (list.length === 1) {\n return `${list[0].key}: ${list[0].preview}`\n }\n return list.map((row) => row.key).join(' · ')\n })\n\n const isOpen = ref(false)\n const expandedRows = reactive(new Set<string>())\n const toggleRow = (key: string) => {\n if (expandedRows.has(key)) {\n expandedRows.delete(key)\n } else {\n expandedRows.add(key)\n }\n }\n</script>\n\n<template>\n <div v-if=\"rows || text\" class=\"pk-chatbot-step-payload\">\n <button\n type=\"button\"\n class=\"pk-chatbot-step-payload__header\"\n :aria-expanded=\"isOpen\"\n @click=\"isOpen = !isOpen\">\n <span class=\"pk-chatbot-step-payload__label\">{{ label }}</span>\n <span class=\"pk-chatbot-step-payload__summary\">\n {{ summary }}\n </span>\n <VvIcon\n :name=\"isOpen ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"shrink-0\" />\n </button>\n <ul v-if=\"isOpen && rows\" class=\"pk-chatbot-step-payload__rows\">\n <li v-for=\"row in rows\" :key=\"row.key\">\n <component\n :is=\"row.full ? 'button' : 'div'\"\n :type=\"row.full ? 'button' : undefined\"\n class=\"pk-chatbot-step-payload__row\"\n :aria-expanded=\"\n row.full ? expandedRows.has(row.key) : undefined\n \"\n @click=\"row.full ? toggleRow(row.key) : undefined\">\n <span class=\"pk-chatbot-step-payload__key\">\n {{ row.key }}\n </span>\n <span class=\"pk-chatbot-step-payload__value\">\n {{ row.preview }}\n </span>\n <VvIcon\n v-if=\"row.full\"\n :name=\"\n expandedRows.has(row.key)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"shrink-0\" />\n </component>\n <pre\n v-if=\"row.full && expandedRows.has(row.key)\"\n class=\"pk-chatbot-step-payload__full\"\n >{{ row.full }}</pre>\n </li>\n </ul>\n <pre v-else-if=\"isOpen\" class=\"pk-chatbot-step-payload__full\">{{\n text\n }}</pre>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-step-payload {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\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-28);\n padding: var(--spacing-4) var(--spacing-8);\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 &__label {\n flex-shrink: 0;\n font-weight: 600;\n font-size: var(--text-10);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--color-word-3);\n }\n\n &__summary {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--font-mono);\n font-size: var(--text-10);\n color: var(--color-word-4);\n }\n\n &__rows > li {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n\n &__row {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-8);\n width: 100%;\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n font-size: var(--text-10);\n\n &:is(button) {\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &__key {\n flex-shrink: 0;\n max-width: 40%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--font-mono);\n color: var(--color-word-4);\n }\n\n &__value {\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-2);\n }\n\n &__full {\n padding: var(--spacing-8);\n border-top: var(--spacing-px) solid var(--color-surface-3);\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 font-size: var(--text-10);\n color: var(--color-word-3);\n }\n }\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PkChatbotStepPayload-l57qHqVf.js","names":["__unplugin_components_0","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_withCtx","_createCommentVNode","_sfc_main","stringifyPayload","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","PREVIEW_LENGTH","PkChatbotStepPayload_default","defineComponent","props","__props","$t","useI18n","stringify","value","indent","toPreview","single","entries","computed","index","item","own","rows","key","preview","full","text","summary","list","row","isOpen","ref","expandedRows","reactive","toggleRow","_component_VvIcon","VvIcon","openBlock","createElementBlock","createElementVNode","_cache","toDisplayString","createVNode","Fragment","renderList","createBlock","resolveDynamicComponent","withCtx","createCommentVNode"],"sources":["../../../../packages/components/src/chat/PkChatbotStepPayload.vue","../../../../packages/components/src/chat/PkChatbotStepPayload.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, reactive, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { stringifyPayload } from './utils'\n\n interface PayloadRow {\n key: string\n preview: string\n full?: string\n }\n\n const props = defineProps<{\n label: string\n value: unknown\n }>()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n // A preview stays on a single line: long enough to recognize the value,\n // short enough to keep the payload readable without expanding anything.\n const PREVIEW_LENGTH = 120\n\n const stringify = (value: unknown, indent = 0): string | undefined => {\n try {\n return JSON.stringify(value, null, indent)\n } catch {\n return undefined\n }\n }\n\n const toPreview = (value: unknown): string => {\n if (value === undefined) {\n return ''\n }\n const text =\n typeof value === 'string' ? value : (stringify(value) ?? '')\n const single = text.replace(/\\s+/g, ' ').trim()\n return single.length > PREVIEW_LENGTH\n ? `${single.slice(0, PREVIEW_LENGTH)}…`\n : single\n }\n\n // Objects are listed by key, arrays by position: either way the payload\n // becomes a list of short lines instead of one wall of JSON.\n const entries = computed<[string, unknown][] | undefined>(() => {\n const value = props.value\n if (Array.isArray(value)) {\n return value.length\n ? value.map(\n (item, index) =>\n [`${index + 1}`, item] as [string, unknown],\n )\n : undefined\n }\n if (value && typeof value === 'object') {\n const own = Object.entries(value as Record<string, unknown>)\n return own.length ? own : undefined\n }\n return undefined\n })\n\n const rows = computed<PayloadRow[] | undefined>(() =>\n entries.value?.map(([key, value]) => {\n const preview = toPreview(value)\n const full = typeof value === 'string' ? value : stringify(value, 2)\n return {\n key,\n preview: preview || '-',\n // Only a value the preview had to cut (long, multiline or\n // nested) is worth its own expandable block.\n full: full && full.trim() !== preview ? full : undefined,\n }\n }),\n )\n\n // Scalars and strings have no rows: they fall back to the plain payload.\n const text = computed(() =>\n rows.value ? undefined : stringifyPayload(props.value),\n )\n\n const summary = computed(() => {\n const list = rows.value\n if (!list) {\n return toPreview(props.value)\n }\n if (Array.isArray(props.value)) {\n return $t('messagePart.stepItems', list.length)\n }\n if (list.length === 1) {\n return `${list[0].key}: ${list[0].preview}`\n }\n return list.map((row) => row.key).join(' · ')\n })\n\n const isOpen = ref(false)\n const expandedRows = reactive(new Set<string>())\n const toggleRow = (key: string) => {\n if (expandedRows.has(key)) {\n expandedRows.delete(key)\n } else {\n expandedRows.add(key)\n }\n }\n</script>\n\n<template>\n <div v-if=\"rows || text\" class=\"pk-chatbot-step-payload\">\n <button\n type=\"button\"\n class=\"pk-chatbot-step-payload__header\"\n :aria-expanded=\"isOpen\"\n @click=\"isOpen = !isOpen\">\n <span class=\"pk-chatbot-step-payload__label\">{{ label }}</span>\n <span class=\"pk-chatbot-step-payload__summary\">\n {{ summary }}\n </span>\n <VvIcon\n :name=\"isOpen ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"shrink-0\" />\n </button>\n <ul v-if=\"isOpen && rows\" class=\"pk-chatbot-step-payload__rows\">\n <li v-for=\"row in rows\" :key=\"row.key\">\n <component\n :is=\"row.full ? 'button' : 'div'\"\n :type=\"row.full ? 'button' : undefined\"\n class=\"pk-chatbot-step-payload__row\"\n :aria-expanded=\"\n row.full ? expandedRows.has(row.key) : undefined\n \"\n @click=\"row.full ? toggleRow(row.key) : undefined\">\n <span class=\"pk-chatbot-step-payload__key\">\n {{ row.key }}\n </span>\n <span class=\"pk-chatbot-step-payload__value\">\n {{ row.preview }}\n </span>\n <VvIcon\n v-if=\"row.full\"\n :name=\"\n expandedRows.has(row.key)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"shrink-0\" />\n </component>\n <pre\n v-if=\"row.full && expandedRows.has(row.key)\"\n class=\"pk-chatbot-step-payload__full\"\n >{{ row.full }}</pre>\n </li>\n </ul>\n <pre v-else-if=\"isOpen\" class=\"pk-chatbot-step-payload__full\">{{\n text\n }}</pre>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-step-payload {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\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-28);\n padding: var(--spacing-4) var(--spacing-8);\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 &__label {\n flex-shrink: 0;\n font-weight: 600;\n font-size: var(--text-10);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--color-word-3);\n }\n\n &__summary {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--font-mono);\n font-size: var(--text-10);\n color: var(--color-word-4);\n }\n\n &__rows > li {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n\n &__row {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-8);\n width: 100%;\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n font-size: var(--text-10);\n\n &:is(button) {\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &__key {\n flex-shrink: 0;\n max-width: 40%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--font-mono);\n color: var(--color-word-4);\n }\n\n &__value {\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-2);\n }\n\n &__full {\n padding: var(--spacing-8);\n border-top: var(--spacing-px) solid var(--color-surface-3);\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 font-size: var(--text-10);\n color: var(--color-word-3);\n }\n }\n</style>\n","<script lang=\"ts\" setup>\n import { computed, reactive, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { stringifyPayload } from './utils'\n\n interface PayloadRow {\n key: string\n preview: string\n full?: string\n }\n\n const props = defineProps<{\n label: string\n value: unknown\n }>()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n // A preview stays on a single line: long enough to recognize the value,\n // short enough to keep the payload readable without expanding anything.\n const PREVIEW_LENGTH = 120\n\n const stringify = (value: unknown, indent = 0): string | undefined => {\n try {\n return JSON.stringify(value, null, indent)\n } catch {\n return undefined\n }\n }\n\n const toPreview = (value: unknown): string => {\n if (value === undefined) {\n return ''\n }\n const text =\n typeof value === 'string' ? value : (stringify(value) ?? '')\n const single = text.replace(/\\s+/g, ' ').trim()\n return single.length > PREVIEW_LENGTH\n ? `${single.slice(0, PREVIEW_LENGTH)}…`\n : single\n }\n\n // Objects are listed by key, arrays by position: either way the payload\n // becomes a list of short lines instead of one wall of JSON.\n const entries = computed<[string, unknown][] | undefined>(() => {\n const value = props.value\n if (Array.isArray(value)) {\n return value.length\n ? value.map(\n (item, index) =>\n [`${index + 1}`, item] as [string, unknown],\n )\n : undefined\n }\n if (value && typeof value === 'object') {\n const own = Object.entries(value as Record<string, unknown>)\n return own.length ? own : undefined\n }\n return undefined\n })\n\n const rows = computed<PayloadRow[] | undefined>(() =>\n entries.value?.map(([key, value]) => {\n const preview = toPreview(value)\n const full = typeof value === 'string' ? value : stringify(value, 2)\n return {\n key,\n preview: preview || '-',\n // Only a value the preview had to cut (long, multiline or\n // nested) is worth its own expandable block.\n full: full && full.trim() !== preview ? full : undefined,\n }\n }),\n )\n\n // Scalars and strings have no rows: they fall back to the plain payload.\n const text = computed(() =>\n rows.value ? undefined : stringifyPayload(props.value),\n )\n\n const summary = computed(() => {\n const list = rows.value\n if (!list) {\n return toPreview(props.value)\n }\n if (Array.isArray(props.value)) {\n return $t('messagePart.stepItems', list.length)\n }\n if (list.length === 1) {\n return `${list[0].key}: ${list[0].preview}`\n }\n return list.map((row) => row.key).join(' · ')\n })\n\n const isOpen = ref(false)\n const expandedRows = reactive(new Set<string>())\n const toggleRow = (key: string) => {\n if (expandedRows.has(key)) {\n expandedRows.delete(key)\n } else {\n expandedRows.add(key)\n }\n }\n</script>\n\n<template>\n <div v-if=\"rows || text\" class=\"pk-chatbot-step-payload\">\n <button\n type=\"button\"\n class=\"pk-chatbot-step-payload__header\"\n :aria-expanded=\"isOpen\"\n @click=\"isOpen = !isOpen\">\n <span class=\"pk-chatbot-step-payload__label\">{{ label }}</span>\n <span class=\"pk-chatbot-step-payload__summary\">\n {{ summary }}\n </span>\n <VvIcon\n :name=\"isOpen ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"shrink-0\" />\n </button>\n <ul v-if=\"isOpen && rows\" class=\"pk-chatbot-step-payload__rows\">\n <li v-for=\"row in rows\" :key=\"row.key\">\n <component\n :is=\"row.full ? 'button' : 'div'\"\n :type=\"row.full ? 'button' : undefined\"\n class=\"pk-chatbot-step-payload__row\"\n :aria-expanded=\"\n row.full ? expandedRows.has(row.key) : undefined\n \"\n @click=\"row.full ? toggleRow(row.key) : undefined\">\n <span class=\"pk-chatbot-step-payload__key\">\n {{ row.key }}\n </span>\n <span class=\"pk-chatbot-step-payload__value\">\n {{ row.preview }}\n </span>\n <VvIcon\n v-if=\"row.full\"\n :name=\"\n expandedRows.has(row.key)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"shrink-0\" />\n </component>\n <pre\n v-if=\"row.full && expandedRows.has(row.key)\"\n class=\"pk-chatbot-step-payload__full\"\n >{{ row.full }}</pre>\n </li>\n </ul>\n <pre v-else-if=\"isOpen\" class=\"pk-chatbot-step-payload__full\">{{\n text\n }}</pre>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-step-payload {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\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-28);\n padding: var(--spacing-4) var(--spacing-8);\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 &__label {\n flex-shrink: 0;\n font-weight: 600;\n font-size: var(--text-10);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--color-word-3);\n }\n\n &__summary {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--font-mono);\n font-size: var(--text-10);\n color: var(--color-word-4);\n }\n\n &__rows > li {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n\n &__row {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-8);\n width: 100%;\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n font-size: var(--text-10);\n\n &:is(button) {\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &__key {\n flex-shrink: 0;\n max-width: 40%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--font-mono);\n color: var(--color-word-4);\n }\n\n &__value {\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-2);\n }\n\n &__full {\n padding: var(--spacing-8);\n border-top: var(--spacing-px) solid var(--color-surface-3);\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 font-size: var(--text-10);\n color: var(--color-word-3);\n }\n }\n</style>\n"],"mappings":"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"}
|
package/dist-vue/_chunks/{PkChatbotStepToolDetail-CMx4cw1D.js → PkChatbotStepToolDetail-ChrOa4W4.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as n } from "./PkChatbotStepPayload-
|
|
1
|
+
import { r as e, v as t } from "./utils-BqPJ6DJT.js";
|
|
2
|
+
import { t as n } from "./PkChatbotStepPayload-l57qHqVf.js";
|
|
3
3
|
import { VvIcon as r } from "@volverjs/ui-vue/components";
|
|
4
4
|
import { Fragment as i, computed as a, createBlock as o, createCommentVNode as s, createElementBlock as c, createElementVNode as l, createVNode as u, defineComponent as d, normalizeStyle as f, openBlock as p, reactive as m, renderList as h, resolveDynamicComponent as g, toDisplayString as _, unref as v, withCtx as y, withModifiers as b } from "vue";
|
|
5
5
|
import { useI18n as x } from "vue-i18n";
|
|
@@ -120,16 +120,16 @@ var S = { class: "pk-chatbot-step-detail" }, C = {
|
|
|
120
120
|
hits: e,
|
|
121
121
|
showEmpty: e !== void 0 && !e.length
|
|
122
122
|
}), ae = () => {
|
|
123
|
-
let
|
|
124
|
-
if (
|
|
123
|
+
let t = F.part.type;
|
|
124
|
+
if (t === "tool-searchInKnowledgeBase") {
|
|
125
125
|
let e = Z(q.value);
|
|
126
126
|
return J.value && (e.facts = [{
|
|
127
127
|
icon: "ri:history-line",
|
|
128
128
|
text: I("messagePart.stepRepeated", J.value)
|
|
129
129
|
}], e.showEmpty = !1), e;
|
|
130
130
|
}
|
|
131
|
-
if (
|
|
132
|
-
if (
|
|
131
|
+
if (t === "tool-retrieveRevisedAnswers") return Z(ie.value);
|
|
132
|
+
if (t === "tool-expandChunkContext") {
|
|
133
133
|
let e = R(U.value, "content");
|
|
134
134
|
if (!e) return {};
|
|
135
135
|
let t = U.value?.totalChunksReturned;
|
|
@@ -139,7 +139,7 @@ var S = { class: "pk-chatbot-step-detail" }, C = {
|
|
|
139
139
|
content: e
|
|
140
140
|
} };
|
|
141
141
|
}
|
|
142
|
-
if (
|
|
142
|
+
if (t === "tool-readDocumentFile") {
|
|
143
143
|
let e = R(U.value, "name");
|
|
144
144
|
if (!e) return {};
|
|
145
145
|
let t = R(U.value, "mimeType");
|
|
@@ -148,7 +148,7 @@ var S = { class: "pk-chatbot-step-detail" }, C = {
|
|
|
148
148
|
text: t ? `${e} (${t})` : e
|
|
149
149
|
}] };
|
|
150
150
|
}
|
|
151
|
-
if (
|
|
151
|
+
if (t === "tool-getWeather") {
|
|
152
152
|
let e = R(U.value, "location") ?? R(H.value, "locationName") ?? z(H.value);
|
|
153
153
|
if (!e) return {};
|
|
154
154
|
let t = R(H.value, "startDate"), n = R(H.value, "endDate");
|
|
@@ -157,15 +157,15 @@ var S = { class: "pk-chatbot-step-detail" }, C = {
|
|
|
157
157
|
text: `${e}${t ? n && n !== t ? ` · ${t} → ${n}` : ` · ${t}` : ""}`
|
|
158
158
|
}] };
|
|
159
159
|
}
|
|
160
|
-
if (
|
|
160
|
+
if (t === "tool-geocode") {
|
|
161
161
|
let e = R(H.value, "locationName") ?? z(H.value), t = R(U.value, "name", "displayName", "locationName", "label") ?? z(U.value), n = e && t && e !== t ? `${e} → ${t}` : e ?? t;
|
|
162
162
|
return n ? { facts: [{
|
|
163
163
|
icon: "ri:map-pin-line",
|
|
164
164
|
text: n
|
|
165
165
|
}] } : {};
|
|
166
166
|
}
|
|
167
|
-
if (
|
|
168
|
-
if (
|
|
167
|
+
if (t === "tool-recallUserMemory") return Z(X(U.value?.memories));
|
|
168
|
+
if (t === "tool-saveUserMemory") {
|
|
169
169
|
let e = X(U.value?.candidates);
|
|
170
170
|
if (e) return Z(e);
|
|
171
171
|
let t = R(H.value, "content") ?? R(U.value, "message", "error");
|
|
@@ -174,7 +174,7 @@ var S = { class: "pk-chatbot-step-detail" }, C = {
|
|
|
174
174
|
text: t
|
|
175
175
|
}] } : {};
|
|
176
176
|
}
|
|
177
|
-
return
|
|
177
|
+
return e.includes(t) ? {
|
|
178
178
|
chips: W.value,
|
|
179
179
|
rawOutput: V.value
|
|
180
180
|
} : {};
|
|
@@ -184,7 +184,7 @@ var S = { class: "pk-chatbot-step-detail" }, C = {
|
|
|
184
184
|
}), $ = m(/* @__PURE__ */ new Set()), oe = (e) => {
|
|
185
185
|
$.has(e) ? $.delete(e) : $.add(e);
|
|
186
186
|
};
|
|
187
|
-
return (
|
|
187
|
+
return (e, a) => {
|
|
188
188
|
let d = r;
|
|
189
189
|
return p(), c("div", S, [
|
|
190
190
|
Q.value.chips?.length ? (p(), c("ul", C, [(p(!0), c(i, null, h(Q.value.chips, (e, t) => (p(), c("li", {
|
|
@@ -245,12 +245,12 @@ var S = { class: "pk-chatbot-step-detail" }, C = {
|
|
|
245
245
|
l("span", re, [l("span", M, _(Q.value.passage.title ?? v(I)("messagePart.stepDocument")), 1)]),
|
|
246
246
|
Q.value.passage.meta ? (p(), c("span", N, _(Q.value.passage.meta), 1)) : s("", !0)
|
|
247
247
|
]), l("div", P, _(Q.value.passage.content), 1)])) : s("", !0),
|
|
248
|
-
v(
|
|
248
|
+
v(t)(Q.value.rawInput) ? (p(), o(n, {
|
|
249
249
|
key: 4,
|
|
250
250
|
label: v(I)("messagePart.stepInput"),
|
|
251
251
|
value: Q.value.rawInput
|
|
252
252
|
}, null, 8, ["label", "value"])) : s("", !0),
|
|
253
|
-
v(
|
|
253
|
+
v(t)(Q.value.rawOutput) ? (p(), o(n, {
|
|
254
254
|
key: 5,
|
|
255
255
|
label: v(I)("messagePart.stepOutput"),
|
|
256
256
|
value: Q.value.rawOutput
|
|
@@ -262,4 +262,4 @@ var S = { class: "pk-chatbot-step-detail" }, C = {
|
|
|
262
262
|
//#endregion
|
|
263
263
|
export { F as t };
|
|
264
264
|
|
|
265
|
-
//# sourceMappingURL=PkChatbotStepToolDetail-
|
|
265
|
+
//# sourceMappingURL=PkChatbotStepToolDetail-ChrOa4W4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbotStepToolDetail-CMx4cw1D.js","names":["__unplugin_components_0","_openBlock","_createElementBlock","_Fragment","_renderList","_createVNode","_createElementVNode","_toDisplayString","_createCommentVNode","_unref","_createBlock","_resolveDynamicComponent","_withCtx","_withModifiers","_normalizeStyle","PkChatbotStepPayload","_sfc_main","hasPayload","WEB_SEARCH_TOOL_TYPES","PkChatbotStepPayload_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","PkChatbotStepToolDetail_default","defineComponent","props","__props","$t","useI18n","asRecord","value","pickString","keys","key","record","formatCoordinates","latitude","longitude","rawInputValue","computed","rawOutputValue","input","output","queryChips","raw","query","spreadRelevance","min","scores","max","score","scoredRecords","records","item","relevances","index","knowledgeHits","results","groups","result","document","source","id","similarity","group","list","g","repeatedCount","count","searchFacts","facts","dropped","unavailable","queries","revisedAnswerHits","memoryHits","content","hitsDetail","hits","buildDetail","type","knowledge","totalChunks","name","mimeType","location","startDate","endDate","from","to","text","candidates","detail","expandedHits","reactive","toggleHit","_component_VvIcon","VvIcon","openBlock","createElementBlock","Fragment","renderList","chipIndex","chip","createVNode","createElementVNode","toDisplayString","createCommentVNode","factIndex","fact","unref","hit","createBlock","resolveDynamicComponent","withCtx","_cache","withModifiers","normalizeStyle"],"sources":["../../../../packages/components/src/chat/PkChatbotStepToolDetail.vue","../../../../packages/components/src/chat/PkChatbotStepToolDetail.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, reactive } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { UIChatMessage } from 'models'\n import PkChatbotStepPayload from './PkChatbotStepPayload.vue'\n import { hasPayload, WEB_SEARCH_TOOL_TYPES } from './utils'\n\n type MessagePart = UIChatMessage['parts'][number]\n\n interface DetailHit {\n key: string\n icon: string\n title: string\n url?: string\n meta?: string\n relevance?: number\n body?: string\n }\n\n interface DetailFact {\n icon: string\n text: string\n }\n\n interface Detail {\n chips?: string[]\n facts?: DetailFact[]\n hits?: DetailHit[]\n showEmpty?: boolean\n passage?: { title?: string; meta?: string; content: string }\n rawInput?: unknown\n rawOutput?: unknown\n }\n\n const props = defineProps<{\n part: MessagePart\n }>()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const asRecord = (value: unknown): Record<string, unknown> | undefined =>\n value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n\n const pickString = (\n record: Record<string, unknown> | undefined,\n ...keys: string[]\n ): string | undefined => {\n for (const key of keys) {\n const value = record?.[key]\n if (typeof value === 'string' && value.trim()) {\n return value\n }\n }\n return undefined\n }\n\n const formatCoordinates = (\n record?: Record<string, unknown>,\n ): string | undefined => {\n const latitude = record?.latitude\n const longitude = record?.longitude\n if (typeof latitude !== 'number' || typeof longitude !== 'number') {\n return undefined\n }\n return `${latitude.toFixed(4)}, ${longitude.toFixed(4)}`\n }\n\n const rawInputValue = computed(() =>\n 'input' in props.part ? props.part.input : undefined,\n )\n const rawOutputValue = computed(() =>\n 'output' in props.part ? props.part.output : undefined,\n )\n const input = computed(() => asRecord(rawInputValue.value))\n const output = computed(() => asRecord(rawOutputValue.value))\n\n // Search queries shown as chips (queries[], query or objective)\n const queryChips = computed<string[]>(() => {\n const raw =\n input.value?.queries ?? input.value?.query ?? input.value?.objective\n if (typeof raw === 'string' && raw.trim()) {\n return [raw]\n }\n if (Array.isArray(raw)) {\n return raw.filter(\n (query): query is string =>\n typeof query === 'string' && Boolean(query.trim()),\n )\n }\n return []\n })\n\n // Raw scores live on different scales depending on the agent search mode\n // (RRF ~0.03, cosine 0 to 1) and tend to cluster, so a plain ratio on the\n // max renders every bar as ~100%. Spread them on a min/max scale with a\n // floor so the ordering stays visible, and hide the bar entirely when it\n // carries no information (single hit or identical scores).\n const spreadRelevance = (scores: number[]): (number | undefined)[] => {\n const min = Math.min(...scores)\n const max = Math.max(...scores)\n if (scores.length < 2 || max <= min) {\n return scores.map(() => undefined)\n }\n const floor = 0.15\n return scores.map(\n (score) => floor + ((score - min) / (max - min)) * (1 - floor),\n )\n }\n\n // Normalizes a raw result array into records paired with a spread\n // relevance derived from each record's `similarity` score.\n const scoredRecords = (\n raw: unknown,\n ):\n | { record: Record<string, unknown>; relevance?: number }[]\n | undefined => {\n if (!Array.isArray(raw)) {\n return undefined\n }\n const records = raw\n .map((item) => asRecord(item))\n .filter((record): record is Record<string, unknown> =>\n Boolean(record),\n )\n const relevances = spreadRelevance(\n records.map((record) =>\n typeof record.similarity === 'number' ? record.similarity : 0,\n ),\n )\n return records.map((record, index) => ({\n record,\n relevance: relevances[index],\n }))\n }\n\n // searchInKnowledgeBase: group chunks by document, relevance compares the\n // best chunk score of each document within the result set.\n const knowledgeHits = computed<DetailHit[] | undefined>(() => {\n const results = output.value?.results\n if (!Array.isArray(results)) {\n return undefined\n }\n interface DocumentGroup {\n id: string\n type: string\n title: string\n url?: string\n similarity: number\n chunks: string[]\n }\n const groups = new Map<string, DocumentGroup>()\n for (const [index, raw] of results.entries()) {\n const result = asRecord(raw)\n if (!result) {\n continue\n }\n const document = asRecord(result.document)\n const source = asRecord(document?.source)\n const id =\n typeof document?.id === 'string' ? document.id : `doc-${index}`\n const similarity =\n typeof result.similarity === 'number' ? result.similarity : 0\n let group = groups.get(id)\n if (!group) {\n group = {\n id,\n type:\n typeof document?.type === 'string' ? document.type : '',\n title:\n pickString(document, 'name') ??\n pickString(source, 'filename', 'url') ??\n $t('messagePart.stepDocument'),\n url: pickString(source, 'url'),\n similarity,\n chunks: [],\n }\n groups.set(id, group)\n }\n group.similarity = Math.max(group.similarity, similarity)\n if (typeof result.content === 'string' && result.content.trim()) {\n group.chunks.push(result.content)\n }\n }\n const list = [...groups.values()]\n const relevances = spreadRelevance(list.map((g) => g.similarity))\n return list.map((group, index) => ({\n key: group.id,\n icon: group.type === 'url' ? 'ri:global-line' : 'ri:file-text-line',\n title: group.title,\n url: group.url,\n meta: $t('messagePart.stepExcerpts', group.chunks.length || 1),\n relevance: relevances[index],\n body: group.chunks.join('\\n\\n· · ·\\n\\n') || undefined,\n }))\n })\n\n // Chunks this turn had already returned come back as references without\n // content (spec retrieval-payload-per-turn, R2). They carry no document\n // name to group by, so they are summarized in one line: without it a\n // search that found only known passages reads as \"no results\".\n const repeatedCount = computed<number>(() => {\n const count = asRecord(output.value?.repeated)?.count\n return typeof count === 'number' ? count : 0\n })\n\n // Two declarations every search tool can attach to its output, and the\n // only ones the model is explicitly told not to read as an answer: a\n // query dropped for exceeding the tool's cap, and a search that ran out\n // of time. Without them a timeout renders as \"no results\", which is the\n // reading whoever opens a chat log would take away too.\n const searchFacts = computed<DetailFact[]>(() => {\n const facts: DetailFact[] = []\n const dropped = asRecord(output.value?.inputTruncated)?.dropped\n if (Array.isArray(dropped) && dropped.length) {\n facts.push({\n icon: 'ri:scissors-cut-line',\n text: $t('messagePart.stepDropped', dropped.length),\n })\n }\n const unavailable = asRecord(output.value?.searchUnavailable)\n if (unavailable) {\n // The corrected-answers tool declares one lookup without naming\n // its query, so an absent list means one search, not none.\n const queries = unavailable.queries\n facts.push({\n icon: 'ri:time-line',\n text: $t(\n 'messagePart.stepTimedOut',\n Array.isArray(queries) ? queries.length : 1,\n ),\n })\n }\n return facts\n })\n\n // retrieveRevisedAnswers: one hit per matched question, the corrected\n // answer is the expandable body.\n const revisedAnswerHits = computed<DetailHit[] | undefined>(() =>\n scoredRecords(output.value?.results)?.map(\n ({ record, relevance }, index) => ({\n key:\n pickString(record, 'revisedAnswerId') ?? `revised-${index}`,\n icon: 'ri:file-edit-line',\n title:\n pickString(record, 'userQuestion') ??\n $t('messagePart.revisedAnswers'),\n relevance,\n body: pickString(record, 'revisedMessage'),\n }),\n ),\n )\n\n // recallUserMemory / saveUserMemory delete candidates: one hit per\n // memory, long facts expand to their full text.\n const memoryHits = (raw: unknown): DetailHit[] | undefined =>\n scoredRecords(raw)?.map(({ record, relevance }, index) => {\n const content = pickString(record, 'content') ?? ''\n return {\n key: pickString(record, 'id') ?? `memory-${index}`,\n icon: 'ri:brain-line',\n title: content,\n meta: pickString(record, 'updatedAt')?.slice(0, 10),\n relevance,\n body: content.length > 120 ? content : undefined,\n }\n })\n\n // Query chips over a list of hits, flagging an explicit empty state so the\n // \"no results\" message shows once the tool has run (hits defined, length 0).\n const hitsDetail = (hits: DetailHit[] | undefined): Detail => ({\n chips: queryChips.value,\n hits,\n showEmpty: hits !== undefined && !hits.length,\n })\n\n // Rich, tool-specific view of the step. Returns {} when the tool has\n // nothing meaningful to show yet (the raw fallback is applied by `detail`).\n const buildDetail = (): Detail => {\n const type = props.part.type\n if (type === 'tool-searchInKnowledgeBase') {\n const knowledge = hitsDetail(knowledgeHits.value)\n if (repeatedCount.value) {\n knowledge.facts = [\n {\n icon: 'ri:history-line',\n text: $t(\n 'messagePart.stepRepeated',\n repeatedCount.value,\n ),\n },\n ]\n knowledge.showEmpty = false\n }\n return knowledge\n }\n if (type === 'tool-retrieveRevisedAnswers') {\n return hitsDetail(revisedAnswerHits.value)\n }\n if (type === 'tool-expandChunkContext') {\n const content = pickString(output.value, 'content')\n if (!content) {\n return {}\n }\n const totalChunks = output.value?.totalChunksReturned\n return {\n passage: {\n title: pickString(output.value, 'documentName'),\n meta:\n typeof totalChunks === 'number'\n ? $t('messagePart.stepExcerpts', totalChunks)\n : undefined,\n content,\n },\n }\n }\n if (type === 'tool-readDocumentFile') {\n const name = pickString(output.value, 'name')\n if (!name) {\n return {}\n }\n const mimeType = pickString(output.value, 'mimeType')\n return {\n facts: [\n {\n icon: 'ri:file-text-line',\n text: mimeType ? `${name} (${mimeType})` : name,\n },\n ],\n }\n }\n if (type === 'tool-getWeather') {\n const location =\n pickString(output.value, 'location') ??\n pickString(input.value, 'locationName') ??\n formatCoordinates(input.value)\n if (!location) {\n return {}\n }\n const startDate = pickString(input.value, 'startDate')\n const endDate = pickString(input.value, 'endDate')\n const dateRange = startDate\n ? endDate && endDate !== startDate\n ? ` · ${startDate} → ${endDate}`\n : ` · ${startDate}`\n : ''\n return {\n facts: [\n {\n icon: 'ri:sun-cloudy-line',\n text: `${location}${dateRange}`,\n },\n ],\n }\n }\n if (type === 'tool-geocode') {\n const from =\n pickString(input.value, 'locationName') ??\n formatCoordinates(input.value)\n const to =\n pickString(\n output.value,\n 'name',\n 'displayName',\n 'locationName',\n 'label',\n ) ?? formatCoordinates(output.value)\n const text =\n from && to && from !== to ? `${from} → ${to}` : (from ?? to)\n if (!text) {\n return {}\n }\n return {\n facts: [\n {\n icon: 'ri:map-pin-line',\n text,\n },\n ],\n }\n }\n if (type === 'tool-recallUserMemory') {\n return hitsDetail(memoryHits(output.value?.memories))\n }\n if (type === 'tool-saveUserMemory') {\n const candidates = memoryHits(output.value?.candidates)\n if (candidates) {\n return hitsDetail(candidates)\n }\n const text =\n pickString(input.value, 'content') ??\n pickString(output.value, 'message', 'error')\n if (!text) {\n return {}\n }\n return {\n facts: [\n {\n icon:\n pickString(input.value, 'action') === 'delete'\n ? 'ri:delete-bin-line'\n : 'ri:save-3-line',\n text,\n },\n ],\n }\n }\n if (WEB_SEARCH_TOOL_TYPES.includes(type)) {\n return {\n chips: queryChips.value,\n rawOutput: rawOutputValue.value,\n }\n }\n return {}\n }\n\n const detail = computed<Detail>(() => {\n const result = buildDetail()\n if (searchFacts.value.length) {\n result.facts = [...searchFacts.value, ...(result.facts ?? [])]\n }\n if (output.value?.searchUnavailable) {\n // \"No results found\" under a search that never answered is the\n // one sentence this fact exists to prevent.\n result.showEmpty = false\n }\n // Nothing meaningful to show for this tool yet (e.g. still running):\n // fall back to the raw payloads so the step body is never empty.\n const hasRichContent =\n result.chips?.length ||\n result.facts?.length ||\n result.passage ||\n result.showEmpty ||\n result.hits?.length\n if (!hasRichContent) {\n result.rawInput = rawInputValue.value\n result.rawOutput ??= rawOutputValue.value\n }\n return result\n })\n\n const expandedHits = reactive(new Set<string>())\n const toggleHit = (key: string) => {\n if (expandedHits.has(key)) {\n expandedHits.delete(key)\n } else {\n expandedHits.add(key)\n }\n }\n</script>\n\n<template>\n <div class=\"pk-chatbot-step-detail\">\n <ul v-if=\"detail.chips?.length\" class=\"pk-chatbot-step-detail__chips\">\n <li\n v-for=\"(chip, chipIndex) in detail.chips\"\n :key=\"`${chipIndex}-${chip}`\"\n class=\"pk-chatbot-step-detail__chip\">\n <VvIcon name=\"ri:search-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-step-detail__chip-text\">\n {{ chip }}\n </span>\n </li>\n </ul>\n <div\n v-for=\"(fact, factIndex) in detail.facts\"\n :key=\"`${factIndex}-${fact.text}`\"\n class=\"pk-chatbot-step-detail__fact\">\n <VvIcon :name=\"fact.icon\" class=\"shrink-0\" />\n <span>{{ fact.text }}</span>\n </div>\n <div v-if=\"detail.showEmpty\" class=\"pk-chatbot-step-detail__empty\">\n {{ $t('messagePart.stepNoResults') }}\n </div>\n <ul\n v-else-if=\"detail.hits?.length\"\n class=\"pk-chatbot-step-detail__hits\">\n <li v-for=\"hit in detail.hits\" :key=\"hit.key\">\n <component\n :is=\"hit.body ? 'button' : 'div'\"\n :type=\"hit.body ? 'button' : undefined\"\n class=\"pk-chatbot-step-detail__hit-header\"\n :aria-expanded=\"\n hit.body ? expandedHits.has(hit.key) : undefined\n \"\n @click=\"hit.body ? toggleHit(hit.key) : undefined\">\n <VvIcon\n :name=\"hit.icon\"\n class=\"pk-chatbot-step-detail__hit-icon\" />\n <span class=\"pk-chatbot-step-detail__hit-heading\">\n <span class=\"pk-chatbot-step-detail__hit-title\">\n {{ hit.title }}\n </span>\n <a\n v-if=\"hit.url\"\n :href=\"hit.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"pk-chatbot-step-detail__link\"\n @click.stop>\n <VvIcon name=\"ri:external-link-line\" />\n </a>\n </span>\n <span\n v-if=\"hit.meta\"\n class=\"pk-chatbot-step-detail__hit-meta\">\n {{ hit.meta }}\n </span>\n <span\n v-if=\"hit.relevance !== undefined\"\n class=\"pk-chatbot-step-detail__relevance\"\n aria-hidden=\"true\">\n <span\n class=\"pk-chatbot-step-detail__relevance-fill\"\n :style=\"{\n width: `${Math.round(hit.relevance * 100)}%`,\n }\" />\n </span>\n <VvIcon\n v-if=\"hit.body\"\n :name=\"\n expandedHits.has(hit.key)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"shrink-0\" />\n </component>\n <div\n v-if=\"hit.body && expandedHits.has(hit.key)\"\n class=\"pk-chatbot-step-detail__hit-body\">\n {{ hit.body }}\n </div>\n </li>\n </ul>\n <div v-if=\"detail.passage\" class=\"pk-chatbot-step-detail__passage\">\n <div class=\"pk-chatbot-step-detail__passage-header\">\n <VvIcon name=\"ri:file-text-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-step-detail__hit-heading\">\n <span class=\"pk-chatbot-step-detail__hit-title\">\n {{\n detail.passage.title ??\n $t('messagePart.stepDocument')\n }}\n </span>\n </span>\n <span\n v-if=\"detail.passage.meta\"\n class=\"pk-chatbot-step-detail__hit-meta\">\n {{ detail.passage.meta }}\n </span>\n </div>\n <div class=\"pk-chatbot-step-detail__passage-content\">\n {{ detail.passage.content }}\n </div>\n </div>\n <PkChatbotStepPayload\n v-if=\"hasPayload(detail.rawInput)\"\n :label=\"$t('messagePart.stepInput')\"\n :value=\"detail.rawInput\" />\n <PkChatbotStepPayload\n v-if=\"hasPayload(detail.rawOutput)\"\n :label=\"$t('messagePart.stepOutput')\"\n :value=\"detail.rawOutput\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-step-detail {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-8);\n font-size: var(--text-12);\n color: var(--color-word-3);\n\n &__chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-4);\n }\n\n &__chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n max-width: 100%;\n padding: var(--spacing-2) var(--spacing-8);\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface-1);\n color: var(--color-word-2);\n }\n\n &__chip-text {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__fact {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n color: var(--color-word-2);\n }\n\n &__empty {\n color: var(--color-word-4);\n font-style: italic;\n }\n\n &__hits {\n display: flex;\n flex-direction: column;\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\n overflow: hidden;\n\n > li + li {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n }\n\n &__hit-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: 100%;\n min-height: var(--spacing-32);\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n\n &:is(button) {\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &__hit-icon {\n flex-shrink: 0;\n color: var(--color-word-3);\n }\n\n // Grows to fill the row so meta, relevance and chevron stay aligned;\n // the title inside only shrinks, keeping the link icon next to the\n // title text instead of pushed to the right edge.\n &__hit-heading {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n flex: 1 1 0;\n min-width: 0;\n }\n\n &__hit-title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-2);\n }\n\n &__hit-meta {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__relevance {\n flex-shrink: 0;\n width: var(--spacing-40);\n height: var(--spacing-4);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface-3);\n overflow: hidden;\n }\n\n &__relevance-fill {\n display: block;\n height: 100%;\n border-radius: inherit;\n background-color: var(--chatbot-main-color, var(--color-word-4));\n }\n\n &__link {\n flex-shrink: 0;\n display: inline-flex;\n color: var(--color-word-4);\n\n &:hover {\n color: var(--color-word-1);\n }\n }\n\n &__hit-body,\n &__passage-content {\n padding: var(--spacing-8);\n max-height: var(--spacing-160);\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--color-word-3);\n }\n\n &__hit-body {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n\n &__passage {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\n overflow: hidden;\n }\n\n &__passage-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n min-height: var(--spacing-32);\n padding: var(--spacing-4) var(--spacing-8);\n border-bottom: var(--spacing-px) solid var(--color-surface-3);\n }\n }\n</style>\n","<script lang=\"ts\" setup>\n import { computed, reactive } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { UIChatMessage } from 'models'\n import PkChatbotStepPayload from './PkChatbotStepPayload.vue'\n import { hasPayload, WEB_SEARCH_TOOL_TYPES } from './utils'\n\n type MessagePart = UIChatMessage['parts'][number]\n\n interface DetailHit {\n key: string\n icon: string\n title: string\n url?: string\n meta?: string\n relevance?: number\n body?: string\n }\n\n interface DetailFact {\n icon: string\n text: string\n }\n\n interface Detail {\n chips?: string[]\n facts?: DetailFact[]\n hits?: DetailHit[]\n showEmpty?: boolean\n passage?: { title?: string; meta?: string; content: string }\n rawInput?: unknown\n rawOutput?: unknown\n }\n\n const props = defineProps<{\n part: MessagePart\n }>()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const asRecord = (value: unknown): Record<string, unknown> | undefined =>\n value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n\n const pickString = (\n record: Record<string, unknown> | undefined,\n ...keys: string[]\n ): string | undefined => {\n for (const key of keys) {\n const value = record?.[key]\n if (typeof value === 'string' && value.trim()) {\n return value\n }\n }\n return undefined\n }\n\n const formatCoordinates = (\n record?: Record<string, unknown>,\n ): string | undefined => {\n const latitude = record?.latitude\n const longitude = record?.longitude\n if (typeof latitude !== 'number' || typeof longitude !== 'number') {\n return undefined\n }\n return `${latitude.toFixed(4)}, ${longitude.toFixed(4)}`\n }\n\n const rawInputValue = computed(() =>\n 'input' in props.part ? props.part.input : undefined,\n )\n const rawOutputValue = computed(() =>\n 'output' in props.part ? props.part.output : undefined,\n )\n const input = computed(() => asRecord(rawInputValue.value))\n const output = computed(() => asRecord(rawOutputValue.value))\n\n // Search queries shown as chips (queries[], query or objective)\n const queryChips = computed<string[]>(() => {\n const raw =\n input.value?.queries ?? input.value?.query ?? input.value?.objective\n if (typeof raw === 'string' && raw.trim()) {\n return [raw]\n }\n if (Array.isArray(raw)) {\n return raw.filter(\n (query): query is string =>\n typeof query === 'string' && Boolean(query.trim()),\n )\n }\n return []\n })\n\n // Raw scores live on different scales depending on the agent search mode\n // (RRF ~0.03, cosine 0 to 1) and tend to cluster, so a plain ratio on the\n // max renders every bar as ~100%. Spread them on a min/max scale with a\n // floor so the ordering stays visible, and hide the bar entirely when it\n // carries no information (single hit or identical scores).\n const spreadRelevance = (scores: number[]): (number | undefined)[] => {\n const min = Math.min(...scores)\n const max = Math.max(...scores)\n if (scores.length < 2 || max <= min) {\n return scores.map(() => undefined)\n }\n const floor = 0.15\n return scores.map(\n (score) => floor + ((score - min) / (max - min)) * (1 - floor),\n )\n }\n\n // Normalizes a raw result array into records paired with a spread\n // relevance derived from each record's `similarity` score.\n const scoredRecords = (\n raw: unknown,\n ):\n | { record: Record<string, unknown>; relevance?: number }[]\n | undefined => {\n if (!Array.isArray(raw)) {\n return undefined\n }\n const records = raw\n .map((item) => asRecord(item))\n .filter((record): record is Record<string, unknown> =>\n Boolean(record),\n )\n const relevances = spreadRelevance(\n records.map((record) =>\n typeof record.similarity === 'number' ? record.similarity : 0,\n ),\n )\n return records.map((record, index) => ({\n record,\n relevance: relevances[index],\n }))\n }\n\n // searchInKnowledgeBase: group chunks by document, relevance compares the\n // best chunk score of each document within the result set.\n const knowledgeHits = computed<DetailHit[] | undefined>(() => {\n const results = output.value?.results\n if (!Array.isArray(results)) {\n return undefined\n }\n interface DocumentGroup {\n id: string\n type: string\n title: string\n url?: string\n similarity: number\n chunks: string[]\n }\n const groups = new Map<string, DocumentGroup>()\n for (const [index, raw] of results.entries()) {\n const result = asRecord(raw)\n if (!result) {\n continue\n }\n const document = asRecord(result.document)\n const source = asRecord(document?.source)\n const id =\n typeof document?.id === 'string' ? document.id : `doc-${index}`\n const similarity =\n typeof result.similarity === 'number' ? result.similarity : 0\n let group = groups.get(id)\n if (!group) {\n group = {\n id,\n type:\n typeof document?.type === 'string' ? document.type : '',\n title:\n pickString(document, 'name') ??\n pickString(source, 'filename', 'url') ??\n $t('messagePart.stepDocument'),\n url: pickString(source, 'url'),\n similarity,\n chunks: [],\n }\n groups.set(id, group)\n }\n group.similarity = Math.max(group.similarity, similarity)\n if (typeof result.content === 'string' && result.content.trim()) {\n group.chunks.push(result.content)\n }\n }\n const list = [...groups.values()]\n const relevances = spreadRelevance(list.map((g) => g.similarity))\n return list.map((group, index) => ({\n key: group.id,\n icon: group.type === 'url' ? 'ri:global-line' : 'ri:file-text-line',\n title: group.title,\n url: group.url,\n meta: $t('messagePart.stepExcerpts', group.chunks.length || 1),\n relevance: relevances[index],\n body: group.chunks.join('\\n\\n· · ·\\n\\n') || undefined,\n }))\n })\n\n // Chunks this turn had already returned come back as references without\n // content (spec retrieval-payload-per-turn, R2). They carry no document\n // name to group by, so they are summarized in one line: without it a\n // search that found only known passages reads as \"no results\".\n const repeatedCount = computed<number>(() => {\n const count = asRecord(output.value?.repeated)?.count\n return typeof count === 'number' ? count : 0\n })\n\n // Two declarations every search tool can attach to its output, and the\n // only ones the model is explicitly told not to read as an answer: a\n // query dropped for exceeding the tool's cap, and a search that ran out\n // of time. Without them a timeout renders as \"no results\", which is the\n // reading whoever opens a chat log would take away too.\n const searchFacts = computed<DetailFact[]>(() => {\n const facts: DetailFact[] = []\n const dropped = asRecord(output.value?.inputTruncated)?.dropped\n if (Array.isArray(dropped) && dropped.length) {\n facts.push({\n icon: 'ri:scissors-cut-line',\n text: $t('messagePart.stepDropped', dropped.length),\n })\n }\n const unavailable = asRecord(output.value?.searchUnavailable)\n if (unavailable) {\n // The corrected-answers tool declares one lookup without naming\n // its query, so an absent list means one search, not none.\n const queries = unavailable.queries\n facts.push({\n icon: 'ri:time-line',\n text: $t(\n 'messagePart.stepTimedOut',\n Array.isArray(queries) ? queries.length : 1,\n ),\n })\n }\n return facts\n })\n\n // retrieveRevisedAnswers: one hit per matched question, the corrected\n // answer is the expandable body.\n const revisedAnswerHits = computed<DetailHit[] | undefined>(() =>\n scoredRecords(output.value?.results)?.map(\n ({ record, relevance }, index) => ({\n key:\n pickString(record, 'revisedAnswerId') ?? `revised-${index}`,\n icon: 'ri:file-edit-line',\n title:\n pickString(record, 'userQuestion') ??\n $t('messagePart.revisedAnswers'),\n relevance,\n body: pickString(record, 'revisedMessage'),\n }),\n ),\n )\n\n // recallUserMemory / saveUserMemory delete candidates: one hit per\n // memory, long facts expand to their full text.\n const memoryHits = (raw: unknown): DetailHit[] | undefined =>\n scoredRecords(raw)?.map(({ record, relevance }, index) => {\n const content = pickString(record, 'content') ?? ''\n return {\n key: pickString(record, 'id') ?? `memory-${index}`,\n icon: 'ri:brain-line',\n title: content,\n meta: pickString(record, 'updatedAt')?.slice(0, 10),\n relevance,\n body: content.length > 120 ? content : undefined,\n }\n })\n\n // Query chips over a list of hits, flagging an explicit empty state so the\n // \"no results\" message shows once the tool has run (hits defined, length 0).\n const hitsDetail = (hits: DetailHit[] | undefined): Detail => ({\n chips: queryChips.value,\n hits,\n showEmpty: hits !== undefined && !hits.length,\n })\n\n // Rich, tool-specific view of the step. Returns {} when the tool has\n // nothing meaningful to show yet (the raw fallback is applied by `detail`).\n const buildDetail = (): Detail => {\n const type = props.part.type\n if (type === 'tool-searchInKnowledgeBase') {\n const knowledge = hitsDetail(knowledgeHits.value)\n if (repeatedCount.value) {\n knowledge.facts = [\n {\n icon: 'ri:history-line',\n text: $t(\n 'messagePart.stepRepeated',\n repeatedCount.value,\n ),\n },\n ]\n knowledge.showEmpty = false\n }\n return knowledge\n }\n if (type === 'tool-retrieveRevisedAnswers') {\n return hitsDetail(revisedAnswerHits.value)\n }\n if (type === 'tool-expandChunkContext') {\n const content = pickString(output.value, 'content')\n if (!content) {\n return {}\n }\n const totalChunks = output.value?.totalChunksReturned\n return {\n passage: {\n title: pickString(output.value, 'documentName'),\n meta:\n typeof totalChunks === 'number'\n ? $t('messagePart.stepExcerpts', totalChunks)\n : undefined,\n content,\n },\n }\n }\n if (type === 'tool-readDocumentFile') {\n const name = pickString(output.value, 'name')\n if (!name) {\n return {}\n }\n const mimeType = pickString(output.value, 'mimeType')\n return {\n facts: [\n {\n icon: 'ri:file-text-line',\n text: mimeType ? `${name} (${mimeType})` : name,\n },\n ],\n }\n }\n if (type === 'tool-getWeather') {\n const location =\n pickString(output.value, 'location') ??\n pickString(input.value, 'locationName') ??\n formatCoordinates(input.value)\n if (!location) {\n return {}\n }\n const startDate = pickString(input.value, 'startDate')\n const endDate = pickString(input.value, 'endDate')\n const dateRange = startDate\n ? endDate && endDate !== startDate\n ? ` · ${startDate} → ${endDate}`\n : ` · ${startDate}`\n : ''\n return {\n facts: [\n {\n icon: 'ri:sun-cloudy-line',\n text: `${location}${dateRange}`,\n },\n ],\n }\n }\n if (type === 'tool-geocode') {\n const from =\n pickString(input.value, 'locationName') ??\n formatCoordinates(input.value)\n const to =\n pickString(\n output.value,\n 'name',\n 'displayName',\n 'locationName',\n 'label',\n ) ?? formatCoordinates(output.value)\n const text =\n from && to && from !== to ? `${from} → ${to}` : (from ?? to)\n if (!text) {\n return {}\n }\n return {\n facts: [\n {\n icon: 'ri:map-pin-line',\n text,\n },\n ],\n }\n }\n if (type === 'tool-recallUserMemory') {\n return hitsDetail(memoryHits(output.value?.memories))\n }\n if (type === 'tool-saveUserMemory') {\n const candidates = memoryHits(output.value?.candidates)\n if (candidates) {\n return hitsDetail(candidates)\n }\n const text =\n pickString(input.value, 'content') ??\n pickString(output.value, 'message', 'error')\n if (!text) {\n return {}\n }\n return {\n facts: [\n {\n icon:\n pickString(input.value, 'action') === 'delete'\n ? 'ri:delete-bin-line'\n : 'ri:save-3-line',\n text,\n },\n ],\n }\n }\n if (WEB_SEARCH_TOOL_TYPES.includes(type)) {\n return {\n chips: queryChips.value,\n rawOutput: rawOutputValue.value,\n }\n }\n return {}\n }\n\n const detail = computed<Detail>(() => {\n const result = buildDetail()\n if (searchFacts.value.length) {\n result.facts = [...searchFacts.value, ...(result.facts ?? [])]\n }\n if (output.value?.searchUnavailable) {\n // \"No results found\" under a search that never answered is the\n // one sentence this fact exists to prevent.\n result.showEmpty = false\n }\n // Nothing meaningful to show for this tool yet (e.g. still running):\n // fall back to the raw payloads so the step body is never empty.\n const hasRichContent =\n result.chips?.length ||\n result.facts?.length ||\n result.passage ||\n result.showEmpty ||\n result.hits?.length\n if (!hasRichContent) {\n result.rawInput = rawInputValue.value\n result.rawOutput ??= rawOutputValue.value\n }\n return result\n })\n\n const expandedHits = reactive(new Set<string>())\n const toggleHit = (key: string) => {\n if (expandedHits.has(key)) {\n expandedHits.delete(key)\n } else {\n expandedHits.add(key)\n }\n }\n</script>\n\n<template>\n <div class=\"pk-chatbot-step-detail\">\n <ul v-if=\"detail.chips?.length\" class=\"pk-chatbot-step-detail__chips\">\n <li\n v-for=\"(chip, chipIndex) in detail.chips\"\n :key=\"`${chipIndex}-${chip}`\"\n class=\"pk-chatbot-step-detail__chip\">\n <VvIcon name=\"ri:search-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-step-detail__chip-text\">\n {{ chip }}\n </span>\n </li>\n </ul>\n <div\n v-for=\"(fact, factIndex) in detail.facts\"\n :key=\"`${factIndex}-${fact.text}`\"\n class=\"pk-chatbot-step-detail__fact\">\n <VvIcon :name=\"fact.icon\" class=\"shrink-0\" />\n <span>{{ fact.text }}</span>\n </div>\n <div v-if=\"detail.showEmpty\" class=\"pk-chatbot-step-detail__empty\">\n {{ $t('messagePart.stepNoResults') }}\n </div>\n <ul\n v-else-if=\"detail.hits?.length\"\n class=\"pk-chatbot-step-detail__hits\">\n <li v-for=\"hit in detail.hits\" :key=\"hit.key\">\n <component\n :is=\"hit.body ? 'button' : 'div'\"\n :type=\"hit.body ? 'button' : undefined\"\n class=\"pk-chatbot-step-detail__hit-header\"\n :aria-expanded=\"\n hit.body ? expandedHits.has(hit.key) : undefined\n \"\n @click=\"hit.body ? toggleHit(hit.key) : undefined\">\n <VvIcon\n :name=\"hit.icon\"\n class=\"pk-chatbot-step-detail__hit-icon\" />\n <span class=\"pk-chatbot-step-detail__hit-heading\">\n <span class=\"pk-chatbot-step-detail__hit-title\">\n {{ hit.title }}\n </span>\n <a\n v-if=\"hit.url\"\n :href=\"hit.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"pk-chatbot-step-detail__link\"\n @click.stop>\n <VvIcon name=\"ri:external-link-line\" />\n </a>\n </span>\n <span\n v-if=\"hit.meta\"\n class=\"pk-chatbot-step-detail__hit-meta\">\n {{ hit.meta }}\n </span>\n <span\n v-if=\"hit.relevance !== undefined\"\n class=\"pk-chatbot-step-detail__relevance\"\n aria-hidden=\"true\">\n <span\n class=\"pk-chatbot-step-detail__relevance-fill\"\n :style=\"{\n width: `${Math.round(hit.relevance * 100)}%`,\n }\" />\n </span>\n <VvIcon\n v-if=\"hit.body\"\n :name=\"\n expandedHits.has(hit.key)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"shrink-0\" />\n </component>\n <div\n v-if=\"hit.body && expandedHits.has(hit.key)\"\n class=\"pk-chatbot-step-detail__hit-body\">\n {{ hit.body }}\n </div>\n </li>\n </ul>\n <div v-if=\"detail.passage\" class=\"pk-chatbot-step-detail__passage\">\n <div class=\"pk-chatbot-step-detail__passage-header\">\n <VvIcon name=\"ri:file-text-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-step-detail__hit-heading\">\n <span class=\"pk-chatbot-step-detail__hit-title\">\n {{\n detail.passage.title ??\n $t('messagePart.stepDocument')\n }}\n </span>\n </span>\n <span\n v-if=\"detail.passage.meta\"\n class=\"pk-chatbot-step-detail__hit-meta\">\n {{ detail.passage.meta }}\n </span>\n </div>\n <div class=\"pk-chatbot-step-detail__passage-content\">\n {{ detail.passage.content }}\n </div>\n </div>\n <PkChatbotStepPayload\n v-if=\"hasPayload(detail.rawInput)\"\n :label=\"$t('messagePart.stepInput')\"\n :value=\"detail.rawInput\" />\n <PkChatbotStepPayload\n v-if=\"hasPayload(detail.rawOutput)\"\n :label=\"$t('messagePart.stepOutput')\"\n :value=\"detail.rawOutput\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-step-detail {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-8);\n font-size: var(--text-12);\n color: var(--color-word-3);\n\n &__chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-4);\n }\n\n &__chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n max-width: 100%;\n padding: var(--spacing-2) var(--spacing-8);\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface-1);\n color: var(--color-word-2);\n }\n\n &__chip-text {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__fact {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n color: var(--color-word-2);\n }\n\n &__empty {\n color: var(--color-word-4);\n font-style: italic;\n }\n\n &__hits {\n display: flex;\n flex-direction: column;\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\n overflow: hidden;\n\n > li + li {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n }\n\n &__hit-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: 100%;\n min-height: var(--spacing-32);\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n\n &:is(button) {\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &__hit-icon {\n flex-shrink: 0;\n color: var(--color-word-3);\n }\n\n // Grows to fill the row so meta, relevance and chevron stay aligned;\n // the title inside only shrinks, keeping the link icon next to the\n // title text instead of pushed to the right edge.\n &__hit-heading {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n flex: 1 1 0;\n min-width: 0;\n }\n\n &__hit-title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-2);\n }\n\n &__hit-meta {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__relevance {\n flex-shrink: 0;\n width: var(--spacing-40);\n height: var(--spacing-4);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface-3);\n overflow: hidden;\n }\n\n &__relevance-fill {\n display: block;\n height: 100%;\n border-radius: inherit;\n background-color: var(--chatbot-main-color, var(--color-word-4));\n }\n\n &__link {\n flex-shrink: 0;\n display: inline-flex;\n color: var(--color-word-4);\n\n &:hover {\n color: var(--color-word-1);\n }\n }\n\n &__hit-body,\n &__passage-content {\n padding: var(--spacing-8);\n max-height: var(--spacing-160);\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--color-word-3);\n }\n\n &__hit-body {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n\n &__passage {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\n overflow: hidden;\n }\n\n &__passage-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n min-height: var(--spacing-32);\n padding: var(--spacing-4) var(--spacing-8);\n border-bottom: var(--spacing-px) solid var(--color-surface-3);\n }\n }\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PkChatbotStepToolDetail-ChrOa4W4.js","names":["__unplugin_components_0","_openBlock","_createElementBlock","_Fragment","_renderList","_createVNode","_createElementVNode","_toDisplayString","_createCommentVNode","_unref","_createBlock","_resolveDynamicComponent","_withCtx","_withModifiers","_normalizeStyle","PkChatbotStepPayload","_sfc_main","WEB_SEARCH_TOOL_TYPES","hasPayload","PkChatbotStepPayload_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","PkChatbotStepToolDetail_default","defineComponent","props","__props","$t","useI18n","asRecord","value","pickString","keys","key","record","formatCoordinates","latitude","longitude","rawInputValue","computed","rawOutputValue","input","output","queryChips","raw","query","spreadRelevance","min","scores","max","score","scoredRecords","records","item","relevances","index","knowledgeHits","results","groups","result","document","source","id","similarity","group","list","g","repeatedCount","count","searchFacts","facts","dropped","unavailable","queries","revisedAnswerHits","memoryHits","content","hitsDetail","hits","buildDetail","type","knowledge","totalChunks","name","mimeType","location","startDate","endDate","from","to","text","candidates","detail","expandedHits","reactive","toggleHit","_component_VvIcon","VvIcon","openBlock","createElementBlock","Fragment","renderList","chipIndex","chip","createVNode","createElementVNode","toDisplayString","createCommentVNode","factIndex","fact","unref","hit","createBlock","resolveDynamicComponent","withCtx","_cache","withModifiers","normalizeStyle"],"sources":["../../../../packages/components/src/chat/PkChatbotStepToolDetail.vue","../../../../packages/components/src/chat/PkChatbotStepToolDetail.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, reactive } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { UIChatMessage } from 'models'\n import PkChatbotStepPayload from './PkChatbotStepPayload.vue'\n import { hasPayload, WEB_SEARCH_TOOL_TYPES } from './utils'\n\n type MessagePart = UIChatMessage['parts'][number]\n\n interface DetailHit {\n key: string\n icon: string\n title: string\n url?: string\n meta?: string\n relevance?: number\n body?: string\n }\n\n interface DetailFact {\n icon: string\n text: string\n }\n\n interface Detail {\n chips?: string[]\n facts?: DetailFact[]\n hits?: DetailHit[]\n showEmpty?: boolean\n passage?: { title?: string; meta?: string; content: string }\n rawInput?: unknown\n rawOutput?: unknown\n }\n\n const props = defineProps<{\n part: MessagePart\n }>()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const asRecord = (value: unknown): Record<string, unknown> | undefined =>\n value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n\n const pickString = (\n record: Record<string, unknown> | undefined,\n ...keys: string[]\n ): string | undefined => {\n for (const key of keys) {\n const value = record?.[key]\n if (typeof value === 'string' && value.trim()) {\n return value\n }\n }\n return undefined\n }\n\n const formatCoordinates = (\n record?: Record<string, unknown>,\n ): string | undefined => {\n const latitude = record?.latitude\n const longitude = record?.longitude\n if (typeof latitude !== 'number' || typeof longitude !== 'number') {\n return undefined\n }\n return `${latitude.toFixed(4)}, ${longitude.toFixed(4)}`\n }\n\n const rawInputValue = computed(() =>\n 'input' in props.part ? props.part.input : undefined,\n )\n const rawOutputValue = computed(() =>\n 'output' in props.part ? props.part.output : undefined,\n )\n const input = computed(() => asRecord(rawInputValue.value))\n const output = computed(() => asRecord(rawOutputValue.value))\n\n // Search queries shown as chips (queries[], query or objective)\n const queryChips = computed<string[]>(() => {\n const raw =\n input.value?.queries ?? input.value?.query ?? input.value?.objective\n if (typeof raw === 'string' && raw.trim()) {\n return [raw]\n }\n if (Array.isArray(raw)) {\n return raw.filter(\n (query): query is string =>\n typeof query === 'string' && Boolean(query.trim()),\n )\n }\n return []\n })\n\n // Raw scores live on different scales depending on the agent search mode\n // (RRF ~0.03, cosine 0 to 1) and tend to cluster, so a plain ratio on the\n // max renders every bar as ~100%. Spread them on a min/max scale with a\n // floor so the ordering stays visible, and hide the bar entirely when it\n // carries no information (single hit or identical scores).\n const spreadRelevance = (scores: number[]): (number | undefined)[] => {\n const min = Math.min(...scores)\n const max = Math.max(...scores)\n if (scores.length < 2 || max <= min) {\n return scores.map(() => undefined)\n }\n const floor = 0.15\n return scores.map(\n (score) => floor + ((score - min) / (max - min)) * (1 - floor),\n )\n }\n\n // Normalizes a raw result array into records paired with a spread\n // relevance derived from each record's `similarity` score.\n const scoredRecords = (\n raw: unknown,\n ):\n | { record: Record<string, unknown>; relevance?: number }[]\n | undefined => {\n if (!Array.isArray(raw)) {\n return undefined\n }\n const records = raw\n .map((item) => asRecord(item))\n .filter((record): record is Record<string, unknown> =>\n Boolean(record),\n )\n const relevances = spreadRelevance(\n records.map((record) =>\n typeof record.similarity === 'number' ? record.similarity : 0,\n ),\n )\n return records.map((record, index) => ({\n record,\n relevance: relevances[index],\n }))\n }\n\n // searchInKnowledgeBase: group chunks by document, relevance compares the\n // best chunk score of each document within the result set.\n const knowledgeHits = computed<DetailHit[] | undefined>(() => {\n const results = output.value?.results\n if (!Array.isArray(results)) {\n return undefined\n }\n interface DocumentGroup {\n id: string\n type: string\n title: string\n url?: string\n similarity: number\n chunks: string[]\n }\n const groups = new Map<string, DocumentGroup>()\n for (const [index, raw] of results.entries()) {\n const result = asRecord(raw)\n if (!result) {\n continue\n }\n const document = asRecord(result.document)\n const source = asRecord(document?.source)\n const id =\n typeof document?.id === 'string' ? document.id : `doc-${index}`\n const similarity =\n typeof result.similarity === 'number' ? result.similarity : 0\n let group = groups.get(id)\n if (!group) {\n group = {\n id,\n type:\n typeof document?.type === 'string' ? document.type : '',\n title:\n pickString(document, 'name') ??\n pickString(source, 'filename', 'url') ??\n $t('messagePart.stepDocument'),\n url: pickString(source, 'url'),\n similarity,\n chunks: [],\n }\n groups.set(id, group)\n }\n group.similarity = Math.max(group.similarity, similarity)\n if (typeof result.content === 'string' && result.content.trim()) {\n group.chunks.push(result.content)\n }\n }\n const list = [...groups.values()]\n const relevances = spreadRelevance(list.map((g) => g.similarity))\n return list.map((group, index) => ({\n key: group.id,\n icon: group.type === 'url' ? 'ri:global-line' : 'ri:file-text-line',\n title: group.title,\n url: group.url,\n meta: $t('messagePart.stepExcerpts', group.chunks.length || 1),\n relevance: relevances[index],\n body: group.chunks.join('\\n\\n· · ·\\n\\n') || undefined,\n }))\n })\n\n // Chunks this turn had already returned come back as references without\n // content (spec retrieval-payload-per-turn, R2). They carry no document\n // name to group by, so they are summarized in one line: without it a\n // search that found only known passages reads as \"no results\".\n const repeatedCount = computed<number>(() => {\n const count = asRecord(output.value?.repeated)?.count\n return typeof count === 'number' ? count : 0\n })\n\n // Two declarations every search tool can attach to its output, and the\n // only ones the model is explicitly told not to read as an answer: a\n // query dropped for exceeding the tool's cap, and a search that ran out\n // of time. Without them a timeout renders as \"no results\", which is the\n // reading whoever opens a chat log would take away too.\n const searchFacts = computed<DetailFact[]>(() => {\n const facts: DetailFact[] = []\n const dropped = asRecord(output.value?.inputTruncated)?.dropped\n if (Array.isArray(dropped) && dropped.length) {\n facts.push({\n icon: 'ri:scissors-cut-line',\n text: $t('messagePart.stepDropped', dropped.length),\n })\n }\n const unavailable = asRecord(output.value?.searchUnavailable)\n if (unavailable) {\n // The corrected-answers tool declares one lookup without naming\n // its query, so an absent list means one search, not none.\n const queries = unavailable.queries\n facts.push({\n icon: 'ri:time-line',\n text: $t(\n 'messagePart.stepTimedOut',\n Array.isArray(queries) ? queries.length : 1,\n ),\n })\n }\n return facts\n })\n\n // retrieveRevisedAnswers: one hit per matched question, the corrected\n // answer is the expandable body.\n const revisedAnswerHits = computed<DetailHit[] | undefined>(() =>\n scoredRecords(output.value?.results)?.map(\n ({ record, relevance }, index) => ({\n key:\n pickString(record, 'revisedAnswerId') ?? `revised-${index}`,\n icon: 'ri:file-edit-line',\n title:\n pickString(record, 'userQuestion') ??\n $t('messagePart.revisedAnswers'),\n relevance,\n body: pickString(record, 'revisedMessage'),\n }),\n ),\n )\n\n // recallUserMemory / saveUserMemory delete candidates: one hit per\n // memory, long facts expand to their full text.\n const memoryHits = (raw: unknown): DetailHit[] | undefined =>\n scoredRecords(raw)?.map(({ record, relevance }, index) => {\n const content = pickString(record, 'content') ?? ''\n return {\n key: pickString(record, 'id') ?? `memory-${index}`,\n icon: 'ri:brain-line',\n title: content,\n meta: pickString(record, 'updatedAt')?.slice(0, 10),\n relevance,\n body: content.length > 120 ? content : undefined,\n }\n })\n\n // Query chips over a list of hits, flagging an explicit empty state so the\n // \"no results\" message shows once the tool has run (hits defined, length 0).\n const hitsDetail = (hits: DetailHit[] | undefined): Detail => ({\n chips: queryChips.value,\n hits,\n showEmpty: hits !== undefined && !hits.length,\n })\n\n // Rich, tool-specific view of the step. Returns {} when the tool has\n // nothing meaningful to show yet (the raw fallback is applied by `detail`).\n const buildDetail = (): Detail => {\n const type = props.part.type\n if (type === 'tool-searchInKnowledgeBase') {\n const knowledge = hitsDetail(knowledgeHits.value)\n if (repeatedCount.value) {\n knowledge.facts = [\n {\n icon: 'ri:history-line',\n text: $t(\n 'messagePart.stepRepeated',\n repeatedCount.value,\n ),\n },\n ]\n knowledge.showEmpty = false\n }\n return knowledge\n }\n if (type === 'tool-retrieveRevisedAnswers') {\n return hitsDetail(revisedAnswerHits.value)\n }\n if (type === 'tool-expandChunkContext') {\n const content = pickString(output.value, 'content')\n if (!content) {\n return {}\n }\n const totalChunks = output.value?.totalChunksReturned\n return {\n passage: {\n title: pickString(output.value, 'documentName'),\n meta:\n typeof totalChunks === 'number'\n ? $t('messagePart.stepExcerpts', totalChunks)\n : undefined,\n content,\n },\n }\n }\n if (type === 'tool-readDocumentFile') {\n const name = pickString(output.value, 'name')\n if (!name) {\n return {}\n }\n const mimeType = pickString(output.value, 'mimeType')\n return {\n facts: [\n {\n icon: 'ri:file-text-line',\n text: mimeType ? `${name} (${mimeType})` : name,\n },\n ],\n }\n }\n if (type === 'tool-getWeather') {\n const location =\n pickString(output.value, 'location') ??\n pickString(input.value, 'locationName') ??\n formatCoordinates(input.value)\n if (!location) {\n return {}\n }\n const startDate = pickString(input.value, 'startDate')\n const endDate = pickString(input.value, 'endDate')\n const dateRange = startDate\n ? endDate && endDate !== startDate\n ? ` · ${startDate} → ${endDate}`\n : ` · ${startDate}`\n : ''\n return {\n facts: [\n {\n icon: 'ri:sun-cloudy-line',\n text: `${location}${dateRange}`,\n },\n ],\n }\n }\n if (type === 'tool-geocode') {\n const from =\n pickString(input.value, 'locationName') ??\n formatCoordinates(input.value)\n const to =\n pickString(\n output.value,\n 'name',\n 'displayName',\n 'locationName',\n 'label',\n ) ?? formatCoordinates(output.value)\n const text =\n from && to && from !== to ? `${from} → ${to}` : (from ?? to)\n if (!text) {\n return {}\n }\n return {\n facts: [\n {\n icon: 'ri:map-pin-line',\n text,\n },\n ],\n }\n }\n if (type === 'tool-recallUserMemory') {\n return hitsDetail(memoryHits(output.value?.memories))\n }\n if (type === 'tool-saveUserMemory') {\n const candidates = memoryHits(output.value?.candidates)\n if (candidates) {\n return hitsDetail(candidates)\n }\n const text =\n pickString(input.value, 'content') ??\n pickString(output.value, 'message', 'error')\n if (!text) {\n return {}\n }\n return {\n facts: [\n {\n icon:\n pickString(input.value, 'action') === 'delete'\n ? 'ri:delete-bin-line'\n : 'ri:save-3-line',\n text,\n },\n ],\n }\n }\n if (WEB_SEARCH_TOOL_TYPES.includes(type)) {\n return {\n chips: queryChips.value,\n rawOutput: rawOutputValue.value,\n }\n }\n return {}\n }\n\n const detail = computed<Detail>(() => {\n const result = buildDetail()\n if (searchFacts.value.length) {\n result.facts = [...searchFacts.value, ...(result.facts ?? [])]\n }\n if (output.value?.searchUnavailable) {\n // \"No results found\" under a search that never answered is the\n // one sentence this fact exists to prevent.\n result.showEmpty = false\n }\n // Nothing meaningful to show for this tool yet (e.g. still running):\n // fall back to the raw payloads so the step body is never empty.\n const hasRichContent =\n result.chips?.length ||\n result.facts?.length ||\n result.passage ||\n result.showEmpty ||\n result.hits?.length\n if (!hasRichContent) {\n result.rawInput = rawInputValue.value\n result.rawOutput ??= rawOutputValue.value\n }\n return result\n })\n\n const expandedHits = reactive(new Set<string>())\n const toggleHit = (key: string) => {\n if (expandedHits.has(key)) {\n expandedHits.delete(key)\n } else {\n expandedHits.add(key)\n }\n }\n</script>\n\n<template>\n <div class=\"pk-chatbot-step-detail\">\n <ul v-if=\"detail.chips?.length\" class=\"pk-chatbot-step-detail__chips\">\n <li\n v-for=\"(chip, chipIndex) in detail.chips\"\n :key=\"`${chipIndex}-${chip}`\"\n class=\"pk-chatbot-step-detail__chip\">\n <VvIcon name=\"ri:search-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-step-detail__chip-text\">\n {{ chip }}\n </span>\n </li>\n </ul>\n <div\n v-for=\"(fact, factIndex) in detail.facts\"\n :key=\"`${factIndex}-${fact.text}`\"\n class=\"pk-chatbot-step-detail__fact\">\n <VvIcon :name=\"fact.icon\" class=\"shrink-0\" />\n <span>{{ fact.text }}</span>\n </div>\n <div v-if=\"detail.showEmpty\" class=\"pk-chatbot-step-detail__empty\">\n {{ $t('messagePart.stepNoResults') }}\n </div>\n <ul\n v-else-if=\"detail.hits?.length\"\n class=\"pk-chatbot-step-detail__hits\">\n <li v-for=\"hit in detail.hits\" :key=\"hit.key\">\n <component\n :is=\"hit.body ? 'button' : 'div'\"\n :type=\"hit.body ? 'button' : undefined\"\n class=\"pk-chatbot-step-detail__hit-header\"\n :aria-expanded=\"\n hit.body ? expandedHits.has(hit.key) : undefined\n \"\n @click=\"hit.body ? toggleHit(hit.key) : undefined\">\n <VvIcon\n :name=\"hit.icon\"\n class=\"pk-chatbot-step-detail__hit-icon\" />\n <span class=\"pk-chatbot-step-detail__hit-heading\">\n <span class=\"pk-chatbot-step-detail__hit-title\">\n {{ hit.title }}\n </span>\n <a\n v-if=\"hit.url\"\n :href=\"hit.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"pk-chatbot-step-detail__link\"\n @click.stop>\n <VvIcon name=\"ri:external-link-line\" />\n </a>\n </span>\n <span\n v-if=\"hit.meta\"\n class=\"pk-chatbot-step-detail__hit-meta\">\n {{ hit.meta }}\n </span>\n <span\n v-if=\"hit.relevance !== undefined\"\n class=\"pk-chatbot-step-detail__relevance\"\n aria-hidden=\"true\">\n <span\n class=\"pk-chatbot-step-detail__relevance-fill\"\n :style=\"{\n width: `${Math.round(hit.relevance * 100)}%`,\n }\" />\n </span>\n <VvIcon\n v-if=\"hit.body\"\n :name=\"\n expandedHits.has(hit.key)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"shrink-0\" />\n </component>\n <div\n v-if=\"hit.body && expandedHits.has(hit.key)\"\n class=\"pk-chatbot-step-detail__hit-body\">\n {{ hit.body }}\n </div>\n </li>\n </ul>\n <div v-if=\"detail.passage\" class=\"pk-chatbot-step-detail__passage\">\n <div class=\"pk-chatbot-step-detail__passage-header\">\n <VvIcon name=\"ri:file-text-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-step-detail__hit-heading\">\n <span class=\"pk-chatbot-step-detail__hit-title\">\n {{\n detail.passage.title ??\n $t('messagePart.stepDocument')\n }}\n </span>\n </span>\n <span\n v-if=\"detail.passage.meta\"\n class=\"pk-chatbot-step-detail__hit-meta\">\n {{ detail.passage.meta }}\n </span>\n </div>\n <div class=\"pk-chatbot-step-detail__passage-content\">\n {{ detail.passage.content }}\n </div>\n </div>\n <PkChatbotStepPayload\n v-if=\"hasPayload(detail.rawInput)\"\n :label=\"$t('messagePart.stepInput')\"\n :value=\"detail.rawInput\" />\n <PkChatbotStepPayload\n v-if=\"hasPayload(detail.rawOutput)\"\n :label=\"$t('messagePart.stepOutput')\"\n :value=\"detail.rawOutput\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-step-detail {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-8);\n font-size: var(--text-12);\n color: var(--color-word-3);\n\n &__chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-4);\n }\n\n &__chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n max-width: 100%;\n padding: var(--spacing-2) var(--spacing-8);\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface-1);\n color: var(--color-word-2);\n }\n\n &__chip-text {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__fact {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n color: var(--color-word-2);\n }\n\n &__empty {\n color: var(--color-word-4);\n font-style: italic;\n }\n\n &__hits {\n display: flex;\n flex-direction: column;\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\n overflow: hidden;\n\n > li + li {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n }\n\n &__hit-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: 100%;\n min-height: var(--spacing-32);\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n\n &:is(button) {\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &__hit-icon {\n flex-shrink: 0;\n color: var(--color-word-3);\n }\n\n // Grows to fill the row so meta, relevance and chevron stay aligned;\n // the title inside only shrinks, keeping the link icon next to the\n // title text instead of pushed to the right edge.\n &__hit-heading {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n flex: 1 1 0;\n min-width: 0;\n }\n\n &__hit-title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-2);\n }\n\n &__hit-meta {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__relevance {\n flex-shrink: 0;\n width: var(--spacing-40);\n height: var(--spacing-4);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface-3);\n overflow: hidden;\n }\n\n &__relevance-fill {\n display: block;\n height: 100%;\n border-radius: inherit;\n background-color: var(--chatbot-main-color, var(--color-word-4));\n }\n\n &__link {\n flex-shrink: 0;\n display: inline-flex;\n color: var(--color-word-4);\n\n &:hover {\n color: var(--color-word-1);\n }\n }\n\n &__hit-body,\n &__passage-content {\n padding: var(--spacing-8);\n max-height: var(--spacing-160);\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--color-word-3);\n }\n\n &__hit-body {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n\n &__passage {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\n overflow: hidden;\n }\n\n &__passage-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n min-height: var(--spacing-32);\n padding: var(--spacing-4) var(--spacing-8);\n border-bottom: var(--spacing-px) solid var(--color-surface-3);\n }\n }\n</style>\n","<script lang=\"ts\" setup>\n import { computed, reactive } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { UIChatMessage } from 'models'\n import PkChatbotStepPayload from './PkChatbotStepPayload.vue'\n import { hasPayload, WEB_SEARCH_TOOL_TYPES } from './utils'\n\n type MessagePart = UIChatMessage['parts'][number]\n\n interface DetailHit {\n key: string\n icon: string\n title: string\n url?: string\n meta?: string\n relevance?: number\n body?: string\n }\n\n interface DetailFact {\n icon: string\n text: string\n }\n\n interface Detail {\n chips?: string[]\n facts?: DetailFact[]\n hits?: DetailHit[]\n showEmpty?: boolean\n passage?: { title?: string; meta?: string; content: string }\n rawInput?: unknown\n rawOutput?: unknown\n }\n\n const props = defineProps<{\n part: MessagePart\n }>()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const asRecord = (value: unknown): Record<string, unknown> | undefined =>\n value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n\n const pickString = (\n record: Record<string, unknown> | undefined,\n ...keys: string[]\n ): string | undefined => {\n for (const key of keys) {\n const value = record?.[key]\n if (typeof value === 'string' && value.trim()) {\n return value\n }\n }\n return undefined\n }\n\n const formatCoordinates = (\n record?: Record<string, unknown>,\n ): string | undefined => {\n const latitude = record?.latitude\n const longitude = record?.longitude\n if (typeof latitude !== 'number' || typeof longitude !== 'number') {\n return undefined\n }\n return `${latitude.toFixed(4)}, ${longitude.toFixed(4)}`\n }\n\n const rawInputValue = computed(() =>\n 'input' in props.part ? props.part.input : undefined,\n )\n const rawOutputValue = computed(() =>\n 'output' in props.part ? props.part.output : undefined,\n )\n const input = computed(() => asRecord(rawInputValue.value))\n const output = computed(() => asRecord(rawOutputValue.value))\n\n // Search queries shown as chips (queries[], query or objective)\n const queryChips = computed<string[]>(() => {\n const raw =\n input.value?.queries ?? input.value?.query ?? input.value?.objective\n if (typeof raw === 'string' && raw.trim()) {\n return [raw]\n }\n if (Array.isArray(raw)) {\n return raw.filter(\n (query): query is string =>\n typeof query === 'string' && Boolean(query.trim()),\n )\n }\n return []\n })\n\n // Raw scores live on different scales depending on the agent search mode\n // (RRF ~0.03, cosine 0 to 1) and tend to cluster, so a plain ratio on the\n // max renders every bar as ~100%. Spread them on a min/max scale with a\n // floor so the ordering stays visible, and hide the bar entirely when it\n // carries no information (single hit or identical scores).\n const spreadRelevance = (scores: number[]): (number | undefined)[] => {\n const min = Math.min(...scores)\n const max = Math.max(...scores)\n if (scores.length < 2 || max <= min) {\n return scores.map(() => undefined)\n }\n const floor = 0.15\n return scores.map(\n (score) => floor + ((score - min) / (max - min)) * (1 - floor),\n )\n }\n\n // Normalizes a raw result array into records paired with a spread\n // relevance derived from each record's `similarity` score.\n const scoredRecords = (\n raw: unknown,\n ):\n | { record: Record<string, unknown>; relevance?: number }[]\n | undefined => {\n if (!Array.isArray(raw)) {\n return undefined\n }\n const records = raw\n .map((item) => asRecord(item))\n .filter((record): record is Record<string, unknown> =>\n Boolean(record),\n )\n const relevances = spreadRelevance(\n records.map((record) =>\n typeof record.similarity === 'number' ? record.similarity : 0,\n ),\n )\n return records.map((record, index) => ({\n record,\n relevance: relevances[index],\n }))\n }\n\n // searchInKnowledgeBase: group chunks by document, relevance compares the\n // best chunk score of each document within the result set.\n const knowledgeHits = computed<DetailHit[] | undefined>(() => {\n const results = output.value?.results\n if (!Array.isArray(results)) {\n return undefined\n }\n interface DocumentGroup {\n id: string\n type: string\n title: string\n url?: string\n similarity: number\n chunks: string[]\n }\n const groups = new Map<string, DocumentGroup>()\n for (const [index, raw] of results.entries()) {\n const result = asRecord(raw)\n if (!result) {\n continue\n }\n const document = asRecord(result.document)\n const source = asRecord(document?.source)\n const id =\n typeof document?.id === 'string' ? document.id : `doc-${index}`\n const similarity =\n typeof result.similarity === 'number' ? result.similarity : 0\n let group = groups.get(id)\n if (!group) {\n group = {\n id,\n type:\n typeof document?.type === 'string' ? document.type : '',\n title:\n pickString(document, 'name') ??\n pickString(source, 'filename', 'url') ??\n $t('messagePart.stepDocument'),\n url: pickString(source, 'url'),\n similarity,\n chunks: [],\n }\n groups.set(id, group)\n }\n group.similarity = Math.max(group.similarity, similarity)\n if (typeof result.content === 'string' && result.content.trim()) {\n group.chunks.push(result.content)\n }\n }\n const list = [...groups.values()]\n const relevances = spreadRelevance(list.map((g) => g.similarity))\n return list.map((group, index) => ({\n key: group.id,\n icon: group.type === 'url' ? 'ri:global-line' : 'ri:file-text-line',\n title: group.title,\n url: group.url,\n meta: $t('messagePart.stepExcerpts', group.chunks.length || 1),\n relevance: relevances[index],\n body: group.chunks.join('\\n\\n· · ·\\n\\n') || undefined,\n }))\n })\n\n // Chunks this turn had already returned come back as references without\n // content (spec retrieval-payload-per-turn, R2). They carry no document\n // name to group by, so they are summarized in one line: without it a\n // search that found only known passages reads as \"no results\".\n const repeatedCount = computed<number>(() => {\n const count = asRecord(output.value?.repeated)?.count\n return typeof count === 'number' ? count : 0\n })\n\n // Two declarations every search tool can attach to its output, and the\n // only ones the model is explicitly told not to read as an answer: a\n // query dropped for exceeding the tool's cap, and a search that ran out\n // of time. Without them a timeout renders as \"no results\", which is the\n // reading whoever opens a chat log would take away too.\n const searchFacts = computed<DetailFact[]>(() => {\n const facts: DetailFact[] = []\n const dropped = asRecord(output.value?.inputTruncated)?.dropped\n if (Array.isArray(dropped) && dropped.length) {\n facts.push({\n icon: 'ri:scissors-cut-line',\n text: $t('messagePart.stepDropped', dropped.length),\n })\n }\n const unavailable = asRecord(output.value?.searchUnavailable)\n if (unavailable) {\n // The corrected-answers tool declares one lookup without naming\n // its query, so an absent list means one search, not none.\n const queries = unavailable.queries\n facts.push({\n icon: 'ri:time-line',\n text: $t(\n 'messagePart.stepTimedOut',\n Array.isArray(queries) ? queries.length : 1,\n ),\n })\n }\n return facts\n })\n\n // retrieveRevisedAnswers: one hit per matched question, the corrected\n // answer is the expandable body.\n const revisedAnswerHits = computed<DetailHit[] | undefined>(() =>\n scoredRecords(output.value?.results)?.map(\n ({ record, relevance }, index) => ({\n key:\n pickString(record, 'revisedAnswerId') ?? `revised-${index}`,\n icon: 'ri:file-edit-line',\n title:\n pickString(record, 'userQuestion') ??\n $t('messagePart.revisedAnswers'),\n relevance,\n body: pickString(record, 'revisedMessage'),\n }),\n ),\n )\n\n // recallUserMemory / saveUserMemory delete candidates: one hit per\n // memory, long facts expand to their full text.\n const memoryHits = (raw: unknown): DetailHit[] | undefined =>\n scoredRecords(raw)?.map(({ record, relevance }, index) => {\n const content = pickString(record, 'content') ?? ''\n return {\n key: pickString(record, 'id') ?? `memory-${index}`,\n icon: 'ri:brain-line',\n title: content,\n meta: pickString(record, 'updatedAt')?.slice(0, 10),\n relevance,\n body: content.length > 120 ? content : undefined,\n }\n })\n\n // Query chips over a list of hits, flagging an explicit empty state so the\n // \"no results\" message shows once the tool has run (hits defined, length 0).\n const hitsDetail = (hits: DetailHit[] | undefined): Detail => ({\n chips: queryChips.value,\n hits,\n showEmpty: hits !== undefined && !hits.length,\n })\n\n // Rich, tool-specific view of the step. Returns {} when the tool has\n // nothing meaningful to show yet (the raw fallback is applied by `detail`).\n const buildDetail = (): Detail => {\n const type = props.part.type\n if (type === 'tool-searchInKnowledgeBase') {\n const knowledge = hitsDetail(knowledgeHits.value)\n if (repeatedCount.value) {\n knowledge.facts = [\n {\n icon: 'ri:history-line',\n text: $t(\n 'messagePart.stepRepeated',\n repeatedCount.value,\n ),\n },\n ]\n knowledge.showEmpty = false\n }\n return knowledge\n }\n if (type === 'tool-retrieveRevisedAnswers') {\n return hitsDetail(revisedAnswerHits.value)\n }\n if (type === 'tool-expandChunkContext') {\n const content = pickString(output.value, 'content')\n if (!content) {\n return {}\n }\n const totalChunks = output.value?.totalChunksReturned\n return {\n passage: {\n title: pickString(output.value, 'documentName'),\n meta:\n typeof totalChunks === 'number'\n ? $t('messagePart.stepExcerpts', totalChunks)\n : undefined,\n content,\n },\n }\n }\n if (type === 'tool-readDocumentFile') {\n const name = pickString(output.value, 'name')\n if (!name) {\n return {}\n }\n const mimeType = pickString(output.value, 'mimeType')\n return {\n facts: [\n {\n icon: 'ri:file-text-line',\n text: mimeType ? `${name} (${mimeType})` : name,\n },\n ],\n }\n }\n if (type === 'tool-getWeather') {\n const location =\n pickString(output.value, 'location') ??\n pickString(input.value, 'locationName') ??\n formatCoordinates(input.value)\n if (!location) {\n return {}\n }\n const startDate = pickString(input.value, 'startDate')\n const endDate = pickString(input.value, 'endDate')\n const dateRange = startDate\n ? endDate && endDate !== startDate\n ? ` · ${startDate} → ${endDate}`\n : ` · ${startDate}`\n : ''\n return {\n facts: [\n {\n icon: 'ri:sun-cloudy-line',\n text: `${location}${dateRange}`,\n },\n ],\n }\n }\n if (type === 'tool-geocode') {\n const from =\n pickString(input.value, 'locationName') ??\n formatCoordinates(input.value)\n const to =\n pickString(\n output.value,\n 'name',\n 'displayName',\n 'locationName',\n 'label',\n ) ?? formatCoordinates(output.value)\n const text =\n from && to && from !== to ? `${from} → ${to}` : (from ?? to)\n if (!text) {\n return {}\n }\n return {\n facts: [\n {\n icon: 'ri:map-pin-line',\n text,\n },\n ],\n }\n }\n if (type === 'tool-recallUserMemory') {\n return hitsDetail(memoryHits(output.value?.memories))\n }\n if (type === 'tool-saveUserMemory') {\n const candidates = memoryHits(output.value?.candidates)\n if (candidates) {\n return hitsDetail(candidates)\n }\n const text =\n pickString(input.value, 'content') ??\n pickString(output.value, 'message', 'error')\n if (!text) {\n return {}\n }\n return {\n facts: [\n {\n icon:\n pickString(input.value, 'action') === 'delete'\n ? 'ri:delete-bin-line'\n : 'ri:save-3-line',\n text,\n },\n ],\n }\n }\n if (WEB_SEARCH_TOOL_TYPES.includes(type)) {\n return {\n chips: queryChips.value,\n rawOutput: rawOutputValue.value,\n }\n }\n return {}\n }\n\n const detail = computed<Detail>(() => {\n const result = buildDetail()\n if (searchFacts.value.length) {\n result.facts = [...searchFacts.value, ...(result.facts ?? [])]\n }\n if (output.value?.searchUnavailable) {\n // \"No results found\" under a search that never answered is the\n // one sentence this fact exists to prevent.\n result.showEmpty = false\n }\n // Nothing meaningful to show for this tool yet (e.g. still running):\n // fall back to the raw payloads so the step body is never empty.\n const hasRichContent =\n result.chips?.length ||\n result.facts?.length ||\n result.passage ||\n result.showEmpty ||\n result.hits?.length\n if (!hasRichContent) {\n result.rawInput = rawInputValue.value\n result.rawOutput ??= rawOutputValue.value\n }\n return result\n })\n\n const expandedHits = reactive(new Set<string>())\n const toggleHit = (key: string) => {\n if (expandedHits.has(key)) {\n expandedHits.delete(key)\n } else {\n expandedHits.add(key)\n }\n }\n</script>\n\n<template>\n <div class=\"pk-chatbot-step-detail\">\n <ul v-if=\"detail.chips?.length\" class=\"pk-chatbot-step-detail__chips\">\n <li\n v-for=\"(chip, chipIndex) in detail.chips\"\n :key=\"`${chipIndex}-${chip}`\"\n class=\"pk-chatbot-step-detail__chip\">\n <VvIcon name=\"ri:search-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-step-detail__chip-text\">\n {{ chip }}\n </span>\n </li>\n </ul>\n <div\n v-for=\"(fact, factIndex) in detail.facts\"\n :key=\"`${factIndex}-${fact.text}`\"\n class=\"pk-chatbot-step-detail__fact\">\n <VvIcon :name=\"fact.icon\" class=\"shrink-0\" />\n <span>{{ fact.text }}</span>\n </div>\n <div v-if=\"detail.showEmpty\" class=\"pk-chatbot-step-detail__empty\">\n {{ $t('messagePart.stepNoResults') }}\n </div>\n <ul\n v-else-if=\"detail.hits?.length\"\n class=\"pk-chatbot-step-detail__hits\">\n <li v-for=\"hit in detail.hits\" :key=\"hit.key\">\n <component\n :is=\"hit.body ? 'button' : 'div'\"\n :type=\"hit.body ? 'button' : undefined\"\n class=\"pk-chatbot-step-detail__hit-header\"\n :aria-expanded=\"\n hit.body ? expandedHits.has(hit.key) : undefined\n \"\n @click=\"hit.body ? toggleHit(hit.key) : undefined\">\n <VvIcon\n :name=\"hit.icon\"\n class=\"pk-chatbot-step-detail__hit-icon\" />\n <span class=\"pk-chatbot-step-detail__hit-heading\">\n <span class=\"pk-chatbot-step-detail__hit-title\">\n {{ hit.title }}\n </span>\n <a\n v-if=\"hit.url\"\n :href=\"hit.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"pk-chatbot-step-detail__link\"\n @click.stop>\n <VvIcon name=\"ri:external-link-line\" />\n </a>\n </span>\n <span\n v-if=\"hit.meta\"\n class=\"pk-chatbot-step-detail__hit-meta\">\n {{ hit.meta }}\n </span>\n <span\n v-if=\"hit.relevance !== undefined\"\n class=\"pk-chatbot-step-detail__relevance\"\n aria-hidden=\"true\">\n <span\n class=\"pk-chatbot-step-detail__relevance-fill\"\n :style=\"{\n width: `${Math.round(hit.relevance * 100)}%`,\n }\" />\n </span>\n <VvIcon\n v-if=\"hit.body\"\n :name=\"\n expandedHits.has(hit.key)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"shrink-0\" />\n </component>\n <div\n v-if=\"hit.body && expandedHits.has(hit.key)\"\n class=\"pk-chatbot-step-detail__hit-body\">\n {{ hit.body }}\n </div>\n </li>\n </ul>\n <div v-if=\"detail.passage\" class=\"pk-chatbot-step-detail__passage\">\n <div class=\"pk-chatbot-step-detail__passage-header\">\n <VvIcon name=\"ri:file-text-line\" class=\"shrink-0\" />\n <span class=\"pk-chatbot-step-detail__hit-heading\">\n <span class=\"pk-chatbot-step-detail__hit-title\">\n {{\n detail.passage.title ??\n $t('messagePart.stepDocument')\n }}\n </span>\n </span>\n <span\n v-if=\"detail.passage.meta\"\n class=\"pk-chatbot-step-detail__hit-meta\">\n {{ detail.passage.meta }}\n </span>\n </div>\n <div class=\"pk-chatbot-step-detail__passage-content\">\n {{ detail.passage.content }}\n </div>\n </div>\n <PkChatbotStepPayload\n v-if=\"hasPayload(detail.rawInput)\"\n :label=\"$t('messagePart.stepInput')\"\n :value=\"detail.rawInput\" />\n <PkChatbotStepPayload\n v-if=\"hasPayload(detail.rawOutput)\"\n :label=\"$t('messagePart.stepOutput')\"\n :value=\"detail.rawOutput\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-step-detail {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-8);\n font-size: var(--text-12);\n color: var(--color-word-3);\n\n &__chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-4);\n }\n\n &__chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n max-width: 100%;\n padding: var(--spacing-2) var(--spacing-8);\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface-1);\n color: var(--color-word-2);\n }\n\n &__chip-text {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__fact {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n color: var(--color-word-2);\n }\n\n &__empty {\n color: var(--color-word-4);\n font-style: italic;\n }\n\n &__hits {\n display: flex;\n flex-direction: column;\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\n overflow: hidden;\n\n > li + li {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n }\n\n &__hit-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n width: 100%;\n min-height: var(--spacing-32);\n padding: var(--spacing-4) var(--spacing-8);\n text-align: left;\n\n &:is(button) {\n cursor: pointer;\n transition: background-color 0.2s ease;\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n }\n\n &__hit-icon {\n flex-shrink: 0;\n color: var(--color-word-3);\n }\n\n // Grows to fill the row so meta, relevance and chevron stay aligned;\n // the title inside only shrinks, keeping the link icon next to the\n // title text instead of pushed to the right edge.\n &__hit-heading {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n flex: 1 1 0;\n min-width: 0;\n }\n\n &__hit-title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--color-word-2);\n }\n\n &__hit-meta {\n color: var(--color-word-4);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n &__relevance {\n flex-shrink: 0;\n width: var(--spacing-40);\n height: var(--spacing-4);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface-3);\n overflow: hidden;\n }\n\n &__relevance-fill {\n display: block;\n height: 100%;\n border-radius: inherit;\n background-color: var(--chatbot-main-color, var(--color-word-4));\n }\n\n &__link {\n flex-shrink: 0;\n display: inline-flex;\n color: var(--color-word-4);\n\n &:hover {\n color: var(--color-word-1);\n }\n }\n\n &__hit-body,\n &__passage-content {\n padding: var(--spacing-8);\n max-height: var(--spacing-160);\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--color-word-3);\n }\n\n &__hit-body {\n border-top: var(--spacing-px) solid var(--color-surface-3);\n }\n\n &__passage {\n border: var(--spacing-px) solid var(--color-surface-3);\n border-radius: var(--rounded);\n background-color: var(--color-surface-1);\n overflow: hidden;\n }\n\n &__passage-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n min-height: var(--spacing-32);\n padding: var(--spacing-4) var(--spacing-8);\n border-bottom: var(--spacing-px) solid var(--color-surface-3);\n }\n }\n</style>\n"],"mappings":"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"}
|