@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,12 +1,12 @@
1
1
  import { C as e } from "./src-BCYjA3mm.js";
2
2
  import { VvIcon as t } from "@volverjs/ui-vue/components";
3
- import { Fragment as n, computed as r, createBlock as i, createCommentVNode as a, createElementBlock as o, createElementVNode as s, createTextVNode as c, createVNode as l, defineComponent as u, normalizeClass as d, normalizeStyle as f, onBeforeUnmount as p, onMounted as m, openBlock as h, ref as g, shallowRef as _, toDisplayString as v, unref as y, withModifiers as b } from "vue";
3
+ import { Fragment as n, computed as r, createBlock as i, createCommentVNode as a, createElementBlock as o, createElementVNode as s, createTextVNode as c, createVNode as l, defineComponent as u, normalizeClass as d, normalizeStyle as f, onBeforeUnmount as p, onMounted as m, openBlock as h, ref as g, shallowRef as _, toDisplayString as v, vShow as y, withDirectives as b, withModifiers as x } from "vue";
4
4
  //#region ../../packages/components/src/composables/useLightbox.ts
5
- var x = "https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.css", S = {
5
+ var S = "https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.css", C = {
6
6
  width: 1200,
7
7
  height: 800
8
8
  };
9
- function C(t) {
9
+ function w(t) {
10
10
  let { gallery: n, children: r = "a[data-pswp-src]", loop: i = !0, downloadable: a = !0, openInNewTab: o = !0 } = t, s = _(null);
11
11
  return p(() => {
12
12
  s.value?.destroy(), s.value = null;
@@ -14,7 +14,7 @@ function C(t) {
14
14
  lightbox: s,
15
15
  init: async () => {
16
16
  if (!n.value) return;
17
- e(x), A();
17
+ e(S), j();
18
18
  let { default: t } = await import("photoswipe/lightbox");
19
19
  s.value = new t({
20
20
  gallery: n.value,
@@ -34,7 +34,7 @@ function C(t) {
34
34
  html: "<svg aria-hidden=\"true\" class=\"pswp__icn\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ffffff\" d=\"M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm11-3v8h-2V6.413l-7.793 7.794l-1.414-1.414L17.585 5H13V3z\"/></svg>",
35
35
  title: "Open in new tab",
36
36
  onClick: (e, t, n) => {
37
- let r = T(n);
37
+ let r = E(n);
38
38
  r && window.open(r, "_blank", "noopener,noreferrer");
39
39
  }
40
40
  }), a && e.registerElement({
@@ -44,15 +44,15 @@ function C(t) {
44
44
  html: "<svg aria-hidden=\"true\" class=\"pswp__icn\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ffffff\" d=\"M3 19h18v2H3zm10-5.828L19.071 7.1l1.414 1.414L12 17L3.515 8.515L4.929 7.1L11 13.173V2h2z\"/></svg>",
45
45
  title: "Download",
46
46
  onClick: (e, t, n) => {
47
- let r = T(n);
48
- r && E(r);
47
+ let r = E(n);
48
+ r && D(r);
49
49
  }
50
50
  }));
51
51
  }), s.value.init();
52
52
  }
53
53
  };
54
54
  }
55
- function w() {
55
+ function T() {
56
56
  let e = g(/* @__PURE__ */ new Map());
57
57
  return {
58
58
  preload: (t) => {
@@ -73,20 +73,20 @@ function w() {
73
73
  }, t.src = n.url;
74
74
  }
75
75
  },
76
- getSize: (t) => e.value.get(t) ?? S
76
+ getSize: (t) => e.value.get(t) ?? C
77
77
  };
78
78
  }
79
- function T(e) {
80
- return e.currSlide?.data?.src ?? e.currSlide?.data?.element?.querySelector("img")?.getAttribute("src") ?? e.currSlide?.data?.element?.querySelector("source")?.getAttribute("srcset") ?? e.currSlide?.data?.element?.getAttribute("src") ?? void 0;
81
- }
82
79
  function E(e) {
83
- let t = D(e), n = document.createElement("a");
84
- n.href = t, n.target = "_blank", n.rel = "noopener noreferrer", n.download = O(e), document.body.appendChild(n), n.click(), document.body.removeChild(n);
80
+ return e.currSlide?.data?.src ?? e.currSlide?.data?.element?.querySelector("img")?.getAttribute("src") ?? e.currSlide?.data?.element?.querySelector("source")?.getAttribute("srcset") ?? e.currSlide?.data?.element?.getAttribute("src") ?? void 0;
85
81
  }
86
82
  function D(e) {
87
- return e.replace(/\/view(\?|$)/, "/download$1");
83
+ let t = O(e), n = document.createElement("a");
84
+ n.href = t, n.target = "_blank", n.rel = "noopener noreferrer", n.download = k(e), document.body.appendChild(n), n.click(), document.body.removeChild(n);
88
85
  }
89
86
  function O(e) {
87
+ return e.replace(/\/view(\?|$)/, "/download$1");
88
+ }
89
+ function k(e) {
90
90
  if (e.startsWith("data:image/svg+xml")) return "diagram.svg";
91
91
  if (e.startsWith("data:")) return `image.${e.split(":")[1].split(";")[0].split("/")[1] || "bin"}`;
92
92
  try {
@@ -96,10 +96,10 @@ function O(e) {
96
96
  return "image";
97
97
  }
98
98
  }
99
- var k = !1;
100
- function A() {
101
- if (k) return;
102
- k = !0;
99
+ var A = !1;
100
+ function j() {
101
+ if (A) return;
102
+ A = !0;
103
103
  let e = document.createElement("style");
104
104
  e.textContent = [
105
105
  ".pswp__counter { font-family: sans-serif; }",
@@ -114,27 +114,27 @@ function A() {
114
114
  }
115
115
  //#endregion
116
116
  //#region ../../packages/components/src/chat/PkChatbotFilePreview.vue?vue&type=script&setup=true&lang.ts
117
- var j = [
117
+ var M = [
118
118
  "href",
119
119
  "data-pswp-src",
120
120
  "data-pswp-width",
121
121
  "data-pswp-height",
122
122
  "data-pswp-caption",
123
123
  "aria-label"
124
- ], M = ["src", "alt"], N = {
124
+ ], N = ["src", "alt"], P = {
125
125
  key: 0,
126
126
  class: "pk-chatbot-file-preview__status pk-chatbot-file-preview__status--overlay"
127
- }, P = {
127
+ }, F = {
128
+ key: 1,
129
+ class: "pk-chatbot-file-preview__status pk-chatbot-file-preview__status--overlay pk-chatbot-file-preview__status--error"
130
+ }, I = {
128
131
  key: 0,
129
132
  class: "pk-chatbot-file-preview__caption"
130
- }, F = ["title"], I = {
131
- key: 1,
132
- class: "pk-chatbot-file-preview__meta"
133
- }, L = ["title"], R = ["title", "aria-label"], z = { class: "pk-chatbot-file-preview__body" }, B = ["title"], V = { class: "pk-chatbot-file-preview__meta" }, H = ["title"], U = [
133
+ }, L = ["title"], R = { class: "pk-chatbot-file-preview__meta" }, z = ["title"], B = ["title", "aria-label"], V = { class: "pk-chatbot-file-preview__body" }, H = ["title"], U = { class: "pk-chatbot-file-preview__meta" }, W = ["title"], G = [
134
134
  "href",
135
135
  "title",
136
136
  "aria-label"
137
- ], W = ["title", "aria-label"], G = /* @__PURE__ */ u({
137
+ ], K = ["title", "aria-label"], q = /* @__PURE__ */ u({
138
138
  __name: "PkChatbotFilePreview",
139
139
  props: {
140
140
  mediaType: {},
@@ -151,7 +151,7 @@ var j = [
151
151
  let u = e, p = r(() => u.mediaType.startsWith("image/")), _ = r(() => {
152
152
  let e = u.filename ?? "", t = e.lastIndexOf(".");
153
153
  return t > 0 ? e.slice(t + 1).toLowerCase() : "";
154
- }), x = r(() => u.mediaType.split("/")[1]?.split(";")[0] ?? ""), S = [
154
+ }), S = r(() => u.mediaType.split("/")[1]?.split(";")[0] ?? ""), C = [
155
155
  {
156
156
  test: /pdf/,
157
157
  kind: {
@@ -226,13 +226,13 @@ var j = [
226
226
  }
227
227
  ], T = r(() => {
228
228
  let e = `${u.mediaType} ${_.value}`;
229
- for (let { test: t, kind: n } of S) if (t.test(e)) return n;
229
+ for (let { test: t, kind: n } of C) if (t.test(e)) return n;
230
230
  return {
231
231
  icon: "ri:file-line",
232
232
  accent: "#64748b",
233
233
  label: "File"
234
234
  };
235
- }), E = r(() => (_.value || x.value || T.value.label).toUpperCase()), D = r(() => {
235
+ }), E = r(() => (_.value || S.value || T.value.label).toUpperCase()), D = r(() => {
236
236
  let e = u.size;
237
237
  if (e === void 0 || e < 0) return;
238
238
  if (e < 1024) return `${e} B`;
@@ -244,18 +244,23 @@ var j = [
244
244
  ], n = e / 1024, r = 0;
245
245
  for (; n >= 1024 && r < t.length - 1;) n /= 1024, r += 1;
246
246
  return `${n < 10 ? n.toFixed(1) : Math.round(n)} ${t[r]}`;
247
- }), O = g(), k = (e) => {
247
+ }), O = g("loading"), k = g(), A = (e) => {
248
248
  let t = e.target;
249
- t.naturalWidth && t.naturalHeight && (O.value = {
249
+ t.naturalWidth && t.naturalHeight && (k.value = {
250
250
  width: t.naturalWidth,
251
251
  height: t.naturalHeight
252
- });
253
- }, A = r(() => [O.value && `${O.value.width}×${O.value.height}`, D.value].filter(Boolean)), G = r(() => [E.value, D.value].filter(Boolean)), K = g(), { preload: q, getSize: J } = w(), { init: Y } = C({
254
- gallery: K,
252
+ }), O.value = "loaded";
253
+ }, j = () => {
254
+ O.value = "error";
255
+ }, q = r(() => k.value ?? X), J = r(() => [k.value && `${k.value.width}×${k.value.height}`, D.value].filter(Boolean)), Y = r(() => [E.value, D.value].filter(Boolean)), X = {
256
+ width: 1200,
257
+ height: 800
258
+ }, Z = g(), { init: Q } = w({
259
+ gallery: Z,
255
260
  loop: !1
256
261
  });
257
262
  return m(async () => {
258
- p.value && (q([{ url: u.url }]), await Y());
263
+ p.value && await Q();
259
264
  }), (r, u) => {
260
265
  let m = t;
261
266
  return p.value ? (h(), o("figure", {
@@ -264,39 +269,41 @@ var j = [
264
269
  }, [
265
270
  s("div", {
266
271
  ref_key: "galleryRef",
267
- ref: K,
272
+ ref: Z,
268
273
  class: "pk-chatbot-file-preview__thumb-wrap"
269
274
  }, [s("a", {
270
275
  href: e.url,
271
276
  "data-pswp-src": e.url,
272
- "data-pswp-width": y(J)(e.url).width,
273
- "data-pswp-height": y(J)(e.url).height,
277
+ "data-pswp-width": q.value.width,
278
+ "data-pswp-height": q.value.height,
274
279
  "data-pswp-caption": e.filename ?? "",
275
280
  class: "pk-chatbot-file-preview__thumb-link",
276
281
  "aria-label": e.filename ?? r.$t("label.file")
277
- }, [s("img", {
282
+ }, [b(s("img", {
278
283
  src: e.url,
279
284
  alt: e.filename,
285
+ decoding: "async",
280
286
  class: "pk-chatbot-file-preview__thumb",
281
- onLoad: k
282
- }, null, 40, M)], 8, j), e.loading ? (h(), o("span", N, [l(m, { name: "line-md:loading-loop" })])) : a("", !0)], 512),
283
- !e.compact && (e.filename || A.value.length) ? (h(), o("figcaption", P, [e.filename ? (h(), o("span", {
287
+ onLoad: A,
288
+ onError: j
289
+ }, null, 40, N), [[y, O.value === "loaded"]])], 8, M), e.loading || O.value === "loading" ? (h(), o("span", P, [l(m, { name: "line-md:loading-loop" })])) : O.value === "error" ? (h(), o("span", F, [l(m, { name: "ri:image-2-line" })])) : a("", !0)], 512),
290
+ e.compact ? a("", !0) : (h(), o("figcaption", I, [e.filename ? (h(), o("span", {
284
291
  key: 0,
285
292
  class: "pk-chatbot-file-preview__name",
286
293
  title: e.filename
287
- }, v(e.filename), 9, F)) : a("", !0), A.value.length ? (h(), o("span", I, [e.error ? (h(), o("span", {
294
+ }, v(e.filename), 9, L)) : a("", !0), s("span", R, [e.error ? (h(), o("span", {
288
295
  key: 0,
289
296
  class: "pk-chatbot-file-preview__error",
290
297
  title: e.error
291
- }, [l(m, { name: "ri:error-warning-line" }), c(" " + v(e.error), 1)], 8, L)) : (h(), o(n, { key: 1 }, [c(v(A.value.join(" · ")), 1)], 64))])) : a("", !0)])) : a("", !0),
298
+ }, [l(m, { name: "ri:error-warning-line" }), c(" " + v(e.error), 1)], 8, z)) : (h(), o(n, { key: 1 }, [c(v(J.value.join(" · ")), 1)], 64))])])),
292
299
  e.removable && !e.loading ? (h(), o("button", {
293
300
  key: 1,
294
301
  type: "button",
295
302
  class: "pk-chatbot-file-preview__remove",
296
303
  title: r.$t("action.remove"),
297
304
  "aria-label": r.$t("action.remove"),
298
- onClick: u[0] ||= b((e) => r.$emit("remove"), ["prevent"])
299
- }, [l(m, { name: "ri:close-line" })], 8, R)) : a("", !0)
305
+ onClick: u[0] ||= x((e) => r.$emit("remove"), ["prevent"])
306
+ }, [l(m, { name: "ri:close-line" })], 8, B)) : a("", !0)
300
307
  ], 2)) : (h(), o("div", {
301
308
  key: 1,
302
309
  class: d(["pk-chatbot-file-preview pk-chatbot-file-preview--doc", {
@@ -316,15 +323,15 @@ var j = [
316
323
  name: T.value.icon,
317
324
  class: "pk-chatbot-file-preview__icon"
318
325
  }, null, 8, ["name"]))], 4),
319
- s("span", z, [e.filename ? (h(), o("span", {
326
+ s("span", V, [e.filename ? (h(), o("span", {
320
327
  key: 0,
321
328
  class: "pk-chatbot-file-preview__name",
322
329
  title: e.filename
323
- }, v(e.filename), 9, B)) : a("", !0), s("span", V, [e.error ? (h(), o("span", {
330
+ }, v(e.filename), 9, H)) : a("", !0), s("span", U, [e.error ? (h(), o("span", {
324
331
  key: 0,
325
332
  class: "pk-chatbot-file-preview__error",
326
333
  title: e.error
327
- }, [l(m, { name: "ri:error-warning-line" }), c(" " + v(e.error), 1)], 8, H)) : (h(), o(n, { key: 1 }, [c(v(G.value.join(" · ")), 1)], 64))])]),
334
+ }, [l(m, { name: "ri:error-warning-line" }), c(" " + v(e.error), 1)], 8, W)) : (h(), o(n, { key: 1 }, [c(v(Y.value.join(" · ")), 1)], 64))])]),
328
335
  !e.loading && !e.error && !e.compact ? (h(), o("a", {
329
336
  key: 0,
330
337
  href: e.url,
@@ -333,7 +340,7 @@ var j = [
333
340
  class: "pk-chatbot-file-preview__open",
334
341
  title: r.$t("action.download"),
335
342
  "aria-label": `${r.$t("action.download")}${e.filename ? ` — ${e.filename}` : ""}`
336
- }, [l(m, { name: "ri:download-2-line" })], 8, U)) : a("", !0),
343
+ }, [l(m, { name: "ri:download-2-line" })], 8, G)) : a("", !0),
337
344
  e.removable && !e.loading ? (h(), o("button", {
338
345
  key: 1,
339
346
  type: "button",
@@ -341,12 +348,12 @@ var j = [
341
348
  title: r.$t("action.remove"),
342
349
  "aria-label": r.$t("action.remove"),
343
350
  onClick: u[1] ||= (e) => r.$emit("remove")
344
- }, [l(m, { name: "ri:close-line" })], 10, W)) : a("", !0)
351
+ }, [l(m, { name: "ri:close-line" })], 10, K)) : a("", !0)
345
352
  ], 2));
346
353
  };
347
354
  }
348
355
  });
349
356
  //#endregion
350
- export { w as n, C as r, G as t };
357
+ export { T as n, w as r, q as t };
351
358
 
352
- //# sourceMappingURL=PkChatbotFilePreview-DG887KhT.js.map
359
+ //# sourceMappingURL=PkChatbotFilePreview-Bf1bdZn8.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PkChatbotFilePreview-Bf1bdZn8.js","names":["$t","$emit"],"sources":["../../../../packages/components/src/composables/useLightbox.ts","../../../../packages/components/src/chat/PkChatbotFilePreview.vue","../../../../packages/components/src/chat/PkChatbotFilePreview.vue"],"sourcesContent":["import {\n onBeforeUnmount,\n ref,\n shallowRef,\n type Ref,\n type ShallowRef,\n} from 'vue'\nimport type PhotoSwipeLightbox from 'photoswipe/lightbox'\nimport type PhotoSwipe from 'photoswipe'\nimport { loadCdnCss } from 'utils'\n\nconst PHOTOSWIPE_CSS =\n 'https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.css'\n\nconst DEFAULT_IMAGE_SIZE = { width: 1200, height: 800 }\n\nexport interface LightboxImage {\n url: string\n caption?: string\n alt?: string\n /** Natural width in px — used by PhotoSwipe for proper sizing. */\n width?: number\n /** Natural height in px — used by PhotoSwipe for proper sizing. */\n height?: number\n}\n\nexport interface LightboxOptions {\n /** Container element whose child anchors are the gallery items */\n gallery: Ref<HTMLElement | undefined>\n /** CSS selector for gallery items inside the container (default: `'a[data-pswp-src]'`) */\n children?: string\n loop?: boolean\n /** Show a download button in the lightbox toolbar */\n downloadable?: boolean\n /** Show an open-in-new-tab button in the lightbox toolbar */\n openInNewTab?: boolean\n}\n\nexport interface UseLightboxReturn {\n lightbox: ShallowRef<PhotoSwipeLightbox | null>\n /** Initialises PhotoSwipe. Call this inside `onMounted`. */\n init: () => Promise<void>\n}\n\n/**\n * Encapsulates PhotoSwipe 5 initialisation, toolbar customisation, and lifecycle cleanup.\n * CSS is injected into `document.head` so the overlay works in both regular DOM and Shadow DOM.\n *\n * Usage:\n * ```ts\n * const { init } = useLightbox({ gallery: galleryRef, downloadable: true })\n * onMounted(init)\n * ```\n */\nexport function useLightbox(options: LightboxOptions): UseLightboxReturn {\n const {\n gallery,\n children = 'a[data-pswp-src]',\n loop = true,\n downloadable = true,\n openInNewTab = true,\n } = options\n\n const lightbox = shallowRef<PhotoSwipeLightbox | null>(null)\n\n const init = async () => {\n if (!gallery.value) {\n return\n }\n\n // PhotoSwipe renders its overlay into document.body, so CSS must be in\n // document.head — not inside a shadow root.\n loadCdnCss(PHOTOSWIPE_CSS)\n injectPhotoSwipeOverrides()\n\n const { default: PhotoSwipeLightbox } =\n await import('photoswipe/lightbox')\n\n lightbox.value = new PhotoSwipeLightbox({\n gallery: gallery.value,\n children,\n pswpModule: () => import('photoswipe'),\n loop,\n arrowPrevSVG:\n '<svg aria-hidden=\"true\" class=\"pswp__icn\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ffffff\" d=\"m10.828 12l4.95 4.95l-1.414 1.415L8 12l6.364-6.364l1.414 1.414z\"/></svg>',\n arrowNextSVG:\n '<svg aria-hidden=\"true\" class=\"pswp__icn\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ffffff\" d=\"m13.172 12l-4.95-4.95l1.414-1.413L16 12l-6.364 6.364l-1.414-1.415z\"/></svg>',\n closeSVG:\n '<svg aria-hidden=\"true\" class=\"pswp__icn\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ffffff\" d=\"m12 10.587l4.95-4.95l1.414 1.414l-4.95 4.95l4.95 4.95l-1.415 1.414l-4.95-4.95l-4.949 4.95l-1.414-1.415l4.95-4.95l-4.95-4.95L7.05 5.638z\"/></svg>',\n zoomSVG:\n '<svg aria-hidden=\"true\" class=\"pswp__icn\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><g class=\"pswp__icn--zoom-in\"><path fill=\"#ffffff\" d=\"m18.031 16.617l4.283 4.282l-1.415 1.415l-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9s9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617m-2.006-.742A6.98 6.98 0 0 0 18 11c0-3.867-3.133-7-7-7s-7 3.133-7 7s3.133 7 7 7a6.98 6.98 0 0 0 4.875-1.975zM10 10V7h2v3h3v2h-3v3h-2v-3H7v-2z\"/></g><g class=\"pswp__icn--zoom-out\"><path fill=\"#ffffff\" d=\"m18.031 16.617l4.283 4.282l-1.415 1.415l-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9s9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617m-2.006-.742A6.98 6.98 0 0 0 18 11c0-3.867-3.133-7-7-7s-7 3.133-7 7s3.133 7 7 7a6.98 6.98 0 0 0 4.875-1.975zM7 10h8v2H7z\"/></g></svg>',\n })\n\n if (openInNewTab || downloadable) {\n lightbox.value.on('uiRegister', () => {\n const ui = lightbox.value!.pswp?.ui\n if (!ui) {\n return\n }\n\n if (openInNewTab) {\n ui.registerElement({\n name: 'open-in-new-tab-button',\n order: 8,\n isButton: true,\n html: '<svg aria-hidden=\"true\" class=\"pswp__icn\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ffffff\" d=\"M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm11-3v8h-2V6.413l-7.793 7.794l-1.414-1.414L17.585 5H13V3z\"/></svg>',\n title: 'Open in new tab',\n onClick: (_, __, pswp) => {\n const url = getCurrentSlideUrl(pswp)\n if (url) {\n window.open(\n url,\n '_blank',\n 'noopener,noreferrer',\n )\n }\n },\n })\n }\n\n if (downloadable) {\n ui.registerElement({\n name: 'download-button',\n order: 9,\n isButton: true,\n html: '<svg aria-hidden=\"true\" class=\"pswp__icn\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#ffffff\" d=\"M3 19h18v2H3zm10-5.828L19.071 7.1l1.414 1.414L12 17L3.515 8.515L4.929 7.1L11 13.173V2h2z\"/></svg>',\n title: 'Download',\n onClick: (_, __, pswp) => {\n const url = getCurrentSlideUrl(pswp)\n if (url) {\n downloadImage(url)\n }\n },\n })\n }\n })\n }\n\n lightbox.value.init()\n }\n\n onBeforeUnmount(() => {\n lightbox.value?.destroy()\n lightbox.value = null\n })\n\n return { lightbox, init }\n}\n\n// ---------------------------------------------------------------------------\n// Image size preloading\n// ---------------------------------------------------------------------------\n\nexport interface UseImageSizesReturn {\n preload: (\n images: Array<{ url: string; width?: number; height?: number }>,\n ) => void\n getSize: (url: string) => { width: number; height: number }\n}\n\n/**\n * Preloads natural image dimensions so PhotoSwipe can size its slides correctly.\n * When `width`/`height` are already provided they are used as-is;\n * otherwise the image is loaded in the background to read `naturalWidth`/`naturalHeight`.\n */\nexport function useImageSizes(): UseImageSizesReturn {\n const sizes = ref(new Map<string, { width: number; height: number }>())\n\n const preload = (\n images: Array<{ url: string; width?: number; height?: number }>,\n ) => {\n for (const image of images) {\n if (image.width && image.height) {\n sizes.value.set(image.url, {\n width: image.width,\n height: image.height,\n })\n continue\n }\n const img = new Image()\n img.onload = () => {\n sizes.value.set(image.url, {\n width: img.naturalWidth,\n height: img.naturalHeight,\n })\n }\n img.src = image.url\n }\n }\n\n const getSize = (url: string) => sizes.value.get(url) ?? DEFAULT_IMAGE_SIZE\n\n return { preload, getSize }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getCurrentSlideUrl(pswp: PhotoSwipe): string | undefined {\n return (\n pswp.currSlide?.data?.src ??\n pswp.currSlide?.data?.element\n ?.querySelector('img')\n ?.getAttribute('src') ??\n pswp.currSlide?.data?.element\n ?.querySelector('source')\n ?.getAttribute('srcset') ??\n pswp.currSlide?.data?.element?.getAttribute('src') ??\n undefined\n )\n}\n\n/**\n * Triggers a client-side download for an image URL.\n *\n * For backend media URLs (.../media/:file/view), navigates to the\n * corresponding /download endpoint which redirects to an Azure SAS URL\n * with `Content-Disposition: attachment` — no CORS issues.\n *\n * For blob: URLs (in-session uploads), uses the fetch → blob approach.\n *\n * For data: URLs (inline SVGs, etc.), use the download attribute.\n *\n * Falls back to opening in a new tab for all other cases.\n */\nfunction downloadImage(url: string): void {\n const downloadUrl = toDownloadUrl(url)\n\n const anchor = document.createElement('a')\n anchor.href = downloadUrl\n anchor.target = '_blank'\n anchor.rel = 'noopener noreferrer'\n // `download` attribute works for same-origin, data: and blob: URLs; for\n // cross-origin it is ignored but the browser still follows Content-Disposition.\n anchor.download = getFilenameFromUrl(url)\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n}\n\n/**\n * Converts a `/view` media URL to its `/download` counterpart.\n * Leaves blob:, data: and all other URLs untouched.\n */\nfunction toDownloadUrl(url: string): string {\n return url.replace(/\\/view(\\?|$)/, '/download$1')\n}\n\nfunction getFilenameFromUrl(url: string): string {\n if (url.startsWith('data:image/svg+xml')) {\n return 'diagram.svg'\n }\n if (url.startsWith('data:')) {\n const mime = url.split(':')[1].split(';')[0]\n const ext = mime.split('/')[1] || 'bin'\n return `image.${ext}`\n }\n try {\n const pathname = new URL(url).pathname\n const name = pathname.split('/').pop()\n return name && name.includes('.') ? name : 'image'\n } catch {\n return 'image'\n }\n}\n\nlet overridesInjected = false\n\n/**\n * Injects a one-time `<style>` into `document.head` that raises\n * PhotoSwipe's z-index above any host UI (e.g. floating chat widgets\n * that use `z-index: 2147483647`).\n */\nfunction injectPhotoSwipeOverrides(): void {\n if (overridesInjected) {\n return\n }\n overridesInjected = true\n const style = document.createElement('style')\n style.textContent = [\n '.pswp__counter { font-family: sans-serif; }',\n '.pswp { z-index: 2147483647 !important; }',\n '.pswp__icn { width: 20px; height: 20px; }',\n '.pswp__button { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; }',\n '.pswp__icn--zoom-out { display: none; }',\n '.pswp--zoomed-in .pswp__icn--zoom-in { display: none; }',\n '.pswp--zoomed-in .pswp__icn--zoom-out { display: block; }',\n '.pswp__button--arrow .pswp__icn {left: auto; right: auto; width: 40px; height: 40px; transform: none; margin-top: -20px; }',\n ].join(' ')\n document.head.appendChild(style)\n}\n","<script lang=\"ts\" setup>\n import { computed, ref, onMounted } from 'vue'\n import { useLightbox } from '../composables/useLightbox'\n\n const props = defineProps<{\n mediaType: string\n url: string\n filename?: string\n /** Size in bytes, when known (e.g. local uploads). */\n size?: number\n loading?: boolean\n error?: string\n removable?: boolean\n /**\n * Dense layout for the composer: a small square thumbnail / slim row,\n * without the metadata caption. The full card is reserved for the\n * message log where the details matter.\n */\n compact?: boolean\n }>()\n\n defineEmits<{\n remove: []\n }>()\n\n const isImage = computed(() => props.mediaType.startsWith('image/'))\n\n // --- File-type identity (icon + accent + human kind label) ---\n\n const extension = computed(() => {\n const name = props.filename ?? ''\n const dot = name.lastIndexOf('.')\n return dot > 0 ? name.slice(dot + 1).toLowerCase() : ''\n })\n\n // mediaType subtype, e.g. \"pdf\" from \"application/pdf\"\n const subtype = computed(\n () => props.mediaType.split('/')[1]?.split(';')[0] ?? '',\n )\n\n type FileKind = {\n icon: string\n // CSS color driving the icon glyph + tile tint\n accent: string\n label: string\n }\n\n const KINDS: Array<{ test: RegExp; kind: FileKind }> = [\n {\n test: /pdf/,\n kind: {\n icon: 'ri:file-pdf-2-line',\n accent: '#e2483d',\n label: 'PDF',\n },\n },\n {\n // Match Word specifically: the generic `document` substring also\n // appears in the XLSX/PPTX OOXML mime types (…officedocument…), so\n // matching it here would mis-label spreadsheets/decks as Word.\n test: /msword|wordprocessingml/,\n kind: {\n icon: 'ri:file-word-2-line',\n accent: '#2b7de9',\n label: 'Word',\n },\n },\n {\n test: /sheet|excel|csv/,\n kind: {\n icon: 'ri:file-excel-2-line',\n accent: '#1f9d57',\n label: 'Spreadsheet',\n },\n },\n {\n test: /presentation|powerpoint/,\n kind: {\n icon: 'ri:file-ppt-2-line',\n accent: '#d98324',\n label: 'Presentation',\n },\n },\n {\n test: /zip|compressed|x-7z|x-rar|x-tar|gzip/,\n kind: {\n icon: 'ri:file-zip-line',\n accent: '#c08a1e',\n label: 'Archive',\n },\n },\n {\n test: /^audio\\//,\n kind: {\n icon: 'ri:file-music-line',\n accent: '#8b5cf6',\n label: 'Audio',\n },\n },\n {\n test: /^video\\//,\n kind: {\n icon: 'ri:film-line',\n accent: '#6366f1',\n label: 'Video',\n },\n },\n {\n test: /json|javascript|typescript|xml|html|x-sh|x-python/,\n kind: {\n icon: 'ri:file-code-line',\n accent: '#0d9488',\n label: 'Code',\n },\n },\n {\n test: /^text\\//,\n kind: {\n icon: 'ri:file-text-line',\n accent: '#64748b',\n label: 'Text',\n },\n },\n ]\n\n const fileKind = computed<FileKind>(() => {\n const haystack = `${props.mediaType} ${extension.value}`\n for (const { test, kind } of KINDS) {\n if (test.test(haystack)) {\n return kind\n }\n }\n return {\n icon: 'ri:file-line',\n accent: '#64748b',\n label: 'File',\n }\n })\n\n // Short type tag shown in the meta line: prefer the real extension, fall\n // back to the mime subtype, then the generic kind label.\n const typeTag = computed(() =>\n (\n extension.value ||\n subtype.value ||\n fileKind.value.label\n ).toUpperCase(),\n )\n\n // --- Size & dimensions ---\n\n const formattedSize = computed(() => {\n const bytes = props.size\n if (bytes === undefined || bytes < 0) {\n return undefined\n }\n if (bytes < 1024) {\n return `${bytes} B`\n }\n const units = ['KB', 'MB', 'GB', 'TB']\n let value = bytes / 1024\n let unitIndex = 0\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024\n unitIndex += 1\n }\n return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unitIndex]}`\n })\n\n // Thumbnail load lifecycle. Driving the spinner and the error fallback\n // off the real <img> events (not a second background fetch) means the\n // image is requested exactly once and reuses the browser cache on any\n // later re-render, instead of re-downloading every time the message list\n // repaints.\n type ThumbStatus = 'loading' | 'loaded' | 'error'\n const thumbStatus = ref<ThumbStatus>('loading')\n\n // Natural pixel dimensions, captured once the thumbnail decodes. Doubles\n // as the size hint PhotoSwipe needs for its slide (falls back until known).\n const dimensions = ref<{ width: number; height: number }>()\n const onImageLoad = (event: Event) => {\n const img = event.target as HTMLImageElement\n if (img.naturalWidth && img.naturalHeight) {\n dimensions.value = {\n width: img.naturalWidth,\n height: img.naturalHeight,\n }\n }\n thumbStatus.value = 'loaded'\n }\n const onImageError = () => {\n thumbStatus.value = 'error'\n }\n\n const pswpSize = computed(() => dimensions.value ?? DEFAULT_PSWP_SIZE)\n\n // Meta fragments joined with \" · \" for the caption / doc subtitle.\n const imageMeta = computed(() =>\n [\n dimensions.value &&\n `${dimensions.value.width}×${dimensions.value.height}`,\n formattedSize.value,\n ].filter(Boolean),\n )\n const docMeta = computed(() =>\n [typeTag.value, formattedSize.value].filter(Boolean),\n )\n\n // --- Lightbox ---\n\n // Slide size PhotoSwipe assumes before the real dimensions are known.\n const DEFAULT_PSWP_SIZE = { width: 1200, height: 800 }\n\n const galleryRef = ref<HTMLElement>()\n const { init } = useLightbox({ gallery: galleryRef, loop: false })\n\n onMounted(async () => {\n if (!isImage.value) {\n return\n }\n await init()\n })\n</script>\n\n<template>\n <!-- Image attachment: a thumbnail tile with a metadata caption -->\n <figure\n v-if=\"isImage\"\n class=\"pk-chatbot-file-preview pk-chatbot-file-preview--image\"\n :class=\"{ 'pk-chatbot-file-preview--compact': compact }\">\n <div ref=\"galleryRef\" class=\"pk-chatbot-file-preview__thumb-wrap\">\n <a\n :href=\"url\"\n :data-pswp-src=\"url\"\n :data-pswp-width=\"pswpSize.width\"\n :data-pswp-height=\"pswpSize.height\"\n :data-pswp-caption=\"filename ?? ''\"\n class=\"pk-chatbot-file-preview__thumb-link\"\n :aria-label=\"filename ?? $t('label.file')\">\n <img\n v-show=\"thumbStatus === 'loaded'\"\n :src=\"url\"\n :alt=\"filename\"\n decoding=\"async\"\n class=\"pk-chatbot-file-preview__thumb\"\n @load=\"onImageLoad\"\n @error=\"onImageError\" />\n </a>\n <!-- Spinner until the thumbnail decodes (or while an upload is in\n flight); a fixed box keeps this from shifting the layout. -->\n <span\n v-if=\"loading || thumbStatus === 'loading'\"\n class=\"pk-chatbot-file-preview__status pk-chatbot-file-preview__status--overlay\">\n <VvIcon name=\"line-md:loading-loop\" />\n </span>\n <!-- Failed to load: a quiet icon instead of the browser's broken\n image glyph. -->\n <span\n v-else-if=\"thumbStatus === 'error'\"\n class=\"pk-chatbot-file-preview__status pk-chatbot-file-preview__status--overlay pk-chatbot-file-preview__status--error\">\n <VvIcon name=\"ri:image-2-line\" />\n </span>\n </div>\n <figcaption v-if=\"!compact\" class=\"pk-chatbot-file-preview__caption\">\n <span\n v-if=\"filename\"\n class=\"pk-chatbot-file-preview__name\"\n :title=\"filename\"\n >{{ filename }}</span\n >\n <span class=\"pk-chatbot-file-preview__meta\">\n <span\n v-if=\"error\"\n class=\"pk-chatbot-file-preview__error\"\n :title=\"error\">\n <VvIcon name=\"ri:error-warning-line\" />\n {{ error }}\n </span>\n <template v-else>{{ imageMeta.join(' · ') }}</template>\n </span>\n </figcaption>\n <button\n v-if=\"removable && !loading\"\n type=\"button\"\n class=\"pk-chatbot-file-preview__remove\"\n :title=\"$t('action.remove')\"\n :aria-label=\"$t('action.remove')\"\n @click.prevent=\"$emit('remove')\">\n <VvIcon name=\"ri:close-line\" />\n </button>\n </figure>\n\n <!-- Document attachment: an icon tile + name + type · size -->\n <div\n v-else\n class=\"pk-chatbot-file-preview pk-chatbot-file-preview--doc\"\n :class=\"{\n 'pk-chatbot-file-preview--error': error,\n 'pk-chatbot-file-preview--compact': compact,\n }\">\n <span\n class=\"pk-chatbot-file-preview__icon-tile\"\n :style=\"{ '--file-accent': fileKind.accent }\">\n <VvIcon\n v-if=\"loading\"\n name=\"line-md:loading-loop\"\n class=\"pk-chatbot-file-preview__icon\" />\n <VvIcon\n v-else\n :name=\"fileKind.icon\"\n class=\"pk-chatbot-file-preview__icon\" />\n </span>\n <span class=\"pk-chatbot-file-preview__body\">\n <span\n v-if=\"filename\"\n class=\"pk-chatbot-file-preview__name\"\n :title=\"filename\"\n >{{ filename }}</span\n >\n <span class=\"pk-chatbot-file-preview__meta\">\n <span\n v-if=\"error\"\n class=\"pk-chatbot-file-preview__error\"\n :title=\"error\">\n <VvIcon name=\"ri:error-warning-line\" />\n {{ error }}\n </span>\n <template v-else>{{ docMeta.join(' · ') }}</template>\n </span>\n </span>\n <a\n v-if=\"!loading && !error && !compact\"\n :href=\"url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"pk-chatbot-file-preview__open\"\n :title=\"$t('action.download')\"\n :aria-label=\"`${$t('action.download')}${filename ? ` — ${filename}` : ''}`\">\n <VvIcon name=\"ri:download-2-line\" />\n </a>\n <button\n v-if=\"removable && !loading\"\n type=\"button\"\n class=\"pk-chatbot-file-preview__remove\"\n :class=\"{ 'pk-chatbot-file-preview__remove--inline': !compact }\"\n :title=\"$t('action.remove')\"\n :aria-label=\"$t('action.remove')\"\n @click=\"$emit('remove')\">\n <VvIcon name=\"ri:close-line\" />\n </button>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-file-preview {\n // Concentric radii: outer card and inner tiles share an 8px step.\n --file-radius: var(--rounded-lg, 12px);\n position: relative;\n max-width: 280px;\n background-color: var(--color-surface-1);\n border: 1px solid var(--color-surface-3);\n border-radius: var(--file-radius);\n color: var(--color-word-2);\n font-size: var(--text-12);\n\n &--error {\n border-color: var(--color-danger);\n }\n\n // --- Document card ---\n &--doc {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n padding: var(--spacing-8);\n min-width: 0;\n transition:\n border-color 0.18s var(--ease-out),\n background-color 0.18s var(--ease-out),\n box-shadow 0.18s var(--ease-out);\n\n &:hover {\n border-color: var(--color-surface-4);\n background-color: var(--color-surface-2);\n box-shadow: var(--shadow-sm);\n }\n }\n\n &__icon-tile {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--spacing-36);\n height: var(--spacing-36);\n // One step down from the card radius keeps the nested tile concentric.\n border-radius: var(--rounded-md, 6px);\n // Tint derived from the file-type accent; glyph sits at full accent.\n background-color: color-mix(\n in srgb,\n var(--file-accent) 14%,\n transparent\n );\n color: var(--file-accent);\n }\n\n &__icon {\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n\n &__body {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n flex: 1;\n }\n\n &__open {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--spacing-28);\n height: var(--spacing-28);\n margin-inline-start: var(--spacing-4);\n border-radius: var(--rounded);\n color: var(--color-word-3);\n text-decoration: none;\n opacity: 0;\n transition:\n opacity 0.18s var(--ease-out),\n color 0.18s var(--ease-out),\n background-color 0.18s var(--ease-out);\n\n &:hover {\n background-color: var(--color-surface-3);\n color: var(--color-word-1);\n }\n }\n\n &--doc:hover &__open,\n &--doc:focus-within &__open {\n opacity: 1;\n }\n\n // Touch devices have no hover: keep the open affordance visible.\n @media (hover: none) {\n &--doc &__open {\n opacity: 1;\n }\n }\n\n // --- Image card ---\n &--image {\n display: inline-flex;\n flex-direction: column;\n margin: 0;\n }\n\n &__thumb-wrap {\n position: relative;\n display: flex;\n // Fixed box: the card claims its final footprint before the\n // image decodes, so the message log never shifts on load.\n width: 280px;\n max-width: 100%;\n height: 200px;\n background-color: var(--color-surface);\n border-radius: var(--file-radius);\n overflow: hidden;\n }\n\n &__thumb-link {\n display: flex;\n width: 100%;\n height: 100%;\n cursor: pointer;\n }\n\n &__thumb {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__caption {\n display: flex;\n flex-direction: column;\n gap: 1px;\n padding: var(--spacing-8);\n min-width: 0;\n }\n\n &__name {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: var(--font-medium, 500);\n color: var(--color-word-1);\n }\n\n &__meta {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: var(--text-10);\n line-height: var(--leading-normal);\n // Reserve the line even while empty: image dimensions arrive\n // only after decode and must not grow the caption.\n min-height: 1.4em;\n color: var(--color-word-4);\n // Stable column for the dimensions/size figures.\n font-variant-numeric: tabular-nums;\n }\n\n &__error {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n color: var(--color-danger);\n\n .vv-icon {\n flex-shrink: 0;\n }\n }\n\n &__status {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n &--overlay {\n position: absolute;\n inset: 0;\n background-color: color-mix(\n in srgb,\n var(--color-surface-1) 70%,\n transparent\n );\n color: var(--color-word-1);\n }\n\n &--error {\n color: var(--color-word-4);\n }\n }\n\n // --- Remove button (input context) ---\n &__remove {\n position: absolute;\n top: 0;\n right: 0;\n transform: translate(50%, -50%);\n display: flex;\n align-items: center;\n justify-content: center;\n // Visible glyph is 18px but the hit area stays a comfortable 28px.\n width: var(--spacing-28);\n height: var(--spacing-28);\n padding: 0;\n border: none;\n border-radius: var(--rounded-full, 9999px);\n background-color: color-mix(\n in srgb,\n var(--color-surface-1) 85%,\n transparent\n );\n color: var(--color-word-2);\n cursor: pointer;\n line-height: 1;\n box-shadow: var(--shadow-sm);\n transition:\n background-color 0.18s var(--ease-out),\n color 0.18s var(--ease-out),\n scale 0.12s var(--ease-out);\n\n &:hover {\n background-color: var(--color-surface-2);\n color: var(--color-word-1);\n }\n\n &:active {\n scale: 0.96;\n }\n\n // Inside the document row the button rides inline, not overlaid\n // (size is inherited from the base).\n &--inline {\n position: static;\n flex-shrink: 0;\n box-shadow: none;\n background-color: transparent;\n color: var(--color-word-4);\n\n &:hover {\n background-color: var(--color-surface-3);\n color: var(--color-word-1);\n }\n }\n }\n\n // --- Compact (composer) chips ---\n // ChatGPT/Gemini-style: every attachment is the SAME height and carries\n // an always-visible round remove control in the top-right corner — no\n // hover-revealed actions, no size mismatch between image and file chips.\n &--compact {\n // Shared chip height so image and file chips align in the row.\n --chip-size: var(--spacing-56);\n max-width: 240px;\n\n &.pk-chatbot-file-preview--image {\n width: var(--chip-size);\n height: var(--chip-size);\n border-radius: var(--rounded-md, 6px);\n }\n\n // Fill the square; the root's `overflow: hidden` rounds the corners.\n .pk-chatbot-file-preview__thumb-wrap,\n .pk-chatbot-file-preview__thumb-link {\n width: 100%;\n height: 100%;\n }\n\n &.pk-chatbot-file-preview--doc {\n height: var(--chip-size);\n gap: var(--spacing-8);\n padding: var(--spacing-8);\n // Reserve room so the corner remove button never overlaps the\n // name/meta text.\n padding-inline-end: var(--spacing-28);\n }\n\n // Square icon tile that fills the chip height, so file chips read at\n // the same scale as image thumbnails.\n .pk-chatbot-file-preview__icon-tile {\n align-self: stretch;\n width: auto;\n height: auto;\n aspect-ratio: 1;\n }\n\n .pk-chatbot-file-preview__icon {\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n }\n\n // Always-visible round remove control in the top-right corner. Sits\n // just inside the chip (an ancestor clips overflow), bigger than before,\n // with an extended hit area for easy tapping.\n &--compact &__remove {\n top: var(--spacing-4);\n right: var(--spacing-4);\n width: var(--spacing-24);\n height: var(--spacing-24);\n border: 1px solid var(--color-surface-4);\n background-color: var(--color-surface);\n color: var(--color-word-1);\n font-size: var(--text-16);\n box-shadow: var(--shadow-sm);\n\n // Comfortable touch/click target beyond the small visible glyph.\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-4));\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n color: var(--color-word-1);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n &--doc,\n &__open,\n &__remove {\n transition: none;\n }\n }\n }\n</style>\n","<script lang=\"ts\" setup>\n import { computed, ref, onMounted } from 'vue'\n import { useLightbox } from '../composables/useLightbox'\n\n const props = defineProps<{\n mediaType: string\n url: string\n filename?: string\n /** Size in bytes, when known (e.g. local uploads). */\n size?: number\n loading?: boolean\n error?: string\n removable?: boolean\n /**\n * Dense layout for the composer: a small square thumbnail / slim row,\n * without the metadata caption. The full card is reserved for the\n * message log where the details matter.\n */\n compact?: boolean\n }>()\n\n defineEmits<{\n remove: []\n }>()\n\n const isImage = computed(() => props.mediaType.startsWith('image/'))\n\n // --- File-type identity (icon + accent + human kind label) ---\n\n const extension = computed(() => {\n const name = props.filename ?? ''\n const dot = name.lastIndexOf('.')\n return dot > 0 ? name.slice(dot + 1).toLowerCase() : ''\n })\n\n // mediaType subtype, e.g. \"pdf\" from \"application/pdf\"\n const subtype = computed(\n () => props.mediaType.split('/')[1]?.split(';')[0] ?? '',\n )\n\n type FileKind = {\n icon: string\n // CSS color driving the icon glyph + tile tint\n accent: string\n label: string\n }\n\n const KINDS: Array<{ test: RegExp; kind: FileKind }> = [\n {\n test: /pdf/,\n kind: {\n icon: 'ri:file-pdf-2-line',\n accent: '#e2483d',\n label: 'PDF',\n },\n },\n {\n // Match Word specifically: the generic `document` substring also\n // appears in the XLSX/PPTX OOXML mime types (…officedocument…), so\n // matching it here would mis-label spreadsheets/decks as Word.\n test: /msword|wordprocessingml/,\n kind: {\n icon: 'ri:file-word-2-line',\n accent: '#2b7de9',\n label: 'Word',\n },\n },\n {\n test: /sheet|excel|csv/,\n kind: {\n icon: 'ri:file-excel-2-line',\n accent: '#1f9d57',\n label: 'Spreadsheet',\n },\n },\n {\n test: /presentation|powerpoint/,\n kind: {\n icon: 'ri:file-ppt-2-line',\n accent: '#d98324',\n label: 'Presentation',\n },\n },\n {\n test: /zip|compressed|x-7z|x-rar|x-tar|gzip/,\n kind: {\n icon: 'ri:file-zip-line',\n accent: '#c08a1e',\n label: 'Archive',\n },\n },\n {\n test: /^audio\\//,\n kind: {\n icon: 'ri:file-music-line',\n accent: '#8b5cf6',\n label: 'Audio',\n },\n },\n {\n test: /^video\\//,\n kind: {\n icon: 'ri:film-line',\n accent: '#6366f1',\n label: 'Video',\n },\n },\n {\n test: /json|javascript|typescript|xml|html|x-sh|x-python/,\n kind: {\n icon: 'ri:file-code-line',\n accent: '#0d9488',\n label: 'Code',\n },\n },\n {\n test: /^text\\//,\n kind: {\n icon: 'ri:file-text-line',\n accent: '#64748b',\n label: 'Text',\n },\n },\n ]\n\n const fileKind = computed<FileKind>(() => {\n const haystack = `${props.mediaType} ${extension.value}`\n for (const { test, kind } of KINDS) {\n if (test.test(haystack)) {\n return kind\n }\n }\n return {\n icon: 'ri:file-line',\n accent: '#64748b',\n label: 'File',\n }\n })\n\n // Short type tag shown in the meta line: prefer the real extension, fall\n // back to the mime subtype, then the generic kind label.\n const typeTag = computed(() =>\n (\n extension.value ||\n subtype.value ||\n fileKind.value.label\n ).toUpperCase(),\n )\n\n // --- Size & dimensions ---\n\n const formattedSize = computed(() => {\n const bytes = props.size\n if (bytes === undefined || bytes < 0) {\n return undefined\n }\n if (bytes < 1024) {\n return `${bytes} B`\n }\n const units = ['KB', 'MB', 'GB', 'TB']\n let value = bytes / 1024\n let unitIndex = 0\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024\n unitIndex += 1\n }\n return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unitIndex]}`\n })\n\n // Thumbnail load lifecycle. Driving the spinner and the error fallback\n // off the real <img> events (not a second background fetch) means the\n // image is requested exactly once and reuses the browser cache on any\n // later re-render, instead of re-downloading every time the message list\n // repaints.\n type ThumbStatus = 'loading' | 'loaded' | 'error'\n const thumbStatus = ref<ThumbStatus>('loading')\n\n // Natural pixel dimensions, captured once the thumbnail decodes. Doubles\n // as the size hint PhotoSwipe needs for its slide (falls back until known).\n const dimensions = ref<{ width: number; height: number }>()\n const onImageLoad = (event: Event) => {\n const img = event.target as HTMLImageElement\n if (img.naturalWidth && img.naturalHeight) {\n dimensions.value = {\n width: img.naturalWidth,\n height: img.naturalHeight,\n }\n }\n thumbStatus.value = 'loaded'\n }\n const onImageError = () => {\n thumbStatus.value = 'error'\n }\n\n const pswpSize = computed(() => dimensions.value ?? DEFAULT_PSWP_SIZE)\n\n // Meta fragments joined with \" · \" for the caption / doc subtitle.\n const imageMeta = computed(() =>\n [\n dimensions.value &&\n `${dimensions.value.width}×${dimensions.value.height}`,\n formattedSize.value,\n ].filter(Boolean),\n )\n const docMeta = computed(() =>\n [typeTag.value, formattedSize.value].filter(Boolean),\n )\n\n // --- Lightbox ---\n\n // Slide size PhotoSwipe assumes before the real dimensions are known.\n const DEFAULT_PSWP_SIZE = { width: 1200, height: 800 }\n\n const galleryRef = ref<HTMLElement>()\n const { init } = useLightbox({ gallery: galleryRef, loop: false })\n\n onMounted(async () => {\n if (!isImage.value) {\n return\n }\n await init()\n })\n</script>\n\n<template>\n <!-- Image attachment: a thumbnail tile with a metadata caption -->\n <figure\n v-if=\"isImage\"\n class=\"pk-chatbot-file-preview pk-chatbot-file-preview--image\"\n :class=\"{ 'pk-chatbot-file-preview--compact': compact }\">\n <div ref=\"galleryRef\" class=\"pk-chatbot-file-preview__thumb-wrap\">\n <a\n :href=\"url\"\n :data-pswp-src=\"url\"\n :data-pswp-width=\"pswpSize.width\"\n :data-pswp-height=\"pswpSize.height\"\n :data-pswp-caption=\"filename ?? ''\"\n class=\"pk-chatbot-file-preview__thumb-link\"\n :aria-label=\"filename ?? $t('label.file')\">\n <img\n v-show=\"thumbStatus === 'loaded'\"\n :src=\"url\"\n :alt=\"filename\"\n decoding=\"async\"\n class=\"pk-chatbot-file-preview__thumb\"\n @load=\"onImageLoad\"\n @error=\"onImageError\" />\n </a>\n <!-- Spinner until the thumbnail decodes (or while an upload is in\n flight); a fixed box keeps this from shifting the layout. -->\n <span\n v-if=\"loading || thumbStatus === 'loading'\"\n class=\"pk-chatbot-file-preview__status pk-chatbot-file-preview__status--overlay\">\n <VvIcon name=\"line-md:loading-loop\" />\n </span>\n <!-- Failed to load: a quiet icon instead of the browser's broken\n image glyph. -->\n <span\n v-else-if=\"thumbStatus === 'error'\"\n class=\"pk-chatbot-file-preview__status pk-chatbot-file-preview__status--overlay pk-chatbot-file-preview__status--error\">\n <VvIcon name=\"ri:image-2-line\" />\n </span>\n </div>\n <figcaption v-if=\"!compact\" class=\"pk-chatbot-file-preview__caption\">\n <span\n v-if=\"filename\"\n class=\"pk-chatbot-file-preview__name\"\n :title=\"filename\"\n >{{ filename }}</span\n >\n <span class=\"pk-chatbot-file-preview__meta\">\n <span\n v-if=\"error\"\n class=\"pk-chatbot-file-preview__error\"\n :title=\"error\">\n <VvIcon name=\"ri:error-warning-line\" />\n {{ error }}\n </span>\n <template v-else>{{ imageMeta.join(' · ') }}</template>\n </span>\n </figcaption>\n <button\n v-if=\"removable && !loading\"\n type=\"button\"\n class=\"pk-chatbot-file-preview__remove\"\n :title=\"$t('action.remove')\"\n :aria-label=\"$t('action.remove')\"\n @click.prevent=\"$emit('remove')\">\n <VvIcon name=\"ri:close-line\" />\n </button>\n </figure>\n\n <!-- Document attachment: an icon tile + name + type · size -->\n <div\n v-else\n class=\"pk-chatbot-file-preview pk-chatbot-file-preview--doc\"\n :class=\"{\n 'pk-chatbot-file-preview--error': error,\n 'pk-chatbot-file-preview--compact': compact,\n }\">\n <span\n class=\"pk-chatbot-file-preview__icon-tile\"\n :style=\"{ '--file-accent': fileKind.accent }\">\n <VvIcon\n v-if=\"loading\"\n name=\"line-md:loading-loop\"\n class=\"pk-chatbot-file-preview__icon\" />\n <VvIcon\n v-else\n :name=\"fileKind.icon\"\n class=\"pk-chatbot-file-preview__icon\" />\n </span>\n <span class=\"pk-chatbot-file-preview__body\">\n <span\n v-if=\"filename\"\n class=\"pk-chatbot-file-preview__name\"\n :title=\"filename\"\n >{{ filename }}</span\n >\n <span class=\"pk-chatbot-file-preview__meta\">\n <span\n v-if=\"error\"\n class=\"pk-chatbot-file-preview__error\"\n :title=\"error\">\n <VvIcon name=\"ri:error-warning-line\" />\n {{ error }}\n </span>\n <template v-else>{{ docMeta.join(' · ') }}</template>\n </span>\n </span>\n <a\n v-if=\"!loading && !error && !compact\"\n :href=\"url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"pk-chatbot-file-preview__open\"\n :title=\"$t('action.download')\"\n :aria-label=\"`${$t('action.download')}${filename ? ` — ${filename}` : ''}`\">\n <VvIcon name=\"ri:download-2-line\" />\n </a>\n <button\n v-if=\"removable && !loading\"\n type=\"button\"\n class=\"pk-chatbot-file-preview__remove\"\n :class=\"{ 'pk-chatbot-file-preview__remove--inline': !compact }\"\n :title=\"$t('action.remove')\"\n :aria-label=\"$t('action.remove')\"\n @click=\"$emit('remove')\">\n <VvIcon name=\"ri:close-line\" />\n </button>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-file-preview {\n // Concentric radii: outer card and inner tiles share an 8px step.\n --file-radius: var(--rounded-lg, 12px);\n position: relative;\n max-width: 280px;\n background-color: var(--color-surface-1);\n border: 1px solid var(--color-surface-3);\n border-radius: var(--file-radius);\n color: var(--color-word-2);\n font-size: var(--text-12);\n\n &--error {\n border-color: var(--color-danger);\n }\n\n // --- Document card ---\n &--doc {\n display: flex;\n align-items: center;\n gap: var(--spacing-8);\n padding: var(--spacing-8);\n min-width: 0;\n transition:\n border-color 0.18s var(--ease-out),\n background-color 0.18s var(--ease-out),\n box-shadow 0.18s var(--ease-out);\n\n &:hover {\n border-color: var(--color-surface-4);\n background-color: var(--color-surface-2);\n box-shadow: var(--shadow-sm);\n }\n }\n\n &__icon-tile {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--spacing-36);\n height: var(--spacing-36);\n // One step down from the card radius keeps the nested tile concentric.\n border-radius: var(--rounded-md, 6px);\n // Tint derived from the file-type accent; glyph sits at full accent.\n background-color: color-mix(\n in srgb,\n var(--file-accent) 14%,\n transparent\n );\n color: var(--file-accent);\n }\n\n &__icon {\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n\n &__body {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n flex: 1;\n }\n\n &__open {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--spacing-28);\n height: var(--spacing-28);\n margin-inline-start: var(--spacing-4);\n border-radius: var(--rounded);\n color: var(--color-word-3);\n text-decoration: none;\n opacity: 0;\n transition:\n opacity 0.18s var(--ease-out),\n color 0.18s var(--ease-out),\n background-color 0.18s var(--ease-out);\n\n &:hover {\n background-color: var(--color-surface-3);\n color: var(--color-word-1);\n }\n }\n\n &--doc:hover &__open,\n &--doc:focus-within &__open {\n opacity: 1;\n }\n\n // Touch devices have no hover: keep the open affordance visible.\n @media (hover: none) {\n &--doc &__open {\n opacity: 1;\n }\n }\n\n // --- Image card ---\n &--image {\n display: inline-flex;\n flex-direction: column;\n margin: 0;\n }\n\n &__thumb-wrap {\n position: relative;\n display: flex;\n // Fixed box: the card claims its final footprint before the\n // image decodes, so the message log never shifts on load.\n width: 280px;\n max-width: 100%;\n height: 200px;\n background-color: var(--color-surface);\n border-radius: var(--file-radius);\n overflow: hidden;\n }\n\n &__thumb-link {\n display: flex;\n width: 100%;\n height: 100%;\n cursor: pointer;\n }\n\n &__thumb {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__caption {\n display: flex;\n flex-direction: column;\n gap: 1px;\n padding: var(--spacing-8);\n min-width: 0;\n }\n\n &__name {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: var(--font-medium, 500);\n color: var(--color-word-1);\n }\n\n &__meta {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: var(--text-10);\n line-height: var(--leading-normal);\n // Reserve the line even while empty: image dimensions arrive\n // only after decode and must not grow the caption.\n min-height: 1.4em;\n color: var(--color-word-4);\n // Stable column for the dimensions/size figures.\n font-variant-numeric: tabular-nums;\n }\n\n &__error {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n color: var(--color-danger);\n\n .vv-icon {\n flex-shrink: 0;\n }\n }\n\n &__status {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n &--overlay {\n position: absolute;\n inset: 0;\n background-color: color-mix(\n in srgb,\n var(--color-surface-1) 70%,\n transparent\n );\n color: var(--color-word-1);\n }\n\n &--error {\n color: var(--color-word-4);\n }\n }\n\n // --- Remove button (input context) ---\n &__remove {\n position: absolute;\n top: 0;\n right: 0;\n transform: translate(50%, -50%);\n display: flex;\n align-items: center;\n justify-content: center;\n // Visible glyph is 18px but the hit area stays a comfortable 28px.\n width: var(--spacing-28);\n height: var(--spacing-28);\n padding: 0;\n border: none;\n border-radius: var(--rounded-full, 9999px);\n background-color: color-mix(\n in srgb,\n var(--color-surface-1) 85%,\n transparent\n );\n color: var(--color-word-2);\n cursor: pointer;\n line-height: 1;\n box-shadow: var(--shadow-sm);\n transition:\n background-color 0.18s var(--ease-out),\n color 0.18s var(--ease-out),\n scale 0.12s var(--ease-out);\n\n &:hover {\n background-color: var(--color-surface-2);\n color: var(--color-word-1);\n }\n\n &:active {\n scale: 0.96;\n }\n\n // Inside the document row the button rides inline, not overlaid\n // (size is inherited from the base).\n &--inline {\n position: static;\n flex-shrink: 0;\n box-shadow: none;\n background-color: transparent;\n color: var(--color-word-4);\n\n &:hover {\n background-color: var(--color-surface-3);\n color: var(--color-word-1);\n }\n }\n }\n\n // --- Compact (composer) chips ---\n // ChatGPT/Gemini-style: every attachment is the SAME height and carries\n // an always-visible round remove control in the top-right corner — no\n // hover-revealed actions, no size mismatch between image and file chips.\n &--compact {\n // Shared chip height so image and file chips align in the row.\n --chip-size: var(--spacing-56);\n max-width: 240px;\n\n &.pk-chatbot-file-preview--image {\n width: var(--chip-size);\n height: var(--chip-size);\n border-radius: var(--rounded-md, 6px);\n }\n\n // Fill the square; the root's `overflow: hidden` rounds the corners.\n .pk-chatbot-file-preview__thumb-wrap,\n .pk-chatbot-file-preview__thumb-link {\n width: 100%;\n height: 100%;\n }\n\n &.pk-chatbot-file-preview--doc {\n height: var(--chip-size);\n gap: var(--spacing-8);\n padding: var(--spacing-8);\n // Reserve room so the corner remove button never overlaps the\n // name/meta text.\n padding-inline-end: var(--spacing-28);\n }\n\n // Square icon tile that fills the chip height, so file chips read at\n // the same scale as image thumbnails.\n .pk-chatbot-file-preview__icon-tile {\n align-self: stretch;\n width: auto;\n height: auto;\n aspect-ratio: 1;\n }\n\n .pk-chatbot-file-preview__icon {\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n }\n\n // Always-visible round remove control in the top-right corner. Sits\n // just inside the chip (an ancestor clips overflow), bigger than before,\n // with an extended hit area for easy tapping.\n &--compact &__remove {\n top: var(--spacing-4);\n right: var(--spacing-4);\n width: var(--spacing-24);\n height: var(--spacing-24);\n border: 1px solid var(--color-surface-4);\n background-color: var(--color-surface);\n color: var(--color-word-1);\n font-size: var(--text-16);\n box-shadow: var(--shadow-sm);\n\n // Comfortable touch/click target beyond the small visible glyph.\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-4));\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n color: var(--color-word-1);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n &--doc,\n &__open,\n &__remove {\n transition: none;\n }\n }\n }\n</style>\n"],"mappings":"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"}