@8wave/ai-elements 0.90.0 → 0.91.0

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 (188) hide show
  1. package/dist/_chunks/{Document-BajUssb0.js → Document-OYsMoc9f.js} +46 -9
  2. package/dist/_chunks/Document-OYsMoc9f.js.map +1 -0
  3. package/dist/_chunks/{PkToolShowSources-CZWBpBBV.js → PkToolShowSources-BHLHRoy2.js} +2 -2
  4. package/dist/_chunks/{PkToolShowSources-CZWBpBBV.js.map → PkToolShowSources-BHLHRoy2.js.map} +1 -1
  5. package/dist/_chunks/{PkToolShowSuggestedReply-1j4lOkRj.js → PkToolShowSuggestedReply-CUk_SK-9.js} +2 -2
  6. package/dist/_chunks/PkToolShowSuggestedReply-CUk_SK-9.js.map +1 -0
  7. package/dist/ai-elements.es.js +4099 -3985
  8. package/dist/ai-elements.es.js.map +1 -1
  9. package/dist-vue/PkChatbot.js +1 -1
  10. package/dist-vue/PkChatbotAuth.js +1 -1
  11. package/dist-vue/PkChatbotEmptyState.js +1 -1
  12. package/dist-vue/PkChatbotFeedbackForm.js +1 -1
  13. package/dist-vue/PkChatbotFilePreview.js +1 -1
  14. package/dist-vue/PkChatbotInput.js +1 -1
  15. package/dist-vue/PkChatbotMessageFeedback.js +1 -1
  16. package/dist-vue/PkChatbotMessages.js +1 -1
  17. package/dist-vue/PkChatbotOutlineRail.js +1 -1
  18. package/dist-vue/PkChatbotStepToolDetail.js +1 -1
  19. package/dist-vue/PkChatbotSteps.js +1 -1
  20. package/dist-vue/PkChatbotViewChat.js +1 -1
  21. package/dist-vue/PkChatbotViewConversations.js +1 -1
  22. package/dist-vue/PkChatbotViewFiles.js +1 -1
  23. package/dist-vue/PkChatbotViewProfile.js +1 -1
  24. package/dist-vue/_chunks/{Document-Dvf2nw6v.js → Document-c1tATTDs.js} +46 -9
  25. package/dist-vue/_chunks/{Document-Dvf2nw6v.js.map → Document-c1tATTDs.js.map} +1 -1
  26. package/dist-vue/_chunks/{PkAvatar-Dv3e4aUl.js → PkAvatar-Bpf6cD4N.js} +1 -1
  27. package/dist-vue/_chunks/{PkAvatar-Dv3e4aUl.js.map → PkAvatar-Bpf6cD4N.js.map} +1 -1
  28. package/dist-vue/_chunks/PkChatbot-DpfxBVue.js +237 -0
  29. package/dist-vue/_chunks/PkChatbot-DpfxBVue.js.map +1 -0
  30. package/dist-vue/_chunks/{PkChatbotAuth-CFqcYxj9.js → PkChatbotAuth-uAPZRV6N.js} +2 -2
  31. package/dist-vue/_chunks/{PkChatbotAuth-CFqcYxj9.js.map → PkChatbotAuth-uAPZRV6N.js.map} +1 -1
  32. package/dist-vue/_chunks/{PkChatbotEmptyState-C70aTfG-.js → PkChatbotEmptyState-JTlPPXdI.js} +2 -2
  33. package/dist-vue/_chunks/{PkChatbotEmptyState-C70aTfG-.js.map → PkChatbotEmptyState-JTlPPXdI.js.map} +1 -1
  34. package/dist-vue/_chunks/{PkChatbotFeedbackForm-Ds2-nOaU.js → PkChatbotFeedbackForm-CHXxzWqQ.js} +2 -2
  35. package/dist-vue/_chunks/{PkChatbotFeedbackForm-Ds2-nOaU.js.map → PkChatbotFeedbackForm-CHXxzWqQ.js.map} +1 -1
  36. package/dist-vue/_chunks/{PkChatbotFilePreview-DG887KhT.js → PkChatbotFilePreview-Bf1bdZn8.js} +61 -54
  37. package/dist-vue/_chunks/PkChatbotFilePreview-Bf1bdZn8.js.map +1 -0
  38. package/dist-vue/_chunks/{PkChatbotInput-BTszEUj2.js → PkChatbotInput-DWS7vqC5.js} +152 -124
  39. package/dist-vue/_chunks/PkChatbotInput-DWS7vqC5.js.map +1 -0
  40. package/dist-vue/_chunks/{PkChatbotMessageFeedback-Cp0zzCP8.js → PkChatbotMessageFeedback-DAqlgPrz.js} +1 -1
  41. package/dist-vue/_chunks/{PkChatbotMessageFeedback-Cp0zzCP8.js.map → PkChatbotMessageFeedback-DAqlgPrz.js.map} +1 -1
  42. package/dist-vue/_chunks/{PkChatbotMessages-DLP7utmG.js → PkChatbotMessages-BR5JUybd.js} +208 -212
  43. package/dist-vue/_chunks/PkChatbotMessages-BR5JUybd.js.map +1 -0
  44. package/dist-vue/_chunks/{PkChatbotOutlineRail-CAIfj2dI.js → PkChatbotOutlineRail-BXrdMIrA.js} +1 -1
  45. package/dist-vue/_chunks/{PkChatbotOutlineRail-CAIfj2dI.js.map → PkChatbotOutlineRail-BXrdMIrA.js.map} +1 -1
  46. package/dist-vue/_chunks/{PkChatbotStepToolDetail-C-ucv_wp.js → PkChatbotStepToolDetail-C2nWevnD.js} +2 -2
  47. package/dist-vue/_chunks/{PkChatbotStepToolDetail-C-ucv_wp.js.map → PkChatbotStepToolDetail-C2nWevnD.js.map} +1 -1
  48. package/dist-vue/_chunks/{PkChatbotSteps-CBOhYZ10.js → PkChatbotSteps-5Ahr-zpZ.js} +4 -4
  49. package/dist-vue/_chunks/{PkChatbotSteps-CBOhYZ10.js.map → PkChatbotSteps-5Ahr-zpZ.js.map} +1 -1
  50. package/dist-vue/_chunks/{PkChatbotViewChat-BZ9IH-sz.js → PkChatbotViewChat-C_t6qEEV.js} +64 -55
  51. package/dist-vue/_chunks/PkChatbotViewChat-C_t6qEEV.js.map +1 -0
  52. package/dist-vue/_chunks/{PkChatbotViewConversations-q5KPJt_D.js → PkChatbotViewConversations-CEaEsnBy.js} +4 -4
  53. package/dist-vue/_chunks/{PkChatbotViewConversations-q5KPJt_D.js.map → PkChatbotViewConversations-CEaEsnBy.js.map} +1 -1
  54. package/dist-vue/_chunks/PkChatbotViewFiles-Bzcn8AEo.js +114 -0
  55. package/dist-vue/_chunks/PkChatbotViewFiles-Bzcn8AEo.js.map +1 -0
  56. package/dist-vue/_chunks/{PkChatbotViewProfile-DTbQlkVL.js → PkChatbotViewProfile-CfFeqjse.js} +4 -4
  57. package/dist-vue/_chunks/{PkChatbotViewProfile-DTbQlkVL.js.map → PkChatbotViewProfile-CfFeqjse.js.map} +1 -1
  58. package/dist-vue/_chunks/{PkRelativeTime-D72zYq42.js → PkRelativeTime-2HoOjv_T.js} +1 -1
  59. package/dist-vue/_chunks/{PkRelativeTime-D72zYq42.js.map → PkRelativeTime-2HoOjv_T.js.map} +1 -1
  60. package/dist-vue/_chunks/{PkStreamingMarkdown-CVAsbuWi.js → PkStreamingMarkdown-C_LQOTVJ.js} +1 -1
  61. package/dist-vue/_chunks/{PkStreamingMarkdown-CVAsbuWi.js.map → PkStreamingMarkdown-C_LQOTVJ.js.map} +1 -1
  62. package/dist-vue/_chunks/{PkSvgViewer-CTYxVK7q.js → PkSvgViewer-Dtx4uluo.js} +2 -2
  63. package/dist-vue/_chunks/{PkSvgViewer-CTYxVK7q.js.map → PkSvgViewer-Dtx4uluo.js.map} +1 -1
  64. package/dist-vue/_chunks/{PkToolShowArtifact-Csjfo9Xl.js → PkToolShowArtifact-CoYM3_A3.js} +3 -3
  65. package/dist-vue/_chunks/{PkToolShowArtifact-Csjfo9Xl.js.map → PkToolShowArtifact-CoYM3_A3.js.map} +1 -1
  66. package/dist-vue/_chunks/{PkToolShowCalendarEvent-B2tHaoqo.js → PkToolShowCalendarEvent-BZYdRwid.js} +2 -2
  67. package/dist-vue/_chunks/{PkToolShowCalendarEvent-B2tHaoqo.js.map → PkToolShowCalendarEvent-BZYdRwid.js.map} +1 -1
  68. package/dist-vue/_chunks/{PkToolShowComparison-BLDtjv3w.js → PkToolShowComparison-C4OP5c1t.js} +2 -2
  69. package/dist-vue/_chunks/{PkToolShowComparison-BLDtjv3w.js.map → PkToolShowComparison-C4OP5c1t.js.map} +1 -1
  70. package/dist-vue/_chunks/{PkToolShowContactForm-CdgD4fd8.js → PkToolShowContactForm-DjX9G8CD.js} +5 -5
  71. package/dist-vue/_chunks/{PkToolShowContactForm-CdgD4fd8.js.map → PkToolShowContactForm-DjX9G8CD.js.map} +1 -1
  72. package/dist-vue/_chunks/{PkToolShowDiagram-DGYQjjGj.js → PkToolShowDiagram-DHwAckwN.js} +2 -2
  73. package/dist-vue/_chunks/{PkToolShowDiagram-DGYQjjGj.js.map → PkToolShowDiagram-DHwAckwN.js.map} +1 -1
  74. package/dist-vue/_chunks/{PkToolShowEmail-D42WDTO9.js → PkToolShowEmail-5waUO5cm.js} +2 -2
  75. package/dist-vue/_chunks/{PkToolShowEmail-D42WDTO9.js.map → PkToolShowEmail-5waUO5cm.js.map} +1 -1
  76. package/dist-vue/_chunks/{PkToolShowForm-BhVSPIAR.js → PkToolShowForm-C7bOdRse.js} +3 -3
  77. package/dist-vue/_chunks/{PkToolShowForm-BhVSPIAR.js.map → PkToolShowForm-C7bOdRse.js.map} +1 -1
  78. package/dist-vue/_chunks/{PkToolShowImageGallery-WANi2hA0.js → PkToolShowImageGallery-D3bqInQn.js} +3 -3
  79. package/dist-vue/_chunks/{PkToolShowImageGallery-WANi2hA0.js.map → PkToolShowImageGallery-D3bqInQn.js.map} +1 -1
  80. package/dist-vue/_chunks/{PkToolShowLocation-RVbaQoU5.js → PkToolShowLocation-DE-dbJEw.js} +2 -2
  81. package/dist-vue/_chunks/{PkToolShowLocation-RVbaQoU5.js.map → PkToolShowLocation-DE-dbJEw.js.map} +1 -1
  82. package/dist-vue/_chunks/{PkToolShowMessage-D8UbJcas.js → PkToolShowMessage-DCbW7AYl.js} +2 -2
  83. package/dist-vue/_chunks/{PkToolShowMessage-D8UbJcas.js.map → PkToolShowMessage-DCbW7AYl.js.map} +1 -1
  84. package/dist-vue/_chunks/{PkToolShowProductList-BQts3MBy.js → PkToolShowProductList-Blsv3Cio.js} +3 -3
  85. package/dist-vue/_chunks/{PkToolShowProductList-BQts3MBy.js.map → PkToolShowProductList-Blsv3Cio.js.map} +1 -1
  86. package/dist-vue/_chunks/{PkToolShowQrCode-BybXCt5e.js → PkToolShowQrCode-BbMTgOW3.js} +2 -2
  87. package/dist-vue/_chunks/{PkToolShowQrCode-BybXCt5e.js.map → PkToolShowQrCode-BbMTgOW3.js.map} +1 -1
  88. package/dist-vue/_chunks/{PkToolShowSources-DRewYfe-.js → PkToolShowSources-B9d3iLVk.js} +5 -5
  89. package/dist-vue/_chunks/{PkToolShowSources-DRewYfe-.js.map → PkToolShowSources-B9d3iLVk.js.map} +1 -1
  90. package/dist-vue/_chunks/{PkToolShowSuggestedReply-DKiYUsaZ.js → PkToolShowSuggestedReply-B516t3Nb.js} +2 -2
  91. package/dist-vue/_chunks/PkToolShowSuggestedReply-B516t3Nb.js.map +1 -0
  92. package/dist-vue/_chunks/{PkToolShowWebPages-IV3OzwJe.js → PkToolShowWebPages-BF2aHJGK.js} +3 -3
  93. package/dist-vue/_chunks/{PkToolShowWebPages-IV3OzwJe.js.map → PkToolShowWebPages-BF2aHJGK.js.map} +1 -1
  94. package/dist-vue/_chunks/{PkUrl-a6l-l4Au.js → PkUrl-DA-6t_2f.js} +1 -1
  95. package/dist-vue/_chunks/{PkUrl-a6l-l4Au.js.map → PkUrl-DA-6t_2f.js.map} +1 -1
  96. package/dist-vue/_chunks/{chatbotProfileContext-R95aQ5bH.js → chatbotProfileContext-R1zi561l.js} +1 -1
  97. package/dist-vue/_chunks/{chatbotProfileContext-R95aQ5bH.js.map → chatbotProfileContext-R1zi561l.js.map} +1 -1
  98. package/dist-vue/_chunks/{constants-DpYDYnZU.js → constants-CWT3GgSb.js} +2 -2
  99. package/dist-vue/_chunks/constants-CWT3GgSb.js.map +1 -0
  100. package/dist-vue/_chunks/{createChatbotApiClient-DXmWtu3b.js → createChatbotApiClient-kU7rCKwN.js} +13 -5
  101. package/dist-vue/_chunks/createChatbotApiClient-kU7rCKwN.js.map +1 -0
  102. package/dist-vue/_chunks/{dist-BbAXqaCM.js → dist-0LxfGh4q.js} +2 -2
  103. package/dist-vue/_chunks/{dist-BbAXqaCM.js.map → dist-0LxfGh4q.js.map} +1 -1
  104. package/dist-vue/_chunks/{dist-BU1gMaJv.js → dist-46HfWmpr.js} +2 -2
  105. package/dist-vue/_chunks/{dist-BU1gMaJv.js.map → dist-46HfWmpr.js.map} +1 -1
  106. package/dist-vue/_chunks/{dist-CpoLpTWk.js → dist-8YCZENrl.js} +3 -3
  107. package/dist-vue/_chunks/{dist-CpoLpTWk.js.map → dist-8YCZENrl.js.map} +1 -1
  108. package/dist-vue/_chunks/{dist-l7mimsz6.js → dist-B34dY8rD.js} +2 -2
  109. package/dist-vue/_chunks/{dist-l7mimsz6.js.map → dist-B34dY8rD.js.map} +1 -1
  110. package/dist-vue/_chunks/{dist-CYrZgvgZ.js → dist-BfbUNalO.js} +4 -4
  111. package/dist-vue/_chunks/{dist-CYrZgvgZ.js.map → dist-BfbUNalO.js.map} +1 -1
  112. package/dist-vue/_chunks/{dist-Begygip9.js → dist-BmRGnWn9.js} +3 -3
  113. package/dist-vue/_chunks/{dist-Begygip9.js.map → dist-BmRGnWn9.js.map} +1 -1
  114. package/dist-vue/_chunks/{dist-BGl_mYmF.js → dist-BoaZDzxm.js} +4 -4
  115. package/dist-vue/_chunks/{dist-BGl_mYmF.js.map → dist-BoaZDzxm.js.map} +1 -1
  116. package/dist-vue/_chunks/{dist-DEyUvwtq.js → dist-Bq9kJLlA.js} +3 -3
  117. package/dist-vue/_chunks/{dist-DEyUvwtq.js.map → dist-Bq9kJLlA.js.map} +1 -1
  118. package/dist-vue/_chunks/{dist-nZs6UeVS.js → dist-Bs-QG-km.js} +1 -1
  119. package/dist-vue/_chunks/{dist-nZs6UeVS.js.map → dist-Bs-QG-km.js.map} +1 -1
  120. package/dist-vue/_chunks/{dist-DinYvSmw.js → dist-BxqLQ_dy.js} +2 -2
  121. package/dist-vue/_chunks/{dist-DinYvSmw.js.map → dist-BxqLQ_dy.js.map} +1 -1
  122. package/dist-vue/_chunks/{dist-Dj7rLAD9.js → dist-Cc0Lyhoq.js} +3 -3
  123. package/dist-vue/_chunks/{dist-Dj7rLAD9.js.map → dist-Cc0Lyhoq.js.map} +1 -1
  124. package/dist-vue/_chunks/{dist-Jh4J_VuU.js → dist-CiYFTV6m.js} +3 -3
  125. package/dist-vue/_chunks/{dist-Jh4J_VuU.js.map → dist-CiYFTV6m.js.map} +1 -1
  126. package/dist-vue/_chunks/{dist-BzsV0Mzp.js → dist-CrcvoPSj.js} +2 -2
  127. package/dist-vue/_chunks/{dist-BzsV0Mzp.js.map → dist-CrcvoPSj.js.map} +1 -1
  128. package/dist-vue/_chunks/{dist-CABEM3v-.js → dist-DK5UPLjU.js} +4 -4
  129. package/dist-vue/_chunks/{dist-CABEM3v-.js.map → dist-DK5UPLjU.js.map} +1 -1
  130. package/dist-vue/_chunks/{dist-uvKhW3hB.js → dist-Da4-ZEZn.js} +3 -3
  131. package/dist-vue/_chunks/{dist-uvKhW3hB.js.map → dist-Da4-ZEZn.js.map} +1 -1
  132. package/dist-vue/_chunks/{dist-SbwxxzT7.js → dist-Dc77ClI_.js} +3 -3
  133. package/dist-vue/_chunks/{dist-SbwxxzT7.js.map → dist-Dc77ClI_.js.map} +1 -1
  134. package/dist-vue/_chunks/{dist-t9ZGnt-g.js → dist-LN9oA4PR.js} +2 -2
  135. package/dist-vue/_chunks/{dist-t9ZGnt-g.js.map → dist-LN9oA4PR.js.map} +1 -1
  136. package/dist-vue/_chunks/{dist-DKzsFjYx.js → dist-gx9K9AtO.js} +5 -5
  137. package/dist-vue/_chunks/{dist-DKzsFjYx.js.map → dist-gx9K9AtO.js.map} +1 -1
  138. package/dist-vue/_chunks/{dist-Bnrde8nq.js → dist-iDVEgNgZ.js} +3 -3
  139. package/dist-vue/_chunks/{dist-Bnrde8nq.js.map → dist-iDVEgNgZ.js.map} +1 -1
  140. package/dist-vue/_chunks/{dist-Byr8BhEG.js → dist-m2_O1KGc.js} +3 -3
  141. package/dist-vue/_chunks/{dist-Byr8BhEG.js.map → dist-m2_O1KGc.js.map} +1 -1
  142. package/dist-vue/_chunks/{dist-C08Qizu9.js → dist-qArLqzVS.js} +2 -2
  143. package/dist-vue/_chunks/{dist-C08Qizu9.js.map → dist-qArLqzVS.js.map} +1 -1
  144. package/dist-vue/_chunks/{dist-6gaEFVNT.js → dist-qJLUge9r.js} +2 -2
  145. package/dist-vue/_chunks/{dist-6gaEFVNT.js.map → dist-qJLUge9r.js.map} +1 -1
  146. package/dist-vue/_chunks/{index.es-ChKj3bPm.js → index.es-W-WNHB7-.js} +1 -1
  147. package/dist-vue/_chunks/{index.es-ChKj3bPm.js.map → index.es-W-WNHB7-.js.map} +1 -1
  148. package/dist-vue/_chunks/{useChatbotStore-GEO6rQ74.js → useChatbotStore-WPRS7mhR.js} +855 -832
  149. package/dist-vue/_chunks/{useChatbotStore-GEO6rQ74.js.map → useChatbotStore-WPRS7mhR.js.map} +1 -1
  150. package/dist-vue/_chunks/{useLocalizedString-DPX1ED4p.js → useLocalizedString-BQzGZyu0.js} +1 -1
  151. package/dist-vue/_chunks/{useLocalizedString-DPX1ED4p.js.map → useLocalizedString-BQzGZyu0.js.map} +1 -1
  152. package/dist-vue/_chunks/utils-BDNQaNua.js +101 -0
  153. package/dist-vue/_chunks/utils-BDNQaNua.js.map +1 -0
  154. package/dist-vue/api.js +1 -1
  155. package/dist-vue/composables.js +4 -4
  156. package/dist-vue/index.js +105 -80
  157. package/dist-vue/index.js.map +1 -1
  158. package/dist-vue/locales.js +10 -2
  159. package/dist-vue/packages/components/src/chat/PkChatbotInput.d.ts +7 -3
  160. package/dist-vue/packages/components/src/chat/PkMessageParts.d.ts +3 -0
  161. package/dist-vue/packages/components/src/chat/constants.d.ts +1 -1
  162. package/dist-vue/packages/components/src/chat/useChatScroll.d.ts +0 -7
  163. package/dist-vue/packages/components/src/table/PkTableSortable.d.ts +4 -0
  164. package/dist-vue/packages/composable/src/chatbot/api/createChatbotApiClient.d.ts +1 -0
  165. package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +42 -3
  166. package/dist-vue/packages/composable/src/index.d.ts +1 -0
  167. package/dist-vue/packages/composable/src/useFileRow.d.ts +24 -0
  168. package/dist-vue/packages/models/src/schema/Agent.d.ts +1 -0
  169. package/dist-vue/packages/models/src/schema/Document.d.ts +12 -0
  170. package/dist-vue/packages/models/src/schema/Platform.d.ts +6 -0
  171. package/dist-vue/packages/models/src/schema/constants.d.ts +7 -2
  172. package/dist-vue/style.css +1 -1
  173. package/package.json +1 -1
  174. package/dist/_chunks/Document-BajUssb0.js.map +0 -1
  175. package/dist/_chunks/PkToolShowSuggestedReply-1j4lOkRj.js.map +0 -1
  176. package/dist-vue/_chunks/PkChatbot-q_W4cKsy.js +0 -237
  177. package/dist-vue/_chunks/PkChatbot-q_W4cKsy.js.map +0 -1
  178. package/dist-vue/_chunks/PkChatbotFilePreview-DG887KhT.js.map +0 -1
  179. package/dist-vue/_chunks/PkChatbotInput-BTszEUj2.js.map +0 -1
  180. package/dist-vue/_chunks/PkChatbotMessages-DLP7utmG.js.map +0 -1
  181. package/dist-vue/_chunks/PkChatbotViewChat-BZ9IH-sz.js.map +0 -1
  182. package/dist-vue/_chunks/PkChatbotViewFiles-B0FNE1Kk.js +0 -86
  183. package/dist-vue/_chunks/PkChatbotViewFiles-B0FNE1Kk.js.map +0 -1
  184. package/dist-vue/_chunks/PkToolShowSuggestedReply-DKiYUsaZ.js.map +0 -1
  185. package/dist-vue/_chunks/constants-DpYDYnZU.js.map +0 -1
  186. package/dist-vue/_chunks/createChatbotApiClient-DXmWtu3b.js.map +0 -1
  187. package/dist-vue/_chunks/utils-BBAveXy4.js +0 -101
  188. package/dist-vue/_chunks/utils-BBAveXy4.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PkChatbotViewConversations-q5KPJt_D.js","names":[],"sources":["../../../../packages/composable/src/chatbot/useConversationActions.ts","../../../../packages/components/src/chat/PkChatbotViewConversations.vue","../../../../packages/components/src/chat/PkChatbotViewConversations.vue"],"sourcesContent":["import { ref } from 'vue'\nimport type { Chat as Conversation } from 'models'\nimport { getTextPart, stripMarkdown } from 'utils'\nimport { useDialogConfirm } from '../useDialogConfirm'\n\n/**\n * Single-line preview for a conversation: explicit title, otherwise the\n * first user message. Used as list label and as search haystack.\n */\nexport function getConversationPreview(conversation: Conversation): string {\n return (\n stripMarkdown(conversation.title) ||\n stripMarkdown(\n getTextPart(\n // The list endpoint isn't Zod-parsed: don't trust the\n // declared types at runtime\n conversation.messages?.find(\n (message) => message.role === 'user',\n ),\n ),\n ) ||\n ''\n )\n}\n\n/**\n * Inline-rename and delete-with-confirm state shared by the conversation\n * lists (PkChatbotViewConversations, PkChatSidebar). Renames are optimistic:\n * on failure the next conversations load reconciles with the server state.\n */\nexport function useConversationActions(actions: {\n renameChatTitle: (chatId: string, title: string) => Promise<unknown>\n deleteChat: (chatId: string) => Promise<unknown>\n}) {\n const editingId = ref<string | null>(null)\n const editingTitle = ref('')\n\n const startEdit = (conversation: Conversation, fallbackTitle = '') => {\n editingId.value = conversation.id\n editingTitle.value = stripMarkdown(conversation.title) || fallbackTitle\n }\n\n const confirmEdit = async (id: string) => {\n const trimmed = editingTitle.value.trim()\n editingId.value = null\n if (trimmed && trimmed.length <= 255) {\n try {\n await actions.renameChatTitle(id, trimmed)\n } catch {\n // Revert optimistically: reload will pick up server state\n }\n }\n }\n\n const cancelEdit = () => {\n editingId.value = null\n }\n\n const onRenameKeydown = (event: KeyboardEvent, id: string) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n confirmEdit(id)\n } else if (event.key === 'Escape') {\n event.preventDefault()\n cancelEdit()\n }\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n const requestDelete = async (id: string) => {\n try {\n const proceed = await openDialogConfirm()\n if (!proceed) {\n return\n }\n await actions.deleteChat(id)\n } catch {\n // Silently fail: next load will reconcile\n }\n }\n\n return {\n editingId,\n editingTitle,\n startEdit,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete,\n }\n}\n","<script setup lang=\"ts\">\n import { inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type { Chat as Conversation, UIChatMessage } from 'models'\n import { stripMarkdown, getTextPart } from 'utils'\n import {\n useChatbotStore,\n useConversationActions,\n useDialogConfirm,\n } from 'composables'\n import PkRelativeTime from '../PkRelativeTime.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { conversations, isLoadingConversations, localChatId } =\n storeToRefs(store)\n const { navigate, startNewChat, renameChatTitle, deleteChat } = store\n\n const {\n editingId,\n editingTitle,\n startEdit: startEditConversation,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete: handleDelete,\n } = useConversationActions({ renameChatTitle, deleteChat })\n\n const openConversation = (id: string) => {\n if (editingId.value) {\n return\n }\n localChatId.value = id\n navigate('chat')\n }\n\n const isCurrentConversation = (conversation: Conversation) => {\n return localChatId.value === conversation.id\n }\n\n const getLastMessage = (\n messages: UIChatMessage[],\n role: 'user' | 'assistant',\n ) => {\n return stripMarkdown(\n getTextPart(messages.filter((m) => m.role === role).slice(-1)[0]) ||\n '',\n )\n }\n\n const startEdit = (conversation: Conversation) => {\n startEditConversation(\n conversation,\n getLastMessage(conversation.messages, 'assistant'),\n )\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n\n // GDPR B.3 — bulk \"delete my conversations\", available to anonymous\n // sessions too (the host wires the endpoint through the profile context)\n const profile = inject(ChatbotProfileContextKey, undefined)\n const isErasingConversations = ref(false)\n const handleEraseAll = async () => {\n const eraseConversations = profile?.eraseConversations\n if (!eraseConversations) {\n return\n }\n const proceed = await openDialogConfirm({\n title: $t('action.deleteMyConversations'),\n questionLabel: $t('message.deleteMyConversationsConfirm'),\n })\n if (!proceed) {\n return\n }\n isErasingConversations.value = true\n try {\n await eraseConversations()\n conversations.value = []\n startNewChat()\n } catch {\n // Same policy as single delete: fail silently and reconcile\n // with the server state\n store.loadConversations(props.agentId)\n } finally {\n isErasingConversations.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingConversations && conversations.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 5\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-64\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"conversations.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon name=\"ri:chat-3-line\" class=\"w-28 h-28 text-word-4\" />\n </div>\n <div class=\"flex flex-col gap-4\">\n <strong class=\"text-16 font-semibold text-word-1\">\n {{ $t('message.noConversations') }}\n </strong>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('message.noConversationsHint') }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n class=\"mt-8\"\n :label=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n </div>\n\n <!-- Conversation list -->\n <ul\n v-else\n class=\"flex flex-col gap-8 overflow-y-auto p-16 pb-64 light-scrollbar\">\n <li v-for=\"conversation in conversations\" :key=\"conversation.id\">\n <button\n type=\"button\"\n class=\"w-full block rounded-md border border-surface-3 p-12 text-left text-14 leading-relaxed cursor-pointer transition-colors hover:border-surface-5 hover:bg-surface-1 focus-within:border-word\"\n :class=\"{\n 'border-brand bg-surface-1':\n isCurrentConversation(conversation),\n }\"\n @click=\"openConversation(conversation.id)\">\n <div class=\"flex gap-8\">\n <template v-if=\"editingId === conversation.id\">\n <VvInputText\n v-model=\"editingTitle\"\n class=\"flex-1\"\n modifiers=\"compact\"\n autofocus\n select-on-focus\n :maxlength=\"255\"\n :placeholder=\"$t('placeholder.insert')\"\n @click.stop\n @keydown=\"\n onRenameKeydown($event, conversation.id)\n \" />\n <VvButtonGroup modifiers=\"compact\">\n <VvButton\n icon=\"ri:save-line\"\n :title=\"$t('action.save')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"\n confirmEdit(conversation.id)\n \" />\n <VvButton\n icon=\"ri:close-line\"\n :title=\"$t('action.cancel')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"cancelEdit()\" />\n </VvButtonGroup>\n </template>\n <template v-else>\n <strong class=\"font-bold truncate block flex-1\">\n {{\n stripMarkdown(conversation.title) ||\n getLastMessage(\n conversation.messages,\n 'assistant',\n )\n }}\n </strong>\n <PkRelativeTime\n class=\"text-word-4 text-smaller shrink-0\"\n :date=\"conversation.lastMessageAt\" />\n <div @click.stop>\n <VvDropdown\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n icon=\"ri:more-2-fill\"\n :title=\"$t('action.moreActions')\"\n modifiers=\"action-quiet-small\" />\n <template #items>\n <VvDropdownAction\n @click=\"startEdit(conversation)\">\n <VvIcon name=\"ri:pencil-line\" />\n {{ $t('action.renameChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n @click=\"\n handleDelete(conversation.id)\n \">\n <VvIcon name=\"ri:delete-bin-line\" />\n {{ $t('action.delete') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </div>\n </template>\n </div>\n <span\n v-if=\"editingId !== conversation.id\"\n class=\"text-12 text-word-4 line-clamp-2\">\n {{\n stripMarkdown(conversation.summary) ||\n getLastMessage(conversation.messages, 'user')\n }}\n </span>\n </button>\n </li>\n </ul>\n\n <!-- Sticky \"new chat\" CTA (hidden on empty state, which has its own CTA) -->\n <div\n v-if=\"conversations.length > 0\"\n class=\"absolute bottom-0 left-0 right-0 flex flex-col items-center gap-4 px-16 pb-16 pt-32 pointer-events-none\"\n :style=\"{\n background:\n 'linear-gradient(to top, var(--color-surface) 55%, transparent)',\n }\">\n <VvButton\n class=\"text-14 pointer-events-auto\"\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n :label=\"$t('action.startNewChat')\"\n @click.stop=\"startNewChat()\" />\n <VvButton\n v-if=\"profile?.eraseConversations\"\n class=\"text-12 pointer-events-auto\"\n modifiers=\"action-quiet-danger\"\n icon=\"ri:delete-bin-line\"\n :loading=\"isErasingConversations\"\n :disabled=\"isErasingConversations\"\n :label=\"$t('action.deleteMyConversations')\"\n @click.stop=\"handleEraseAll()\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type { Chat as Conversation, UIChatMessage } from 'models'\n import { stripMarkdown, getTextPart } from 'utils'\n import {\n useChatbotStore,\n useConversationActions,\n useDialogConfirm,\n } from 'composables'\n import PkRelativeTime from '../PkRelativeTime.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { conversations, isLoadingConversations, localChatId } =\n storeToRefs(store)\n const { navigate, startNewChat, renameChatTitle, deleteChat } = store\n\n const {\n editingId,\n editingTitle,\n startEdit: startEditConversation,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete: handleDelete,\n } = useConversationActions({ renameChatTitle, deleteChat })\n\n const openConversation = (id: string) => {\n if (editingId.value) {\n return\n }\n localChatId.value = id\n navigate('chat')\n }\n\n const isCurrentConversation = (conversation: Conversation) => {\n return localChatId.value === conversation.id\n }\n\n const getLastMessage = (\n messages: UIChatMessage[],\n role: 'user' | 'assistant',\n ) => {\n return stripMarkdown(\n getTextPart(messages.filter((m) => m.role === role).slice(-1)[0]) ||\n '',\n )\n }\n\n const startEdit = (conversation: Conversation) => {\n startEditConversation(\n conversation,\n getLastMessage(conversation.messages, 'assistant'),\n )\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n\n // GDPR B.3 — bulk \"delete my conversations\", available to anonymous\n // sessions too (the host wires the endpoint through the profile context)\n const profile = inject(ChatbotProfileContextKey, undefined)\n const isErasingConversations = ref(false)\n const handleEraseAll = async () => {\n const eraseConversations = profile?.eraseConversations\n if (!eraseConversations) {\n return\n }\n const proceed = await openDialogConfirm({\n title: $t('action.deleteMyConversations'),\n questionLabel: $t('message.deleteMyConversationsConfirm'),\n })\n if (!proceed) {\n return\n }\n isErasingConversations.value = true\n try {\n await eraseConversations()\n conversations.value = []\n startNewChat()\n } catch {\n // Same policy as single delete: fail silently and reconcile\n // with the server state\n store.loadConversations(props.agentId)\n } finally {\n isErasingConversations.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingConversations && conversations.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 5\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-64\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"conversations.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon name=\"ri:chat-3-line\" class=\"w-28 h-28 text-word-4\" />\n </div>\n <div class=\"flex flex-col gap-4\">\n <strong class=\"text-16 font-semibold text-word-1\">\n {{ $t('message.noConversations') }}\n </strong>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('message.noConversationsHint') }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n class=\"mt-8\"\n :label=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n </div>\n\n <!-- Conversation list -->\n <ul\n v-else\n class=\"flex flex-col gap-8 overflow-y-auto p-16 pb-64 light-scrollbar\">\n <li v-for=\"conversation in conversations\" :key=\"conversation.id\">\n <button\n type=\"button\"\n class=\"w-full block rounded-md border border-surface-3 p-12 text-left text-14 leading-relaxed cursor-pointer transition-colors hover:border-surface-5 hover:bg-surface-1 focus-within:border-word\"\n :class=\"{\n 'border-brand bg-surface-1':\n isCurrentConversation(conversation),\n }\"\n @click=\"openConversation(conversation.id)\">\n <div class=\"flex gap-8\">\n <template v-if=\"editingId === conversation.id\">\n <VvInputText\n v-model=\"editingTitle\"\n class=\"flex-1\"\n modifiers=\"compact\"\n autofocus\n select-on-focus\n :maxlength=\"255\"\n :placeholder=\"$t('placeholder.insert')\"\n @click.stop\n @keydown=\"\n onRenameKeydown($event, conversation.id)\n \" />\n <VvButtonGroup modifiers=\"compact\">\n <VvButton\n icon=\"ri:save-line\"\n :title=\"$t('action.save')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"\n confirmEdit(conversation.id)\n \" />\n <VvButton\n icon=\"ri:close-line\"\n :title=\"$t('action.cancel')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"cancelEdit()\" />\n </VvButtonGroup>\n </template>\n <template v-else>\n <strong class=\"font-bold truncate block flex-1\">\n {{\n stripMarkdown(conversation.title) ||\n getLastMessage(\n conversation.messages,\n 'assistant',\n )\n }}\n </strong>\n <PkRelativeTime\n class=\"text-word-4 text-smaller shrink-0\"\n :date=\"conversation.lastMessageAt\" />\n <div @click.stop>\n <VvDropdown\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n icon=\"ri:more-2-fill\"\n :title=\"$t('action.moreActions')\"\n modifiers=\"action-quiet-small\" />\n <template #items>\n <VvDropdownAction\n @click=\"startEdit(conversation)\">\n <VvIcon name=\"ri:pencil-line\" />\n {{ $t('action.renameChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n @click=\"\n handleDelete(conversation.id)\n \">\n <VvIcon name=\"ri:delete-bin-line\" />\n {{ $t('action.delete') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </div>\n </template>\n </div>\n <span\n v-if=\"editingId !== conversation.id\"\n class=\"text-12 text-word-4 line-clamp-2\">\n {{\n stripMarkdown(conversation.summary) ||\n getLastMessage(conversation.messages, 'user')\n }}\n </span>\n </button>\n </li>\n </ul>\n\n <!-- Sticky \"new chat\" CTA (hidden on empty state, which has its own CTA) -->\n <div\n v-if=\"conversations.length > 0\"\n class=\"absolute bottom-0 left-0 right-0 flex flex-col items-center gap-4 px-16 pb-16 pt-32 pointer-events-none\"\n :style=\"{\n background:\n 'linear-gradient(to top, var(--color-surface) 55%, transparent)',\n }\">\n <VvButton\n class=\"text-14 pointer-events-auto\"\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n :label=\"$t('action.startNewChat')\"\n @click.stop=\"startNewChat()\" />\n <VvButton\n v-if=\"profile?.eraseConversations\"\n class=\"text-12 pointer-events-auto\"\n modifiers=\"action-quiet-danger\"\n icon=\"ri:delete-bin-line\"\n :loading=\"isErasingConversations\"\n :disabled=\"isErasingConversations\"\n :label=\"$t('action.deleteMyConversations')\"\n @click.stop=\"handleEraseAll()\" />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkChatbotViewConversations-CEaEsnBy.js","names":[],"sources":["../../../../packages/composable/src/chatbot/useConversationActions.ts","../../../../packages/components/src/chat/PkChatbotViewConversations.vue","../../../../packages/components/src/chat/PkChatbotViewConversations.vue"],"sourcesContent":["import { ref } from 'vue'\nimport type { Chat as Conversation } from 'models'\nimport { getTextPart, stripMarkdown } from 'utils'\nimport { useDialogConfirm } from '../useDialogConfirm'\n\n/**\n * Single-line preview for a conversation: explicit title, otherwise the\n * first user message. Used as list label and as search haystack.\n */\nexport function getConversationPreview(conversation: Conversation): string {\n return (\n stripMarkdown(conversation.title) ||\n stripMarkdown(\n getTextPart(\n // The list endpoint isn't Zod-parsed: don't trust the\n // declared types at runtime\n conversation.messages?.find(\n (message) => message.role === 'user',\n ),\n ),\n ) ||\n ''\n )\n}\n\n/**\n * Inline-rename and delete-with-confirm state shared by the conversation\n * lists (PkChatbotViewConversations, PkChatSidebar). Renames are optimistic:\n * on failure the next conversations load reconciles with the server state.\n */\nexport function useConversationActions(actions: {\n renameChatTitle: (chatId: string, title: string) => Promise<unknown>\n deleteChat: (chatId: string) => Promise<unknown>\n}) {\n const editingId = ref<string | null>(null)\n const editingTitle = ref('')\n\n const startEdit = (conversation: Conversation, fallbackTitle = '') => {\n editingId.value = conversation.id\n editingTitle.value = stripMarkdown(conversation.title) || fallbackTitle\n }\n\n const confirmEdit = async (id: string) => {\n const trimmed = editingTitle.value.trim()\n editingId.value = null\n if (trimmed && trimmed.length <= 255) {\n try {\n await actions.renameChatTitle(id, trimmed)\n } catch {\n // Revert optimistically: reload will pick up server state\n }\n }\n }\n\n const cancelEdit = () => {\n editingId.value = null\n }\n\n const onRenameKeydown = (event: KeyboardEvent, id: string) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n confirmEdit(id)\n } else if (event.key === 'Escape') {\n event.preventDefault()\n cancelEdit()\n }\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n const requestDelete = async (id: string) => {\n try {\n const proceed = await openDialogConfirm()\n if (!proceed) {\n return\n }\n await actions.deleteChat(id)\n } catch {\n // Silently fail: next load will reconcile\n }\n }\n\n return {\n editingId,\n editingTitle,\n startEdit,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete,\n }\n}\n","<script setup lang=\"ts\">\n import { inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type { Chat as Conversation, UIChatMessage } from 'models'\n import { stripMarkdown, getTextPart } from 'utils'\n import {\n useChatbotStore,\n useConversationActions,\n useDialogConfirm,\n } from 'composables'\n import PkRelativeTime from '../PkRelativeTime.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { conversations, isLoadingConversations, localChatId } =\n storeToRefs(store)\n const { navigate, startNewChat, renameChatTitle, deleteChat } = store\n\n const {\n editingId,\n editingTitle,\n startEdit: startEditConversation,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete: handleDelete,\n } = useConversationActions({ renameChatTitle, deleteChat })\n\n const openConversation = (id: string) => {\n if (editingId.value) {\n return\n }\n localChatId.value = id\n navigate('chat')\n }\n\n const isCurrentConversation = (conversation: Conversation) => {\n return localChatId.value === conversation.id\n }\n\n const getLastMessage = (\n messages: UIChatMessage[],\n role: 'user' | 'assistant',\n ) => {\n return stripMarkdown(\n getTextPart(messages.filter((m) => m.role === role).slice(-1)[0]) ||\n '',\n )\n }\n\n const startEdit = (conversation: Conversation) => {\n startEditConversation(\n conversation,\n getLastMessage(conversation.messages, 'assistant'),\n )\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n\n // GDPR B.3 — bulk \"delete my conversations\", available to anonymous\n // sessions too (the host wires the endpoint through the profile context)\n const profile = inject(ChatbotProfileContextKey, undefined)\n const isErasingConversations = ref(false)\n const handleEraseAll = async () => {\n const eraseConversations = profile?.eraseConversations\n if (!eraseConversations) {\n return\n }\n const proceed = await openDialogConfirm({\n title: $t('action.deleteMyConversations'),\n questionLabel: $t('message.deleteMyConversationsConfirm'),\n })\n if (!proceed) {\n return\n }\n isErasingConversations.value = true\n try {\n await eraseConversations()\n conversations.value = []\n startNewChat()\n } catch {\n // Same policy as single delete: fail silently and reconcile\n // with the server state\n store.loadConversations(props.agentId)\n } finally {\n isErasingConversations.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingConversations && conversations.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 5\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-64\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"conversations.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon name=\"ri:chat-3-line\" class=\"w-28 h-28 text-word-4\" />\n </div>\n <div class=\"flex flex-col gap-4\">\n <strong class=\"text-16 font-semibold text-word-1\">\n {{ $t('message.noConversations') }}\n </strong>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('message.noConversationsHint') }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n class=\"mt-8\"\n :label=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n </div>\n\n <!-- Conversation list -->\n <ul\n v-else\n class=\"flex flex-col gap-8 overflow-y-auto p-16 pb-64 light-scrollbar\">\n <li v-for=\"conversation in conversations\" :key=\"conversation.id\">\n <button\n type=\"button\"\n class=\"w-full block rounded-md border border-surface-3 p-12 text-left text-14 leading-relaxed cursor-pointer transition-colors hover:border-surface-5 hover:bg-surface-1 focus-within:border-word\"\n :class=\"{\n 'border-brand bg-surface-1':\n isCurrentConversation(conversation),\n }\"\n @click=\"openConversation(conversation.id)\">\n <div class=\"flex gap-8\">\n <template v-if=\"editingId === conversation.id\">\n <VvInputText\n v-model=\"editingTitle\"\n class=\"flex-1\"\n modifiers=\"compact\"\n autofocus\n select-on-focus\n :maxlength=\"255\"\n :placeholder=\"$t('placeholder.insert')\"\n @click.stop\n @keydown=\"\n onRenameKeydown($event, conversation.id)\n \" />\n <VvButtonGroup modifiers=\"compact\">\n <VvButton\n icon=\"ri:save-line\"\n :title=\"$t('action.save')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"\n confirmEdit(conversation.id)\n \" />\n <VvButton\n icon=\"ri:close-line\"\n :title=\"$t('action.cancel')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"cancelEdit()\" />\n </VvButtonGroup>\n </template>\n <template v-else>\n <strong class=\"font-bold truncate block flex-1\">\n {{\n stripMarkdown(conversation.title) ||\n getLastMessage(\n conversation.messages,\n 'assistant',\n )\n }}\n </strong>\n <PkRelativeTime\n class=\"text-word-4 text-smaller shrink-0\"\n :date=\"conversation.lastMessageAt\" />\n <div @click.stop>\n <VvDropdown\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n icon=\"ri:more-2-fill\"\n :title=\"$t('action.moreActions')\"\n modifiers=\"action-quiet-small\" />\n <template #items>\n <VvDropdownAction\n @click=\"startEdit(conversation)\">\n <VvIcon name=\"ri:pencil-line\" />\n {{ $t('action.renameChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n @click=\"\n handleDelete(conversation.id)\n \">\n <VvIcon name=\"ri:delete-bin-line\" />\n {{ $t('action.delete') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </div>\n </template>\n </div>\n <span\n v-if=\"editingId !== conversation.id\"\n class=\"text-12 text-word-4 line-clamp-2\">\n {{\n stripMarkdown(conversation.summary) ||\n getLastMessage(conversation.messages, 'user')\n }}\n </span>\n </button>\n </li>\n </ul>\n\n <!-- Sticky \"new chat\" CTA (hidden on empty state, which has its own CTA) -->\n <div\n v-if=\"conversations.length > 0\"\n class=\"absolute bottom-0 left-0 right-0 flex flex-col items-center gap-4 px-16 pb-16 pt-32 pointer-events-none\"\n :style=\"{\n background:\n 'linear-gradient(to top, var(--color-surface) 55%, transparent)',\n }\">\n <VvButton\n class=\"text-14 pointer-events-auto\"\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n :label=\"$t('action.startNewChat')\"\n @click.stop=\"startNewChat()\" />\n <VvButton\n v-if=\"profile?.eraseConversations\"\n class=\"text-12 pointer-events-auto\"\n modifiers=\"action-quiet-danger\"\n icon=\"ri:delete-bin-line\"\n :loading=\"isErasingConversations\"\n :disabled=\"isErasingConversations\"\n :label=\"$t('action.deleteMyConversations')\"\n @click.stop=\"handleEraseAll()\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type { Chat as Conversation, UIChatMessage } from 'models'\n import { stripMarkdown, getTextPart } from 'utils'\n import {\n useChatbotStore,\n useConversationActions,\n useDialogConfirm,\n } from 'composables'\n import PkRelativeTime from '../PkRelativeTime.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { conversations, isLoadingConversations, localChatId } =\n storeToRefs(store)\n const { navigate, startNewChat, renameChatTitle, deleteChat } = store\n\n const {\n editingId,\n editingTitle,\n startEdit: startEditConversation,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete: handleDelete,\n } = useConversationActions({ renameChatTitle, deleteChat })\n\n const openConversation = (id: string) => {\n if (editingId.value) {\n return\n }\n localChatId.value = id\n navigate('chat')\n }\n\n const isCurrentConversation = (conversation: Conversation) => {\n return localChatId.value === conversation.id\n }\n\n const getLastMessage = (\n messages: UIChatMessage[],\n role: 'user' | 'assistant',\n ) => {\n return stripMarkdown(\n getTextPart(messages.filter((m) => m.role === role).slice(-1)[0]) ||\n '',\n )\n }\n\n const startEdit = (conversation: Conversation) => {\n startEditConversation(\n conversation,\n getLastMessage(conversation.messages, 'assistant'),\n )\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n\n // GDPR B.3 — bulk \"delete my conversations\", available to anonymous\n // sessions too (the host wires the endpoint through the profile context)\n const profile = inject(ChatbotProfileContextKey, undefined)\n const isErasingConversations = ref(false)\n const handleEraseAll = async () => {\n const eraseConversations = profile?.eraseConversations\n if (!eraseConversations) {\n return\n }\n const proceed = await openDialogConfirm({\n title: $t('action.deleteMyConversations'),\n questionLabel: $t('message.deleteMyConversationsConfirm'),\n })\n if (!proceed) {\n return\n }\n isErasingConversations.value = true\n try {\n await eraseConversations()\n conversations.value = []\n startNewChat()\n } catch {\n // Same policy as single delete: fail silently and reconcile\n // with the server state\n store.loadConversations(props.agentId)\n } finally {\n isErasingConversations.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingConversations && conversations.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 5\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-64\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"conversations.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon name=\"ri:chat-3-line\" class=\"w-28 h-28 text-word-4\" />\n </div>\n <div class=\"flex flex-col gap-4\">\n <strong class=\"text-16 font-semibold text-word-1\">\n {{ $t('message.noConversations') }}\n </strong>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('message.noConversationsHint') }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n class=\"mt-8\"\n :label=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n </div>\n\n <!-- Conversation list -->\n <ul\n v-else\n class=\"flex flex-col gap-8 overflow-y-auto p-16 pb-64 light-scrollbar\">\n <li v-for=\"conversation in conversations\" :key=\"conversation.id\">\n <button\n type=\"button\"\n class=\"w-full block rounded-md border border-surface-3 p-12 text-left text-14 leading-relaxed cursor-pointer transition-colors hover:border-surface-5 hover:bg-surface-1 focus-within:border-word\"\n :class=\"{\n 'border-brand bg-surface-1':\n isCurrentConversation(conversation),\n }\"\n @click=\"openConversation(conversation.id)\">\n <div class=\"flex gap-8\">\n <template v-if=\"editingId === conversation.id\">\n <VvInputText\n v-model=\"editingTitle\"\n class=\"flex-1\"\n modifiers=\"compact\"\n autofocus\n select-on-focus\n :maxlength=\"255\"\n :placeholder=\"$t('placeholder.insert')\"\n @click.stop\n @keydown=\"\n onRenameKeydown($event, conversation.id)\n \" />\n <VvButtonGroup modifiers=\"compact\">\n <VvButton\n icon=\"ri:save-line\"\n :title=\"$t('action.save')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"\n confirmEdit(conversation.id)\n \" />\n <VvButton\n icon=\"ri:close-line\"\n :title=\"$t('action.cancel')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"cancelEdit()\" />\n </VvButtonGroup>\n </template>\n <template v-else>\n <strong class=\"font-bold truncate block flex-1\">\n {{\n stripMarkdown(conversation.title) ||\n getLastMessage(\n conversation.messages,\n 'assistant',\n )\n }}\n </strong>\n <PkRelativeTime\n class=\"text-word-4 text-smaller shrink-0\"\n :date=\"conversation.lastMessageAt\" />\n <div @click.stop>\n <VvDropdown\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n icon=\"ri:more-2-fill\"\n :title=\"$t('action.moreActions')\"\n modifiers=\"action-quiet-small\" />\n <template #items>\n <VvDropdownAction\n @click=\"startEdit(conversation)\">\n <VvIcon name=\"ri:pencil-line\" />\n {{ $t('action.renameChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n @click=\"\n handleDelete(conversation.id)\n \">\n <VvIcon name=\"ri:delete-bin-line\" />\n {{ $t('action.delete') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </div>\n </template>\n </div>\n <span\n v-if=\"editingId !== conversation.id\"\n class=\"text-12 text-word-4 line-clamp-2\">\n {{\n stripMarkdown(conversation.summary) ||\n getLastMessage(conversation.messages, 'user')\n }}\n </span>\n </button>\n </li>\n </ul>\n\n <!-- Sticky \"new chat\" CTA (hidden on empty state, which has its own CTA) -->\n <div\n v-if=\"conversations.length > 0\"\n class=\"absolute bottom-0 left-0 right-0 flex flex-col items-center gap-4 px-16 pb-16 pt-32 pointer-events-none\"\n :style=\"{\n background:\n 'linear-gradient(to top, var(--color-surface) 55%, transparent)',\n }\">\n <VvButton\n class=\"text-14 pointer-events-auto\"\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n :label=\"$t('action.startNewChat')\"\n @click.stop=\"startNewChat()\" />\n <VvButton\n v-if=\"profile?.eraseConversations\"\n class=\"text-12 pointer-events-auto\"\n modifiers=\"action-quiet-danger\"\n icon=\"ri:delete-bin-line\"\n :loading=\"isErasingConversations\"\n :disabled=\"isErasingConversations\"\n :label=\"$t('action.deleteMyConversations')\"\n @click.stop=\"handleEraseAll()\" />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ import { t as e } from "./useChatbotStore-WPRS7mhR.js";
2
+ import { a as t, n, o as r } from "./Document-c1tATTDs.js";
3
+ import { t as i } from "./useDialogConfirm-CCGTnprd.js";
4
+ import { VvButton as a, VvIcon as o } from "@volverjs/ui-vue/components";
5
+ import { Fragment as s, createBlock as c, createElementBlock as l, createElementVNode as u, createVNode as d, defineComponent as f, openBlock as p, reactive as m, ref as h, renderList as g, toDisplayString as _, unref as v } from "vue";
6
+ import { useI18n as y } from "vue-i18n";
7
+ import { storeToRefs as b } from "pinia";
8
+ //#region ../../packages/composable/src/useFileRow.ts
9
+ function x(e) {
10
+ let { locale: n } = y({ useScope: "global" }), i = m(/* @__PURE__ */ new Set());
11
+ return {
12
+ showThumb: (e) => t(e.mimeType) && !i.has(e.documentId),
13
+ thumbUrl: (t) => e.preview(t.documentId),
14
+ onThumbError: (e) => {
15
+ i.add(e.documentId);
16
+ },
17
+ openFile: (t, n = !1) => {
18
+ let i = !n && r(t.mimeType);
19
+ window.open(e.view(t.documentId, i), "_blank", "noopener");
20
+ },
21
+ formatDate: (e) => new Date(e).toLocaleDateString(n.value, {
22
+ year: "numeric",
23
+ month: "short",
24
+ day: "numeric"
25
+ })
26
+ };
27
+ }
28
+ //#endregion
29
+ //#region ../../packages/components/src/chat/PkChatbotViewFiles.vue?vue&type=script&setup=true&lang.ts
30
+ var S = { class: "flex flex-col flex-1 min-h-0 relative" }, C = {
31
+ key: 0,
32
+ class: "flex flex-col gap-8 p-16 vv-skeleton"
33
+ }, w = {
34
+ key: 1,
35
+ class: "flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center"
36
+ }, T = { class: "w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center" }, E = { class: "text-14 text-word-3 leading-relaxed" }, D = {
37
+ key: 2,
38
+ class: "pk-chatbot-files"
39
+ }, O = { class: "pk-chatbot-files__icon" }, k = ["src", "onError"], A = ["title", "onClick"], j = { class: "pk-chatbot-files__name" }, M = { class: "pk-chatbot-files__date" }, N = { class: "pk-chatbot-files__actions" }, P = /* @__PURE__ */ f({
40
+ __name: "PkChatbotViewFiles",
41
+ props: { agentId: {} },
42
+ setup(t) {
43
+ let r = t, { t: f } = y({ useScope: "global" }), { openDialogConfirm: m } = i(), P = e(r.agentId), { chatFiles: F, isLoadingChatFiles: I } = b(P), { showThumb: L, thumbUrl: R, onThumbError: z, openFile: B, formatDate: V } = x({
44
+ view: P.getChatFileUrl,
45
+ preview: P.getChatFilePreviewUrl
46
+ }), H = h(null), U = async (e) => {
47
+ if (await m({
48
+ title: f("userFiles.excludeTitle"),
49
+ questionLabel: f("userFiles.excludeConfirm")
50
+ })) {
51
+ H.value = e.documentId;
52
+ try {
53
+ await P.deleteChatFile(e.documentId);
54
+ } catch {} finally {
55
+ H.value = null;
56
+ }
57
+ }
58
+ };
59
+ return (e, t) => {
60
+ let r = o, i = a;
61
+ return p(), l("div", S, [v(I) && v(F).length === 0 ? (p(), l("div", C, [(p(), l(s, null, g(4, (e) => u("div", {
62
+ key: e,
63
+ class: "vv-skeleton__item rounded-md h-56"
64
+ })), 64))])) : v(F).length === 0 ? (p(), l("div", w, [u("div", T, [d(r, {
65
+ name: "ri:folder-open-line",
66
+ class: "w-28 h-28 text-word-4"
67
+ })]), u("span", E, _(v(f)("userFiles.chatFilesEmpty")), 1)])) : (p(), l("ul", D, [(p(!0), l(s, null, g(v(F), (e) => (p(), l("li", {
68
+ key: e.documentId,
69
+ class: "pk-chatbot-files__item"
70
+ }, [
71
+ u("span", O, [v(L)(e) ? (p(), l("img", {
72
+ key: 0,
73
+ src: v(R)(e),
74
+ loading: "lazy",
75
+ decoding: "async",
76
+ alt: "",
77
+ class: "pk-chatbot-files__thumb",
78
+ onError: (t) => v(z)(e)
79
+ }, null, 40, k)) : (p(), c(r, {
80
+ key: 1,
81
+ name: v(n)(e.mimeType)
82
+ }, null, 8, ["name"]))]),
83
+ u("button", {
84
+ type: "button",
85
+ class: "pk-chatbot-files__main",
86
+ title: v(f)("userFiles.open"),
87
+ onClick: (t) => v(B)(e)
88
+ }, [u("span", j, _(e.name || v(f)("userFiles.untitled")), 1), u("span", M, _(v(V)(e.createdAt)), 1)], 8, A),
89
+ u("div", N, [d(i, {
90
+ modifiers: "action-quiet xs",
91
+ icon: "ri:download-line",
92
+ title: v(f)("action.download"),
93
+ onClick: (t) => v(B)(e, !0)
94
+ }, null, 8, ["title", "onClick"]), d(i, {
95
+ modifiers: "action-quiet-danger xs",
96
+ icon: "ri:delete-bin-line",
97
+ title: v(f)("userFiles.exclude"),
98
+ loading: H.value === e.documentId,
99
+ disabled: H.value === e.documentId,
100
+ onClick: (t) => U(e)
101
+ }, null, 8, [
102
+ "title",
103
+ "loading",
104
+ "disabled",
105
+ "onClick"
106
+ ])])
107
+ ]))), 128))]))]);
108
+ };
109
+ }
110
+ });
111
+ //#endregion
112
+ export { P as t };
113
+
114
+ //# sourceMappingURL=PkChatbotViewFiles-Bzcn8AEo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PkChatbotViewFiles-Bzcn8AEo.js","names":[],"sources":["../../../../packages/composable/src/useFileRow.ts","../../../../packages/components/src/chat/PkChatbotViewFiles.vue","../../../../packages/components/src/chat/PkChatbotViewFiles.vue"],"sourcesContent":["import { reactive } from 'vue'\nimport { useI18n } from 'vue-i18n'\nimport { isImageMime, isInlineViewable } from 'models'\n\n/** Minimal shape a file-list row needs for the shared display helpers. */\nexport interface FileRowLike {\n documentId: string\n mimeType: string | null\n}\n\n/**\n * Shared display logic for the user-files list rows, used by both the in-app\n * table (PjUserFilesTable) and the embedded widget (PkChatbotViewFiles). The two\n * surfaces differ only in how they build URLs (authenticated store vs widget API\n * client), so the URL builders are injected.\n *\n * - `view(id, inline)` → opens/downloads the original file.\n * - `preview(id)` → small resized blob for the row thumbnail.\n */\nexport function useFileRow(urls: {\n view: (documentId: string, inline: boolean) => string\n preview: (documentId: string) => string\n}) {\n const { locale } = useI18n({ useScope: 'global' })\n\n // Image rows show a thumbnail; if it fails to load (auth/redirect, or the\n // preview isn't generated yet) we fall back to the file-type icon.\n const thumbError = reactive(new Set<string>())\n const showThumb = (file: FileRowLike): boolean =>\n isImageMime(file.mimeType) && !thumbError.has(file.documentId)\n const thumbUrl = (file: FileRowLike): string =>\n urls.preview(file.documentId)\n const onThumbError = (file: FileRowLike): void => {\n thumbError.add(file.documentId)\n }\n\n // Name click previews images/PDF inline; an explicit download forces an\n // attachment (inline=false → download SAS).\n const openFile = (file: FileRowLike, download = false): void => {\n const inline = !download && isInlineViewable(file.mimeType)\n window.open(urls.view(file.documentId, inline), '_blank', 'noopener')\n }\n\n const formatDate = (iso: string): string =>\n new Date(iso).toLocaleDateString(locale.value, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n\n return { showThumb, thumbUrl, onThumbError, openFile, formatDate }\n}\n","<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { fileIconForMime } from 'models'\n import {\n useChatbotStore,\n useDialogConfirm,\n useFileRow,\n type ChatbotFile,\n } from 'composables'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n const { openDialogConfirm } = useDialogConfirm()\n\n const store = useChatbotStore(props.agentId)\n const { chatFiles, isLoadingChatFiles } = storeToRefs(store)\n const { showThumb, thumbUrl, onThumbError, openFile, formatDate } =\n useFileRow({\n view: store.getChatFileUrl,\n preview: store.getChatFilePreviewUrl,\n })\n\n const deletingId = ref<string | null>(null)\n\n const handleExclude = async (file: ChatbotFile) => {\n const proceed = await openDialogConfirm({\n title: $t('userFiles.excludeTitle'),\n questionLabel: $t('userFiles.excludeConfirm'),\n })\n if (!proceed) {\n return\n }\n deletingId.value = file.documentId\n try {\n await store.deleteChatFile(file.documentId)\n } catch {\n // surfaced by the host's error handling\n } finally {\n deletingId.value = null\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-56\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"chatFiles.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon\n name=\"ri:folder-open-line\"\n class=\"w-28 h-28 text-word-4\" />\n </div>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('userFiles.chatFilesEmpty') }}\n </span>\n </div>\n\n <!-- File list -->\n <ul v-else class=\"pk-chatbot-files\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"pk-chatbot-files__item\">\n <span class=\"pk-chatbot-files__icon\">\n <img\n v-if=\"showThumb(file)\"\n :src=\"thumbUrl(file)\"\n loading=\"lazy\"\n decoding=\"async\"\n alt=\"\"\n class=\"pk-chatbot-files__thumb\"\n @error=\"onThumbError(file)\" />\n <VvIcon v-else :name=\"fileIconForMime(file.mimeType)\" />\n </span>\n <button\n type=\"button\"\n class=\"pk-chatbot-files__main\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__name\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"pk-chatbot-files__date\">\n {{ formatDate(file.createdAt) }}\n </span>\n </button>\n <div class=\"pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet xs\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-danger xs\"\n icon=\"ri:delete-bin-line\"\n :title=\"$t('userFiles.exclude')\"\n :loading=\"deletingId === file.documentId\"\n :disabled=\"deletingId === file.documentId\"\n @click=\"handleExclude(file)\" />\n </div>\n </li>\n </ul>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-files {\n display: flex;\n flex-direction: column;\n list-style: none;\n margin: 0;\n padding: var(--spacing-8);\n overflow-y: auto;\n\n &__item {\n display: flex;\n align-items: center;\n gap: var(--spacing-10);\n padding: var(--spacing-8) var(--spacing-10);\n border-radius: var(--rounded-md);\n transition: var(--transition-colors);\n\n // Separator between files\n & + & {\n border-top: 1px solid var(--color-surface-3);\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\n &__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-36);\n height: var(--spacing-36);\n flex-shrink: 0;\n border-radius: var(--rounded-md);\n background-color: var(--color-surface-1);\n color: var(--color-word-3);\n overflow: hidden;\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n text-align: left;\n background: none;\n border: 0;\n padding: 0;\n cursor: pointer;\n }\n\n &__name {\n font-size: var(--text-14);\n color: var(--color-word-1);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__date {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2);\n flex-shrink: 0;\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { fileIconForMime } from 'models'\n import {\n useChatbotStore,\n useDialogConfirm,\n useFileRow,\n type ChatbotFile,\n } from 'composables'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n const { openDialogConfirm } = useDialogConfirm()\n\n const store = useChatbotStore(props.agentId)\n const { chatFiles, isLoadingChatFiles } = storeToRefs(store)\n const { showThumb, thumbUrl, onThumbError, openFile, formatDate } =\n useFileRow({\n view: store.getChatFileUrl,\n preview: store.getChatFilePreviewUrl,\n })\n\n const deletingId = ref<string | null>(null)\n\n const handleExclude = async (file: ChatbotFile) => {\n const proceed = await openDialogConfirm({\n title: $t('userFiles.excludeTitle'),\n questionLabel: $t('userFiles.excludeConfirm'),\n })\n if (!proceed) {\n return\n }\n deletingId.value = file.documentId\n try {\n await store.deleteChatFile(file.documentId)\n } catch {\n // surfaced by the host's error handling\n } finally {\n deletingId.value = null\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-56\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"chatFiles.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon\n name=\"ri:folder-open-line\"\n class=\"w-28 h-28 text-word-4\" />\n </div>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('userFiles.chatFilesEmpty') }}\n </span>\n </div>\n\n <!-- File list -->\n <ul v-else class=\"pk-chatbot-files\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"pk-chatbot-files__item\">\n <span class=\"pk-chatbot-files__icon\">\n <img\n v-if=\"showThumb(file)\"\n :src=\"thumbUrl(file)\"\n loading=\"lazy\"\n decoding=\"async\"\n alt=\"\"\n class=\"pk-chatbot-files__thumb\"\n @error=\"onThumbError(file)\" />\n <VvIcon v-else :name=\"fileIconForMime(file.mimeType)\" />\n </span>\n <button\n type=\"button\"\n class=\"pk-chatbot-files__main\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__name\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"pk-chatbot-files__date\">\n {{ formatDate(file.createdAt) }}\n </span>\n </button>\n <div class=\"pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet xs\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-danger xs\"\n icon=\"ri:delete-bin-line\"\n :title=\"$t('userFiles.exclude')\"\n :loading=\"deletingId === file.documentId\"\n :disabled=\"deletingId === file.documentId\"\n @click=\"handleExclude(file)\" />\n </div>\n </li>\n </ul>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-files {\n display: flex;\n flex-direction: column;\n list-style: none;\n margin: 0;\n padding: var(--spacing-8);\n overflow-y: auto;\n\n &__item {\n display: flex;\n align-items: center;\n gap: var(--spacing-10);\n padding: var(--spacing-8) var(--spacing-10);\n border-radius: var(--rounded-md);\n transition: var(--transition-colors);\n\n // Separator between files\n & + & {\n border-top: 1px solid var(--color-surface-3);\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\n &__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-36);\n height: var(--spacing-36);\n flex-shrink: 0;\n border-radius: var(--rounded-md);\n background-color: var(--color-surface-1);\n color: var(--color-word-3);\n overflow: hidden;\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n text-align: left;\n background: none;\n border: 0;\n padding: 0;\n cursor: pointer;\n }\n\n &__name {\n font-size: var(--text-14);\n color: var(--color-word-1);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__date {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2);\n flex-shrink: 0;\n }\n }\n</style>\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { t as e } from "./useChatbotStore-GEO6rQ74.js";
2
- import { t } from "./PkAvatar-Dv3e4aUl.js";
3
- import { t as n } from "./chatbotProfileContext-R95aQ5bH.js";
1
+ import { t as e } from "./useChatbotStore-WPRS7mhR.js";
2
+ import { t } from "./PkAvatar-Bpf6cD4N.js";
3
+ import { t as n } from "./chatbotProfileContext-R1zi561l.js";
4
4
  import { VvAlert as r, VvButton as i, VvIcon as a } from "@volverjs/ui-vue/components";
5
5
  import { computed as o, createBlock as s, createCommentVNode as c, createElementBlock as l, createElementVNode as u, createVNode as d, defineComponent as f, inject as p, openBlock as m, ref as h, renderSlot as g, toDisplayString as _, unref as v } from "vue";
6
6
  import { useI18n as y } from "vue-i18n";
@@ -80,4 +80,4 @@ var x = { class: "flex flex-col flex-1 min-h-0 overflow-y-auto" }, S = { class:
80
80
  //#endregion
81
81
  export { N as t };
82
82
 
83
- //# sourceMappingURL=PkChatbotViewProfile-DTbQlkVL.js.map
83
+ //# sourceMappingURL=PkChatbotViewProfile-CfFeqjse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkChatbotViewProfile-DTbQlkVL.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbotViewProfile.vue","../../../../packages/components/src/chat/PkChatbotViewProfile.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { useChatbotStore } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { name: agentName, agentInterface } = storeToRefs(store)\n\n const profile = inject(ChatbotProfileContextKey, undefined)\n const user = computed(() => profile?.user.value ?? null)\n const canManage = computed(() => profile?.canManageProfile.value ?? false)\n const profileUrl = computed(() => profile?.profileUrl.value)\n\n const isSigningOut = ref(false)\n const errorMessage = ref<string>()\n\n async function onSignOut() {\n if (!profile) {\n return\n }\n isSigningOut.value = true\n try {\n await profile.signOut()\n } catch (error) {\n errorMessage.value =\n error instanceof Error ? error.message : $t('message.error')\n } finally {\n isSigningOut.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 overflow-y-auto\">\n <!-- Hero: user identity -->\n <div\n class=\"flex flex-col items-center gap-sm pt-lg px-lg pb-md bg-surface-1 border-b border-surface-3\">\n <PkAvatar\n modifiers=\"surface ring\"\n class=\"w-64 h-64\"\n :name=\"user?.name ?? agentName\"\n :img-src=\"user?.image ?? agentInterface?.logo\" />\n <div class=\"flex flex-col items-center gap-6 text-center\">\n <strong\n v-if=\"user?.name ?? agentName\"\n class=\"text-16 font-semibold text-word-1 leading-tight\">\n {{ user?.name ?? agentName }}\n </strong>\n <span\n v-if=\"user?.email\"\n class=\"text-12 text-word-3 leading-none\">\n {{ user.email }}\n </span>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"flex flex-col gap-sm p-md flex-1\">\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n :content=\"errorMessage\" />\n\n <!-- Manage profile link card -->\n <a\n v-if=\"canManage && profileUrl\"\n :href=\"profileUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"flex items-center gap-12 p-12 rounded-lg border border-surface-3 hover:border-surface-5 hover:bg-surface-1 transition-colors cursor-pointer no-underline\">\n <div\n class=\"w-40 h-40 rounded-md bg-surface-2 border border-surface-3 flex items-center justify-center shrink-0\">\n <VvIcon\n name=\"ri:user-settings-line\"\n class=\"w-20 h-20 text-word-2\" />\n </div>\n <div class=\"flex flex-col flex-1 min-w-0 gap-1\">\n <span class=\"text-14 font-medium text-word-1 leading-tight\">\n {{ $t('action.manageProfile') }}\n </span>\n <span class=\"text-12 text-word-3 leading-tight\">\n {{ $t('label.manageProfileDescription') }}\n </span>\n </div>\n <VvIcon\n name=\"ri:arrow-right-s-line\"\n class=\"w-16 h-16 text-word-4 shrink-0\" />\n </a>\n </div>\n\n <!-- Footer: sign out -->\n <div class=\"px-md py-sm\">\n <VvButton\n modifiers=\"secondary-danger\"\n class=\"w-full\"\n icon=\"ri:logout-box-line\"\n :loading=\"isSigningOut\"\n :disabled=\"isSigningOut\"\n :label=\"$t('action.signOut')\"\n @click=\"onSignOut\" />\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { useChatbotStore } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { name: agentName, agentInterface } = storeToRefs(store)\n\n const profile = inject(ChatbotProfileContextKey, undefined)\n const user = computed(() => profile?.user.value ?? null)\n const canManage = computed(() => profile?.canManageProfile.value ?? false)\n const profileUrl = computed(() => profile?.profileUrl.value)\n\n const isSigningOut = ref(false)\n const errorMessage = ref<string>()\n\n async function onSignOut() {\n if (!profile) {\n return\n }\n isSigningOut.value = true\n try {\n await profile.signOut()\n } catch (error) {\n errorMessage.value =\n error instanceof Error ? error.message : $t('message.error')\n } finally {\n isSigningOut.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 overflow-y-auto\">\n <!-- Hero: user identity -->\n <div\n class=\"flex flex-col items-center gap-sm pt-lg px-lg pb-md bg-surface-1 border-b border-surface-3\">\n <PkAvatar\n modifiers=\"surface ring\"\n class=\"w-64 h-64\"\n :name=\"user?.name ?? agentName\"\n :img-src=\"user?.image ?? agentInterface?.logo\" />\n <div class=\"flex flex-col items-center gap-6 text-center\">\n <strong\n v-if=\"user?.name ?? agentName\"\n class=\"text-16 font-semibold text-word-1 leading-tight\">\n {{ user?.name ?? agentName }}\n </strong>\n <span\n v-if=\"user?.email\"\n class=\"text-12 text-word-3 leading-none\">\n {{ user.email }}\n </span>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"flex flex-col gap-sm p-md flex-1\">\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n :content=\"errorMessage\" />\n\n <!-- Manage profile link card -->\n <a\n v-if=\"canManage && profileUrl\"\n :href=\"profileUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"flex items-center gap-12 p-12 rounded-lg border border-surface-3 hover:border-surface-5 hover:bg-surface-1 transition-colors cursor-pointer no-underline\">\n <div\n class=\"w-40 h-40 rounded-md bg-surface-2 border border-surface-3 flex items-center justify-center shrink-0\">\n <VvIcon\n name=\"ri:user-settings-line\"\n class=\"w-20 h-20 text-word-2\" />\n </div>\n <div class=\"flex flex-col flex-1 min-w-0 gap-1\">\n <span class=\"text-14 font-medium text-word-1 leading-tight\">\n {{ $t('action.manageProfile') }}\n </span>\n <span class=\"text-12 text-word-3 leading-tight\">\n {{ $t('label.manageProfileDescription') }}\n </span>\n </div>\n <VvIcon\n name=\"ri:arrow-right-s-line\"\n class=\"w-16 h-16 text-word-4 shrink-0\" />\n </a>\n </div>\n\n <!-- Footer: sign out -->\n <div class=\"px-md py-sm\">\n <VvButton\n modifiers=\"secondary-danger\"\n class=\"w-full\"\n icon=\"ri:logout-box-line\"\n :loading=\"isSigningOut\"\n :disabled=\"isSigningOut\"\n :label=\"$t('action.signOut')\"\n @click=\"onSignOut\" />\n </div>\n\n <slot />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAQI,IAAM,IAAQ,GAER,EAAE,GAAG,MAAO,EAAQ,EAAE,UAAU,SAAS,CAAC,GAG1C,EAAE,MAAM,GAAW,sBAAmB,EAD9B,EAAgB,EAAM,OACoB,CAAK,GAEvD,IAAU,EAAO,GAA0B,KAAA,CAAS,GACpD,IAAO,QAAe,GAAS,KAAK,SAAS,IAAI,GACjD,IAAY,QAAe,GAAS,iBAAiB,SAAS,EAAK,GACnE,IAAa,QAAe,GAAS,WAAW,KAAK,GAErD,IAAe,EAAI,EAAK,GACxB,IAAe,EAAY;EAEjC,eAAe,IAAY;GAClB,OAGL;MAAa,QAAQ;IACrB,IAAI;KACA,MAAM,EAAQ,QAAQ;IAC1B,SAAS,GAAO;KACZ,EAAa,QACT,aAAiB,QAAQ,EAAM,UAAU,EAAG,eAAe;IACnE,UAAU;KACN,EAAa,QAAQ;IACzB;GARqB;EASzB;;;eAIA,EAsEM,OAtEN,GAsEM;IApEF,EAmBM,OAnBN,GAmBM,CAjBF,EAIqD,GAAA;KAHjD,WAAU;KACV,OAAM;KACL,MAAM,EAAA,OAAM,QAAQ,EAAA,CAAA;KACpB,WAAS,EAAA,OAAM,SAAS,EAAA,CAAA,GAAgB;sCAC7C,EAWM,OAXN,GAWM,CATQ,EAAA,OAAM,QAAQ,EAAA,CAAA,KAAA,EAAA,GADxB,EAIS,UAJT,GAIS,EADF,EAAA,OAAM,QAAQ,EAAA,CAAA,CAAS,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAGpB,EAAA,OAAM,SAAA,EAAA,GADhB,EAIO,QAJP,GAIO,EADA,EAAA,MAAK,KAAK,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,CAAA,CAAA;IAMzB,EA+BM,OA/BN,GA+BM,CA7BQ,EAAA,SAAA,EAAA,GADV,EAG8B,GAAA;;KAD1B,WAAU;KACT,SAAS,EAAA;2CAIJ,EAAA,SAAa,EAAA,SAAA,EAAA,GADvB,EAuBI,KAAA;;KArBC,MAAM,EAAA;KACP,QAAO;KACP,KAAI;KACJ,OAAM;;KACN,EAKM,OALN,GAKM,CAHF,EAEoC,GAAA;MADhC,MAAK;MACL,OAAM;;KAEd,EAOM,OAPN,GAOM,CANF,EAEO,QAFP,GAEO,EADA,EAAA,CAAA,EAAE,sBAAA,CAAA,GAAA,CAAA,GAET,EAEO,QAFP,GAEO,EADA,EAAA,CAAA,EAAE,gCAAA,CAAA,GAAA,CAAA,CAAA,CAAA;KAGb,EAE6C,GAAA;MADzC,MAAK;MACL,OAAM;;;IAKlB,EASM,OATN,GASM,CARF,EAOyB,GAAA;KANrB,WAAU;KACV,OAAM;KACN,MAAK;KACJ,SAAS,EAAA;KACT,UAAU,EAAA;KACV,OAAO,EAAA,CAAA,EAAE,gBAAA;KACT,SAAO;;;;;;IAGhB,EAAQ,EAAA,QAAA,SAAA"}
1
+ {"version":3,"file":"PkChatbotViewProfile-CfFeqjse.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbotViewProfile.vue","../../../../packages/components/src/chat/PkChatbotViewProfile.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { useChatbotStore } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { name: agentName, agentInterface } = storeToRefs(store)\n\n const profile = inject(ChatbotProfileContextKey, undefined)\n const user = computed(() => profile?.user.value ?? null)\n const canManage = computed(() => profile?.canManageProfile.value ?? false)\n const profileUrl = computed(() => profile?.profileUrl.value)\n\n const isSigningOut = ref(false)\n const errorMessage = ref<string>()\n\n async function onSignOut() {\n if (!profile) {\n return\n }\n isSigningOut.value = true\n try {\n await profile.signOut()\n } catch (error) {\n errorMessage.value =\n error instanceof Error ? error.message : $t('message.error')\n } finally {\n isSigningOut.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 overflow-y-auto\">\n <!-- Hero: user identity -->\n <div\n class=\"flex flex-col items-center gap-sm pt-lg px-lg pb-md bg-surface-1 border-b border-surface-3\">\n <PkAvatar\n modifiers=\"surface ring\"\n class=\"w-64 h-64\"\n :name=\"user?.name ?? agentName\"\n :img-src=\"user?.image ?? agentInterface?.logo\" />\n <div class=\"flex flex-col items-center gap-6 text-center\">\n <strong\n v-if=\"user?.name ?? agentName\"\n class=\"text-16 font-semibold text-word-1 leading-tight\">\n {{ user?.name ?? agentName }}\n </strong>\n <span\n v-if=\"user?.email\"\n class=\"text-12 text-word-3 leading-none\">\n {{ user.email }}\n </span>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"flex flex-col gap-sm p-md flex-1\">\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n :content=\"errorMessage\" />\n\n <!-- Manage profile link card -->\n <a\n v-if=\"canManage && profileUrl\"\n :href=\"profileUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"flex items-center gap-12 p-12 rounded-lg border border-surface-3 hover:border-surface-5 hover:bg-surface-1 transition-colors cursor-pointer no-underline\">\n <div\n class=\"w-40 h-40 rounded-md bg-surface-2 border border-surface-3 flex items-center justify-center shrink-0\">\n <VvIcon\n name=\"ri:user-settings-line\"\n class=\"w-20 h-20 text-word-2\" />\n </div>\n <div class=\"flex flex-col flex-1 min-w-0 gap-1\">\n <span class=\"text-14 font-medium text-word-1 leading-tight\">\n {{ $t('action.manageProfile') }}\n </span>\n <span class=\"text-12 text-word-3 leading-tight\">\n {{ $t('label.manageProfileDescription') }}\n </span>\n </div>\n <VvIcon\n name=\"ri:arrow-right-s-line\"\n class=\"w-16 h-16 text-word-4 shrink-0\" />\n </a>\n </div>\n\n <!-- Footer: sign out -->\n <div class=\"px-md py-sm\">\n <VvButton\n modifiers=\"secondary-danger\"\n class=\"w-full\"\n icon=\"ri:logout-box-line\"\n :loading=\"isSigningOut\"\n :disabled=\"isSigningOut\"\n :label=\"$t('action.signOut')\"\n @click=\"onSignOut\" />\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { useChatbotStore } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { name: agentName, agentInterface } = storeToRefs(store)\n\n const profile = inject(ChatbotProfileContextKey, undefined)\n const user = computed(() => profile?.user.value ?? null)\n const canManage = computed(() => profile?.canManageProfile.value ?? false)\n const profileUrl = computed(() => profile?.profileUrl.value)\n\n const isSigningOut = ref(false)\n const errorMessage = ref<string>()\n\n async function onSignOut() {\n if (!profile) {\n return\n }\n isSigningOut.value = true\n try {\n await profile.signOut()\n } catch (error) {\n errorMessage.value =\n error instanceof Error ? error.message : $t('message.error')\n } finally {\n isSigningOut.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 overflow-y-auto\">\n <!-- Hero: user identity -->\n <div\n class=\"flex flex-col items-center gap-sm pt-lg px-lg pb-md bg-surface-1 border-b border-surface-3\">\n <PkAvatar\n modifiers=\"surface ring\"\n class=\"w-64 h-64\"\n :name=\"user?.name ?? agentName\"\n :img-src=\"user?.image ?? agentInterface?.logo\" />\n <div class=\"flex flex-col items-center gap-6 text-center\">\n <strong\n v-if=\"user?.name ?? agentName\"\n class=\"text-16 font-semibold text-word-1 leading-tight\">\n {{ user?.name ?? agentName }}\n </strong>\n <span\n v-if=\"user?.email\"\n class=\"text-12 text-word-3 leading-none\">\n {{ user.email }}\n </span>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"flex flex-col gap-sm p-md flex-1\">\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n :content=\"errorMessage\" />\n\n <!-- Manage profile link card -->\n <a\n v-if=\"canManage && profileUrl\"\n :href=\"profileUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"flex items-center gap-12 p-12 rounded-lg border border-surface-3 hover:border-surface-5 hover:bg-surface-1 transition-colors cursor-pointer no-underline\">\n <div\n class=\"w-40 h-40 rounded-md bg-surface-2 border border-surface-3 flex items-center justify-center shrink-0\">\n <VvIcon\n name=\"ri:user-settings-line\"\n class=\"w-20 h-20 text-word-2\" />\n </div>\n <div class=\"flex flex-col flex-1 min-w-0 gap-1\">\n <span class=\"text-14 font-medium text-word-1 leading-tight\">\n {{ $t('action.manageProfile') }}\n </span>\n <span class=\"text-12 text-word-3 leading-tight\">\n {{ $t('label.manageProfileDescription') }}\n </span>\n </div>\n <VvIcon\n name=\"ri:arrow-right-s-line\"\n class=\"w-16 h-16 text-word-4 shrink-0\" />\n </a>\n </div>\n\n <!-- Footer: sign out -->\n <div class=\"px-md py-sm\">\n <VvButton\n modifiers=\"secondary-danger\"\n class=\"w-full\"\n icon=\"ri:logout-box-line\"\n :loading=\"isSigningOut\"\n :disabled=\"isSigningOut\"\n :label=\"$t('action.signOut')\"\n @click=\"onSignOut\" />\n </div>\n\n <slot />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAQI,IAAM,IAAQ,GAER,EAAE,GAAG,MAAO,EAAQ,EAAE,UAAU,SAAS,CAAC,GAG1C,EAAE,MAAM,GAAW,sBAAmB,EAD9B,EAAgB,EAAM,OACoB,CAAK,GAEvD,IAAU,EAAO,GAA0B,KAAA,CAAS,GACpD,IAAO,QAAe,GAAS,KAAK,SAAS,IAAI,GACjD,IAAY,QAAe,GAAS,iBAAiB,SAAS,EAAK,GACnE,IAAa,QAAe,GAAS,WAAW,KAAK,GAErD,IAAe,EAAI,EAAK,GACxB,IAAe,EAAY;EAEjC,eAAe,IAAY;GAClB,OAGL;MAAa,QAAQ;IACrB,IAAI;KACA,MAAM,EAAQ,QAAQ;IAC1B,SAAS,GAAO;KACZ,EAAa,QACT,aAAiB,QAAQ,EAAM,UAAU,EAAG,eAAe;IACnE,UAAU;KACN,EAAa,QAAQ;IACzB;GARqB;EASzB;;;eAIA,EAsEM,OAtEN,GAsEM;IApEF,EAmBM,OAnBN,GAmBM,CAjBF,EAIqD,GAAA;KAHjD,WAAU;KACV,OAAM;KACL,MAAM,EAAA,OAAM,QAAQ,EAAA,CAAA;KACpB,WAAS,EAAA,OAAM,SAAS,EAAA,CAAA,GAAgB;sCAC7C,EAWM,OAXN,GAWM,CATQ,EAAA,OAAM,QAAQ,EAAA,CAAA,KAAA,EAAA,GADxB,EAIS,UAJT,GAIS,EADF,EAAA,OAAM,QAAQ,EAAA,CAAA,CAAS,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAGpB,EAAA,OAAM,SAAA,EAAA,GADhB,EAIO,QAJP,GAIO,EADA,EAAA,MAAK,KAAK,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,CAAA,CAAA;IAMzB,EA+BM,OA/BN,GA+BM,CA7BQ,EAAA,SAAA,EAAA,GADV,EAG8B,GAAA;;KAD1B,WAAU;KACT,SAAS,EAAA;2CAIJ,EAAA,SAAa,EAAA,SAAA,EAAA,GADvB,EAuBI,KAAA;;KArBC,MAAM,EAAA;KACP,QAAO;KACP,KAAI;KACJ,OAAM;;KACN,EAKM,OALN,GAKM,CAHF,EAEoC,GAAA;MADhC,MAAK;MACL,OAAM;;KAEd,EAOM,OAPN,GAOM,CANF,EAEO,QAFP,GAEO,EADA,EAAA,CAAA,EAAE,sBAAA,CAAA,GAAA,CAAA,GAET,EAEO,QAFP,GAEO,EADA,EAAA,CAAA,EAAE,gCAAA,CAAA,GAAA,CAAA,CAAA,CAAA;KAGb,EAE6C,GAAA;MADzC,MAAK;MACL,OAAM;;;IAKlB,EASM,OATN,GASM,CARF,EAOyB,GAAA;KANrB,WAAU;KACV,OAAM;KACN,MAAK;KACJ,SAAS,EAAA;KACT,UAAU,EAAA;KACV,OAAO,EAAA,CAAA,EAAE,gBAAA;KACT,SAAO;;;;;;IAGhB,EAAQ,EAAA,QAAA,SAAA"}
@@ -66,4 +66,4 @@ var l = () => {
66
66
  //#endregion
67
67
  export { l as n, d as t };
68
68
 
69
- //# sourceMappingURL=PkRelativeTime-D72zYq42.js.map
69
+ //# sourceMappingURL=PkRelativeTime-2HoOjv_T.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkRelativeTime-D72zYq42.js","names":[],"sources":["../../../../packages/composable/src/useTime.ts","../../../../packages/components/src/PkRelativeTime.vue","../../../../packages/components/src/PkRelativeTime.vue"],"sourcesContent":["import { useI18n } from 'vue-i18n'\n\nexport const useTime = () => {\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const formatDelta = (delta?: number) => {\n if (!delta)\n return {\n value: 0,\n unit: $t('time.unit.second', 0),\n }\n const seconds = Math.floor(delta / 1000)\n const minutes = Math.floor(delta / (1000 * 60))\n const hours = Math.floor(delta / (1000 * 60 * 60))\n const days = Math.floor(delta / (1000 * 60 * 60 * 24))\n\n if (seconds < 60) {\n return {\n value: seconds,\n unit: $t('time.unit.second', seconds),\n }\n }\n\n if (minutes < 60) {\n return {\n value: minutes,\n unit: $t('time.unit.minute', minutes),\n }\n }\n\n if (hours < 24) {\n return {\n value: hours,\n unit: $t('time.unit.hour', hours),\n }\n }\n\n return {\n value: days,\n unit: $t('time.unit.day', days),\n }\n }\n\n const formatDeltaToString = (delta?: number) => {\n const { value, unit } = formatDelta(delta)\n return `${value} ${unit}`\n }\n\n return {\n formatDelta,\n formatDeltaToString,\n }\n}\n","<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useTime } from 'composables/src/useTime'\n\n const props = withDefaults(\n defineProps<{\n date: Date | string | number\n endDate?: Date | string | number\n autoUpdate?: boolean\n updateInterval?: number\n }>(),\n {\n endDate: undefined,\n autoUpdate: true,\n updateInterval: 1000,\n },\n )\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const now = ref(Date.now())\n let intervalId: ReturnType<typeof setTimeout> | null = null\n\n const targetDate = computed(() => {\n if (props.date instanceof Date) {\n return props.date.getTime()\n }\n if (typeof props.date === 'string') {\n return new Date(props.date).getTime()\n }\n return props.date\n })\n\n const targetEndDate = computed(() => {\n if (props.endDate === undefined) {\n return null\n }\n if (props.endDate instanceof Date) {\n return props.endDate.getTime()\n }\n if (typeof props.endDate === 'string') {\n return new Date(props.endDate).getTime()\n }\n return props.endDate\n })\n\n const deltaMs = computed(() => {\n const reference = targetEndDate.value ?? now.value\n return targetDate.value - reference\n })\n\n const isInFuture = computed(() => {\n return deltaMs.value > 0\n })\n\n const absoluteDelta = computed(() => {\n return Math.abs(deltaMs.value)\n })\n\n const { formatDelta } = useTime()\n const formattedTime = computed(() => {\n const delta = absoluteDelta.value\n return formatDelta(delta)\n })\n\n const displayText = computed(() => {\n const { value, unit } = formattedTime.value\n\n if (props.endDate) {\n return `${value} ${unit}`\n }\n\n if (isInFuture.value) {\n return `${$t('time.future')} ${value} ${unit}`\n }\n\n return `${value} ${unit} ${$t('time.past')}`\n })\n\n const dateTimeAttribute = computed(() => {\n return new Date(targetDate.value).toISOString()\n })\n\n const getAdaptiveInterval = () => {\n const delta = Math.abs(Date.now() - targetDate.value)\n if (delta < 60_000) {\n return props.updateInterval\n }\n if (delta < 3_600_000) {\n return 60_000\n }\n return 3_600_000\n }\n\n const updateNow = () => {\n now.value = Date.now()\n if (props.autoUpdate && !props.endDate) {\n intervalId = setTimeout(updateNow, getAdaptiveInterval())\n }\n }\n\n onMounted(() => {\n if (props.autoUpdate && !props.endDate) {\n intervalId = setTimeout(updateNow, getAdaptiveInterval())\n }\n })\n\n onUnmounted(() => {\n if (intervalId) {\n clearTimeout(intervalId)\n }\n })\n</script>\n\n<template>\n <time :datetime=\"dateTimeAttribute\">\n {{ displayText }}\n </time>\n</template>\n","<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useTime } from 'composables/src/useTime'\n\n const props = withDefaults(\n defineProps<{\n date: Date | string | number\n endDate?: Date | string | number\n autoUpdate?: boolean\n updateInterval?: number\n }>(),\n {\n endDate: undefined,\n autoUpdate: true,\n updateInterval: 1000,\n },\n )\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const now = ref(Date.now())\n let intervalId: ReturnType<typeof setTimeout> | null = null\n\n const targetDate = computed(() => {\n if (props.date instanceof Date) {\n return props.date.getTime()\n }\n if (typeof props.date === 'string') {\n return new Date(props.date).getTime()\n }\n return props.date\n })\n\n const targetEndDate = computed(() => {\n if (props.endDate === undefined) {\n return null\n }\n if (props.endDate instanceof Date) {\n return props.endDate.getTime()\n }\n if (typeof props.endDate === 'string') {\n return new Date(props.endDate).getTime()\n }\n return props.endDate\n })\n\n const deltaMs = computed(() => {\n const reference = targetEndDate.value ?? now.value\n return targetDate.value - reference\n })\n\n const isInFuture = computed(() => {\n return deltaMs.value > 0\n })\n\n const absoluteDelta = computed(() => {\n return Math.abs(deltaMs.value)\n })\n\n const { formatDelta } = useTime()\n const formattedTime = computed(() => {\n const delta = absoluteDelta.value\n return formatDelta(delta)\n })\n\n const displayText = computed(() => {\n const { value, unit } = formattedTime.value\n\n if (props.endDate) {\n return `${value} ${unit}`\n }\n\n if (isInFuture.value) {\n return `${$t('time.future')} ${value} ${unit}`\n }\n\n return `${value} ${unit} ${$t('time.past')}`\n })\n\n const dateTimeAttribute = computed(() => {\n return new Date(targetDate.value).toISOString()\n })\n\n const getAdaptiveInterval = () => {\n const delta = Math.abs(Date.now() - targetDate.value)\n if (delta < 60_000) {\n return props.updateInterval\n }\n if (delta < 3_600_000) {\n return 60_000\n }\n return 3_600_000\n }\n\n const updateNow = () => {\n now.value = Date.now()\n if (props.autoUpdate && !props.endDate) {\n intervalId = setTimeout(updateNow, getAdaptiveInterval())\n }\n }\n\n onMounted(() => {\n if (props.autoUpdate && !props.endDate) {\n intervalId = setTimeout(updateNow, getAdaptiveInterval())\n }\n })\n\n onUnmounted(() => {\n if (intervalId) {\n clearTimeout(intervalId)\n }\n })\n</script>\n\n<template>\n <time :datetime=\"dateTimeAttribute\">\n {{ displayText }}\n </time>\n</template>\n"],"mappings":";;;AAEA,IAAa,UAAgB;CACzB,IAAM,EAAE,GAAG,MAAO,EAAQ,EACtB,UAAU,SACd,CAAC,GAEK,KAAe,MAAmB;EACpC,IAAI,CAAC,GACD,OAAO;GACH,OAAO;GACP,MAAM,EAAG,oBAAoB,CAAC;EAClC;EACJ,IAAM,IAAU,KAAK,MAAM,IAAQ,GAAI,GACjC,IAAU,KAAK,MAAM,KAAS,MAAO,GAAG,GACxC,IAAQ,KAAK,MAAM,KAAS,MAAO,KAAK,GAAG,GAC3C,IAAO,KAAK,MAAM,KAAS,MAAO,KAAK,KAAK,GAAG;EAuBrD,OArBI,IAAU,KACH;GACH,OAAO;GACP,MAAM,EAAG,oBAAoB,CAAO;EACxC,IAGA,IAAU,KACH;GACH,OAAO;GACP,MAAM,EAAG,oBAAoB,CAAO;EACxC,IAGA,IAAQ,KACD;GACH,OAAO;GACP,MAAM,EAAG,kBAAkB,CAAK;EACpC,IAGG;GACH,OAAO;GACP,MAAM,EAAG,iBAAiB,CAAI;EAClC;CACJ;CAOA,OAAO;EACH;EACA,sBAPyB,MAAmB;GAC5C,IAAM,EAAE,UAAO,YAAS,EAAY,CAAK;GACzC,OAAO,GAAG,EAAM,GAAG;EACvB;CAKA;AACJ;;;;;;;;;;;;ECjDI,IAAM,IAAQ,GAcR,EAAE,GAAG,MAAO,EAAQ,EACtB,UAAU,SACd,CAAC,GAEK,IAAM,EAAI,KAAK,IAAI,CAAC,GACtB,IAAmD,MAEjD,IAAa,QACX,EAAM,gBAAgB,OACf,EAAM,KAAK,QAAQ,IAE1B,OAAO,EAAM,QAAS,WACf,IAAI,KAAK,EAAM,IAAI,EAAE,QAAQ,IAEjC,EAAM,IAChB,GAEK,IAAgB,QACd,EAAM,YAAY,KAAA,IACX,OAEP,EAAM,mBAAmB,OAClB,EAAM,QAAQ,QAAQ,IAE7B,OAAO,EAAM,WAAY,WAClB,IAAI,KAAK,EAAM,OAAO,EAAE,QAAQ,IAEpC,EAAM,OAChB,GAEK,IAAU,QAAe;GAC3B,IAAM,IAAY,EAAc,SAAS,EAAI;GAC7C,OAAO,EAAW,QAAQ;EAC9B,CAAC,GAEK,IAAa,QACR,EAAQ,QAAQ,CAC1B,GAEK,IAAgB,QACX,KAAK,IAAI,EAAQ,KAAK,CAChC,GAEK,EAAE,mBAAgB,EAAQ,GAC1B,IAAgB,QAAe;GACjC,IAAM,IAAQ,EAAc;GAC5B,OAAO,EAAY,CAAK;EAC5B,CAAC,GAEK,IAAc,QAAe;GAC/B,IAAM,EAAE,UAAO,YAAS,EAAc;GAUtC,OARI,EAAM,UACC,GAAG,EAAM,GAAG,MAGnB,EAAW,QACJ,GAAG,EAAG,aAAa,EAAE,GAAG,EAAM,GAAG,MAGrC,GAAG,EAAM,GAAG,EAAK,GAAG,EAAG,WAAW;EAC7C,CAAC,GAEK,IAAoB,QACf,IAAI,KAAK,EAAW,KAAK,EAAE,YAAY,CACjD,GAEK,UAA4B;GAC9B,IAAM,IAAQ,KAAK,IAAI,KAAK,IAAI,IAAI,EAAW,KAAK;GAOpD,OANI,IAAQ,MACD,EAAM,iBAEb,IAAQ,OACD,MAEJ;EACX,GAEM,UAAkB;GAEpB,AADA,EAAI,QAAQ,KAAK,IAAI,GACjB,EAAM,cAAc,CAAC,EAAM,YAC3B,IAAa,WAAW,GAAW,EAAoB,CAAC;EAEhE;SAEA,QAAgB;GACZ,AAAI,EAAM,cAAc,CAAC,EAAM,YAC3B,IAAa,WAAW,GAAW,EAAoB,CAAC;EAEhE,CAAC,GAED,QAAkB;GACd,AAAI,KACA,aAAa,CAAU;EAE/B,CAAC,mBAID,EAEO,QAAA,EAFA,UAAU,EAAA,MAAiB,GAAA,EAC3B,EAAA,KAAW,GAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"PkRelativeTime-2HoOjv_T.js","names":[],"sources":["../../../../packages/composable/src/useTime.ts","../../../../packages/components/src/PkRelativeTime.vue","../../../../packages/components/src/PkRelativeTime.vue"],"sourcesContent":["import { useI18n } from 'vue-i18n'\n\nexport const useTime = () => {\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const formatDelta = (delta?: number) => {\n if (!delta)\n return {\n value: 0,\n unit: $t('time.unit.second', 0),\n }\n const seconds = Math.floor(delta / 1000)\n const minutes = Math.floor(delta / (1000 * 60))\n const hours = Math.floor(delta / (1000 * 60 * 60))\n const days = Math.floor(delta / (1000 * 60 * 60 * 24))\n\n if (seconds < 60) {\n return {\n value: seconds,\n unit: $t('time.unit.second', seconds),\n }\n }\n\n if (minutes < 60) {\n return {\n value: minutes,\n unit: $t('time.unit.minute', minutes),\n }\n }\n\n if (hours < 24) {\n return {\n value: hours,\n unit: $t('time.unit.hour', hours),\n }\n }\n\n return {\n value: days,\n unit: $t('time.unit.day', days),\n }\n }\n\n const formatDeltaToString = (delta?: number) => {\n const { value, unit } = formatDelta(delta)\n return `${value} ${unit}`\n }\n\n return {\n formatDelta,\n formatDeltaToString,\n }\n}\n","<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useTime } from 'composables/src/useTime'\n\n const props = withDefaults(\n defineProps<{\n date: Date | string | number\n endDate?: Date | string | number\n autoUpdate?: boolean\n updateInterval?: number\n }>(),\n {\n endDate: undefined,\n autoUpdate: true,\n updateInterval: 1000,\n },\n )\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const now = ref(Date.now())\n let intervalId: ReturnType<typeof setTimeout> | null = null\n\n const targetDate = computed(() => {\n if (props.date instanceof Date) {\n return props.date.getTime()\n }\n if (typeof props.date === 'string') {\n return new Date(props.date).getTime()\n }\n return props.date\n })\n\n const targetEndDate = computed(() => {\n if (props.endDate === undefined) {\n return null\n }\n if (props.endDate instanceof Date) {\n return props.endDate.getTime()\n }\n if (typeof props.endDate === 'string') {\n return new Date(props.endDate).getTime()\n }\n return props.endDate\n })\n\n const deltaMs = computed(() => {\n const reference = targetEndDate.value ?? now.value\n return targetDate.value - reference\n })\n\n const isInFuture = computed(() => {\n return deltaMs.value > 0\n })\n\n const absoluteDelta = computed(() => {\n return Math.abs(deltaMs.value)\n })\n\n const { formatDelta } = useTime()\n const formattedTime = computed(() => {\n const delta = absoluteDelta.value\n return formatDelta(delta)\n })\n\n const displayText = computed(() => {\n const { value, unit } = formattedTime.value\n\n if (props.endDate) {\n return `${value} ${unit}`\n }\n\n if (isInFuture.value) {\n return `${$t('time.future')} ${value} ${unit}`\n }\n\n return `${value} ${unit} ${$t('time.past')}`\n })\n\n const dateTimeAttribute = computed(() => {\n return new Date(targetDate.value).toISOString()\n })\n\n const getAdaptiveInterval = () => {\n const delta = Math.abs(Date.now() - targetDate.value)\n if (delta < 60_000) {\n return props.updateInterval\n }\n if (delta < 3_600_000) {\n return 60_000\n }\n return 3_600_000\n }\n\n const updateNow = () => {\n now.value = Date.now()\n if (props.autoUpdate && !props.endDate) {\n intervalId = setTimeout(updateNow, getAdaptiveInterval())\n }\n }\n\n onMounted(() => {\n if (props.autoUpdate && !props.endDate) {\n intervalId = setTimeout(updateNow, getAdaptiveInterval())\n }\n })\n\n onUnmounted(() => {\n if (intervalId) {\n clearTimeout(intervalId)\n }\n })\n</script>\n\n<template>\n <time :datetime=\"dateTimeAttribute\">\n {{ displayText }}\n </time>\n</template>\n","<script setup lang=\"ts\">\n import { computed, onMounted, onUnmounted, ref } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useTime } from 'composables/src/useTime'\n\n const props = withDefaults(\n defineProps<{\n date: Date | string | number\n endDate?: Date | string | number\n autoUpdate?: boolean\n updateInterval?: number\n }>(),\n {\n endDate: undefined,\n autoUpdate: true,\n updateInterval: 1000,\n },\n )\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const now = ref(Date.now())\n let intervalId: ReturnType<typeof setTimeout> | null = null\n\n const targetDate = computed(() => {\n if (props.date instanceof Date) {\n return props.date.getTime()\n }\n if (typeof props.date === 'string') {\n return new Date(props.date).getTime()\n }\n return props.date\n })\n\n const targetEndDate = computed(() => {\n if (props.endDate === undefined) {\n return null\n }\n if (props.endDate instanceof Date) {\n return props.endDate.getTime()\n }\n if (typeof props.endDate === 'string') {\n return new Date(props.endDate).getTime()\n }\n return props.endDate\n })\n\n const deltaMs = computed(() => {\n const reference = targetEndDate.value ?? now.value\n return targetDate.value - reference\n })\n\n const isInFuture = computed(() => {\n return deltaMs.value > 0\n })\n\n const absoluteDelta = computed(() => {\n return Math.abs(deltaMs.value)\n })\n\n const { formatDelta } = useTime()\n const formattedTime = computed(() => {\n const delta = absoluteDelta.value\n return formatDelta(delta)\n })\n\n const displayText = computed(() => {\n const { value, unit } = formattedTime.value\n\n if (props.endDate) {\n return `${value} ${unit}`\n }\n\n if (isInFuture.value) {\n return `${$t('time.future')} ${value} ${unit}`\n }\n\n return `${value} ${unit} ${$t('time.past')}`\n })\n\n const dateTimeAttribute = computed(() => {\n return new Date(targetDate.value).toISOString()\n })\n\n const getAdaptiveInterval = () => {\n const delta = Math.abs(Date.now() - targetDate.value)\n if (delta < 60_000) {\n return props.updateInterval\n }\n if (delta < 3_600_000) {\n return 60_000\n }\n return 3_600_000\n }\n\n const updateNow = () => {\n now.value = Date.now()\n if (props.autoUpdate && !props.endDate) {\n intervalId = setTimeout(updateNow, getAdaptiveInterval())\n }\n }\n\n onMounted(() => {\n if (props.autoUpdate && !props.endDate) {\n intervalId = setTimeout(updateNow, getAdaptiveInterval())\n }\n })\n\n onUnmounted(() => {\n if (intervalId) {\n clearTimeout(intervalId)\n }\n })\n</script>\n\n<template>\n <time :datetime=\"dateTimeAttribute\">\n {{ displayText }}\n </time>\n</template>\n"],"mappings":";;;AAEA,IAAa,UAAgB;CACzB,IAAM,EAAE,GAAG,MAAO,EAAQ,EACtB,UAAU,SACd,CAAC,GAEK,KAAe,MAAmB;EACpC,IAAI,CAAC,GACD,OAAO;GACH,OAAO;GACP,MAAM,EAAG,oBAAoB,CAAC;EAClC;EACJ,IAAM,IAAU,KAAK,MAAM,IAAQ,GAAI,GACjC,IAAU,KAAK,MAAM,KAAS,MAAO,GAAG,GACxC,IAAQ,KAAK,MAAM,KAAS,MAAO,KAAK,GAAG,GAC3C,IAAO,KAAK,MAAM,KAAS,MAAO,KAAK,KAAK,GAAG;EAuBrD,OArBI,IAAU,KACH;GACH,OAAO;GACP,MAAM,EAAG,oBAAoB,CAAO;EACxC,IAGA,IAAU,KACH;GACH,OAAO;GACP,MAAM,EAAG,oBAAoB,CAAO;EACxC,IAGA,IAAQ,KACD;GACH,OAAO;GACP,MAAM,EAAG,kBAAkB,CAAK;EACpC,IAGG;GACH,OAAO;GACP,MAAM,EAAG,iBAAiB,CAAI;EAClC;CACJ;CAOA,OAAO;EACH;EACA,sBAPyB,MAAmB;GAC5C,IAAM,EAAE,UAAO,YAAS,EAAY,CAAK;GACzC,OAAO,GAAG,EAAM,GAAG;EACvB;CAKA;AACJ;;;;;;;;;;;;ECjDI,IAAM,IAAQ,GAcR,EAAE,GAAG,MAAO,EAAQ,EACtB,UAAU,SACd,CAAC,GAEK,IAAM,EAAI,KAAK,IAAI,CAAC,GACtB,IAAmD,MAEjD,IAAa,QACX,EAAM,gBAAgB,OACf,EAAM,KAAK,QAAQ,IAE1B,OAAO,EAAM,QAAS,WACf,IAAI,KAAK,EAAM,IAAI,EAAE,QAAQ,IAEjC,EAAM,IAChB,GAEK,IAAgB,QACd,EAAM,YAAY,KAAA,IACX,OAEP,EAAM,mBAAmB,OAClB,EAAM,QAAQ,QAAQ,IAE7B,OAAO,EAAM,WAAY,WAClB,IAAI,KAAK,EAAM,OAAO,EAAE,QAAQ,IAEpC,EAAM,OAChB,GAEK,IAAU,QAAe;GAC3B,IAAM,IAAY,EAAc,SAAS,EAAI;GAC7C,OAAO,EAAW,QAAQ;EAC9B,CAAC,GAEK,IAAa,QACR,EAAQ,QAAQ,CAC1B,GAEK,IAAgB,QACX,KAAK,IAAI,EAAQ,KAAK,CAChC,GAEK,EAAE,mBAAgB,EAAQ,GAC1B,IAAgB,QAAe;GACjC,IAAM,IAAQ,EAAc;GAC5B,OAAO,EAAY,CAAK;EAC5B,CAAC,GAEK,IAAc,QAAe;GAC/B,IAAM,EAAE,UAAO,YAAS,EAAc;GAUtC,OARI,EAAM,UACC,GAAG,EAAM,GAAG,MAGnB,EAAW,QACJ,GAAG,EAAG,aAAa,EAAE,GAAG,EAAM,GAAG,MAGrC,GAAG,EAAM,GAAG,EAAK,GAAG,EAAG,WAAW;EAC7C,CAAC,GAEK,IAAoB,QACf,IAAI,KAAK,EAAW,KAAK,EAAE,YAAY,CACjD,GAEK,UAA4B;GAC9B,IAAM,IAAQ,KAAK,IAAI,KAAK,IAAI,IAAI,EAAW,KAAK;GAOpD,OANI,IAAQ,MACD,EAAM,iBAEb,IAAQ,OACD,MAEJ;EACX,GAEM,UAAkB;GAEpB,AADA,EAAI,QAAQ,KAAK,IAAI,GACjB,EAAM,cAAc,CAAC,EAAM,YAC3B,IAAa,WAAW,GAAW,EAAoB,CAAC;EAEhE;SAEA,QAAgB;GACZ,AAAI,EAAM,cAAc,CAAC,EAAM,YAC3B,IAAa,WAAW,GAAW,EAAoB,CAAC;EAEhE,CAAC,GAED,QAAkB;GACd,AAAI,KACA,aAAa,CAAU;EAE/B,CAAC,mBAID,EAEO,QAAA,EAFA,UAAU,EAAA,MAAiB,GAAA,EAC3B,EAAA,KAAW,GAAA,GAAA,CAAA"}
@@ -2215,4 +2215,4 @@ var Sn = ["innerHTML"], Cn = /* @__PURE__ */ a({
2215
2215
  //#endregion
2216
2216
  export { Cn as t };
2217
2217
 
2218
- //# sourceMappingURL=PkStreamingMarkdown-CVAsbuWi.js.map
2218
+ //# sourceMappingURL=PkStreamingMarkdown-C_LQOTVJ.js.map