@8wave/ai-elements 0.110.0 → 0.111.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +1 -0
  2. package/dist/_chunks/{Agent-Su_LDF9_.js → Agent-tE7ZJPxG.js} +80 -78
  3. package/dist/_chunks/Agent-tE7ZJPxG.js.map +1 -0
  4. package/dist/_chunks/{Document-DecVclEe.js → Document-Bmx6774B.js} +33 -21
  5. package/dist/_chunks/Document-Bmx6774B.js.map +1 -0
  6. package/dist/_chunks/Media-Cft7Pwrh.js +108 -0
  7. package/dist/_chunks/Media-Cft7Pwrh.js.map +1 -0
  8. package/dist/_chunks/{PkLazyImg-ClL4nr95.js → PkLazyImg-Dbj2dDXA.js} +2 -2
  9. package/dist/_chunks/{PkLazyImg-ClL4nr95.js.map → PkLazyImg-Dbj2dDXA.js.map} +1 -1
  10. package/dist/_chunks/{PkStreamingMarkdown-CwqsJnJJ.js → PkStreamingMarkdown-CNI8aV8t.js} +636 -427
  11. package/dist/_chunks/PkStreamingMarkdown-CNI8aV8t.js.map +1 -0
  12. package/dist/_chunks/{PkToolRequestGeolocation-CEVpl50q.js → PkToolRequestGeolocation-Bzy4IYSp.js} +4 -4
  13. package/dist/_chunks/PkToolRequestGeolocation-Bzy4IYSp.js.map +1 -0
  14. package/dist/_chunks/{PkToolRequestOAuthConnection-CncDU-Kl.js → PkToolRequestOAuthConnection-13CQH07t.js} +5 -5
  15. package/dist/_chunks/PkToolRequestOAuthConnection-13CQH07t.js.map +1 -0
  16. package/dist/_chunks/{PkToolShowArtifact-M6jlSmyJ.js → PkToolShowArtifact-CGbHDEie.js} +4 -4
  17. package/dist/_chunks/{PkToolShowArtifact-M6jlSmyJ.js.map → PkToolShowArtifact-CGbHDEie.js.map} +1 -1
  18. package/dist/_chunks/{PkToolShowCalendarEvent-C41JgOUA.js → PkToolShowCalendarEvent-DIFX_rUz.js} +3 -3
  19. package/dist/_chunks/{PkToolShowCalendarEvent-C41JgOUA.js.map → PkToolShowCalendarEvent-DIFX_rUz.js.map} +1 -1
  20. package/dist/_chunks/{PkToolShowComparison-DbbUD9pg.js → PkToolShowComparison-CWLFWT9h.js} +7 -7
  21. package/dist/_chunks/PkToolShowComparison-CWLFWT9h.js.map +1 -0
  22. package/dist/_chunks/{PkToolShowContactForm-D_dW59Dl.js → PkToolShowContactForm-CYxgatUA.js} +5 -5
  23. package/dist/_chunks/PkToolShowContactForm-CYxgatUA.js.map +1 -0
  24. package/dist/_chunks/{PkToolShowDiagram-Hy9qh68O.js → PkToolShowDiagram-Dx1Ba0PG.js} +4 -4
  25. package/dist/_chunks/PkToolShowDiagram-Dx1Ba0PG.js.map +1 -0
  26. package/dist/_chunks/{PkToolShowEmail-Dn3JHYIR.js → PkToolShowEmail-DnFFgzom.js} +3 -3
  27. package/dist/_chunks/{PkToolShowEmail-Dn3JHYIR.js.map → PkToolShowEmail-DnFFgzom.js.map} +1 -1
  28. package/dist/_chunks/{PkToolShowForm-DbJHMDbt.js → PkToolShowForm-B6x3Lp2i.js} +7 -8
  29. package/dist/_chunks/PkToolShowForm-B6x3Lp2i.js.map +1 -0
  30. package/dist/_chunks/{PkToolShowImageGallery-C1lzlO0y.js → PkToolShowImageGallery-D99N_Plf.js} +2 -2
  31. package/dist/_chunks/{PkToolShowImageGallery-C1lzlO0y.js.map → PkToolShowImageGallery-D99N_Plf.js.map} +1 -1
  32. package/dist/_chunks/{PkToolShowLocation-CKhPxKFA.js → PkToolShowLocation-AfnrtFVJ.js} +3 -3
  33. package/dist/_chunks/{PkToolShowLocation-CKhPxKFA.js.map → PkToolShowLocation-AfnrtFVJ.js.map} +1 -1
  34. package/dist/_chunks/{PkToolShowMessage-U9CCU7eG.js → PkToolShowMessage-DUo2c-im.js} +3 -3
  35. package/dist/_chunks/{PkToolShowMessage-U9CCU7eG.js.map → PkToolShowMessage-DUo2c-im.js.map} +1 -1
  36. package/dist/_chunks/{PkToolShowProductList-UeiRMLHu.js → PkToolShowProductList-BN6Qk1_y.js} +3 -3
  37. package/dist/_chunks/{PkToolShowProductList-UeiRMLHu.js.map → PkToolShowProductList-BN6Qk1_y.js.map} +1 -1
  38. package/dist/_chunks/{PkToolShowQrCode-CypLrfh5.js → PkToolShowQrCode-BLqeBecX.js} +3 -3
  39. package/dist/_chunks/{PkToolShowQrCode-CypLrfh5.js.map → PkToolShowQrCode-BLqeBecX.js.map} +1 -1
  40. package/dist/_chunks/{PkToolShowSources-B69JmwbZ.js → PkToolShowSources-DpZb5FUP.js} +5 -5
  41. package/dist/_chunks/PkToolShowSources-DpZb5FUP.js.map +1 -0
  42. package/dist/_chunks/{PkToolShowSuggestedReply-CKxSH0Y9.js → PkToolShowSuggestedReply-C10nNgBs.js} +3 -3
  43. package/dist/_chunks/{PkToolShowSuggestedReply-CKxSH0Y9.js.map → PkToolShowSuggestedReply-C10nNgBs.js.map} +1 -1
  44. package/dist/_chunks/{PkToolShowVideo-BgKxdBcC.js → PkToolShowVideo-S9b4QHUj.js} +2 -2
  45. package/dist/_chunks/{PkToolShowVideo-BgKxdBcC.js.map → PkToolShowVideo-S9b4QHUj.js.map} +1 -1
  46. package/dist/_chunks/{PkToolShowWeather-LPd3tC77.js → PkToolShowWeather-CqhG430w.js} +2 -2
  47. package/dist/_chunks/PkToolShowWeather-CqhG430w.js.map +1 -0
  48. package/dist/_chunks/{PkToolShowWebPages-ep_erFF0.js → PkToolShowWebPages-txAZi_iG.js} +3 -3
  49. package/dist/_chunks/{PkToolShowWebPages-ep_erFF0.js.map → PkToolShowWebPages-txAZi_iG.js.map} +1 -1
  50. package/dist/_chunks/PkUrl-BPbjZJ1A.js.map +1 -1
  51. package/dist/_chunks/{VvCheckboxGroup.es-Bdu2gFLN.js → VvCheckboxGroup.es-CTrF3pue.js} +2 -2
  52. package/dist/_chunks/{VvCheckboxGroup.es-Bdu2gFLN.js.map → VvCheckboxGroup.es-CTrF3pue.js.map} +1 -1
  53. package/dist/_chunks/{VvCombobox.es-C0wpJIr_.js → VvCombobox.es-BWiXmQ_S.js} +10 -10
  54. package/dist/_chunks/{VvCombobox.es-C0wpJIr_.js.map → VvCombobox.es-BWiXmQ_S.js.map} +1 -1
  55. package/dist/_chunks/{VvInputText.es-Zgtw2YNw.js → VvInputText.es-Bo9I6g8H.js} +6 -6
  56. package/dist/_chunks/{VvInputText.es-Zgtw2YNw.js.map → VvInputText.es-Bo9I6g8H.js.map} +1 -1
  57. package/dist/_chunks/{VvRadioGroup.es-DZLL19SC.js → VvRadioGroup.es-9ooHCuik.js} +2 -2
  58. package/dist/_chunks/{VvRadioGroup.es-DZLL19SC.js.map → VvRadioGroup.es-9ooHCuik.js.map} +1 -1
  59. package/dist/_chunks/{VvSelect.es-ovnZRHG5.js → VvSelect.es-DG0VuWtP.js} +2 -2
  60. package/dist/_chunks/{VvSelect.es-ovnZRHG5.js.map → VvSelect.es-DG0VuWtP.js.map} +1 -1
  61. package/dist/_chunks/{VvTextarea.es-DbP5Lt2T.js → VvTextarea.es-Dg3pxdZy.js} +6 -6
  62. package/dist/_chunks/{VvTextarea.es-DbP5Lt2T.js.map → VvTextarea.es-Dg3pxdZy.js.map} +1 -1
  63. package/dist/_chunks/{browser-NgY6LXg-.js → browser-B0Xnbqi7.js} +726 -751
  64. package/dist/_chunks/browser-B0Xnbqi7.js.map +1 -0
  65. package/dist/_chunks/{dist-n5iFNYf-.js → dist-QdjZA6MJ.js} +143 -133
  66. package/dist/_chunks/{dist-n5iFNYf-.js.map → dist-QdjZA6MJ.js.map} +1 -1
  67. package/dist/_chunks/{index.es-dtdl3VFG.js → index.es-0YpCa4c5.js} +6 -6
  68. package/dist/_chunks/{index.es-dtdl3VFG.js.map → index.es-0YpCa4c5.js.map} +1 -1
  69. package/dist/_chunks/{index.es-BJnUA_36.js → index.es-CHkJ2TMJ.js} +10 -10
  70. package/dist/_chunks/{index.es-BJnUA_36.js.map → index.es-CHkJ2TMJ.js.map} +1 -1
  71. package/dist/_chunks/text-DbSEK8si.js.map +1 -1
  72. package/dist/ai-elements.es.js +3077 -3134
  73. package/dist/ai-elements.es.js.map +1 -1
  74. package/dist-vue/PkChatbot.js +1 -1
  75. package/dist-vue/PkChatbotEmptyState.js +1 -1
  76. package/dist-vue/PkChatbotError.js +1 -1
  77. package/dist-vue/PkChatbotFeedbackForm.js +1 -1
  78. package/dist-vue/PkChatbotFilePreview.js +1 -1
  79. package/dist-vue/PkChatbotInput.js +1 -1
  80. package/dist-vue/PkChatbotMessages.js +1 -1
  81. package/dist-vue/PkChatbotStepPayload.js +1 -1
  82. package/dist-vue/PkChatbotStepToolDetail.js +1 -1
  83. package/dist-vue/PkChatbotSteps.js +1 -1
  84. package/dist-vue/PkChatbotViewChat.js +1 -1
  85. package/dist-vue/PkChatbotViewConversations.js +1 -1
  86. package/dist-vue/PkChatbotViewFiles.js +1 -1
  87. package/dist-vue/PkChatbotViewProfile.js +1 -1
  88. package/dist-vue/_chunks/{Agent-C8BsTAxk.js → Agent-De2gFNjR.js} +165 -176
  89. package/dist-vue/_chunks/Agent-De2gFNjR.js.map +1 -0
  90. package/dist-vue/_chunks/{Document-CaUfI_SZ.js → Document-8umjeTVS.js} +84 -72
  91. package/dist-vue/_chunks/Document-8umjeTVS.js.map +1 -0
  92. package/dist-vue/_chunks/Media-_HfFLHGD.js.map +1 -1
  93. package/dist-vue/_chunks/{PkChatbot-o7k7d-fj.js → PkChatbot-CMajc7tE.js} +61 -61
  94. package/dist-vue/_chunks/PkChatbot-CMajc7tE.js.map +1 -0
  95. package/dist-vue/_chunks/{PkChatbotEmptyState-BvaeBxhx.js → PkChatbotEmptyState-BtzBYOHJ.js} +2 -2
  96. package/dist-vue/_chunks/{PkChatbotEmptyState-BvaeBxhx.js.map → PkChatbotEmptyState-BtzBYOHJ.js.map} +1 -1
  97. package/dist-vue/_chunks/{PkChatbotError-CBP2mf-n.js → PkChatbotError-gvDpRcEz.js} +21 -19
  98. package/dist-vue/_chunks/PkChatbotError-gvDpRcEz.js.map +1 -0
  99. package/dist-vue/_chunks/{PkChatbotFeedbackForm-DO1QIgmo.js → PkChatbotFeedbackForm-CxLOdwkm.js} +2 -2
  100. package/dist-vue/_chunks/PkChatbotFeedbackForm-CxLOdwkm.js.map +1 -0
  101. package/dist-vue/_chunks/{PkChatbotFilePreview-DO1wZPPj.js → PkChatbotFilePreview-CwiVQDF2.js} +3 -3
  102. package/dist-vue/_chunks/PkChatbotFilePreview-CwiVQDF2.js.map +1 -0
  103. package/dist-vue/_chunks/{PkChatbotInput-D8x52cVm.js → PkChatbotInput-MSpF6SB6.js} +140 -125
  104. package/dist-vue/_chunks/PkChatbotInput-MSpF6SB6.js.map +1 -0
  105. package/dist-vue/_chunks/PkChatbotMessages-Bz5q8EtT.js +776 -0
  106. package/dist-vue/_chunks/PkChatbotMessages-Bz5q8EtT.js.map +1 -0
  107. package/dist-vue/_chunks/{PkChatbotStepPayload-BH6piqOi.js → PkChatbotStepPayload-XW4gT9AZ.js} +2 -2
  108. package/dist-vue/_chunks/{PkChatbotStepPayload-BH6piqOi.js.map → PkChatbotStepPayload-XW4gT9AZ.js.map} +1 -1
  109. package/dist-vue/_chunks/{PkChatbotStepToolDetail-BJ7xUvgi.js → PkChatbotStepToolDetail-C4-K6Glw.js} +3 -3
  110. package/dist-vue/_chunks/{PkChatbotStepToolDetail-BJ7xUvgi.js.map → PkChatbotStepToolDetail-C4-K6Glw.js.map} +1 -1
  111. package/dist-vue/_chunks/PkChatbotSteps-CrqjWpv0.js +190 -0
  112. package/dist-vue/_chunks/PkChatbotSteps-CrqjWpv0.js.map +1 -0
  113. package/dist-vue/_chunks/{PkChatbotViewChat-CNH_glPI.js → PkChatbotViewChat-DXu5ncaU.js} +10 -10
  114. package/dist-vue/_chunks/{PkChatbotViewChat-CNH_glPI.js.map → PkChatbotViewChat-DXu5ncaU.js.map} +1 -1
  115. package/dist-vue/_chunks/{PkChatbotViewConversations-BENvHLZt.js → PkChatbotViewConversations-3LaZPFyw.js} +2 -2
  116. package/dist-vue/_chunks/{PkChatbotViewConversations-BENvHLZt.js.map → PkChatbotViewConversations-3LaZPFyw.js.map} +1 -1
  117. package/dist-vue/_chunks/{PkChatbotViewFiles-qmLESi1V.js → PkChatbotViewFiles-CTqnbsCj.js} +3 -3
  118. package/dist-vue/_chunks/{PkChatbotViewFiles-qmLESi1V.js.map → PkChatbotViewFiles-CTqnbsCj.js.map} +1 -1
  119. package/dist-vue/_chunks/{PkChatbotViewProfile-DzfZnitQ.js → PkChatbotViewProfile-B-Mp1jGQ.js} +2 -2
  120. package/dist-vue/_chunks/{PkChatbotViewProfile-DzfZnitQ.js.map → PkChatbotViewProfile-B-Mp1jGQ.js.map} +1 -1
  121. package/dist-vue/_chunks/PkRelativeTime--1cIphlA.js.map +1 -1
  122. package/dist-vue/_chunks/{PkStreamingMarkdown-D8_LwZMR.js → PkStreamingMarkdown-D0IOaETI.js} +432 -419
  123. package/dist-vue/_chunks/PkStreamingMarkdown-D0IOaETI.js.map +1 -0
  124. package/dist-vue/_chunks/{PkSvgViewer-DPpE9fiz.js → PkSvgViewer-D-0M5bqg.js} +2 -2
  125. package/dist-vue/_chunks/PkSvgViewer-D-0M5bqg.js.map +1 -0
  126. package/dist-vue/_chunks/{PkToolRequestGeolocation-D1ZPmWiB.js → PkToolRequestGeolocation-Q3N3jlO8.js} +3 -3
  127. package/dist-vue/_chunks/PkToolRequestGeolocation-Q3N3jlO8.js.map +1 -0
  128. package/dist-vue/_chunks/{PkToolRequestOAuthConnection-CoJyKEE6.js → PkToolRequestOAuthConnection-lYoTJIF_.js} +4 -4
  129. package/dist-vue/_chunks/PkToolRequestOAuthConnection-lYoTJIF_.js.map +1 -0
  130. package/dist-vue/_chunks/{PkToolShowArtifact-CEXdfOGG.js → PkToolShowArtifact-DHdJuzDi.js} +2 -2
  131. package/dist-vue/_chunks/{PkToolShowArtifact-CEXdfOGG.js.map → PkToolShowArtifact-DHdJuzDi.js.map} +1 -1
  132. package/dist-vue/_chunks/{PkToolShowComparison-DMSjIpCR.js → PkToolShowComparison-BLn4pkno.js} +5 -5
  133. package/dist-vue/_chunks/PkToolShowComparison-BLn4pkno.js.map +1 -0
  134. package/dist-vue/_chunks/{PkToolShowContactForm-DHmzAIVF.js → PkToolShowContactForm-DceSyuX8.js} +3 -3
  135. package/dist-vue/_chunks/PkToolShowContactForm-DceSyuX8.js.map +1 -0
  136. package/dist-vue/_chunks/{PkToolShowDiagram-DuDp5EHd.js → PkToolShowDiagram-pwc0wpNo.js} +4 -4
  137. package/dist-vue/_chunks/PkToolShowDiagram-pwc0wpNo.js.map +1 -0
  138. package/dist-vue/_chunks/{PkToolShowEmail-CavhsPvi.js → PkToolShowEmail-DF__nKpR.js} +2 -2
  139. package/dist-vue/_chunks/{PkToolShowEmail-CavhsPvi.js.map → PkToolShowEmail-DF__nKpR.js.map} +1 -1
  140. package/dist-vue/_chunks/{PkToolShowForm-CO5A_j9L.js → PkToolShowForm-Dx10jOXm.js} +7 -7
  141. package/dist-vue/_chunks/PkToolShowForm-Dx10jOXm.js.map +1 -0
  142. package/dist-vue/_chunks/{PkToolShowImageGallery-V9U3LJfF.js → PkToolShowImageGallery-DudQzBx4.js} +2 -2
  143. package/dist-vue/_chunks/{PkToolShowImageGallery-V9U3LJfF.js.map → PkToolShowImageGallery-DudQzBx4.js.map} +1 -1
  144. package/dist-vue/_chunks/{PkToolShowMessage-DQojxDiV.js → PkToolShowMessage-Ck6VP-gw.js} +2 -2
  145. package/dist-vue/_chunks/{PkToolShowMessage-DQojxDiV.js.map → PkToolShowMessage-Ck6VP-gw.js.map} +1 -1
  146. package/dist-vue/_chunks/{PkToolShowProductList-Cn0yewM3.js → PkToolShowProductList-zfM8cx-l.js} +2 -2
  147. package/dist-vue/_chunks/{PkToolShowProductList-Cn0yewM3.js.map → PkToolShowProductList-zfM8cx-l.js.map} +1 -1
  148. package/dist-vue/_chunks/{PkToolShowSources-BvWOzVkQ.js → PkToolShowSources-Dlaz2Y77.js} +5 -5
  149. package/dist-vue/_chunks/PkToolShowSources-Dlaz2Y77.js.map +1 -0
  150. package/dist-vue/_chunks/{PkToolShowSuggestedReply-DQFOVonE.js → PkToolShowSuggestedReply-DWLkUh2L.js} +2 -2
  151. package/dist-vue/_chunks/{PkToolShowSuggestedReply-DQFOVonE.js.map → PkToolShowSuggestedReply-DWLkUh2L.js.map} +1 -1
  152. package/dist-vue/_chunks/PkToolShowWeather-Bzqalzh8.js.map +1 -1
  153. package/dist-vue/_chunks/{PkToolShowWebPages-Bzkkn9GV.js → PkToolShowWebPages-CosBYLrE.js} +2 -2
  154. package/dist-vue/_chunks/{PkToolShowWebPages-Bzkkn9GV.js.map → PkToolShowWebPages-CosBYLrE.js.map} +1 -1
  155. package/dist-vue/_chunks/{PkUrl-xUBKVJCv.js → PkUrl-B5CNTQj9.js} +2 -2
  156. package/dist-vue/_chunks/PkUrl-B5CNTQj9.js.map +1 -0
  157. package/dist-vue/_chunks/{createChatbotApiClient-CJ_5_3IT.js → createChatbotApiClient-MkaPbGWB.js} +6 -3
  158. package/dist-vue/_chunks/createChatbotApiClient-MkaPbGWB.js.map +1 -0
  159. package/dist-vue/_chunks/{useChatbotStore-CiRAuq7v.js → useChatbotStore-BzfqjiYz.js} +6 -3
  160. package/dist-vue/_chunks/useChatbotStore-BzfqjiYz.js.map +1 -0
  161. package/dist-vue/_chunks/useDialogConfirm-KWz0ohCQ.js.map +1 -1
  162. package/dist-vue/_chunks/{utils-Coqik0XI.js → utils-C1666_6C.js} +58 -43
  163. package/dist-vue/_chunks/utils-C1666_6C.js.map +1 -0
  164. package/dist-vue/api.js +1 -1
  165. package/dist-vue/composables.js +3 -3
  166. package/dist-vue/index.js +3869 -3490
  167. package/dist-vue/index.js.map +1 -1
  168. package/dist-vue/locales.js +36 -8
  169. package/dist-vue/packages/components/src/chat/PkAgentSettingsPanel.d.ts +20 -3
  170. package/dist-vue/packages/components/src/chat/PkChatButton.d.ts +3 -2
  171. package/dist-vue/packages/components/src/chat/PkLanguageModel.d.ts +26 -2
  172. package/dist-vue/packages/components/src/chat/PkLanguageModelCombobox.d.ts +53 -1
  173. package/dist-vue/packages/components/src/chat/PkModelOption.d.ts +19 -0
  174. package/dist-vue/packages/components/src/chat/PkReasoningSettings.d.ts +6 -0
  175. package/dist-vue/packages/components/src/chat/PkStreamingMarkdown.d.ts +6 -0
  176. package/dist-vue/packages/components/src/chat/PkStreamingMarkdownAutoscroll.d.ts +1 -0
  177. package/dist-vue/packages/components/src/chat/constants.d.ts +5 -18
  178. package/dist-vue/packages/components/src/chat/index.d.ts +3 -1
  179. package/dist-vue/packages/components/src/chat/modelCatalog.d.ts +46 -0
  180. package/dist-vue/packages/components/src/chat/utils.d.ts +16 -0
  181. package/dist-vue/packages/components/src/table/PkTableSortable.d.ts +13 -1
  182. package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +18 -0
  183. package/dist-vue/packages/composable/src/useChatbotError.d.ts +2 -0
  184. package/dist-vue/packages/models/src/schema/Agent.d.ts +70 -10
  185. package/dist-vue/packages/models/src/schema/Chat.d.ts +31 -0
  186. package/dist-vue/packages/models/src/schema/ModelProvider.d.ts +316 -0
  187. package/dist-vue/packages/models/src/schema/Organization.d.ts +142 -2
  188. package/dist-vue/packages/models/src/schema/ReasoningChat.d.ts +20 -0
  189. package/dist-vue/packages/models/src/schema/SubAgent.d.ts +20 -0
  190. package/dist-vue/packages/models/src/schema/UserOverview.d.ts +41 -0
  191. package/dist-vue/packages/models/src/schema/constants.d.ts +56 -91
  192. package/dist-vue/packages/models/src/schema/index.d.ts +1 -0
  193. package/dist-vue/style.css +1 -1
  194. package/package.json +1 -1
  195. package/dist/_chunks/Agent-Su_LDF9_.js.map +0 -1
  196. package/dist/_chunks/Document-DecVclEe.js.map +0 -1
  197. package/dist/_chunks/Media-C6SOQfJZ.js +0 -122
  198. package/dist/_chunks/Media-C6SOQfJZ.js.map +0 -1
  199. package/dist/_chunks/PkStreamingMarkdown-CwqsJnJJ.js.map +0 -1
  200. package/dist/_chunks/PkToolRequestGeolocation-CEVpl50q.js.map +0 -1
  201. package/dist/_chunks/PkToolRequestOAuthConnection-CncDU-Kl.js.map +0 -1
  202. package/dist/_chunks/PkToolShowComparison-DbbUD9pg.js.map +0 -1
  203. package/dist/_chunks/PkToolShowContactForm-D_dW59Dl.js.map +0 -1
  204. package/dist/_chunks/PkToolShowDiagram-Hy9qh68O.js.map +0 -1
  205. package/dist/_chunks/PkToolShowForm-DbJHMDbt.js.map +0 -1
  206. package/dist/_chunks/PkToolShowSources-B69JmwbZ.js.map +0 -1
  207. package/dist/_chunks/PkToolShowWeather-LPd3tC77.js.map +0 -1
  208. package/dist/_chunks/browser-NgY6LXg-.js.map +0 -1
  209. package/dist/_chunks/utils-YJ0ZOLDG.js +0 -185
  210. package/dist/_chunks/utils-YJ0ZOLDG.js.map +0 -1
  211. package/dist-vue/_chunks/Agent-C8BsTAxk.js.map +0 -1
  212. package/dist-vue/_chunks/Document-CaUfI_SZ.js.map +0 -1
  213. package/dist-vue/_chunks/PkChatbot-o7k7d-fj.js.map +0 -1
  214. package/dist-vue/_chunks/PkChatbotError-CBP2mf-n.js.map +0 -1
  215. package/dist-vue/_chunks/PkChatbotFeedbackForm-DO1QIgmo.js.map +0 -1
  216. package/dist-vue/_chunks/PkChatbotFilePreview-DO1wZPPj.js.map +0 -1
  217. package/dist-vue/_chunks/PkChatbotInput-D8x52cVm.js.map +0 -1
  218. package/dist-vue/_chunks/PkChatbotMessages-BFlU046I.js +0 -771
  219. package/dist-vue/_chunks/PkChatbotMessages-BFlU046I.js.map +0 -1
  220. package/dist-vue/_chunks/PkChatbotSteps-DtV1beDc.js +0 -180
  221. package/dist-vue/_chunks/PkChatbotSteps-DtV1beDc.js.map +0 -1
  222. package/dist-vue/_chunks/PkStreamingMarkdown-D8_LwZMR.js.map +0 -1
  223. package/dist-vue/_chunks/PkSvgViewer-DPpE9fiz.js.map +0 -1
  224. package/dist-vue/_chunks/PkToolRequestGeolocation-D1ZPmWiB.js.map +0 -1
  225. package/dist-vue/_chunks/PkToolRequestOAuthConnection-CoJyKEE6.js.map +0 -1
  226. package/dist-vue/_chunks/PkToolShowComparison-DMSjIpCR.js.map +0 -1
  227. package/dist-vue/_chunks/PkToolShowContactForm-DHmzAIVF.js.map +0 -1
  228. package/dist-vue/_chunks/PkToolShowDiagram-DuDp5EHd.js.map +0 -1
  229. package/dist-vue/_chunks/PkToolShowForm-CO5A_j9L.js.map +0 -1
  230. package/dist-vue/_chunks/PkToolShowSources-BvWOzVkQ.js.map +0 -1
  231. package/dist-vue/_chunks/PkUrl-xUBKVJCv.js.map +0 -1
  232. package/dist-vue/_chunks/createChatbotApiClient-CJ_5_3IT.js.map +0 -1
  233. package/dist-vue/_chunks/useChatbotStore-CiRAuq7v.js.map +0 -1
  234. package/dist-vue/_chunks/utils-Coqik0XI.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkToolRequestOAuthConnection-CoJyKEE6.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolRequestOAuthConnection.vue","../../../../packages/components/src/chat/PkToolRequestOAuthConnection.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { isVoiceRenderAck } from 'models'\n\n const props = defineProps<{\n part: unknown\n resolveConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; authorizeUrl: string }>\n checkConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; connected: boolean }>\n }>()\n\n const emit = defineEmits<{\n connected: [mcpServerId: string]\n }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n /** How often the connection is re-read while the sign-in window is out. */\n const POLL_INTERVAL_MS = 2000\n /**\n * How long we keep asking. The only end to a sign-in nobody finishes:\n * `popup.closed` cannot be that signal, because the browser disowns the\n * handle when the provider severs the opener (that is what\n * `Cross-Origin-Opener-Policy` does), so it reads as closed from the first\n * check of the very flow this watch exists for. Long enough for a password\n * manager and a second factor.\n */\n const POLL_TIMEOUT_MS = 5 * 60 * 1000\n\n const toolPart = computed(() => {\n return props.part as {\n state?: string\n toolCallId?: string\n input?: { serverName: string }\n output?: unknown\n }\n })\n\n const state = computed(() => toolPart.value.state ?? 'unknown')\n const serverName = computed(() => toolPart.value.input?.serverName)\n\n /**\n * A card drawn during a CALL arrives already \"answered\": the turn could\n * not wait for the connection, so the backend closed the tool with a note\n * saying the card was drawn (see `isVoiceRenderAck`, same rule as the\n * forms). That note is not a connection, and without this the card\n * rendered its inert fallback: the caller was told to connect the account\n * and given nothing to click (measured 2026-08-07).\n */\n const isRenderAck = computed(() => isVoiceRenderAck(toolPart.value.output))\n\n /** The output this card wrote itself once the connection went through. */\n const isConnectedByOutput = computed(\n () =>\n (toolPart.value.output as { connected?: unknown } | undefined)\n ?.connected === true,\n )\n\n const isAwaitingConnection = computed(\n () => state.value === 'input-available' || isRenderAck.value,\n )\n\n const mcpServerId = ref<string | null>(null)\n /**\n * Set once THIS card's sign-in went through, and never from a plain read of\n * the connection: a card still asking is the turn saying it could not use\n * that integration, and a token row can outlive its usefulness (a refresh\n * that failed transiently keeps the row, so the turn sees no usable token\n * while a status read still sees one). Trusting the read there would have\n * left the card claiming success, with no button, on a turn still waiting.\n * It doubles as the guard that resumes the turn exactly once, whichever\n * channel got there first.\n */\n const hasConnected = ref(false)\n /** A sign-in window we opened is still out there. */\n const isSigningIn = ref(false)\n const oauthError = ref<string | null>(null)\n\n const isDone = computed(\n () => hasConnected.value || isConnectedByOutput.value,\n )\n const canConnect = computed(\n () => Boolean(serverName.value) && Boolean(props.resolveConnection),\n )\n\n /**\n * Whether the server says this user has connected, asked without minting\n * anything: no OAuth `state` is burned by looking.\n */\n async function isConnectedNow(): Promise<boolean> {\n if (!serverName.value || !props.checkConnection) {\n return false\n }\n try {\n const status = await props.checkConnection(serverName.value)\n mcpServerId.value = status.mcpServerId\n return status.connected\n } catch {\n // Not knowing is not an error to show: the sign-in window is still\n // out there and the next read may well answer.\n return false\n }\n }\n\n let pollTimer: ReturnType<typeof setTimeout> | undefined\n /**\n * Which sign-in a running tick belongs to. A tick suspended on its read\n * outlives the attempt that scheduled it: without this it woke up inside\n * the NEXT attempt, restarted its own chain over that one's timer and then\n * stopped it on its own older deadline.\n */\n let signInAttempt = 0\n /** The window this attempt opened, the only sender we accept a report from. */\n let signInWindow: Window | null = null\n\n function stopWatching() {\n if (pollTimer !== undefined) {\n clearTimeout(pollTimer)\n pollTimer = undefined\n }\n signInAttempt += 1\n signInWindow = null\n isSigningIn.value = false\n }\n\n function settleConnected(connectedServerId: string) {\n stopWatching()\n if (hasConnected.value) {\n return\n }\n hasConnected.value = true\n mcpServerId.value = connectedServerId\n oauthError.value = null\n emit('connected', connectedServerId)\n }\n\n /**\n * Watches the connection from THIS window while the sign-in window is away.\n *\n * The callback page reports through `window.opener`, and a provider that\n * sends `Cross-Origin-Opener-Policy` anywhere in its redirect chain takes\n * that opener away: the connection then succeeds with nobody listening, the\n * card stays on its button and the suspended turn is never resumed\n * (reported 2026-08-27). This channel never goes through that window, so it\n * survives whatever the provider does to it. Chained rather than periodic:\n * a read slower than the interval would otherwise overlap the next one.\n */\n function watchForConnection(popup: Window) {\n stopWatching()\n const attempt = signInAttempt\n signInWindow = popup\n isSigningIn.value = true\n const deadline = Date.now() + POLL_TIMEOUT_MS\n const tick = async () => {\n if (attempt !== signInAttempt || Date.now() > deadline) {\n if (attempt === signInAttempt) {\n stopWatching()\n }\n return\n }\n const connected = await isConnectedNow()\n if (attempt !== signInAttempt) {\n return\n }\n if (connected && mcpServerId.value) {\n settleConnected(mcpServerId.value)\n return\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n\n /**\n * The callback page's own report, when the browser let it through: faster\n * than the poll, and the only channel carrying the failure reason.\n *\n * Accepted only from the window this card opened, and only while that\n * sign-in is out: the listener stays up for as long as the card is on\n * screen, and nothing else with a handle on this page may resume a turn by\n * posting a message at it.\n */\n function onMessage(event: MessageEvent) {\n if (\n event.data?.type !== 'mcp-oauth-complete' ||\n !isSigningIn.value ||\n event.source !== signInWindow\n ) {\n return\n }\n const reportedServerId = event.data.mcpServerId as string | undefined\n if (\n reportedServerId &&\n mcpServerId.value &&\n reportedServerId !== mcpServerId.value\n ) {\n return\n }\n if (!event.data.success) {\n // A failure carries no server id: the callback never got that far.\n stopWatching()\n oauthError.value = event.data.error ?? 'Unknown error'\n return\n }\n const connectedServerId = reportedServerId ?? mcpServerId.value\n if (connectedServerId) {\n settleConnected(connectedServerId)\n }\n }\n\n /**\n * Opens the provider's sign-in, on an authorize URL minted NOW.\n *\n * The URL carries an OAuth `state` that lives ten minutes server-side.\n * Minted when the card rendered, it was routinely expired by the time\n * anyone clicked, and the callback then failed on a sign-in the user had\n * just completed. The window is opened blank first, synchronously, or the\n * popup blocker kills it while we wait for the URL.\n */\n async function openAuthWindow() {\n if (\n !serverName.value ||\n !props.resolveConnection ||\n isSigningIn.value\n ) {\n return\n }\n oauthError.value = null\n const popup = window.open(\n '',\n 'mcp_oauth',\n 'width=600,height=700,menubar=no,toolbar=no',\n )\n if (!popup) {\n oauthError.value = $t('auth.popupBlocked')\n return\n }\n watchForConnection(popup)\n try {\n const connection = await props.resolveConnection(serverName.value)\n mcpServerId.value = connection.mcpServerId\n popup.location.href = connection.authorizeUrl\n } catch (err) {\n stopWatching()\n popup.close()\n oauthError.value =\n err instanceof Error ? err.message : 'Failed to resolve OAuth'\n }\n }\n\n // For the card's whole life, not just between the click and the answer: a\n // card that re-renders while the sign-in window is away used to lose the\n // listener with it, and the completion landed on nobody.\n onMounted(() => window.addEventListener('message', onMessage))\n onUnmounted(() => {\n window.removeEventListener('message', onMessage)\n stopWatching()\n })\n</script>\n\n<template>\n <!-- The same frame and header as the other tool cards: this was the only\n one drawing itself as a tinted box (DESIGN.md). -->\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface text-12\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:link-m\" class=\"text-16 shrink-0\" />\n <strong class=\"truncate\">{{ serverName }}</strong>\n </div>\n <div class=\"p-sm\">\n <!-- Connected: just now, or by this card on an earlier visit -->\n <template v-if=\"isDone\">\n <div class=\"flex items-center gap-8\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-word-3\" />\n <strong class=\"font-medium text-word-1\">\n {{\n $t('messagePart.requestOAuthConnectionSuccess', {\n name: serverName,\n })\n }}\n </strong>\n </div>\n </template>\n\n <!-- Signing in: the window is out and we are watching for it -->\n <template v-else-if=\"isSigningIn\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"line-md:loading-loop\" />\n <span class=\"text-10\">{{\n $t('messagePart.requestOAuthConnection')\n }}</span>\n </div>\n </template>\n\n <!-- OAuth error -->\n <template v-else-if=\"oauthError\">\n <div class=\"flex flex-col gap-xs\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:close-line\" class=\"text-danger\" />\n <strong class=\"text-10\">\n {{ oauthError }}\n </strong>\n </div>\n <VvButton\n v-if=\"canConnect\"\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </div>\n </template>\n\n <!-- Waiting on the user: show the connect button -->\n <template v-else-if=\"isAwaitingConnection && canConnect\">\n <div class=\"flex items-center gap-6 text-word-3 mb-xs\">\n <span>\n {{\n $t('messagePart.requestOAuthConnectionPrompt', {\n name: serverName,\n })\n }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </template>\n <!-- Fallback for unknown state -->\n <template v-else>\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:link-m\" />\n <span class=\"text-10\">\n {{\n $t('messagePart.requestOAuthConnectionInfo', {\n name: serverName,\n })\n }}\n </span>\n </div>\n </template>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { isVoiceRenderAck } from 'models'\n\n const props = defineProps<{\n part: unknown\n resolveConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; authorizeUrl: string }>\n checkConnection?: (\n serverName: string,\n ) => Promise<{ mcpServerId: string; connected: boolean }>\n }>()\n\n const emit = defineEmits<{\n connected: [mcpServerId: string]\n }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n /** How often the connection is re-read while the sign-in window is out. */\n const POLL_INTERVAL_MS = 2000\n /**\n * How long we keep asking. The only end to a sign-in nobody finishes:\n * `popup.closed` cannot be that signal, because the browser disowns the\n * handle when the provider severs the opener (that is what\n * `Cross-Origin-Opener-Policy` does), so it reads as closed from the first\n * check of the very flow this watch exists for. Long enough for a password\n * manager and a second factor.\n */\n const POLL_TIMEOUT_MS = 5 * 60 * 1000\n\n const toolPart = computed(() => {\n return props.part as {\n state?: string\n toolCallId?: string\n input?: { serverName: string }\n output?: unknown\n }\n })\n\n const state = computed(() => toolPart.value.state ?? 'unknown')\n const serverName = computed(() => toolPart.value.input?.serverName)\n\n /**\n * A card drawn during a CALL arrives already \"answered\": the turn could\n * not wait for the connection, so the backend closed the tool with a note\n * saying the card was drawn (see `isVoiceRenderAck`, same rule as the\n * forms). That note is not a connection, and without this the card\n * rendered its inert fallback: the caller was told to connect the account\n * and given nothing to click (measured 2026-08-07).\n */\n const isRenderAck = computed(() => isVoiceRenderAck(toolPart.value.output))\n\n /** The output this card wrote itself once the connection went through. */\n const isConnectedByOutput = computed(\n () =>\n (toolPart.value.output as { connected?: unknown } | undefined)\n ?.connected === true,\n )\n\n const isAwaitingConnection = computed(\n () => state.value === 'input-available' || isRenderAck.value,\n )\n\n const mcpServerId = ref<string | null>(null)\n /**\n * Set once THIS card's sign-in went through, and never from a plain read of\n * the connection: a card still asking is the turn saying it could not use\n * that integration, and a token row can outlive its usefulness (a refresh\n * that failed transiently keeps the row, so the turn sees no usable token\n * while a status read still sees one). Trusting the read there would have\n * left the card claiming success, with no button, on a turn still waiting.\n * It doubles as the guard that resumes the turn exactly once, whichever\n * channel got there first.\n */\n const hasConnected = ref(false)\n /** A sign-in window we opened is still out there. */\n const isSigningIn = ref(false)\n const oauthError = ref<string | null>(null)\n\n const isDone = computed(\n () => hasConnected.value || isConnectedByOutput.value,\n )\n const canConnect = computed(\n () => Boolean(serverName.value) && Boolean(props.resolveConnection),\n )\n\n /**\n * Whether the server says this user has connected, asked without minting\n * anything: no OAuth `state` is burned by looking.\n */\n async function isConnectedNow(): Promise<boolean> {\n if (!serverName.value || !props.checkConnection) {\n return false\n }\n try {\n const status = await props.checkConnection(serverName.value)\n mcpServerId.value = status.mcpServerId\n return status.connected\n } catch {\n // Not knowing is not an error to show: the sign-in window is still\n // out there and the next read may well answer.\n return false\n }\n }\n\n let pollTimer: ReturnType<typeof setTimeout> | undefined\n /**\n * Which sign-in a running tick belongs to. A tick suspended on its read\n * outlives the attempt that scheduled it: without this it woke up inside\n * the NEXT attempt, restarted its own chain over that one's timer and then\n * stopped it on its own older deadline.\n */\n let signInAttempt = 0\n /** The window this attempt opened, the only sender we accept a report from. */\n let signInWindow: Window | null = null\n\n function stopWatching() {\n if (pollTimer !== undefined) {\n clearTimeout(pollTimer)\n pollTimer = undefined\n }\n signInAttempt += 1\n signInWindow = null\n isSigningIn.value = false\n }\n\n function settleConnected(connectedServerId: string) {\n stopWatching()\n if (hasConnected.value) {\n return\n }\n hasConnected.value = true\n mcpServerId.value = connectedServerId\n oauthError.value = null\n emit('connected', connectedServerId)\n }\n\n /**\n * Watches the connection from THIS window while the sign-in window is away.\n *\n * The callback page reports through `window.opener`, and a provider that\n * sends `Cross-Origin-Opener-Policy` anywhere in its redirect chain takes\n * that opener away: the connection then succeeds with nobody listening, the\n * card stays on its button and the suspended turn is never resumed\n * (reported 2026-08-27). This channel never goes through that window, so it\n * survives whatever the provider does to it. Chained rather than periodic:\n * a read slower than the interval would otherwise overlap the next one.\n */\n function watchForConnection(popup: Window) {\n stopWatching()\n const attempt = signInAttempt\n signInWindow = popup\n isSigningIn.value = true\n const deadline = Date.now() + POLL_TIMEOUT_MS\n const tick = async () => {\n if (attempt !== signInAttempt || Date.now() > deadline) {\n if (attempt === signInAttempt) {\n stopWatching()\n }\n return\n }\n const connected = await isConnectedNow()\n if (attempt !== signInAttempt) {\n return\n }\n if (connected && mcpServerId.value) {\n settleConnected(mcpServerId.value)\n return\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n pollTimer = setTimeout(tick, POLL_INTERVAL_MS)\n }\n\n /**\n * The callback page's own report, when the browser let it through: faster\n * than the poll, and the only channel carrying the failure reason.\n *\n * Accepted only from the window this card opened, and only while that\n * sign-in is out: the listener stays up for as long as the card is on\n * screen, and nothing else with a handle on this page may resume a turn by\n * posting a message at it.\n */\n function onMessage(event: MessageEvent) {\n if (\n event.data?.type !== 'mcp-oauth-complete' ||\n !isSigningIn.value ||\n event.source !== signInWindow\n ) {\n return\n }\n const reportedServerId = event.data.mcpServerId as string | undefined\n if (\n reportedServerId &&\n mcpServerId.value &&\n reportedServerId !== mcpServerId.value\n ) {\n return\n }\n if (!event.data.success) {\n // A failure carries no server id: the callback never got that far.\n stopWatching()\n oauthError.value = event.data.error ?? 'Unknown error'\n return\n }\n const connectedServerId = reportedServerId ?? mcpServerId.value\n if (connectedServerId) {\n settleConnected(connectedServerId)\n }\n }\n\n /**\n * Opens the provider's sign-in, on an authorize URL minted NOW.\n *\n * The URL carries an OAuth `state` that lives ten minutes server-side.\n * Minted when the card rendered, it was routinely expired by the time\n * anyone clicked, and the callback then failed on a sign-in the user had\n * just completed. The window is opened blank first, synchronously, or the\n * popup blocker kills it while we wait for the URL.\n */\n async function openAuthWindow() {\n if (\n !serverName.value ||\n !props.resolveConnection ||\n isSigningIn.value\n ) {\n return\n }\n oauthError.value = null\n const popup = window.open(\n '',\n 'mcp_oauth',\n 'width=600,height=700,menubar=no,toolbar=no',\n )\n if (!popup) {\n oauthError.value = $t('auth.popupBlocked')\n return\n }\n watchForConnection(popup)\n try {\n const connection = await props.resolveConnection(serverName.value)\n mcpServerId.value = connection.mcpServerId\n popup.location.href = connection.authorizeUrl\n } catch (err) {\n stopWatching()\n popup.close()\n oauthError.value =\n err instanceof Error ? err.message : 'Failed to resolve OAuth'\n }\n }\n\n // For the card's whole life, not just between the click and the answer: a\n // card that re-renders while the sign-in window is away used to lose the\n // listener with it, and the completion landed on nobody.\n onMounted(() => window.addEventListener('message', onMessage))\n onUnmounted(() => {\n window.removeEventListener('message', onMessage)\n stopWatching()\n })\n</script>\n\n<template>\n <!-- The same frame and header as the other tool cards: this was the only\n one drawing itself as a tinted box (DESIGN.md). -->\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface text-12\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:link-m\" class=\"text-16 shrink-0\" />\n <strong class=\"truncate\">{{ serverName }}</strong>\n </div>\n <div class=\"p-sm\">\n <!-- Connected: just now, or by this card on an earlier visit -->\n <template v-if=\"isDone\">\n <div class=\"flex items-center gap-8\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-word-3\" />\n <strong class=\"font-medium text-word-1\">\n {{\n $t('messagePart.requestOAuthConnectionSuccess', {\n name: serverName,\n })\n }}\n </strong>\n </div>\n </template>\n\n <!-- Signing in: the window is out and we are watching for it -->\n <template v-else-if=\"isSigningIn\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"line-md:loading-loop\" />\n <span class=\"text-10\">{{\n $t('messagePart.requestOAuthConnection')\n }}</span>\n </div>\n </template>\n\n <!-- OAuth error -->\n <template v-else-if=\"oauthError\">\n <div class=\"flex flex-col gap-xs\">\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:close-line\" class=\"text-danger\" />\n <strong class=\"text-10\">\n {{ oauthError }}\n </strong>\n </div>\n <VvButton\n v-if=\"canConnect\"\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </div>\n </template>\n\n <!-- Waiting on the user: show the connect button -->\n <template v-else-if=\"isAwaitingConnection && canConnect\">\n <div class=\"flex items-center gap-6 text-word-3 mb-xs\">\n <span>\n {{\n $t('messagePart.requestOAuthConnectionPrompt', {\n name: serverName,\n })\n }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary-small\"\n icon=\"ri:external-link-line\"\n :label=\"\n $t('messagePart.requestOAuthConnectionButton', {\n name: serverName,\n })\n \"\n @click=\"openAuthWindow\" />\n </template>\n <!-- Fallback for unknown state -->\n <template v-else>\n <div class=\"flex items-center gap-8 text-word-3\">\n <VvIcon name=\"ri:link-m\" />\n <span class=\"text-10\">\n {{\n $t('messagePart.requestOAuthConnectionInfo', {\n name: serverName,\n })\n }}\n </span>\n </div>\n </template>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkToolShowComparison-DMSjIpCR.js","names":["$t"],"sources":["../../../../packages/components/src/chat/PkToolShowComparison.vue","../../../../packages/components/src/chat/PkToolShowComparison.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <div class=\"overflow-x-auto\">\n <table class=\"w-full text-12 border-collapse bg-surface\">\n <thead>\n <tr>\n <th\n class=\"text-left text-word-3 font-medium p-8 border-b border-surface-3 min-w-32\"></th>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n class=\"text-center text-word-1 p-8 border-b border-surface-3 min-w-24\"\n :class=\"\n item.highlighted\n ? 'font-semibold bg-surface-1'\n : 'font-medium'\n \">\n {{ item.name }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(feature, fIdx) in features\"\n :key=\"feature\"\n :class=\"{ 'bg-surface-1': fIdx % 2 !== 0 }\">\n <td\n class=\"text-word-3 font-medium p-8 whitespace-nowrap\"\n :class=\"{\n 'border-b border-surface-3':\n fIdx !== features.length - 1,\n }\">\n {{ feature.replace(/_/g, ' ') }}\n </td>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n class=\"text-center p-8 text-word-2\"\n :class=\"{\n 'border-b border-surface-3':\n fIdx !== features.length - 1,\n 'bg-surface-1': item.highlighted,\n }\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <div class=\"overflow-x-auto\">\n <table class=\"w-full text-12 border-collapse bg-surface\">\n <thead>\n <tr>\n <th\n class=\"text-left text-word-3 font-medium p-8 border-b border-surface-3 min-w-32\"></th>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n class=\"text-center text-word-1 p-8 border-b border-surface-3 min-w-24\"\n :class=\"\n item.highlighted\n ? 'font-semibold bg-surface-1'\n : 'font-medium'\n \">\n {{ item.name }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"(feature, fIdx) in features\"\n :key=\"feature\"\n :class=\"{ 'bg-surface-1': fIdx % 2 !== 0 }\">\n <td\n class=\"text-word-3 font-medium p-8 whitespace-nowrap\"\n :class=\"{\n 'border-b border-surface-3':\n fIdx !== features.length - 1,\n }\">\n {{ feature.replace(/_/g, ' ') }}\n </td>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n class=\"text-center p-8 text-word-2\"\n :class=\"{\n 'border-b border-surface-3':\n fIdx !== features.length - 1,\n 'bg-surface-1': item.highlighted,\n }\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;EAII,IAAM,IAAQ,GAIR,EAAE,SAAM,cAAW,EAAa,GAEhC,IAAW,QACA,EAAM,IAWtB,GAEK,IAAW,QAAe;GAC5B,IAAM,IAAQ,EAAS,MAAM,OAAO;GAIpC,OAHI,CAAC,KAAS,EAAM,WAAW,IACpB,CAAC,IAEL,OAAO,KAAK,EAAM,EAAE,CAAC,MAAM;EACtC,CAAC,GAEK,UAAkB;GACpB,IAAM,IAAQ,EAAS,MAAM,OAAO;GACpC,IAAI,CAAC,GACD;GAEJ,IAAM,IAAS,CAAC,IAAI,GAAG,EAAM,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,GAAI,GACpD,IAAO,EAAS,MAAM,KAAK,MAC7B,CAAC,EAAE,QAAQ,MAAM,GAAG,GAAG,GAAG,EAAM,KAAK,MAAM,EAAE,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,KAC3D,GACJ,CACJ;GACA,EAAK,CAAC,GAAQ,GAAG,CAAI,CAAC,CAAC,KAAK,IAAI,CAAC;EACrC;;;GAKU,OAAA,EAAA,OAAU,OAAO,OAAO,UADlC,EAAA,GAAA,EAgEM,OAhEN,GAgEM,CA7DF,EAaM,OAbN,GAaM;IAXF,EAAgD,GAAA;KAAxC,MAAK;KAAiB,OAAM;;IACpC,EAEW,UAFX,GAEW,EADP,EAAA,MAAS,MAAM,SAASA,EAAAA,GAAE,kBAAA,CAAA,GAAA,CAAA;IAE9B,EAMgB,GAAA;KAND,WAAU;KAAU,OAAM;;KACrC,SAAA,QAIyB,CAJzB,EAIyB,GAAA;MAHrB,WAAU;MACT,MAAM,EAAA,CAAA,IAAM,kBAAA;MACZ,OAAO,EAAA,CAAA,IAASA,EAAAA,GAAE,eAAA,IAAoBA,EAAAA,GAAE,aAAA;MACxC,SAAO;;;;GAGpB,CAAA,GAAA,EA8CM,OA9CN,GA8CM,CA7CF,EA4CQ,SA5CR,GA4CQ,CA3CJ,EAgBQ,SAAA,MAAA,CAfJ,EAcK,MAAA,MAAA,CAbD,AAAA,EAAA,OAAA,EAC0F,MAAA,EAAtF,OAAM,2EAA0E,GAAA,MAAA,EAAA,IACpF,EAAA,EAAA,GAAA,EAUK,GAAA,MAAA,EATc,EAAA,MAAS,MAAM,QAAvB,OADX,EAAA,GAAA,EAUK,MAAA;IARA,KAAK,EAAK;IACX,OAAK,EAAA,CAAC,kEACmC,EAAK,cAAA,+BAAA,aAAA,CAAA;GAK3C,GAAA,EAAA,EAAK,IAAI,GAAA,CAAA,EAIxB,GAAA,GAAA,EAAA,CAAA,CAAA,CAAA,GAAA,EAyBQ,SAAA,MAAA,EAxBJ,EAAA,EAAA,GAAA,EAuBK,GAAA,MAAA,EAtByB,EAAA,QAAlB,GAAS,OADrB,EAAA,GAAA,EAuBK,MAAA;IArBA,KAAK;IACL,OAAK,EAAA,EAAA,gBAAoB,IAAI,KAAA,EAAA,CAAA;GAC9B,GAAA,CAAA,EAOK,MAAA,EAND,OAAK,EAAA,CAAC,iDAA+C,EAAA,6BACsD,MAAS,EAAA,MAAS,SAAM,EAAA,CAAA,CAAA,EAIhI,GAAA,EAAA,EAAQ,QAAO,MAAA,GAAA,CAAA,GAAA,CAAA,IAEtB,EAAA,EAAA,GAAA,EAUK,GAAA,MAAA,EATc,EAAA,MAAS,MAAM,QAAvB,OADX,EAAA,GAAA,EAUK,MAAA;IARA,KAAK,EAAK;IACX,OAAK,EAAA,CAAC,+BAA6B;KACwE,6BAAA,MAAS,EAAA,MAAS,SAAM;KAAsD,gBAAA,EAAK;;GAK3L,GAAA,EAAA,EAAK,OAAO,MAAO,GAAA,GAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkToolShowContactForm-DHmzAIVF.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolShowContactForm.vue","../../../../packages/components/src/chat/PkToolShowContactForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { useSafeLocalStorage, useLocalizedString } from 'composables'\n import type { LocalizedString } from 'models'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const props = defineProps<{\n part: unknown\n submitted?: boolean\n loading?: boolean\n error?: string\n readonly?: boolean\n privacyPolicyNotice?: LocalizedString\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n type: string\n toolCallId: string\n input?: {\n name: string\n email: string\n phone?: string\n conversationContext?: Record<string, unknown>\n }\n }\n return part\n })\n\n const input = computed(() => toolPart.value.input)\n\n const localSubmitted = useSafeLocalStorage<boolean>(\n computed(\n () =>\n `${toolPart.value.toolCallId}-${toolPart.value.type}-submitted`,\n ),\n false,\n )\n\n const LeadFormSchema = z.object({\n name: z.string().min(1).max(255).default(''),\n email: z.email().max(255).default(''),\n phone: z\n .string()\n .max(50)\n .regex(/^[+0-9\\s\\-.()]*$/, $t('validation.invalidPhoneNumber'))\n .optional(),\n })\n\n const emit = defineEmits<{\n submit: [data: Record<string, unknown>]\n }>()\n\n const { VvForm, VvFormField, formData } = useForm(LeadFormSchema, {\n lazyLoad: true,\n })\n\n // Pre-populate form with input values\n watch(\n input,\n (newInput) => {\n formData.value = {\n name: newInput?.name || '',\n email: newInput?.email || '',\n phone: newInput?.phone || '',\n }\n },\n { immediate: true },\n )\n\n watch(\n () => props.submitted,\n (submitted) => {\n if (submitted) {\n localSubmitted.value = true\n }\n },\n { immediate: true },\n )\n\n const isSubmitted = computed(() => {\n return props.submitted || localSubmitted.value\n })\n\n const submitLead = async (data: Record<string, unknown>): Promise<void> => {\n emit('submit', { ...data, metadata: input.value?.conversationContext })\n }\n\n const privacyNotice = useLocalizedString(\n () => props.privacyPolicyNotice,\n 'message.defaultPrivacyPolicyNotice',\n )\n</script>\n\n<template>\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:send-plane-2-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.contactUs') }}</strong>\n </div>\n\n <Transition mode=\"out-in\">\n <div v-if=\"!isSubmitted\" class=\"p-sm\">\n <VvForm v-model=\"formData\" :readonly @submit=\"submitLead\">\n <VvFormField\n name=\"name\"\n type=\"text\"\n :label=\"$t('label.name')\"\n :placeholder=\"$t('placeholder.name')\"\n icon=\"ri:user-line\"\n modifiers=\"compact no-label\"\n class=\"mb-md\" />\n <VvFormField\n name=\"email\"\n type=\"email\"\n :label=\"$t('label.email')\"\n :placeholder=\"$t('placeholder.email')\"\n icon=\"ri:mail-line\"\n modifiers=\"compact no-label\"\n class=\"mb-md\" />\n <VvFormField\n name=\"phone\"\n type=\"tel\"\n :label=\"$t('label.phone')\"\n :placeholder=\"$t('placeholder.phone')\"\n icon=\"ri:phone-line\"\n class=\"mb-md\"\n modifiers=\"compact no-label\" />\n\n <div class=\"flex justify-between items-center gap-16 mt-xs\">\n <div\n class=\"text-smaller text-word-3 flex items-center gap-8\">\n <VvIcon\n name=\"ri:shield-check-line\"\n class=\"text-16 shrink-0\" />\n <PkStreamingMarkdown\n :markdown=\"privacyNotice\"\n class=\"wysiwyg\" />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading\n :disabled=\"loading || readonly\"\n icon-position=\"right\"\n class=\"shrink-0\"\n :label=\"$t('action.submit')\" />\n </div>\n </VvForm>\n </div>\n <div v-else class=\"p-sm\">\n <!-- One confirmation row across every tool card: a neutral\n check, the title in weight, the body quiet. A completed\n submission is a confirmation, not an alarm (DESIGN.md). -->\n <div class=\"flex items-center gap-sm\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-word-3\" />\n <div>\n <strong class=\"font-medium block text-word-1\">\n {{ $t('message.leadSubmittedTitle') }}\n </strong>\n <p class=\"text-word-3\">\n {{ $t('message.leadSubmittedMessage') }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { useSafeLocalStorage, useLocalizedString } from 'composables'\n import type { LocalizedString } from 'models'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const props = defineProps<{\n part: unknown\n submitted?: boolean\n loading?: boolean\n error?: string\n readonly?: boolean\n privacyPolicyNotice?: LocalizedString\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n type: string\n toolCallId: string\n input?: {\n name: string\n email: string\n phone?: string\n conversationContext?: Record<string, unknown>\n }\n }\n return part\n })\n\n const input = computed(() => toolPart.value.input)\n\n const localSubmitted = useSafeLocalStorage<boolean>(\n computed(\n () =>\n `${toolPart.value.toolCallId}-${toolPart.value.type}-submitted`,\n ),\n false,\n )\n\n const LeadFormSchema = z.object({\n name: z.string().min(1).max(255).default(''),\n email: z.email().max(255).default(''),\n phone: z\n .string()\n .max(50)\n .regex(/^[+0-9\\s\\-.()]*$/, $t('validation.invalidPhoneNumber'))\n .optional(),\n })\n\n const emit = defineEmits<{\n submit: [data: Record<string, unknown>]\n }>()\n\n const { VvForm, VvFormField, formData } = useForm(LeadFormSchema, {\n lazyLoad: true,\n })\n\n // Pre-populate form with input values\n watch(\n input,\n (newInput) => {\n formData.value = {\n name: newInput?.name || '',\n email: newInput?.email || '',\n phone: newInput?.phone || '',\n }\n },\n { immediate: true },\n )\n\n watch(\n () => props.submitted,\n (submitted) => {\n if (submitted) {\n localSubmitted.value = true\n }\n },\n { immediate: true },\n )\n\n const isSubmitted = computed(() => {\n return props.submitted || localSubmitted.value\n })\n\n const submitLead = async (data: Record<string, unknown>): Promise<void> => {\n emit('submit', { ...data, metadata: input.value?.conversationContext })\n }\n\n const privacyNotice = useLocalizedString(\n () => props.privacyPolicyNotice,\n 'message.defaultPrivacyPolicyNotice',\n )\n</script>\n\n<template>\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:send-plane-2-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.contactUs') }}</strong>\n </div>\n\n <Transition mode=\"out-in\">\n <div v-if=\"!isSubmitted\" class=\"p-sm\">\n <VvForm v-model=\"formData\" :readonly @submit=\"submitLead\">\n <VvFormField\n name=\"name\"\n type=\"text\"\n :label=\"$t('label.name')\"\n :placeholder=\"$t('placeholder.name')\"\n icon=\"ri:user-line\"\n modifiers=\"compact no-label\"\n class=\"mb-md\" />\n <VvFormField\n name=\"email\"\n type=\"email\"\n :label=\"$t('label.email')\"\n :placeholder=\"$t('placeholder.email')\"\n icon=\"ri:mail-line\"\n modifiers=\"compact no-label\"\n class=\"mb-md\" />\n <VvFormField\n name=\"phone\"\n type=\"tel\"\n :label=\"$t('label.phone')\"\n :placeholder=\"$t('placeholder.phone')\"\n icon=\"ri:phone-line\"\n class=\"mb-md\"\n modifiers=\"compact no-label\" />\n\n <div class=\"flex justify-between items-center gap-16 mt-xs\">\n <div\n class=\"text-smaller text-word-3 flex items-center gap-8\">\n <VvIcon\n name=\"ri:shield-check-line\"\n class=\"text-16 shrink-0\" />\n <PkStreamingMarkdown\n :markdown=\"privacyNotice\"\n class=\"wysiwyg\" />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading\n :disabled=\"loading || readonly\"\n icon-position=\"right\"\n class=\"shrink-0\"\n :label=\"$t('action.submit')\" />\n </div>\n </VvForm>\n </div>\n <div v-else class=\"p-sm\">\n <!-- One confirmation row across every tool card: a neutral\n check, the title in weight, the body quiet. A completed\n submission is a confirmation, not an alarm (DESIGN.md). -->\n <div class=\"flex items-center gap-sm\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-word-3\" />\n <div>\n <strong class=\"font-medium block text-word-1\">\n {{ $t('message.leadSubmittedTitle') }}\n </strong>\n <p class=\"text-word-3\">\n {{ $t('message.leadSubmittedMessage') }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkToolShowDiagram-DuDp5EHd.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) return\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"line-md:loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) return\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"line-md:loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkToolShowForm-CO5A_j9L.js","names":[],"sources":["../../../../packages/components/src/PkEditorRadioButtons.vue","../../../../packages/components/src/PkEditorRadioButtons.vue","../../../../packages/components/src/PkEditorCheckboxButtons.vue","../../../../packages/components/src/PkEditorCheckboxButtons.vue","../../../../packages/components/src/chat/PkToolShowForm.vue","../../../../packages/components/src/chat/PkToolShowForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import type { Option } from '@volverjs/ui-vue'\n import { computed, useId } from 'vue'\n\n const props = defineProps<{\n name: string\n label?: string\n hintLabel?: string\n invalid?: boolean\n invalidLabel?: string | string[]\n required?: boolean\n options: Option[]\n readonly?: boolean\n disabled?: boolean\n modifiers?: string | string[]\n /**\n * Render the options as one segmented control (a recessed track with\n * the chosen option raised) instead of a row of boxed cards. Suits a\n * choice between a few short named states, such as a level or a mode.\n */\n segmented?: boolean\n }>()\n\n const modelValue = defineModel<\n string | number | boolean | symbol | unknown[] | undefined\n >({ default: undefined })\n\n // Native radios group by their name attribute across the whole document, so\n // two instances sharing one name become a single group: the browser unchecks\n // the other instance's inputs and Vue never puts them back, because its own\n // model did not change. It happened with \"theme\", offered by the profile\n // menu and the agent settings panel at the same time, and it can happen to\n // any two tool forms with the same step id. Scope the name to the instance.\n const uid = useId()\n const groupName = computed(() => `${props.name}-${uid}`)\n\n const radioModifiers = computed(() => {\n const toReturn = props.modifiers\n ? Array.isArray(props.modifiers)\n ? props.modifiers\n : [props.modifiers]\n : []\n return [...toReturn, props.segmented ? 'segment' : 'button']\n })\n</script>\n\n<template>\n <VvRadioGroup\n v-model=\"modelValue\"\n :name=\"groupName\"\n :label\n :hint-label=\"hintLabel\"\n :invalid\n :invalid-label=\"invalidLabel\"\n :modifiers=\"segmented ? 'segmented' : 'buttons'\">\n <VvRadio\n v-for=\"(option, key) in options\"\n :key\n :name=\"groupName\"\n :required\n :readonly\n :disabled\n :value=\"option.value\"\n :modifiers=\"radioModifiers\">\n <slot :name=\"option.value\" v-bind=\"{ option }\">\n {{ option.label }}\n </slot>\n </VvRadio>\n </VvRadioGroup>\n</template>\n","<script setup lang=\"ts\">\n import type { Option } from '@volverjs/ui-vue'\n import { computed, useId } from 'vue'\n\n const props = defineProps<{\n name: string\n label?: string\n hintLabel?: string\n invalid?: boolean\n invalidLabel?: string | string[]\n required?: boolean\n options: Option[]\n readonly?: boolean\n disabled?: boolean\n modifiers?: string | string[]\n /**\n * Render the options as one segmented control (a recessed track with\n * the chosen option raised) instead of a row of boxed cards. Suits a\n * choice between a few short named states, such as a level or a mode.\n */\n segmented?: boolean\n }>()\n\n const modelValue = defineModel<\n string | number | boolean | symbol | unknown[] | undefined\n >({ default: undefined })\n\n // Native radios group by their name attribute across the whole document, so\n // two instances sharing one name become a single group: the browser unchecks\n // the other instance's inputs and Vue never puts them back, because its own\n // model did not change. It happened with \"theme\", offered by the profile\n // menu and the agent settings panel at the same time, and it can happen to\n // any two tool forms with the same step id. Scope the name to the instance.\n const uid = useId()\n const groupName = computed(() => `${props.name}-${uid}`)\n\n const radioModifiers = computed(() => {\n const toReturn = props.modifiers\n ? Array.isArray(props.modifiers)\n ? props.modifiers\n : [props.modifiers]\n : []\n return [...toReturn, props.segmented ? 'segment' : 'button']\n })\n</script>\n\n<template>\n <VvRadioGroup\n v-model=\"modelValue\"\n :name=\"groupName\"\n :label\n :hint-label=\"hintLabel\"\n :invalid\n :invalid-label=\"invalidLabel\"\n :modifiers=\"segmented ? 'segmented' : 'buttons'\">\n <VvRadio\n v-for=\"(option, key) in options\"\n :key\n :name=\"groupName\"\n :required\n :readonly\n :disabled\n :value=\"option.value\"\n :modifiers=\"radioModifiers\">\n <slot :name=\"option.value\" v-bind=\"{ option }\">\n {{ option.label }}\n </slot>\n </VvRadio>\n </VvRadioGroup>\n</template>\n","<script setup lang=\"ts\">\n import type { Option } from '@volverjs/ui-vue'\n\n defineProps<{\n name: string\n required?: boolean\n options: Option[]\n readonly?: boolean\n disabled?: boolean\n }>()\n\n const modelValue = defineModel<unknown[] | undefined>({\n default: undefined,\n })\n</script>\n\n<template>\n <VvCheckboxGroup v-model=\"modelValue\" :name modifiers=\"buttons\">\n <VvCheckbox\n v-for=\"(option, key) in options\"\n :key\n :name\n :required\n :readonly\n :disabled\n :value=\"option.value\"\n modifiers=\"button\">\n <slot :name=\"String(option.value)\" v-bind=\"{ option }\">\n {{ option.label }}\n </slot>\n </VvCheckbox>\n </VvCheckboxGroup>\n</template>\n","<script setup lang=\"ts\">\n import type { Option } from '@volverjs/ui-vue'\n\n defineProps<{\n name: string\n required?: boolean\n options: Option[]\n readonly?: boolean\n disabled?: boolean\n }>()\n\n const modelValue = defineModel<unknown[] | undefined>({\n default: undefined,\n })\n</script>\n\n<template>\n <VvCheckboxGroup v-model=\"modelValue\" :name modifiers=\"buttons\">\n <VvCheckbox\n v-for=\"(option, key) in options\"\n :key\n :name\n :required\n :readonly\n :disabled\n :value=\"option.value\"\n modifiers=\"button\">\n <slot :name=\"String(option.value)\" v-bind=\"{ option }\">\n {{ option.label }}\n </slot>\n </VvCheckbox>\n </VvCheckboxGroup>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { isVoiceRenderAck } from 'models'\n import { formAnswerSlots, formStepOptions } from './utils'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkEditorRadioButtons from '../PkEditorRadioButtons.vue'\n import PkEditorCheckboxButtons from '../PkEditorCheckboxButtons.vue'\n\n // --- Types ---\n\n interface FormOption {\n label: string\n description?: string\n }\n\n interface FormStep {\n id: string\n question: string\n message?: string\n type?: 'text' | 'number' | 'date'\n options?: FormOption[]\n multiSelect?: boolean\n allowFreeformInput?: boolean\n placeholder?: string\n }\n\n interface FormAnswer {\n id: string\n value: string | string[] | number | null\n }\n\n interface ToolPartShape {\n state?: 'input-available' | 'output-available' | 'output-error'\n toolCallId?: string\n // AI SDK v5 encodes the tool name in `type` (e.g. 'tool-showMultipleChoice').\n // `toolName` is kept for backward compatibility but is normally absent.\n type?: string\n toolName?: string\n input?: {\n title?: string\n steps?: FormStep[]\n // Legacy showMultipleChoice fields\n question?: string\n options?: (string | FormOption)[]\n }\n // The web card submits `{ answers: [...] }`; the platform channels\n // (Teams modal) submit a flat record keyed by step id.\n output?: string | { answers: FormAnswer[] } | Record<string, unknown>\n }\n\n type AnswerValue = string | string[] | number | null\n\n // --- Props & Emits ---\n\n const props = defineProps<{\n part: unknown\n allowCustomAnswer?: boolean\n /**\n * Answered during a call. Nothing in the part says so (the answer went\n * to the agent as speech, not to the tool call), so the surface that\n * sent it is the only one that knows, and without it the same form\n * could be submitted again and again.\n */\n answered?: boolean\n }>()\n\n const emit = defineEmits<{\n select: [answer: string | { answers: FormAnswer[] }]\n }>()\n\n const { t: $t, d: $d } = useI18n({ useScope: 'global' })\n\n // --- Parsed tool part ---\n\n const toolPart = computed(\n () => JSON.parse(JSON.stringify(props.part)) as ToolPartShape,\n )\n\n const isLegacy = computed(\n () =>\n toolPart.value.toolName === 'showMultipleChoice' ||\n toolPart.value.type === 'tool-showMultipleChoice',\n )\n\n /**\n * A form drawn during a CALL is already \"answered\" as far as the tool loop\n * is concerned: the turn could not wait for it, so the backend closed it\n * with a note saying the widget was drawn (see `isVoiceRenderAck`). That\n * note is not an answer, and the caller still has to give one.\n */\n const isRenderAck = computed(() => isVoiceRenderAck(toolPart.value.output))\n\n const isInteractive = computed(\n () =>\n !props.answered &&\n (toolPart.value.state === 'input-available' || isRenderAck.value),\n )\n\n /** Single-step with no custom-answer and no freeform → auto-submit on option pick */\n const isAutoSubmitStep = computed(() => {\n const step = currentStep.value\n if (!step) return false\n return (\n steps.value.length === 1 &&\n !step.allowFreeformInput &&\n !props.allowCustomAnswer\n )\n })\n\n /** Legacy tool should auto-submit on option pick regardless of allowCustomAnswer */\n const isLegacyAutoSubmit = computed(\n () =>\n isLegacy.value &&\n steps.value.length === 1 &&\n !currentStep.value?.multiSelect,\n )\n\n /**\n * True only for the steps that send themselves as soon as an option is\n * picked: the single case where the footer button would be redundant.\n * Every other shape needs it, single-step forms included: with\n * `allowFreeformInput` (or `allowCustomAnswer`) the radio pick no longer\n * auto-submits, so without the footer the user was left with a chosen\n * option and no way to send it.\n */\n const isSelfSubmitting = computed(\n () =>\n Boolean(currentStep.value?.options) &&\n !currentStep.value?.multiSelect &&\n (isAutoSubmitStep.value || isLegacyAutoSubmit.value),\n )\n\n // --- Steps ---\n\n const steps = computed<FormStep[]>(() => {\n const input = toolPart.value.input\n if (input?.steps?.length) {\n return input.steps\n }\n // Legacy showMultipleChoice → normalize into a single step\n if (input?.question && input?.options) {\n return [\n {\n id: 'legacy',\n question: input.question,\n options: input.options.map((o) =>\n typeof o === 'string' ? { label: o } : o,\n ),\n },\n ]\n }\n return []\n })\n\n /**\n * The reason the card has nothing to draw. A call the SDK rejected keeps\n * `state: 'output-error'` and moves the arguments it could not validate to\n * `rawInput`, so `input` is empty and there are no steps: without this the\n * component rendered NOTHING, and the user was left with a question that\n * had been announced and never appeared.\n */\n const hasFailed = computed(\n () => toolPart.value.state === 'output-error' && !steps.value.length,\n )\n\n // --- Navigation state ---\n\n const currentStepIndex = ref(0)\n const currentStep = computed(() => steps.value[currentStepIndex.value])\n const canGoBack = computed(() => currentStepIndex.value > 0)\n const canGoNext = computed(\n () => currentStepIndex.value < steps.value.length - 1,\n )\n const isMultiStep = computed(() => steps.value.length > 1)\n\n const progress = computed(() => {\n if (!isMultiStep.value) return null\n return Math.round(\n ((currentStepIndex.value + 1) / steps.value.length) * 100,\n )\n })\n\n // --- Answers ---\n\n /**\n * One slot PER STEP, keyed by position and not by step id: the ids come\n * from the model and nothing enforces they are unique, and keyed by id two\n * steps sharing one collapsed onto a single answer. See `formAnswerSlots`.\n */\n const answers = reactive<Record<number, AnswerValue>>({})\n const customAnswer = ref('')\n\n // The freeform field belongs to the step showing it: text left behind\n // (unsubmitted, or arriving from a late input update) must not follow the\n // user to the next or previous question.\n watch(currentStepIndex, () => {\n customAnswer.value = ''\n })\n\n // Single scalar answer shared by radio buttons and freeform text/number/date inputs.\n const scalarValue = computed<string | number | undefined>({\n get: () => {\n const val = currentStep.value\n ? answers[currentStepIndex.value]\n : undefined\n if (val == null || Array.isArray(val)) return undefined\n return val\n },\n set: (val) => {\n if (currentStep.value) {\n answers[currentStepIndex.value] = val ?? null\n }\n },\n })\n\n const activeCheckboxValue = computed<unknown[] | undefined>({\n get: () => {\n const val = currentStep.value\n ? answers[currentStepIndex.value]\n : undefined\n return Array.isArray(val) ? val : []\n },\n set: (val) => {\n if (currentStep.value) {\n answers[currentStepIndex.value] = (val as string[]) ?? []\n }\n },\n })\n\n // --- Initialize answers from existing output ---\n\n /** The `{ answers: [...] }` list, when the output carries one. */\n function outputAnswers(output: ToolPartShape['output']) {\n if (!output || typeof output === 'string') return undefined\n const list = (output as { answers?: unknown }).answers\n return Array.isArray(list) ? (list as FormAnswer[]) : undefined\n }\n\n watch(\n () => toolPart.value.output,\n (output) => {\n if (!output || isVoiceRenderAck(output)) return\n // Every shape an answered form comes back in (the web card's list,\n // a legacy plain string, the flat record a platform channel posts)\n // lands on the same per-position slots: see `formAnswerSlots`.\n Object.assign(\n answers,\n formAnswerSlots(steps.value, output) as Record<\n number,\n AnswerValue\n >,\n )\n },\n { immediate: true },\n )\n\n // --- Restore custom answer from output when it doesn't match any option ---\n\n const normalizedOutput = computed(() => {\n const out = toolPart.value.output\n if (!out || isVoiceRenderAck(out)) return undefined\n if (typeof out === 'string') return out\n const list = outputAnswers(out)\n if (list?.length === 1) {\n const val = list[0].value\n return Array.isArray(val) ? val.join(', ') : String(val)\n }\n return undefined\n })\n\n watch(\n normalizedOutput,\n (value) => {\n if (!value || !currentStep.value) return\n const optionLabels = (currentStep.value.options ?? []).map(\n (o) => o.label,\n )\n if (!optionLabels.includes(value)) {\n customAnswer.value = value\n }\n },\n { immediate: true },\n )\n\n // --- Option mapping for radio/checkbox components ---\n\n const radioOptions = computed(() => formStepOptions(currentStep.value))\n\n // --- Navigation & Submission ---\n\n /** Footer button, and Enter on a pure freeform step. */\n function goNext() {\n // A typed freeform answer wins over the picked option: it is the more\n // recent intent, and the footer button is now the visible way to send\n // it (the freeform field only submits on Enter).\n if (customAnswer.value.trim()) {\n submitCustomAnswer()\n return\n }\n if (canGoNext.value) {\n currentStepIndex.value++\n } else {\n submitForm()\n }\n }\n\n function goBack() {\n if (canGoBack.value) {\n currentStepIndex.value--\n }\n }\n\n function submitForm() {\n if (!isInteractive.value) return\n\n const finalAnswers: FormAnswer[] = steps.value.map((step, index) => ({\n id: step.id,\n value: answers[index] ?? null,\n }))\n\n // Legacy backward compatibility: single-step, single-select → return plain string\n if (\n isLegacyAutoSubmit.value &&\n !currentStep.value?.allowFreeformInput\n ) {\n const val = finalAnswers[0].value\n emit('select', Array.isArray(val) ? val[0] : (val as string))\n } else {\n emit('select', { answers: finalAnswers })\n }\n }\n\n /** Enter inside the freeform field shown alongside the options. */\n function submitCustomAnswer() {\n const value = customAnswer.value.trim()\n if (!value) return\n\n answers[currentStepIndex.value] = value\n // Cleared before navigating: `goNext()` reads it back to route a typed\n // answer here, so leaving it set would recurse.\n customAnswer.value = ''\n if (steps.value.length === 1) {\n submitForm()\n } else {\n goNext()\n }\n }\n\n function onRadioChange() {\n if (!isInteractive.value) return\n if (isAutoSubmitStep.value || isLegacyAutoSubmit.value) {\n submitForm()\n }\n }\n\n // --- Summary helpers ---\n\n function getSummaryValue(step: FormStep, index: number) {\n const val = answers[index]\n if (val == null) return '—'\n if (Array.isArray(val)) return val.join(', ')\n if (step.type === 'date') return $d(new Date(val), 'short')\n return val\n }\n</script>\n\n<template>\n <div\n v-if=\"hasFailed\"\n class=\"text-12 text-danger flex items-center gap-6 py-8\">\n <span class=\"i-vv-alert-circle text-16 shrink-0\" />\n <span>{{ $t('message.formRenderError') }}</span>\n </div>\n <div\n v-else-if=\"steps.length\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden flex flex-col bg-surface\">\n <!-- Header -->\n <div\n v-if=\"toolPart.input?.title || (isInteractive && steps.length > 1)\"\n class=\"text-12 border-b border-surface-3 text-word-3 bg-surface-1\">\n <div\n v-if=\"toolPart.input?.title\"\n class=\"px-sm py-10 font-bold\"\n :class=\"{\n 'border-b border-surface-3':\n isInteractive && steps.length > 1,\n }\">\n {{ toolPart.input?.title }}\n </div>\n <!-- Progress for multi-step -->\n <div\n v-if=\"isInteractive && steps.length > 1\"\n class=\"flex flex-col gap-4 p-sm\">\n <div class=\"flex justify-between text-10 text-word-3\">\n <span>\n {{\n $t('showForm.step', {\n current: currentStepIndex + 1,\n total: steps.length,\n })\n }}\n </span>\n <span>{{ progress }}%</span>\n </div>\n <div class=\"h-4 bg-surface-3 rounded-full overflow-hidden\">\n <div\n class=\"h-full bg-word-4 transition-all duration-300\"\n :style=\"{ width: `${progress}%` }\"></div>\n </div>\n </div>\n </div>\n\n <!-- Input Available State -->\n <template v-if=\"isInteractive\">\n <!-- Current Step Content -->\n <div class=\"px-sm py-16 flex flex-col gap-12\">\n <div class=\"leading-normal text-12\">\n <strong class=\"font-semibold\">\n {{ currentStep.question }}\n </strong>\n <PkStreamingMarkdown\n v-if=\"currentStep.message\"\n :markdown=\"currentStep.message\"\n class=\"text-word-2 mt-4\" />\n </div>\n\n <!-- Checkbox list for multi-select -->\n <PkEditorCheckboxButtons\n v-if=\"currentStep.options && currentStep.multiSelect\"\n v-model=\"activeCheckboxValue\"\n :name=\"currentStep.id\"\n :options=\"radioOptions\"\n :readonly=\"!isInteractive\"\n class=\"mb-0\"\n vertical>\n <template\n v-for=\"option in radioOptions\"\n :key=\"option.value\"\n #[option.value]>\n <!-- Shared option layout: label + optional description -->\n <div class=\"text-12\">\n <div class=\"font-semibold\">{{ option.label }}</div>\n <div v-if=\"option.description\" class=\"text-word-3\">\n {{ option.description }}\n </div>\n </div>\n </template>\n </PkEditorCheckboxButtons>\n\n <!-- Radio buttons for single-select -->\n <PkEditorRadioButtons\n v-else-if=\"currentStep.options && !currentStep.multiSelect\"\n v-model=\"scalarValue\"\n :name=\"currentStep.id\"\n :options=\"radioOptions\"\n :readonly=\"!isInteractive\"\n vertical\n class=\"mb-0\"\n @update:model-value=\"onRadioChange\">\n <template\n v-for=\"option in radioOptions\"\n :key=\"option.value\"\n #[option.value]>\n <!-- Shared option layout: label + optional description -->\n <div class=\"text-12\">\n <div class=\"font-semibold\">{{ option.label }}</div>\n <div v-if=\"option.description\" class=\"text-word-3\">\n {{ option.description }}\n </div>\n </div>\n </template>\n </PkEditorRadioButtons>\n\n <!-- Pure Freeform Input (text, number, date) -->\n <div\n v-else-if=\"!currentStep.options\"\n class=\"flex flex-col gap-6 w-full\">\n <VvInputText\n :key=\"currentStep.id\"\n v-model=\"scalarValue\"\n :placeholder=\"\n currentStep.placeholder || $t('placeholder.insert')\n \"\n :type=\"currentStep.type\"\n class=\"w-full\"\n :name=\"currentStep.id\"\n modifiers=\"compact\"\n :disabled=\"!isInteractive\"\n @keyup.enter=\"goNext\" />\n </div>\n </div>\n\n <!-- Custom footer freeform input (shown alongside options when allowFreeformInput) -->\n <div\n v-if=\"currentStep.allowFreeformInput && currentStep.options\"\n class=\"px-sm pb-sm border-t border-surface-3\">\n <form\n class=\"flex gap-xs pt-sm\"\n @submit.prevent=\"submitCustomAnswer\">\n <VvInputText\n :key=\"`${currentStep.id}-custom`\"\n v-model=\"customAnswer\"\n class=\"flex-1\"\n :name=\"`${currentStep.id}-custom`\"\n modifiers=\"compact\"\n :disabled=\"!isInteractive\"\n :placeholder=\"\n currentStep.placeholder ||\n $t('showForm.otherPlaceholder')\n \" />\n </form>\n </div>\n\n <!-- Footer navigation: shown for every step that does not submit\n itself on option pick (see `isSelfSubmitting`). -->\n <div\n v-if=\"!isSelfSubmitting\"\n class=\"px-sm py-10 bg-surface border-t border-surface-3 flex justify-between gap-12\">\n <VvButton\n v-if=\"canGoBack\"\n modifiers=\"action-quiet\"\n icon=\"ri:arrow-left-s-line\"\n :label=\"$t('action.back')\"\n @click.stop=\"goBack\" />\n <div v-else></div>\n\n <VvButton\n :modifiers=\"canGoNext ? 'action-quiet' : 'action-primary'\"\n :icon=\"\n canGoNext\n ? 'ri:arrow-right-s-line'\n : 'ri:send-plane-2-line'\n \"\n icon-position=\"right\"\n :label=\"canGoNext ? $t('action.next') : $t('action.submit')\"\n @click.stop=\"goNext\" />\n </div>\n </template>\n\n <!-- Output Available State (Multi-step Summary) -->\n <template v-else>\n <div class=\"px-sm py-12 bg-surface space-y-10\">\n <ul class=\"flex flex-col gap-8\">\n <li\n v-for=\"(step, index) in steps\"\n :key=\"index\"\n class=\"leading-normal\">\n <div class=\"text-12 text-word-3\">\n {{ step.question }}\n </div>\n <span class=\"text-12 text-word-2 font-semibold\">\n {{ getSummaryValue(step, index) }}\n </span>\n </li>\n </ul>\n </div>\n <div\n class=\"px-sm py-12 bg-surface border-t border-surface-3 flex items-center gap-10\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-word-3\" />\n <div class=\"flex flex-col\">\n <span class=\"font-medium text-12 text-word-1 leading-tight\">\n {{ $t('showForm.completed') }}\n </span>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { isVoiceRenderAck } from 'models'\n import { formAnswerSlots, formStepOptions } from './utils'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkEditorRadioButtons from '../PkEditorRadioButtons.vue'\n import PkEditorCheckboxButtons from '../PkEditorCheckboxButtons.vue'\n\n // --- Types ---\n\n interface FormOption {\n label: string\n description?: string\n }\n\n interface FormStep {\n id: string\n question: string\n message?: string\n type?: 'text' | 'number' | 'date'\n options?: FormOption[]\n multiSelect?: boolean\n allowFreeformInput?: boolean\n placeholder?: string\n }\n\n interface FormAnswer {\n id: string\n value: string | string[] | number | null\n }\n\n interface ToolPartShape {\n state?: 'input-available' | 'output-available' | 'output-error'\n toolCallId?: string\n // AI SDK v5 encodes the tool name in `type` (e.g. 'tool-showMultipleChoice').\n // `toolName` is kept for backward compatibility but is normally absent.\n type?: string\n toolName?: string\n input?: {\n title?: string\n steps?: FormStep[]\n // Legacy showMultipleChoice fields\n question?: string\n options?: (string | FormOption)[]\n }\n // The web card submits `{ answers: [...] }`; the platform channels\n // (Teams modal) submit a flat record keyed by step id.\n output?: string | { answers: FormAnswer[] } | Record<string, unknown>\n }\n\n type AnswerValue = string | string[] | number | null\n\n // --- Props & Emits ---\n\n const props = defineProps<{\n part: unknown\n allowCustomAnswer?: boolean\n /**\n * Answered during a call. Nothing in the part says so (the answer went\n * to the agent as speech, not to the tool call), so the surface that\n * sent it is the only one that knows, and without it the same form\n * could be submitted again and again.\n */\n answered?: boolean\n }>()\n\n const emit = defineEmits<{\n select: [answer: string | { answers: FormAnswer[] }]\n }>()\n\n const { t: $t, d: $d } = useI18n({ useScope: 'global' })\n\n // --- Parsed tool part ---\n\n const toolPart = computed(\n () => JSON.parse(JSON.stringify(props.part)) as ToolPartShape,\n )\n\n const isLegacy = computed(\n () =>\n toolPart.value.toolName === 'showMultipleChoice' ||\n toolPart.value.type === 'tool-showMultipleChoice',\n )\n\n /**\n * A form drawn during a CALL is already \"answered\" as far as the tool loop\n * is concerned: the turn could not wait for it, so the backend closed it\n * with a note saying the widget was drawn (see `isVoiceRenderAck`). That\n * note is not an answer, and the caller still has to give one.\n */\n const isRenderAck = computed(() => isVoiceRenderAck(toolPart.value.output))\n\n const isInteractive = computed(\n () =>\n !props.answered &&\n (toolPart.value.state === 'input-available' || isRenderAck.value),\n )\n\n /** Single-step with no custom-answer and no freeform → auto-submit on option pick */\n const isAutoSubmitStep = computed(() => {\n const step = currentStep.value\n if (!step) return false\n return (\n steps.value.length === 1 &&\n !step.allowFreeformInput &&\n !props.allowCustomAnswer\n )\n })\n\n /** Legacy tool should auto-submit on option pick regardless of allowCustomAnswer */\n const isLegacyAutoSubmit = computed(\n () =>\n isLegacy.value &&\n steps.value.length === 1 &&\n !currentStep.value?.multiSelect,\n )\n\n /**\n * True only for the steps that send themselves as soon as an option is\n * picked: the single case where the footer button would be redundant.\n * Every other shape needs it, single-step forms included: with\n * `allowFreeformInput` (or `allowCustomAnswer`) the radio pick no longer\n * auto-submits, so without the footer the user was left with a chosen\n * option and no way to send it.\n */\n const isSelfSubmitting = computed(\n () =>\n Boolean(currentStep.value?.options) &&\n !currentStep.value?.multiSelect &&\n (isAutoSubmitStep.value || isLegacyAutoSubmit.value),\n )\n\n // --- Steps ---\n\n const steps = computed<FormStep[]>(() => {\n const input = toolPart.value.input\n if (input?.steps?.length) {\n return input.steps\n }\n // Legacy showMultipleChoice → normalize into a single step\n if (input?.question && input?.options) {\n return [\n {\n id: 'legacy',\n question: input.question,\n options: input.options.map((o) =>\n typeof o === 'string' ? { label: o } : o,\n ),\n },\n ]\n }\n return []\n })\n\n /**\n * The reason the card has nothing to draw. A call the SDK rejected keeps\n * `state: 'output-error'` and moves the arguments it could not validate to\n * `rawInput`, so `input` is empty and there are no steps: without this the\n * component rendered NOTHING, and the user was left with a question that\n * had been announced and never appeared.\n */\n const hasFailed = computed(\n () => toolPart.value.state === 'output-error' && !steps.value.length,\n )\n\n // --- Navigation state ---\n\n const currentStepIndex = ref(0)\n const currentStep = computed(() => steps.value[currentStepIndex.value])\n const canGoBack = computed(() => currentStepIndex.value > 0)\n const canGoNext = computed(\n () => currentStepIndex.value < steps.value.length - 1,\n )\n const isMultiStep = computed(() => steps.value.length > 1)\n\n const progress = computed(() => {\n if (!isMultiStep.value) return null\n return Math.round(\n ((currentStepIndex.value + 1) / steps.value.length) * 100,\n )\n })\n\n // --- Answers ---\n\n /**\n * One slot PER STEP, keyed by position and not by step id: the ids come\n * from the model and nothing enforces they are unique, and keyed by id two\n * steps sharing one collapsed onto a single answer. See `formAnswerSlots`.\n */\n const answers = reactive<Record<number, AnswerValue>>({})\n const customAnswer = ref('')\n\n // The freeform field belongs to the step showing it: text left behind\n // (unsubmitted, or arriving from a late input update) must not follow the\n // user to the next or previous question.\n watch(currentStepIndex, () => {\n customAnswer.value = ''\n })\n\n // Single scalar answer shared by radio buttons and freeform text/number/date inputs.\n const scalarValue = computed<string | number | undefined>({\n get: () => {\n const val = currentStep.value\n ? answers[currentStepIndex.value]\n : undefined\n if (val == null || Array.isArray(val)) return undefined\n return val\n },\n set: (val) => {\n if (currentStep.value) {\n answers[currentStepIndex.value] = val ?? null\n }\n },\n })\n\n const activeCheckboxValue = computed<unknown[] | undefined>({\n get: () => {\n const val = currentStep.value\n ? answers[currentStepIndex.value]\n : undefined\n return Array.isArray(val) ? val : []\n },\n set: (val) => {\n if (currentStep.value) {\n answers[currentStepIndex.value] = (val as string[]) ?? []\n }\n },\n })\n\n // --- Initialize answers from existing output ---\n\n /** The `{ answers: [...] }` list, when the output carries one. */\n function outputAnswers(output: ToolPartShape['output']) {\n if (!output || typeof output === 'string') return undefined\n const list = (output as { answers?: unknown }).answers\n return Array.isArray(list) ? (list as FormAnswer[]) : undefined\n }\n\n watch(\n () => toolPart.value.output,\n (output) => {\n if (!output || isVoiceRenderAck(output)) return\n // Every shape an answered form comes back in (the web card's list,\n // a legacy plain string, the flat record a platform channel posts)\n // lands on the same per-position slots: see `formAnswerSlots`.\n Object.assign(\n answers,\n formAnswerSlots(steps.value, output) as Record<\n number,\n AnswerValue\n >,\n )\n },\n { immediate: true },\n )\n\n // --- Restore custom answer from output when it doesn't match any option ---\n\n const normalizedOutput = computed(() => {\n const out = toolPart.value.output\n if (!out || isVoiceRenderAck(out)) return undefined\n if (typeof out === 'string') return out\n const list = outputAnswers(out)\n if (list?.length === 1) {\n const val = list[0].value\n return Array.isArray(val) ? val.join(', ') : String(val)\n }\n return undefined\n })\n\n watch(\n normalizedOutput,\n (value) => {\n if (!value || !currentStep.value) return\n const optionLabels = (currentStep.value.options ?? []).map(\n (o) => o.label,\n )\n if (!optionLabels.includes(value)) {\n customAnswer.value = value\n }\n },\n { immediate: true },\n )\n\n // --- Option mapping for radio/checkbox components ---\n\n const radioOptions = computed(() => formStepOptions(currentStep.value))\n\n // --- Navigation & Submission ---\n\n /** Footer button, and Enter on a pure freeform step. */\n function goNext() {\n // A typed freeform answer wins over the picked option: it is the more\n // recent intent, and the footer button is now the visible way to send\n // it (the freeform field only submits on Enter).\n if (customAnswer.value.trim()) {\n submitCustomAnswer()\n return\n }\n if (canGoNext.value) {\n currentStepIndex.value++\n } else {\n submitForm()\n }\n }\n\n function goBack() {\n if (canGoBack.value) {\n currentStepIndex.value--\n }\n }\n\n function submitForm() {\n if (!isInteractive.value) return\n\n const finalAnswers: FormAnswer[] = steps.value.map((step, index) => ({\n id: step.id,\n value: answers[index] ?? null,\n }))\n\n // Legacy backward compatibility: single-step, single-select → return plain string\n if (\n isLegacyAutoSubmit.value &&\n !currentStep.value?.allowFreeformInput\n ) {\n const val = finalAnswers[0].value\n emit('select', Array.isArray(val) ? val[0] : (val as string))\n } else {\n emit('select', { answers: finalAnswers })\n }\n }\n\n /** Enter inside the freeform field shown alongside the options. */\n function submitCustomAnswer() {\n const value = customAnswer.value.trim()\n if (!value) return\n\n answers[currentStepIndex.value] = value\n // Cleared before navigating: `goNext()` reads it back to route a typed\n // answer here, so leaving it set would recurse.\n customAnswer.value = ''\n if (steps.value.length === 1) {\n submitForm()\n } else {\n goNext()\n }\n }\n\n function onRadioChange() {\n if (!isInteractive.value) return\n if (isAutoSubmitStep.value || isLegacyAutoSubmit.value) {\n submitForm()\n }\n }\n\n // --- Summary helpers ---\n\n function getSummaryValue(step: FormStep, index: number) {\n const val = answers[index]\n if (val == null) return '—'\n if (Array.isArray(val)) return val.join(', ')\n if (step.type === 'date') return $d(new Date(val), 'short')\n return val\n }\n</script>\n\n<template>\n <div\n v-if=\"hasFailed\"\n class=\"text-12 text-danger flex items-center gap-6 py-8\">\n <span class=\"i-vv-alert-circle text-16 shrink-0\" />\n <span>{{ $t('message.formRenderError') }}</span>\n </div>\n <div\n v-else-if=\"steps.length\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden flex flex-col bg-surface\">\n <!-- Header -->\n <div\n v-if=\"toolPart.input?.title || (isInteractive && steps.length > 1)\"\n class=\"text-12 border-b border-surface-3 text-word-3 bg-surface-1\">\n <div\n v-if=\"toolPart.input?.title\"\n class=\"px-sm py-10 font-bold\"\n :class=\"{\n 'border-b border-surface-3':\n isInteractive && steps.length > 1,\n }\">\n {{ toolPart.input?.title }}\n </div>\n <!-- Progress for multi-step -->\n <div\n v-if=\"isInteractive && steps.length > 1\"\n class=\"flex flex-col gap-4 p-sm\">\n <div class=\"flex justify-between text-10 text-word-3\">\n <span>\n {{\n $t('showForm.step', {\n current: currentStepIndex + 1,\n total: steps.length,\n })\n }}\n </span>\n <span>{{ progress }}%</span>\n </div>\n <div class=\"h-4 bg-surface-3 rounded-full overflow-hidden\">\n <div\n class=\"h-full bg-word-4 transition-all duration-300\"\n :style=\"{ width: `${progress}%` }\"></div>\n </div>\n </div>\n </div>\n\n <!-- Input Available State -->\n <template v-if=\"isInteractive\">\n <!-- Current Step Content -->\n <div class=\"px-sm py-16 flex flex-col gap-12\">\n <div class=\"leading-normal text-12\">\n <strong class=\"font-semibold\">\n {{ currentStep.question }}\n </strong>\n <PkStreamingMarkdown\n v-if=\"currentStep.message\"\n :markdown=\"currentStep.message\"\n class=\"text-word-2 mt-4\" />\n </div>\n\n <!-- Checkbox list for multi-select -->\n <PkEditorCheckboxButtons\n v-if=\"currentStep.options && currentStep.multiSelect\"\n v-model=\"activeCheckboxValue\"\n :name=\"currentStep.id\"\n :options=\"radioOptions\"\n :readonly=\"!isInteractive\"\n class=\"mb-0\"\n vertical>\n <template\n v-for=\"option in radioOptions\"\n :key=\"option.value\"\n #[option.value]>\n <!-- Shared option layout: label + optional description -->\n <div class=\"text-12\">\n <div class=\"font-semibold\">{{ option.label }}</div>\n <div v-if=\"option.description\" class=\"text-word-3\">\n {{ option.description }}\n </div>\n </div>\n </template>\n </PkEditorCheckboxButtons>\n\n <!-- Radio buttons for single-select -->\n <PkEditorRadioButtons\n v-else-if=\"currentStep.options && !currentStep.multiSelect\"\n v-model=\"scalarValue\"\n :name=\"currentStep.id\"\n :options=\"radioOptions\"\n :readonly=\"!isInteractive\"\n vertical\n class=\"mb-0\"\n @update:model-value=\"onRadioChange\">\n <template\n v-for=\"option in radioOptions\"\n :key=\"option.value\"\n #[option.value]>\n <!-- Shared option layout: label + optional description -->\n <div class=\"text-12\">\n <div class=\"font-semibold\">{{ option.label }}</div>\n <div v-if=\"option.description\" class=\"text-word-3\">\n {{ option.description }}\n </div>\n </div>\n </template>\n </PkEditorRadioButtons>\n\n <!-- Pure Freeform Input (text, number, date) -->\n <div\n v-else-if=\"!currentStep.options\"\n class=\"flex flex-col gap-6 w-full\">\n <VvInputText\n :key=\"currentStep.id\"\n v-model=\"scalarValue\"\n :placeholder=\"\n currentStep.placeholder || $t('placeholder.insert')\n \"\n :type=\"currentStep.type\"\n class=\"w-full\"\n :name=\"currentStep.id\"\n modifiers=\"compact\"\n :disabled=\"!isInteractive\"\n @keyup.enter=\"goNext\" />\n </div>\n </div>\n\n <!-- Custom footer freeform input (shown alongside options when allowFreeformInput) -->\n <div\n v-if=\"currentStep.allowFreeformInput && currentStep.options\"\n class=\"px-sm pb-sm border-t border-surface-3\">\n <form\n class=\"flex gap-xs pt-sm\"\n @submit.prevent=\"submitCustomAnswer\">\n <VvInputText\n :key=\"`${currentStep.id}-custom`\"\n v-model=\"customAnswer\"\n class=\"flex-1\"\n :name=\"`${currentStep.id}-custom`\"\n modifiers=\"compact\"\n :disabled=\"!isInteractive\"\n :placeholder=\"\n currentStep.placeholder ||\n $t('showForm.otherPlaceholder')\n \" />\n </form>\n </div>\n\n <!-- Footer navigation: shown for every step that does not submit\n itself on option pick (see `isSelfSubmitting`). -->\n <div\n v-if=\"!isSelfSubmitting\"\n class=\"px-sm py-10 bg-surface border-t border-surface-3 flex justify-between gap-12\">\n <VvButton\n v-if=\"canGoBack\"\n modifiers=\"action-quiet\"\n icon=\"ri:arrow-left-s-line\"\n :label=\"$t('action.back')\"\n @click.stop=\"goBack\" />\n <div v-else></div>\n\n <VvButton\n :modifiers=\"canGoNext ? 'action-quiet' : 'action-primary'\"\n :icon=\"\n canGoNext\n ? 'ri:arrow-right-s-line'\n : 'ri:send-plane-2-line'\n \"\n icon-position=\"right\"\n :label=\"canGoNext ? $t('action.next') : $t('action.submit')\"\n @click.stop=\"goNext\" />\n </div>\n </template>\n\n <!-- Output Available State (Multi-step Summary) -->\n <template v-else>\n <div class=\"px-sm py-12 bg-surface space-y-10\">\n <ul class=\"flex flex-col gap-8\">\n <li\n v-for=\"(step, index) in steps\"\n :key=\"index\"\n class=\"leading-normal\">\n <div class=\"text-12 text-word-3\">\n {{ step.question }}\n </div>\n <span class=\"text-12 text-word-2 font-semibold\">\n {{ getSummaryValue(step, index) }}\n </span>\n </li>\n </ul>\n </div>\n <div\n class=\"px-sm py-12 bg-surface border-t border-surface-3 flex items-center gap-10\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-word-3\" />\n <div class=\"flex flex-col\">\n <span class=\"font-medium text-12 text-word-1 leading-tight\">\n {{ $t('showForm.completed') }}\n </span>\n </div>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkToolShowSources-BvWOzVkQ.js","names":["$t"],"sources":["../../../../packages/components/src/chat/PkChunkPreview.vue","../../../../packages/components/src/chat/PkChunkPreview.vue","../../../../packages/components/src/chat/PkDocumentHeader.vue","../../../../packages/components/src/chat/PkDocumentHeader.vue","../../../../packages/components/src/chat/PkToolShowSources.vue","../../../../packages/components/src/chat/PkToolShowSources.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { stripMarkdown } from 'utils'\n\n const props = defineProps<{\n content: string\n similarity?: number\n maxSimilarity?: number\n expanded: boolean\n }>()\n\n const similarityPercent = computed(() => {\n if (props.similarity == null) {\n return null\n }\n const max =\n props.maxSimilarity && props.maxSimilarity > 0\n ? props.maxSimilarity\n : 1\n return Math.round((props.similarity / max) * 100)\n })\n</script>\n\n<template>\n <VvIcon name=\"ri:text-snippet\" class=\"shrink-0 text-12 text-word-4\" />\n <span class=\"text-10 text-word-2 truncate flex-1\" :title=\"content\">\n {{ stripMarkdown(content) }}\n </span>\n <span v-if=\"similarityPercent != null\" class=\"text-10 text-word-4 shrink-0\">\n {{ similarityPercent }}%\n </span>\n <VvIcon\n :name=\"expanded ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-14 text-word-4 shrink-0\" />\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { stripMarkdown } from 'utils'\n\n const props = defineProps<{\n content: string\n similarity?: number\n maxSimilarity?: number\n expanded: boolean\n }>()\n\n const similarityPercent = computed(() => {\n if (props.similarity == null) {\n return null\n }\n const max =\n props.maxSimilarity && props.maxSimilarity > 0\n ? props.maxSimilarity\n : 1\n return Math.round((props.similarity / max) * 100)\n })\n</script>\n\n<template>\n <VvIcon name=\"ri:text-snippet\" class=\"shrink-0 text-12 text-word-4\" />\n <span class=\"text-10 text-word-2 truncate flex-1\" :title=\"content\">\n {{ stripMarkdown(content) }}\n </span>\n <span v-if=\"similarityPercent != null\" class=\"text-10 text-word-4 shrink-0\">\n {{ similarityPercent }}%\n </span>\n <VvIcon\n :name=\"expanded ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-14 text-word-4 shrink-0\" />\n</template>\n","<script setup lang=\"ts\">\n import type { DocumentType } from 'models'\n import { getTypeIcon } from 'models'\n import PkUrl from '../PkUrl.vue'\n\n defineProps<{\n type: string\n name: string\n url?: string\n filename?: string\n }>()\n</script>\n\n<template>\n <VvIcon\n :name=\"getTypeIcon(type as DocumentType)\"\n class=\"shrink-0 text-14 text-word-3\" />\n <div class=\"flex flex-col gap-2 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate block\">\n {{ name }}\n </strong>\n <span v-if=\"filename\" class=\"text-10 text-word-4 truncate\">\n {{ filename }}\n </span>\n <span\n v-if=\"url\"\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-10\" />\n <PkUrl :url=\"url\" />\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import type { DocumentType } from 'models'\n import { getTypeIcon } from 'models'\n import PkUrl from '../PkUrl.vue'\n\n defineProps<{\n type: string\n name: string\n url?: string\n filename?: string\n }>()\n</script>\n\n<template>\n <VvIcon\n :name=\"getTypeIcon(type as DocumentType)\"\n class=\"shrink-0 text-14 text-word-3\" />\n <div class=\"flex flex-col gap-2 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate block\">\n {{ name }}\n </strong>\n <span v-if=\"filename\" class=\"text-10 text-word-4 truncate\">\n {{ filename }}\n </span>\n <span\n v-if=\"url\"\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-10\" />\n <PkUrl :url=\"url\" />\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive } from 'vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkChunkPreview from './PkChunkPreview.vue'\n import PkDocumentHeader from './PkDocumentHeader.vue'\n\n interface SourceItem {\n chunkId: string\n documentId: string\n documentName: string\n documentType: string\n language: string\n content: string\n chunkIndex?: number | null\n similarity?: number\n sourceUrl?: string\n sourceFilename?: string\n sourceDescription?: string\n sourceImageUrl?: string\n }\n\n const props = defineProps<{\n part: unknown\n onExpandContext?: (payload: {\n documentId: string\n chunkIndex: number\n }) => Promise<string>\n onDownload?: (documentId: string) => void\n }>()\n\n const toolPart = computed(() => {\n return props.part as {\n input?: {\n sources: SourceItem[]\n }\n }\n })\n\n const sources = computed(() => toolPart.value.input?.sources || [])\n\n const groupedSources = computed(() => {\n const groups = new Map<\n string,\n { document: SourceItem; chunks: SourceItem[] }\n >()\n for (const source of sources.value) {\n const existing = groups.get(source.documentId)\n if (existing) {\n existing.chunks.push(source)\n } else {\n groups.set(source.documentId, {\n document: source,\n chunks: [source],\n })\n }\n }\n return [...groups.values()]\n })\n\n const maxSimilarity = computed(() => {\n return Math.max(...sources.value.map((s) => s.similarity ?? 0), 0)\n })\n\n const expandedDocuments = reactive(new Set<string>())\n const expandedChunks = reactive(new Set<string>())\n const contextData = reactive(new Map<string, string>())\n const loadingContext = reactive(new Set<string>())\n\n const toggleDocument = (documentId: string) => {\n if (expandedDocuments.has(documentId)) {\n expandedDocuments.delete(documentId)\n } else {\n expandedDocuments.add(documentId)\n }\n }\n\n const toggleChunk = (chunkId: string) => {\n if (expandedChunks.has(chunkId)) {\n expandedChunks.delete(chunkId)\n } else {\n expandedChunks.add(chunkId)\n }\n }\n\n const handleExpandContext = async (source: SourceItem) => {\n if (source.chunkIndex == null || !props.onExpandContext) {\n return\n }\n loadingContext.add(source.chunkId)\n try {\n const content = await props.onExpandContext({\n documentId: source.documentId,\n chunkIndex: source.chunkIndex,\n })\n contextData.set(source.chunkId, content)\n } finally {\n loadingContext.delete(source.chunkId)\n }\n }\n\n const clearContext = (chunkId: string) => {\n contextData.delete(chunkId)\n }\n\n const showMore = ref(false)\n const visibleGroups = computed(() => {\n if (showMore.value) {\n return groupedSources.value\n }\n return groupedSources.value.slice(0, 3)\n })\n const hasMore = computed(() => groupedSources.value.length > 3)\n</script>\n\n<template>\n <div v-if=\"sources.length\" class=\"flex flex-col gap-xs w-full\">\n <div class=\"flex items-center gap-4 text-12 text-word-3 font-semibold\">\n <VvIcon name=\"ri:article-line\" class=\"text-14\" />\n {{ $t('label.sources') }}\n <span class=\"text-word-4 font-normal\">\n ({{ groupedSources.length }})\n </span>\n </div>\n\n <ul class=\"flex flex-col gap-xs\">\n <li\n v-for=\"group in visibleGroups\"\n :key=\"group.document.documentId\"\n class=\"border border-surface-3 rounded-xl overflow-hidden\">\n <!-- Document header -->\n <button\n class=\"flex items-center gap-8 p-8 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleDocument(group.document.documentId)\">\n <PkDocumentHeader\n :type=\"group.document.documentType\"\n :name=\"group.document.documentName\"\n :url=\"group.document.sourceUrl\"\n :filename=\"group.document.sourceFilename\" />\n <div class=\"flex items-center gap-4 shrink-0\">\n <span\n v-if=\"group.chunks.length > 1\"\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2\">\n {{ group.chunks.length }}\n </span>\n <span\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2 uppercase\">\n {{ group.document.language }}\n </span>\n <VvIcon\n :name=\"\n expandedDocuments.has(group.document.documentId)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"text-16 text-word-3\" />\n </div>\n </button>\n\n <!-- Expanded content -->\n <div\n v-if=\"expandedDocuments.has(group.document.documentId)\"\n class=\"border-t border-surface-3\">\n <PkStreamingMarkdown\n v-if=\"group.document.sourceDescription\"\n :markdown=\"group.document.sourceDescription\"\n class=\"px-8 py-4 text-10 text-word-3 bg-surface-1 border-b border-surface-3\" />\n\n <!-- Chunk excerpts -->\n <div\n v-for=\"chunk in group.chunks\"\n :key=\"chunk.chunkId\"\n class=\"border-b border-surface-3 last:border-b-0\">\n <button\n class=\"flex items-center gap-8 px-8 py-6 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleChunk(chunk.chunkId)\">\n <PkChunkPreview\n :content=\"chunk.content\"\n :similarity=\"chunk.similarity\"\n :max-similarity=\"maxSimilarity\"\n :expanded=\"expandedChunks.has(chunk.chunkId)\" />\n </button>\n\n <!-- Expanded chunk content -->\n <div\n v-if=\"expandedChunks.has(chunk.chunkId)\"\n class=\"bg-surface-1 border-t border-surface-3\">\n <div class=\"p-8 text-10 text-word-3\">\n <transition mode=\"out-in\">\n <!-- Context expansion -->\n <PkStreamingMarkdown\n v-if=\"contextData.has(chunk.chunkId)\"\n :markdown=\"\n contextData.get(chunk.chunkId)!\n \"\n class=\"wysiwyg\" />\n\n <!-- Chunk content -->\n <PkStreamingMarkdown\n v-else\n :markdown=\"chunk.content\"\n class=\"wysiwyg\" />\n </transition>\n </div>\n\n <!-- Actions bar -->\n <div\n class=\"flex items-center gap-8 px-8 py-6 border-t border-surface-3 bg-surface\">\n <!-- Expand / collapse context -->\n <button\n v-if=\"\n onExpandContext &&\n chunk.chunkIndex != null &&\n !contextData.has(chunk.chunkId)\n \"\n class=\"text-10 text-word-3 hover:text-word flex items-center gap-4 cursor-pointer\"\n :disabled=\"\n loadingContext.has(chunk.chunkId)\n \"\n @click=\"handleExpandContext(chunk)\">\n <VvIcon\n :name=\"\n loadingContext.has(chunk.chunkId)\n ? 'line-md:loading-loop'\n : 'ri:expand-up-down-line'\n \"\n class=\"text-12\" />\n {{ $t('label.expandContext') }}\n </button>\n <button\n v-else-if=\"contextData.has(chunk.chunkId)\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n @click=\"clearContext(chunk.chunkId)\">\n <VvIcon\n name=\"ri:collapse-vertical-line\"\n class=\"text-12\" />\n {{ $t('label.collapseContext') }}\n </button>\n\n <!-- Open source URL -->\n <a\n v-if=\"chunk.sourceUrl\"\n :href=\"chunk.sourceUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 ml-auto\">\n <VvIcon\n name=\"ri:external-link-line\"\n class=\"text-12\" />\n {{ $t('label.openSource') }}\n </a>\n\n <!-- Download file -->\n <button\n v-if=\"\n onDownload &&\n chunk.documentType === 'file'\n \"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n :class=\"{\n 'ml-auto': !chunk.sourceUrl,\n }\"\n @click=\"onDownload(chunk.documentId)\">\n <VvIcon\n name=\"ri:download-line\"\n class=\"text-12\" />\n {{ $t('label.downloadFile') }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </li>\n </ul>\n\n <!-- Show more -->\n <button\n v-if=\"hasMore\"\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: groupedSources.length - 3,\n })\n }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive } from 'vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkChunkPreview from './PkChunkPreview.vue'\n import PkDocumentHeader from './PkDocumentHeader.vue'\n\n interface SourceItem {\n chunkId: string\n documentId: string\n documentName: string\n documentType: string\n language: string\n content: string\n chunkIndex?: number | null\n similarity?: number\n sourceUrl?: string\n sourceFilename?: string\n sourceDescription?: string\n sourceImageUrl?: string\n }\n\n const props = defineProps<{\n part: unknown\n onExpandContext?: (payload: {\n documentId: string\n chunkIndex: number\n }) => Promise<string>\n onDownload?: (documentId: string) => void\n }>()\n\n const toolPart = computed(() => {\n return props.part as {\n input?: {\n sources: SourceItem[]\n }\n }\n })\n\n const sources = computed(() => toolPart.value.input?.sources || [])\n\n const groupedSources = computed(() => {\n const groups = new Map<\n string,\n { document: SourceItem; chunks: SourceItem[] }\n >()\n for (const source of sources.value) {\n const existing = groups.get(source.documentId)\n if (existing) {\n existing.chunks.push(source)\n } else {\n groups.set(source.documentId, {\n document: source,\n chunks: [source],\n })\n }\n }\n return [...groups.values()]\n })\n\n const maxSimilarity = computed(() => {\n return Math.max(...sources.value.map((s) => s.similarity ?? 0), 0)\n })\n\n const expandedDocuments = reactive(new Set<string>())\n const expandedChunks = reactive(new Set<string>())\n const contextData = reactive(new Map<string, string>())\n const loadingContext = reactive(new Set<string>())\n\n const toggleDocument = (documentId: string) => {\n if (expandedDocuments.has(documentId)) {\n expandedDocuments.delete(documentId)\n } else {\n expandedDocuments.add(documentId)\n }\n }\n\n const toggleChunk = (chunkId: string) => {\n if (expandedChunks.has(chunkId)) {\n expandedChunks.delete(chunkId)\n } else {\n expandedChunks.add(chunkId)\n }\n }\n\n const handleExpandContext = async (source: SourceItem) => {\n if (source.chunkIndex == null || !props.onExpandContext) {\n return\n }\n loadingContext.add(source.chunkId)\n try {\n const content = await props.onExpandContext({\n documentId: source.documentId,\n chunkIndex: source.chunkIndex,\n })\n contextData.set(source.chunkId, content)\n } finally {\n loadingContext.delete(source.chunkId)\n }\n }\n\n const clearContext = (chunkId: string) => {\n contextData.delete(chunkId)\n }\n\n const showMore = ref(false)\n const visibleGroups = computed(() => {\n if (showMore.value) {\n return groupedSources.value\n }\n return groupedSources.value.slice(0, 3)\n })\n const hasMore = computed(() => groupedSources.value.length > 3)\n</script>\n\n<template>\n <div v-if=\"sources.length\" class=\"flex flex-col gap-xs w-full\">\n <div class=\"flex items-center gap-4 text-12 text-word-3 font-semibold\">\n <VvIcon name=\"ri:article-line\" class=\"text-14\" />\n {{ $t('label.sources') }}\n <span class=\"text-word-4 font-normal\">\n ({{ groupedSources.length }})\n </span>\n </div>\n\n <ul class=\"flex flex-col gap-xs\">\n <li\n v-for=\"group in visibleGroups\"\n :key=\"group.document.documentId\"\n class=\"border border-surface-3 rounded-xl overflow-hidden\">\n <!-- Document header -->\n <button\n class=\"flex items-center gap-8 p-8 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleDocument(group.document.documentId)\">\n <PkDocumentHeader\n :type=\"group.document.documentType\"\n :name=\"group.document.documentName\"\n :url=\"group.document.sourceUrl\"\n :filename=\"group.document.sourceFilename\" />\n <div class=\"flex items-center gap-4 shrink-0\">\n <span\n v-if=\"group.chunks.length > 1\"\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2\">\n {{ group.chunks.length }}\n </span>\n <span\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2 uppercase\">\n {{ group.document.language }}\n </span>\n <VvIcon\n :name=\"\n expandedDocuments.has(group.document.documentId)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"text-16 text-word-3\" />\n </div>\n </button>\n\n <!-- Expanded content -->\n <div\n v-if=\"expandedDocuments.has(group.document.documentId)\"\n class=\"border-t border-surface-3\">\n <PkStreamingMarkdown\n v-if=\"group.document.sourceDescription\"\n :markdown=\"group.document.sourceDescription\"\n class=\"px-8 py-4 text-10 text-word-3 bg-surface-1 border-b border-surface-3\" />\n\n <!-- Chunk excerpts -->\n <div\n v-for=\"chunk in group.chunks\"\n :key=\"chunk.chunkId\"\n class=\"border-b border-surface-3 last:border-b-0\">\n <button\n class=\"flex items-center gap-8 px-8 py-6 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleChunk(chunk.chunkId)\">\n <PkChunkPreview\n :content=\"chunk.content\"\n :similarity=\"chunk.similarity\"\n :max-similarity=\"maxSimilarity\"\n :expanded=\"expandedChunks.has(chunk.chunkId)\" />\n </button>\n\n <!-- Expanded chunk content -->\n <div\n v-if=\"expandedChunks.has(chunk.chunkId)\"\n class=\"bg-surface-1 border-t border-surface-3\">\n <div class=\"p-8 text-10 text-word-3\">\n <transition mode=\"out-in\">\n <!-- Context expansion -->\n <PkStreamingMarkdown\n v-if=\"contextData.has(chunk.chunkId)\"\n :markdown=\"\n contextData.get(chunk.chunkId)!\n \"\n class=\"wysiwyg\" />\n\n <!-- Chunk content -->\n <PkStreamingMarkdown\n v-else\n :markdown=\"chunk.content\"\n class=\"wysiwyg\" />\n </transition>\n </div>\n\n <!-- Actions bar -->\n <div\n class=\"flex items-center gap-8 px-8 py-6 border-t border-surface-3 bg-surface\">\n <!-- Expand / collapse context -->\n <button\n v-if=\"\n onExpandContext &&\n chunk.chunkIndex != null &&\n !contextData.has(chunk.chunkId)\n \"\n class=\"text-10 text-word-3 hover:text-word flex items-center gap-4 cursor-pointer\"\n :disabled=\"\n loadingContext.has(chunk.chunkId)\n \"\n @click=\"handleExpandContext(chunk)\">\n <VvIcon\n :name=\"\n loadingContext.has(chunk.chunkId)\n ? 'line-md:loading-loop'\n : 'ri:expand-up-down-line'\n \"\n class=\"text-12\" />\n {{ $t('label.expandContext') }}\n </button>\n <button\n v-else-if=\"contextData.has(chunk.chunkId)\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n @click=\"clearContext(chunk.chunkId)\">\n <VvIcon\n name=\"ri:collapse-vertical-line\"\n class=\"text-12\" />\n {{ $t('label.collapseContext') }}\n </button>\n\n <!-- Open source URL -->\n <a\n v-if=\"chunk.sourceUrl\"\n :href=\"chunk.sourceUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 ml-auto\">\n <VvIcon\n name=\"ri:external-link-line\"\n class=\"text-12\" />\n {{ $t('label.openSource') }}\n </a>\n\n <!-- Download file -->\n <button\n v-if=\"\n onDownload &&\n chunk.documentType === 'file'\n \"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n :class=\"{\n 'ml-auto': !chunk.sourceUrl,\n }\"\n @click=\"onDownload(chunk.documentId)\">\n <VvIcon\n name=\"ri:download-line\"\n class=\"text-12\" />\n {{ $t('label.downloadFile') }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </li>\n </ul>\n\n <!-- Show more -->\n <button\n v-if=\"hasMore\"\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: groupedSources.length - 3,\n })\n }}\n </button>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"PkUrl-xUBKVJCv.js","names":[],"sources":["../../../../packages/components/src/PkUrl.vue","../../../../packages/components/src/PkUrl.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useShortUrlResolver } from 'composables'\n\n interface Props {\n url: string\n maxLength?: number\n domainClass?: string\n target?: '_blank' | '_self' | '_parent' | '_top'\n rel?: string\n muted?: boolean\n }\n\n const props = withDefaults(defineProps<Props>(), {\n maxLength: 60,\n domainClass: undefined,\n target: '_blank',\n rel: 'noopener noreferrer',\n muted: false,\n })\n\n // Only the displayed URL text is resolved to its real target; the href is\n // kept as the short url so the redirect endpoint still handles tracking.\n const { resolve } = useShortUrlResolver()\n const resolved = resolve(() => props.url)\n const displayUrl = computed(() => resolved.value.url)\n const isResolving = computed(() => resolved.value.loading)\n\n const parsedUrl = computed(() => {\n try {\n const urlObj = new URL(displayUrl.value ?? props.url)\n let hostname = urlObj.hostname\n\n // Remove www. prefix\n if (hostname.startsWith('www.')) hostname = hostname.substring(4)\n\n // Get pathname and remove trailing slash\n let pathname = urlObj.pathname\n if (pathname === '/') pathname = ''\n\n // Split pathname into segments\n const segments = pathname\n .split('/')\n .filter((segment) => segment.length > 0)\n\n return {\n domain: hostname,\n segments,\n fullPath: hostname + pathname,\n }\n } catch {\n // If URL parsing fails, return the original URL\n const fallback = displayUrl.value ?? props.url\n return {\n domain: fallback,\n segments: [],\n fullPath: fallback,\n }\n }\n })\n\n const truncatedUrl = computed(() => {\n if (parsedUrl.value.fullPath.length <= props.maxLength)\n return parsedUrl.value\n\n // If too long, truncate segments from the beginning\n const domain = parsedUrl.value.domain\n const allSegments = parsedUrl.value.segments\n\n // Always show at least the last segment\n if (allSegments.length === 0) {\n return {\n domain,\n segments: [],\n fullPath: domain,\n }\n }\n\n const segments: string[] = []\n\n // Calculate from the end\n let currentLength = domain.length\n\n // Try to fit segments from the end\n for (let i = allSegments.length - 1; i >= 0; i--) {\n const segment = allSegments[i]\n\n // Always include at least the last segment\n if (segments.length === 0) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else if (currentLength + segment.length + 1 <= props.maxLength) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else {\n segments.unshift('...')\n break\n }\n }\n\n return {\n domain,\n segments,\n fullPath: domain + '/' + segments.join('/'),\n }\n })\n</script>\n\n<template>\n <Component\n :is=\"muted ? 'span' : 'a'\"\n class=\"pk-url\"\n :href=\"muted ? undefined : props.url\"\n :target=\"muted ? undefined : props.target\"\n :rel=\"muted ? undefined : props.rel\">\n <span v-if=\"isResolving\" class=\"pk-url__skeleton vv-skeleton\">\n <span class=\"vv-skeleton__item\" />\n </span>\n <template v-else>\n <span :class=\"['pk-url__domain', domainClass]\">{{\n truncatedUrl.domain\n }}</span>\n <template\n v-for=\"(segment, index) in truncatedUrl.segments\"\n :key=\"index\">\n <span class=\"pk-url__separator\">›</span>\n <span class=\"pk-url__segment\">{{ segment }}</span>\n </template>\n </template>\n </Component>\n</template>\n\n<style lang=\"scss\">\n @layer volver.components {\n .pk-url {\n display: inline-flex;\n align-items: center;\n gap: 0.5ch;\n }\n\n .pk-url__skeleton {\n display: inline-flex;\n width: 12ch;\n max-width: 100%;\n\n .vv-skeleton__item {\n width: 100%;\n height: 1em;\n border-radius: 0.25em;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useShortUrlResolver } from 'composables'\n\n interface Props {\n url: string\n maxLength?: number\n domainClass?: string\n target?: '_blank' | '_self' | '_parent' | '_top'\n rel?: string\n muted?: boolean\n }\n\n const props = withDefaults(defineProps<Props>(), {\n maxLength: 60,\n domainClass: undefined,\n target: '_blank',\n rel: 'noopener noreferrer',\n muted: false,\n })\n\n // Only the displayed URL text is resolved to its real target; the href is\n // kept as the short url so the redirect endpoint still handles tracking.\n const { resolve } = useShortUrlResolver()\n const resolved = resolve(() => props.url)\n const displayUrl = computed(() => resolved.value.url)\n const isResolving = computed(() => resolved.value.loading)\n\n const parsedUrl = computed(() => {\n try {\n const urlObj = new URL(displayUrl.value ?? props.url)\n let hostname = urlObj.hostname\n\n // Remove www. prefix\n if (hostname.startsWith('www.')) hostname = hostname.substring(4)\n\n // Get pathname and remove trailing slash\n let pathname = urlObj.pathname\n if (pathname === '/') pathname = ''\n\n // Split pathname into segments\n const segments = pathname\n .split('/')\n .filter((segment) => segment.length > 0)\n\n return {\n domain: hostname,\n segments,\n fullPath: hostname + pathname,\n }\n } catch {\n // If URL parsing fails, return the original URL\n const fallback = displayUrl.value ?? props.url\n return {\n domain: fallback,\n segments: [],\n fullPath: fallback,\n }\n }\n })\n\n const truncatedUrl = computed(() => {\n if (parsedUrl.value.fullPath.length <= props.maxLength)\n return parsedUrl.value\n\n // If too long, truncate segments from the beginning\n const domain = parsedUrl.value.domain\n const allSegments = parsedUrl.value.segments\n\n // Always show at least the last segment\n if (allSegments.length === 0) {\n return {\n domain,\n segments: [],\n fullPath: domain,\n }\n }\n\n const segments: string[] = []\n\n // Calculate from the end\n let currentLength = domain.length\n\n // Try to fit segments from the end\n for (let i = allSegments.length - 1; i >= 0; i--) {\n const segment = allSegments[i]\n\n // Always include at least the last segment\n if (segments.length === 0) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else if (currentLength + segment.length + 1 <= props.maxLength) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else {\n segments.unshift('...')\n break\n }\n }\n\n return {\n domain,\n segments,\n fullPath: domain + '/' + segments.join('/'),\n }\n })\n</script>\n\n<template>\n <Component\n :is=\"muted ? 'span' : 'a'\"\n class=\"pk-url\"\n :href=\"muted ? undefined : props.url\"\n :target=\"muted ? undefined : props.target\"\n :rel=\"muted ? undefined : props.rel\">\n <span v-if=\"isResolving\" class=\"pk-url__skeleton vv-skeleton\">\n <span class=\"vv-skeleton__item\" />\n </span>\n <template v-else>\n <span :class=\"['pk-url__domain', domainClass]\">{{\n truncatedUrl.domain\n }}</span>\n <template\n v-for=\"(segment, index) in truncatedUrl.segments\"\n :key=\"index\">\n <span class=\"pk-url__separator\">›</span>\n <span class=\"pk-url__segment\">{{ segment }}</span>\n </template>\n </template>\n </Component>\n</template>\n\n<style lang=\"scss\">\n @layer volver.components {\n .pk-url {\n display: inline-flex;\n align-items: center;\n gap: 0.5ch;\n }\n\n .pk-url__skeleton {\n display: inline-flex;\n width: 12ch;\n max-width: 100%;\n\n .vv-skeleton__item {\n width: 100%;\n height: 1em;\n border-radius: 0.25em;\n }\n }\n }\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EAaI,IAAM,IAAQ,GAUR,EAAE,eAAY,EAAoB,GAClC,IAAW,QAAc,EAAM,GAAG,GAClC,IAAa,QAAe,EAAS,MAAM,GAAG,GAC9C,IAAc,QAAe,EAAS,MAAM,OAAO,GAEnD,IAAY,QAAe;GAC7B,IAAI;IACA,IAAM,IAAS,IAAI,IAAI,EAAW,SAAS,EAAM,GAAG,GAChD,IAAW,EAAO;IAGtB,AAAI,EAAS,WAAW,MAAM,MAAG,IAAW,EAAS,UAAU,CAAC;IAGhE,IAAI,IAAW,EAAO;IACtB,AAAI,MAAa,QAAK,IAAW;IAGjC,IAAM,IAAW,EACZ,MAAM,GAAG,CAAA,CACT,QAAQ,MAAY,EAAQ,SAAS,CAAC;IAE3C,OAAO;KACH,QAAQ;KACR;KACA,UAAU,IAAW;IACzB;GACJ,QAAQ;IAEJ,IAAM,IAAW,EAAW,SAAS,EAAM;IAC3C,OAAO;KACH,QAAQ;KACR,UAAU,CAAC;KACX,UAAU;IACd;GACJ;EACJ,CAAC,GAEK,IAAe,QAAe;GAChC,IAAI,EAAU,MAAM,SAAS,UAAU,EAAM,WACzC,OAAO,EAAU;GAGrB,IAAM,IAAS,EAAU,MAAM,QACzB,IAAc,EAAU,MAAM;GAGpC,IAAI,EAAY,WAAW,GACvB,OAAO;IACH;IACA,UAAU,CAAC;IACX,UAAU;GACd;GAGJ,IAAM,IAAqB,CAAC,GAGxB,IAAgB,EAAO;GAG3B,KAAK,IAAI,IAAI,EAAY,SAAS,GAAG,KAAK,GAAG,KAAK;IAC9C,IAAM,IAAU,EAAY;IAG5B,IAAI,EAAS,WAAW,GAEpB,AADA,KAAiB,EAAQ,SAAS,GAClC,EAAS,QAAQ,CAAO;SACrB,IAAI,IAAgB,EAAQ,SAAS,KAAK,EAAM,WAEnD,AADA,KAAiB,EAAQ,SAAS,GAClC,EAAS,QAAQ,CAAO;SACrB;KACH,EAAS,QAAQ,KAAK;KACtB;IACJ;GACJ;GAEA,OAAO;IACH;IACA;IACA,UAAU,IAAS,MAAM,EAAS,KAAK,GAAG;GAC9C;EACJ,CAAC;oBAID,EAAA,GAAA,EAoBY,EAnBH,EAAA,QAAK,SAAA,GAAA,GAAA;GACV,OAAM;GACL,MAAM,EAAA,QAAQ,KAAA,IAAY,EAAM;GAChC,QAAQ,EAAA,QAAQ,KAAA,IAAY,EAAM;GAClC,KAAK,EAAA,QAAQ,KAAA,IAAY,EAAM;;GAChC,SAAA,QAEO,CAFK,EAAA,SAAZ,EAAA,GAAA,EAEO,QAFP,GAEO,CAAA,GAAA,AAAA,EAAA,OAAA,CADH,EAAkC,QAAA,EAA5B,OAAM,oBAAmB,GAAA,MAAA,EAAA,CAAA,CAAA,CAAA,MAEnC,EAAA,GAAA,EAUW,GAAA,EAAA,KAAA,EAAA,GAAA,CATP,EAES,QAAA,EAFF,OAAK,EAAA,CAAA,kBAAqB,EAAA,WAAW,CAAA,EACxC,GAAA,EAAA,EAAA,MAAa,MAAM,GAAA,CAAA,IAEvB,EAAA,EAAA,GAAA,EAKW,GAAA,MAAA,EAJoB,EAAA,MAAa,WAAhC,GAAS,OACX,EAAA,GAAA,EAAA,GAAA,EAAA,KAAA,EAAK,GAAA,CACX,AAAA,EAAA,OAAA,EAAwC,QAAA,EAAlC,OAAM,oBAAmB,GAAC,KAAC,EAAA,GACjC,EAAkD,QAAlD,GAAkD,EAAjB,CAAO,GAAA,CAAA,CAAA,GAAA,EAAA"}