@8wave/ai-elements 0.71.0 → 0.74.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 (165) hide show
  1. package/README.md +62 -15
  2. package/dist/_chunks/{PkToolShowArtifact-D-dU41VG.js → PkToolShowArtifact-c_vCWUpQ.js} +2 -2
  3. package/dist/_chunks/{PkToolShowArtifact-D-dU41VG.js.map → PkToolShowArtifact-c_vCWUpQ.js.map} +1 -1
  4. package/dist/_chunks/{PkToolShowCalendarEvent-B33fWbkk.js → PkToolShowCalendarEvent-D2VHu1V3.js} +3 -3
  5. package/dist/_chunks/{PkToolShowCalendarEvent-B33fWbkk.js.map → PkToolShowCalendarEvent-D2VHu1V3.js.map} +1 -1
  6. package/dist/_chunks/{PkToolShowComparison-CcKHsr36.js → PkToolShowComparison-C2ZUH0w4.js} +3 -3
  7. package/dist/_chunks/{PkToolShowComparison-CcKHsr36.js.map → PkToolShowComparison-C2ZUH0w4.js.map} +1 -1
  8. package/dist/_chunks/{PkToolShowContactForm-SmOSbCsp.js → PkToolShowContactForm-6C4uWDtf.js} +3 -3
  9. package/dist/_chunks/{PkToolShowContactForm-SmOSbCsp.js.map → PkToolShowContactForm-6C4uWDtf.js.map} +1 -1
  10. package/dist/_chunks/{PkToolShowEmail-BOivL2p_.js → PkToolShowEmail-BuyCHUU5.js} +3 -3
  11. package/dist/_chunks/{PkToolShowEmail-BOivL2p_.js.map → PkToolShowEmail-BuyCHUU5.js.map} +1 -1
  12. package/dist/_chunks/{PkToolShowImageGallery-Dy0vFEU-.js → PkToolShowImageGallery-ByldZ0nj.js} +2 -2
  13. package/dist/_chunks/{PkToolShowImageGallery-Dy0vFEU-.js.map → PkToolShowImageGallery-ByldZ0nj.js.map} +1 -1
  14. package/dist/_chunks/{PkToolShowLocation-US6u26ra.js → PkToolShowLocation-BCFnC5c7.js} +3 -3
  15. package/dist/_chunks/{PkToolShowLocation-US6u26ra.js.map → PkToolShowLocation-BCFnC5c7.js.map} +1 -1
  16. package/dist/_chunks/{PkToolShowMessage-Cjslxq94.js → PkToolShowMessage-6Bsfi3BC.js} +3 -3
  17. package/dist/_chunks/{PkToolShowMessage-Cjslxq94.js.map → PkToolShowMessage-6Bsfi3BC.js.map} +1 -1
  18. package/dist/_chunks/{PkToolShowMultipleChoice-BEwiJCLu.js → PkToolShowMultipleChoice-BLNTD2lK.js} +3 -3
  19. package/dist/_chunks/{PkToolShowMultipleChoice-BEwiJCLu.js.map → PkToolShowMultipleChoice-BLNTD2lK.js.map} +1 -1
  20. package/dist/_chunks/{PkToolShowProductList-D3Qe8yQM.js → PkToolShowProductList-B4S5_m-f.js} +2 -2
  21. package/dist/_chunks/{PkToolShowProductList-D3Qe8yQM.js.map → PkToolShowProductList-B4S5_m-f.js.map} +1 -1
  22. package/dist/_chunks/{PkToolShowQrCode-B64sdI0N.js → PkToolShowQrCode-BVZlzYDq.js} +3 -3
  23. package/dist/_chunks/{PkToolShowQrCode-B64sdI0N.js.map → PkToolShowQrCode-BVZlzYDq.js.map} +1 -1
  24. package/dist/_chunks/{PkToolShowSources-CK3lqqoU.js → PkToolShowSources-BQo5DRwt.js} +13 -4
  25. package/dist/_chunks/PkToolShowSources-BQo5DRwt.js.map +1 -0
  26. package/dist/_chunks/{PkToolShowSuggestedReply-BGWA08C7.js → PkToolShowSuggestedReply-BxDI6yp_.js} +2 -2
  27. package/dist/_chunks/{PkToolShowSuggestedReply-BGWA08C7.js.map → PkToolShowSuggestedReply-BxDI6yp_.js.map} +1 -1
  28. package/dist/_chunks/{PkToolShowWeather-ClMzD04y.js → PkToolShowWeather-DR6egBIP.js} +3 -3
  29. package/dist/_chunks/{PkToolShowWeather-ClMzD04y.js.map → PkToolShowWeather-DR6egBIP.js.map} +1 -1
  30. package/dist/_chunks/{PkToolShowWebPages-BCqywwEK.js → PkToolShowWebPages-D8RngE-F.js} +2 -2
  31. package/dist/_chunks/{PkToolShowWebPages-BCqywwEK.js.map → PkToolShowWebPages-D8RngE-F.js.map} +1 -1
  32. package/dist/_chunks/{index.es-Z3Hu-QIb.js → index.es-BGFmj59S.js} +2429 -1559
  33. package/dist/_chunks/{index.es-Z3Hu-QIb.js.map → index.es-BGFmj59S.js.map} +1 -1
  34. package/dist/ai-elements.es.js +3026 -3063
  35. package/dist/ai-elements.es.js.map +1 -1
  36. package/dist-vue/PkChatbot.js +1 -1
  37. package/dist-vue/PkChatbotFeedbackForm.js +1 -1
  38. package/dist-vue/PkChatbotFilePreview.js +1 -1
  39. package/dist-vue/PkChatbotInput.js +1 -1
  40. package/dist-vue/PkChatbotMessages.js +1 -1
  41. package/dist-vue/PkChatbotViewChat.js +1 -1
  42. package/dist-vue/PkChatbotViewConversations.js +1 -1
  43. package/dist-vue/PkChatbotViewProfile.js +1 -1
  44. package/dist-vue/_chunks/PkChatbot-woRkjgP5.js +190 -0
  45. package/dist-vue/_chunks/{PkChatbot-CkxgCXC4.js.map → PkChatbot-woRkjgP5.js.map} +1 -1
  46. package/dist-vue/_chunks/{PkChatbotFeedbackForm-CUnS-WyR.js → PkChatbotFeedbackForm-D-lx1URv.js} +2 -2
  47. package/dist-vue/_chunks/{PkChatbotFeedbackForm-CUnS-WyR.js.map → PkChatbotFeedbackForm-D-lx1URv.js.map} +1 -1
  48. package/dist-vue/_chunks/{PkChatbotFilePreview-JSpi3pmN.js → PkChatbotFilePreview-DHzuGtz5.js} +2 -2
  49. package/dist-vue/_chunks/{PkChatbotFilePreview-JSpi3pmN.js.map → PkChatbotFilePreview-DHzuGtz5.js.map} +1 -1
  50. package/dist-vue/_chunks/{PkChatbotInput-ODPhsqZt.js → PkChatbotInput-LHE0HZ9z.js} +3 -3
  51. package/dist-vue/_chunks/{PkChatbotInput-ODPhsqZt.js.map → PkChatbotInput-LHE0HZ9z.js.map} +1 -1
  52. package/dist-vue/_chunks/{PkChatbotMessages-B5EK-qDo.js → PkChatbotMessages-BxTeEm3j.js} +15 -15
  53. package/dist-vue/_chunks/{PkChatbotMessages-B5EK-qDo.js.map → PkChatbotMessages-BxTeEm3j.js.map} +1 -1
  54. package/dist-vue/_chunks/{PkChatbotViewChat-C4haTRtG.js → PkChatbotViewChat-BoEfZeco.js} +9 -9
  55. package/dist-vue/_chunks/{PkChatbotViewChat-C4haTRtG.js.map → PkChatbotViewChat-BoEfZeco.js.map} +1 -1
  56. package/dist-vue/_chunks/PkChatbotViewConversations-DvVc1arX.js +163 -0
  57. package/dist-vue/_chunks/PkChatbotViewConversations-DvVc1arX.js.map +1 -0
  58. package/dist-vue/_chunks/{PkChatbotViewProfile-Cafl_Jf8.js → PkChatbotViewProfile-Dev_6pki.js} +2 -2
  59. package/dist-vue/_chunks/{PkChatbotViewProfile-Cafl_Jf8.js.map → PkChatbotViewProfile-Dev_6pki.js.map} +1 -1
  60. package/dist-vue/_chunks/{PkRelativeTime-jP41qAJ5.js → PkRelativeTime-WZ2aPcp_.js} +1 -1
  61. package/dist-vue/_chunks/{PkRelativeTime-jP41qAJ5.js.map → PkRelativeTime-WZ2aPcp_.js.map} +1 -1
  62. package/dist-vue/_chunks/{PkStreamingMarkdown-CtiMH6FD.js → PkStreamingMarkdown-B4gnJ4hk.js} +1 -1
  63. package/dist-vue/_chunks/{PkStreamingMarkdown-CtiMH6FD.js.map → PkStreamingMarkdown-B4gnJ4hk.js.map} +1 -1
  64. package/dist-vue/_chunks/{PkToolShowArtifact-C2DgB_1z.js → PkToolShowArtifact-EvbUZSOf.js} +2 -2
  65. package/dist-vue/_chunks/{PkToolShowArtifact-C2DgB_1z.js.map → PkToolShowArtifact-EvbUZSOf.js.map} +1 -1
  66. package/dist-vue/_chunks/{PkToolShowContactForm-BzzbXuhh.js → PkToolShowContactForm-CsDOqSJK.js} +3 -3
  67. package/dist-vue/_chunks/{PkToolShowContactForm-BzzbXuhh.js.map → PkToolShowContactForm-CsDOqSJK.js.map} +1 -1
  68. package/dist-vue/_chunks/{PkToolShowImageGallery-DQXrJYop.js → PkToolShowImageGallery-B7Bt6ZGv.js} +2 -2
  69. package/dist-vue/_chunks/{PkToolShowImageGallery-DQXrJYop.js.map → PkToolShowImageGallery-B7Bt6ZGv.js.map} +1 -1
  70. package/dist-vue/_chunks/{PkToolShowLocation-McDIqI8y.js → PkToolShowLocation-DteWf0Cs.js} +1 -1
  71. package/dist-vue/_chunks/{PkToolShowLocation-McDIqI8y.js.map → PkToolShowLocation-DteWf0Cs.js.map} +1 -1
  72. package/dist-vue/_chunks/{PkToolShowMultipleChoice-DW3m6VbJ.js → PkToolShowMultipleChoice-DZXfWtQp.js} +1 -1
  73. package/dist-vue/_chunks/{PkToolShowMultipleChoice-DW3m6VbJ.js.map → PkToolShowMultipleChoice-DZXfWtQp.js.map} +1 -1
  74. package/dist-vue/_chunks/{PkToolShowProductList-BDSJs7bn.js → PkToolShowProductList-CtqWK0x4.js} +2 -2
  75. package/dist-vue/_chunks/{PkToolShowProductList-BDSJs7bn.js.map → PkToolShowProductList-CtqWK0x4.js.map} +1 -1
  76. package/dist-vue/_chunks/{PkToolShowSources-BXeGRm4D.js → PkToolShowSources-ZtXFkIHt.js} +14 -5
  77. package/dist-vue/_chunks/PkToolShowSources-ZtXFkIHt.js.map +1 -0
  78. package/dist-vue/_chunks/{PkToolShowSuggestedReply-Dm3BTWYg.js → PkToolShowSuggestedReply-30m9yWDL.js} +1 -1
  79. package/dist-vue/_chunks/{PkToolShowSuggestedReply-Dm3BTWYg.js.map → PkToolShowSuggestedReply-30m9yWDL.js.map} +1 -1
  80. package/dist-vue/_chunks/{PkToolShowWebPages-DnYVhLVU.js → PkToolShowWebPages-DZIdrdWs.js} +2 -2
  81. package/dist-vue/_chunks/{PkToolShowWebPages-DnYVhLVU.js.map → PkToolShowWebPages-DZIdrdWs.js.map} +1 -1
  82. package/dist-vue/_chunks/{PkUrl-CH4pWBR8.js → PkUrl-CGbSBfuP.js} +1 -1
  83. package/dist-vue/_chunks/{PkUrl-CH4pWBR8.js.map → PkUrl-CGbSBfuP.js.map} +1 -1
  84. package/dist-vue/_chunks/{createChatbotApiClient-OA7JscBS.js → createChatbotApiClient-YJegM3ni.js} +141 -119
  85. package/dist-vue/_chunks/createChatbotApiClient-YJegM3ni.js.map +1 -0
  86. package/dist-vue/_chunks/{dist-Bs6dYy7x.js → dist-21vPGg1O.js} +3 -3
  87. package/dist-vue/_chunks/{dist-Bs6dYy7x.js.map → dist-21vPGg1O.js.map} +1 -1
  88. package/dist-vue/_chunks/{dist-Bb1zRSg4.js → dist-BGXTbYew2.js} +4 -4
  89. package/dist-vue/_chunks/dist-BGXTbYew2.js.map +1 -0
  90. package/dist-vue/_chunks/{dist-BrlD3co0.js → dist-BL9cumqH.js} +2 -2
  91. package/dist-vue/_chunks/{dist-BrlD3co0.js.map → dist-BL9cumqH.js.map} +1 -1
  92. package/dist-vue/_chunks/{dist-B9Pw4_a5.js → dist-BU-VgsYM.js} +3 -3
  93. package/dist-vue/_chunks/{dist-B9Pw4_a5.js.map → dist-BU-VgsYM.js.map} +1 -1
  94. package/dist-vue/_chunks/{dist-D-tjAaHK.js → dist-BU5m1pTR.js} +3 -3
  95. package/dist-vue/_chunks/{dist-D-tjAaHK.js.map → dist-BU5m1pTR.js.map} +1 -1
  96. package/dist-vue/_chunks/{dist-Dn0cKZ5Q.js → dist-BbP2I_Or.js} +2 -2
  97. package/dist-vue/_chunks/{dist-Dn0cKZ5Q.js.map → dist-BbP2I_Or.js.map} +1 -1
  98. package/dist-vue/_chunks/{dist-Cvsv0YEw.js → dist-BvmTki6w.js} +3 -3
  99. package/dist-vue/_chunks/{dist-Cvsv0YEw.js.map → dist-BvmTki6w.js.map} +1 -1
  100. package/dist-vue/_chunks/{dist-QzbLuLIV.js → dist-C3dPzENq.js} +3 -3
  101. package/dist-vue/_chunks/{dist-QzbLuLIV.js.map → dist-C3dPzENq.js.map} +1 -1
  102. package/dist-vue/_chunks/{dist-BMWOJZqC.js → dist-C6AWJq9a.js} +2 -2
  103. package/dist-vue/_chunks/{dist-BMWOJZqC.js.map → dist-C6AWJq9a.js.map} +1 -1
  104. package/dist-vue/_chunks/{dist-BQCdUoYc.js → dist-CGNGkvWf.js} +4 -4
  105. package/dist-vue/_chunks/{dist-BQCdUoYc.js.map → dist-CGNGkvWf.js.map} +1 -1
  106. package/dist-vue/_chunks/{dist-JOgG1zY3.js → dist-CIN_Avbm.js} +1 -1
  107. package/dist-vue/_chunks/{dist-JOgG1zY3.js.map → dist-CIN_Avbm.js.map} +1 -1
  108. package/dist-vue/_chunks/{dist-NKG-Bt-Q.js → dist-CePpgudw.js} +3 -3
  109. package/dist-vue/_chunks/{dist-NKG-Bt-Q.js.map → dist-CePpgudw.js.map} +1 -1
  110. package/dist-vue/_chunks/{dist-C13mHkC-.js → dist-CtMo7ig_.js} +2 -2
  111. package/dist-vue/_chunks/{dist-C13mHkC-.js.map → dist-CtMo7ig_.js.map} +1 -1
  112. package/dist-vue/_chunks/{dist-DucfoJQX.js → dist-CwG072cD.js} +3 -3
  113. package/dist-vue/_chunks/{dist-DucfoJQX.js.map → dist-CwG072cD.js.map} +1 -1
  114. package/dist-vue/_chunks/{dist-_4yQQX_L.js → dist-DFvVVU0-.js} +2 -2
  115. package/dist-vue/_chunks/{dist-_4yQQX_L.js.map → dist-DFvVVU0-.js.map} +1 -1
  116. package/dist-vue/_chunks/{dist-C_tRbeXQ.js → dist-DGz57kzK.js} +2 -2
  117. package/dist-vue/_chunks/{dist-C_tRbeXQ.js.map → dist-DGz57kzK.js.map} +1 -1
  118. package/dist-vue/_chunks/{dist-C2tC3pDh.js → dist-DT30Xa-D.js} +2 -2
  119. package/dist-vue/_chunks/{dist-C2tC3pDh.js.map → dist-DT30Xa-D.js.map} +1 -1
  120. package/dist-vue/_chunks/{dist-DNn7FqmI.js → dist-DoRhRpjL.js} +2 -2
  121. package/dist-vue/_chunks/{dist-DNn7FqmI.js.map → dist-DoRhRpjL.js.map} +1 -1
  122. package/dist-vue/_chunks/{dist-CQmgfFIN.js → dist-GnhLE5-A.js} +4 -4
  123. package/dist-vue/_chunks/{dist-CQmgfFIN.js.map → dist-GnhLE5-A.js.map} +1 -1
  124. package/dist-vue/_chunks/{dist-1ToEz3Zq.js → dist-QzWOn2Kx.js} +3 -3
  125. package/dist-vue/_chunks/{dist-1ToEz3Zq.js.map → dist-QzWOn2Kx.js.map} +1 -1
  126. package/dist-vue/_chunks/{dist-e9UFeG14.js → dist-uYEYs0EJ.js} +2 -2
  127. package/dist-vue/_chunks/{dist-e9UFeG14.js.map → dist-uYEYs0EJ.js.map} +1 -1
  128. package/dist-vue/_chunks/{dist-BpT8aqtS.js → dist-ukeBjdwq.js} +4 -4
  129. package/dist-vue/_chunks/{dist-BpT8aqtS.js.map → dist-ukeBjdwq.js.map} +1 -1
  130. package/dist-vue/_chunks/{dist-BHTSdHeX.js → dist-wLB8N32T.js} +3 -3
  131. package/dist-vue/_chunks/{dist-BHTSdHeX.js.map → dist-wLB8N32T.js.map} +1 -1
  132. package/dist-vue/_chunks/{index.es-BnOT0fHY.js → index.es-CrPSzhoZ.js} +1 -1
  133. package/dist-vue/_chunks/{index.es-BnOT0fHY.js.map → index.es-CrPSzhoZ.js.map} +1 -1
  134. package/dist-vue/_chunks/{useChatbotStore-BNGyIqjI.js → useChatbotStore-DGL81KJa.js} +868 -852
  135. package/dist-vue/_chunks/{useChatbotStore-BNGyIqjI.js.map → useChatbotStore-DGL81KJa.js.map} +1 -1
  136. package/dist-vue/_chunks/{useLightbox-BiZUwsmB.js → useLightbox-1sB7fmFb.js} +1 -1
  137. package/dist-vue/_chunks/{useLightbox-BiZUwsmB.js.map → useLightbox-1sB7fmFb.js.map} +1 -1
  138. package/dist-vue/_chunks/{utils-DO7emdsn.js → utils-BegUBK7s.js} +1 -1
  139. package/dist-vue/_chunks/{utils-DO7emdsn.js.map → utils-BegUBK7s.js.map} +1 -1
  140. package/dist-vue/api.js +1 -1
  141. package/dist-vue/apps/web-component/src/composables/useChatbotAgent.d.ts +3 -1
  142. package/dist-vue/composables.js +3 -3
  143. package/dist-vue/index.js +3780 -3498
  144. package/dist-vue/index.js.map +1 -1
  145. package/dist-vue/locales.js +56 -20
  146. package/dist-vue/packages/components/src/PkEditorPrompt.d.ts +2 -0
  147. package/dist-vue/packages/components/src/chat/PkAgentSettingsPanel.d.ts +6 -3
  148. package/dist-vue/packages/components/src/chat/PkEditorChannels.d.ts +16 -0
  149. package/dist-vue/packages/composable/src/chatbot/api/createChatbotApiClient.d.ts +2 -0
  150. package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +2 -3553
  151. package/dist-vue/packages/models/src/schema/Agent.d.ts +15 -5
  152. package/dist-vue/packages/models/src/schema/AgentChatStatistics.d.ts +160 -0
  153. package/dist-vue/packages/models/src/schema/Chat.d.ts +6 -0
  154. package/dist-vue/packages/models/src/schema/Document.d.ts +18 -0
  155. package/dist-vue/packages/models/src/schema/ReasoningChat.d.ts +4 -2
  156. package/dist-vue/packages/models/src/schema/SubAgent.d.ts +5 -3
  157. package/dist-vue/style.css +1 -1
  158. package/package.json +2 -2
  159. package/dist/_chunks/PkToolShowSources-CK3lqqoU.js.map +0 -1
  160. package/dist-vue/_chunks/PkChatbot-CkxgCXC4.js +0 -190
  161. package/dist-vue/_chunks/PkChatbotViewConversations--vd3MWYy.js +0 -40
  162. package/dist-vue/_chunks/PkChatbotViewConversations--vd3MWYy.js.map +0 -1
  163. package/dist-vue/_chunks/PkToolShowSources-BXeGRm4D.js.map +0 -1
  164. package/dist-vue/_chunks/createChatbotApiClient-OA7JscBS.js.map +0 -1
  165. package/dist-vue/_chunks/dist-Bb1zRSg4.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowWeather-ClMzD04y.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolShowWeather.vue","../../../../packages/components/src/chat/PkToolShowWeather.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n\n interface WeatherCurrent {\n temperature: number\n feelsLike: number\n humidity: number\n precipitation: number\n weatherCode: number\n windSpeed: number\n windDirection: number\n uvIndex: number\n }\n\n interface WeatherHourly {\n time: string\n temperature: number\n precipitationProbability: number\n precipitation: number\n weatherCode: number\n windSpeed: number\n }\n\n interface WeatherDaily {\n date: string\n maxTemp: number\n minTemp: number\n weatherCode: number\n precipitationSum: number\n precipitationProbabilityMax: number\n uvIndexMax: number\n sunrise: string\n sunset: string\n }\n\n interface AirQuality {\n aqi: number\n pm10: number\n pm25: number\n no2: number\n ozone: number\n }\n\n interface WeatherData {\n locationName: string\n latitude: number\n longitude: number\n timezone: string\n current: WeatherCurrent | null\n hourly: WeatherHourly[]\n daily: WeatherDaily[]\n airQuality: AirQuality | null\n }\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { t: $t, d: $d, n: $n } = useI18n({ useScope: 'global' })\n\n const toolPart = computed(() => {\n return props.part as { input?: WeatherData }\n })\n\n const data = computed(() => toolPart.value.input)\n\n const selectedDayIndex = ref(0)\n\n const dailyOpen = ref(!data.value?.current)\n\n watch(selectedDayIndex, (index) => {\n if (index > 0) {\n dailyOpen.value = true\n }\n })\n\n const selectedDay = computed(\n () => data.value?.daily?.[selectedDayIndex.value],\n )\n\n /** Hourly entries filtered to the selected day's date */\n const filteredHourly = computed(() => {\n if (!data.value?.hourly?.length || !selectedDay.value) {\n return data.value?.hourly ?? []\n }\n const dayDate = selectedDay.value.date\n return data.value.hourly.filter((h) => h.time.startsWith(dayDate))\n })\n\n /**\n * Parses a date/time string in various formats the LLM might produce.\n * Tries ISO, space-separated, time-only, and common human-readable formats.\n */\n function safeDate(value: string): Date {\n // 1. Try native parsing (works for ISO 8601 and many standard formats)\n const direct = new Date(value)\n if (!Number.isNaN(direct.getTime())) {\n return direct\n }\n\n // 2. Replace space separator → T (\"2024-04-15 14:00\" → \"2024-04-15T14:00\")\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/.test(value)) {\n const withT = new Date(value.replace(' ', 'T'))\n if (!Number.isNaN(withT.getTime())) {\n return withT\n }\n }\n\n // 3. Time-only (\"14:00\" or \"14:00:00\") → today at that hour\n const timeOnly = value.match(/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/)\n if (timeOnly) {\n const today = new Date()\n today.setHours(\n Number(timeOnly[1]),\n Number(timeOnly[2]),\n Number(timeOnly[3] ?? 0),\n 0,\n )\n return today\n }\n\n // 4. Date-only (\"2024-04-15\") → noon to avoid timezone edge\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const noon = new Date(value + 'T12:00:00')\n if (!Number.isNaN(noon.getTime())) {\n return noon\n }\n }\n\n // Last resort: return an obviously invalid sentinel so callers can handle it\n return new Date(NaN)\n }\n\n /** Maps WMO weather code to an Iconify wi: icon name */\n function wmoToIcon(code: number): string {\n if (code === 0) {\n return 'wi:day-sunny'\n }\n if (code === 1) {\n return 'wi:day-sunny-overcast'\n }\n if (code === 2) {\n return 'wi:day-cloudy'\n }\n if (code === 3) {\n return 'wi:cloudy'\n }\n if (code === 45 || code === 48) {\n return 'wi:fog'\n }\n if (code >= 51 && code <= 55) {\n return 'wi:sprinkle'\n }\n if (code === 56 || code === 57) {\n return 'wi:sleet'\n }\n if (code >= 61 && code <= 65) {\n return 'wi:rain'\n }\n if (code === 66 || code === 67) {\n return 'wi:rain-mix'\n }\n if (code >= 71 && code <= 77) {\n return 'wi:snow'\n }\n if (code >= 80 && code <= 82) {\n return 'wi:showers'\n }\n if (code === 85 || code === 86) {\n return 'wi:snow-wind'\n }\n if (code === 95) {\n return 'wi:thunderstorm'\n }\n if (code === 96 || code === 99) {\n return 'wi:storm-showers'\n }\n return 'wi:na'\n }\n\n /**\n * Returns a formatted day label for daily forecast.\n * When daily has 1-2 entries, use the date string directly.\n * Otherwise, use index-based computation from today.\n */\n function formatDay(isoDate: string, index: number, total: number): string {\n const parsed = safeDate(isoDate + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n const today = new Date()\n today.setHours(12, 0, 0, 0)\n const diffDays = Math.round(\n (parsed.getTime() - today.getTime()) / 86_400_000,\n )\n if (diffDays === 0) {\n return $t('weather.today')\n }\n return $d(parsed, 'weekday-day')\n }\n if (index === 0 && total <= 7) {\n return $t('weather.today')\n }\n const fallback = new Date()\n fallback.setDate(fallback.getDate() + index)\n return $d(fallback, 'weekday-day')\n }\n\n const headerDate = computed(() => {\n if (selectedDay.value) {\n const parsed = safeDate(selectedDay.value.date + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n return formatDay(\n selectedDay.value.date,\n selectedDayIndex.value,\n data.value.daily.length,\n )\n }\n }\n return formatDay(safeDate(new Date().toISOString().slice(0, 10)), 0, 1)\n })\n\n /** Returns AQI label and CSS class for European AQI */\n function aqiInfo(aqi: number): { label: string; class: string } {\n if (aqi <= 20) {\n return {\n label: $t('weather.aqiGood'),\n class: 'text-green-600 bg-green-50',\n }\n }\n if (aqi <= 40) {\n return {\n label: $t('weather.aqiFair'),\n class: 'text-lime-600 bg-lime-50',\n }\n }\n if (aqi <= 60) {\n return {\n label: $t('weather.aqiModerate'),\n class: 'text-yellow-600 bg-yellow-50',\n }\n }\n if (aqi <= 80) {\n return {\n label: $t('weather.aqiPoor'),\n class: 'text-orange-600 bg-orange-50',\n }\n }\n if (aqi <= 100) {\n return {\n label: $t('weather.aqiVeryPoor'),\n class: 'text-red-600 bg-red-50',\n }\n }\n return {\n label: $t('weather.aqiHazardous'),\n class: 'text-purple-700 bg-purple-50',\n }\n }\n\n /** Formats wind direction degrees to compass label */\n function windDirectionLabel(deg: number): string {\n const dirs = ['N', 'NE', 'E', 'SE', 'S', 'SO', 'O', 'NO']\n return dirs[Math.round(deg / 45) % 8]\n }\n</script>\n\n<template>\n <div\n v-if=\"data\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <!-- Header -->\n <div\n class=\"px-sm py-6 bg-surface-1 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:temp-cold-line\" class=\"text-16\" />\n <strong class=\"font-bold truncate\">\n {{ data.locationName }}\n </strong>\n <span class=\"ml-auto shrink-0 capitalize\">\n {{ headerDate }}\n </span>\n </div>\n\n <!-- Current conditions (only when real-time current is available AND first day is selected) -->\n <div\n v-if=\"data.current && selectedDayIndex === 0\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(data.current.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <span class=\"text-48 font-light leading-none text-word-1\">\n {{ $n(data.current.temperature, 'integer') }}&deg;\n </span>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div class=\"flex items-center gap-6\">\n {{ $t('weather.feelsLike') }}\n {{ $n(data.current.feelsLike, 'integer') }}&deg;\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:water-percent-line\" class=\"text-14\" />\n {{ data.current.humidity }}%\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14\" />\n {{ $n(data.current.windSpeed, 'integer') }} km/h\n {{ windDirectionLabel(data.current.windDirection) }}\n </div>\n <div\n v-if=\"data.current.uvIndex > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(data.current.uvIndex, 'integer') }}\n </div>\n </div>\n </div>\n\n <!-- Day summary (when no real-time current, or a non-today day is selected) -->\n <div\n v-else-if=\"selectedDay\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(selectedDay.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <div class=\"flex flex-col leading-none\">\n <span class=\"text-48 font-light text-word-1\">\n {{ $n(selectedDay.maxTemp, 'integer') }}&deg;\n </span>\n <span class=\"text-20 text-word-3\">\n {{ $n(selectedDay.minTemp, 'integer') }}&deg;\n </span>\n </div>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div\n v-if=\"selectedDay.precipitationProbabilityMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:drop-line\" class=\"text-14\" />\n {{ selectedDay.precipitationProbabilityMax }}% ({{\n selectedDay.precipitationSum\n }}\n mm)\n </div>\n <div\n v-if=\"selectedDay.uvIndexMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(selectedDay.uvIndexMax, 'integer') }}\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-foggy-line\" class=\"text-14\" />\n {{ $d(safeDate(selectedDay.sunrise), 'time') }}\n –\n {{ $d(safeDate(selectedDay.sunset), 'time') }}\n </div>\n </div>\n </div>\n\n <!-- Hourly forecast (filtered to selected day) -->\n <transition>\n <div\n v-if=\"filteredHourly.length > 0\"\n class=\"border-t border-surface-3\">\n <div\n class=\"flex overflow-x-auto gap-2 px-sm py-8 light-scrollbar\">\n <div\n v-for=\"hour in filteredHourly\"\n :key=\"hour.time\"\n class=\"flex flex-col items-center gap-4 min-w-48 px-4 py-6 rounded-lg text-11 text-word-3 shrink-0\">\n <span class=\"text-word-3\">{{\n Number.isNaN(safeDate(hour.time).getTime())\n ? hour.time\n : $d(safeDate(hour.time), 'time')\n }}</span>\n <VvIcon\n :name=\"wmoToIcon(hour.weatherCode)\"\n class=\"text-20 text-word-2\" />\n <span class=\"font-medium text-word-1\">\n {{ $n(hour.temperature, 'integer') }}&deg;\n </span>\n <span\n v-if=\"hour.precipitationProbability > 0\"\n class=\"text-word-5 text-smaller\">\n {{ hour.precipitationProbability }}%\n </span>\n </div>\n </div>\n </div>\n </transition>\n\n <!-- Daily forecast (clickable, inside accordion) -->\n <transition>\n <VvAccordion\n v-if=\"data.daily.length > 1\"\n v-model=\"dailyOpen\"\n modifiers=\"details no-padding\"\n :title=\"$t('weather.dailyForecast')\"\n class=\"border-t border-surface-3\">\n <button\n v-for=\"(day, index) in data.daily\"\n :key=\"day.date\"\n type=\"button\"\n class=\"flex items-center gap-sm px-sm py-8 text-12 w-full text-left transition-colors cursor-pointer border-t border-surface-3\"\n :class=\"\n index === selectedDayIndex\n ? 'bg-surface-2'\n : 'hover:bg-surface-1'\n \"\n @click=\"selectedDayIndex = index\">\n <span class=\"w-48 shrink-0 text-word-3 capitalize\">\n {{ formatDay(day.date, index, data.daily.length) }}\n </span>\n <VvIcon\n :name=\"wmoToIcon(day.weatherCode)\"\n class=\"text-18 text-word-2 shrink-0\" />\n <span\n v-if=\"day.precipitationProbabilityMax > 0\"\n class=\"text-word-5 text-11 w-32 shrink-0\">\n {{ day.precipitationProbabilityMax }}%\n </span>\n <span v-else class=\"w-32 shrink-0\" />\n <div class=\"ml-auto flex items-center gap-8 text-word-3\">\n <span class=\"text-word-1 font-semibold\">\n {{ $n(day.maxTemp, 'integer') }}&deg;\n </span>\n <span>{{ $n(day.minTemp, 'integer') }}&deg;</span>\n </div>\n </button>\n </VvAccordion>\n </transition>\n\n <!-- Air quality -->\n <div\n v-if=\"data.airQuality\"\n class=\"border-t border-surface-3 px-sm py-8 flex items-center gap-8 text-12\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14 text-word-3\" />\n <span class=\"text-word-3\">{{ $t('weather.airQuality') }}</span>\n <span\n :class=\"[\n 'ml-auto px-8 py-2 text-11 font-semibold',\n aqiInfo(data.airQuality.aqi).class,\n ]\">\n {{ aqiInfo(data.airQuality.aqi).label }}\n ({{ data.airQuality.aqi }})\n </span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n\n interface WeatherCurrent {\n temperature: number\n feelsLike: number\n humidity: number\n precipitation: number\n weatherCode: number\n windSpeed: number\n windDirection: number\n uvIndex: number\n }\n\n interface WeatherHourly {\n time: string\n temperature: number\n precipitationProbability: number\n precipitation: number\n weatherCode: number\n windSpeed: number\n }\n\n interface WeatherDaily {\n date: string\n maxTemp: number\n minTemp: number\n weatherCode: number\n precipitationSum: number\n precipitationProbabilityMax: number\n uvIndexMax: number\n sunrise: string\n sunset: string\n }\n\n interface AirQuality {\n aqi: number\n pm10: number\n pm25: number\n no2: number\n ozone: number\n }\n\n interface WeatherData {\n locationName: string\n latitude: number\n longitude: number\n timezone: string\n current: WeatherCurrent | null\n hourly: WeatherHourly[]\n daily: WeatherDaily[]\n airQuality: AirQuality | null\n }\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { t: $t, d: $d, n: $n } = useI18n({ useScope: 'global' })\n\n const toolPart = computed(() => {\n return props.part as { input?: WeatherData }\n })\n\n const data = computed(() => toolPart.value.input)\n\n const selectedDayIndex = ref(0)\n\n const dailyOpen = ref(!data.value?.current)\n\n watch(selectedDayIndex, (index) => {\n if (index > 0) {\n dailyOpen.value = true\n }\n })\n\n const selectedDay = computed(\n () => data.value?.daily?.[selectedDayIndex.value],\n )\n\n /** Hourly entries filtered to the selected day's date */\n const filteredHourly = computed(() => {\n if (!data.value?.hourly?.length || !selectedDay.value) {\n return data.value?.hourly ?? []\n }\n const dayDate = selectedDay.value.date\n return data.value.hourly.filter((h) => h.time.startsWith(dayDate))\n })\n\n /**\n * Parses a date/time string in various formats the LLM might produce.\n * Tries ISO, space-separated, time-only, and common human-readable formats.\n */\n function safeDate(value: string): Date {\n // 1. Try native parsing (works for ISO 8601 and many standard formats)\n const direct = new Date(value)\n if (!Number.isNaN(direct.getTime())) {\n return direct\n }\n\n // 2. Replace space separator → T (\"2024-04-15 14:00\" → \"2024-04-15T14:00\")\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/.test(value)) {\n const withT = new Date(value.replace(' ', 'T'))\n if (!Number.isNaN(withT.getTime())) {\n return withT\n }\n }\n\n // 3. Time-only (\"14:00\" or \"14:00:00\") → today at that hour\n const timeOnly = value.match(/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/)\n if (timeOnly) {\n const today = new Date()\n today.setHours(\n Number(timeOnly[1]),\n Number(timeOnly[2]),\n Number(timeOnly[3] ?? 0),\n 0,\n )\n return today\n }\n\n // 4. Date-only (\"2024-04-15\") → noon to avoid timezone edge\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const noon = new Date(value + 'T12:00:00')\n if (!Number.isNaN(noon.getTime())) {\n return noon\n }\n }\n\n // Last resort: return an obviously invalid sentinel so callers can handle it\n return new Date(NaN)\n }\n\n /** Maps WMO weather code to an Iconify wi: icon name */\n function wmoToIcon(code: number): string {\n if (code === 0) {\n return 'wi:day-sunny'\n }\n if (code === 1) {\n return 'wi:day-sunny-overcast'\n }\n if (code === 2) {\n return 'wi:day-cloudy'\n }\n if (code === 3) {\n return 'wi:cloudy'\n }\n if (code === 45 || code === 48) {\n return 'wi:fog'\n }\n if (code >= 51 && code <= 55) {\n return 'wi:sprinkle'\n }\n if (code === 56 || code === 57) {\n return 'wi:sleet'\n }\n if (code >= 61 && code <= 65) {\n return 'wi:rain'\n }\n if (code === 66 || code === 67) {\n return 'wi:rain-mix'\n }\n if (code >= 71 && code <= 77) {\n return 'wi:snow'\n }\n if (code >= 80 && code <= 82) {\n return 'wi:showers'\n }\n if (code === 85 || code === 86) {\n return 'wi:snow-wind'\n }\n if (code === 95) {\n return 'wi:thunderstorm'\n }\n if (code === 96 || code === 99) {\n return 'wi:storm-showers'\n }\n return 'wi:na'\n }\n\n /**\n * Returns a formatted day label for daily forecast.\n * When daily has 1-2 entries, use the date string directly.\n * Otherwise, use index-based computation from today.\n */\n function formatDay(isoDate: string, index: number, total: number): string {\n const parsed = safeDate(isoDate + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n const today = new Date()\n today.setHours(12, 0, 0, 0)\n const diffDays = Math.round(\n (parsed.getTime() - today.getTime()) / 86_400_000,\n )\n if (diffDays === 0) {\n return $t('weather.today')\n }\n return $d(parsed, 'weekday-day')\n }\n if (index === 0 && total <= 7) {\n return $t('weather.today')\n }\n const fallback = new Date()\n fallback.setDate(fallback.getDate() + index)\n return $d(fallback, 'weekday-day')\n }\n\n const headerDate = computed(() => {\n if (selectedDay.value) {\n const parsed = safeDate(selectedDay.value.date + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n return formatDay(\n selectedDay.value.date,\n selectedDayIndex.value,\n data.value.daily.length,\n )\n }\n }\n return formatDay(safeDate(new Date().toISOString().slice(0, 10)), 0, 1)\n })\n\n /** Returns AQI label and CSS class for European AQI */\n function aqiInfo(aqi: number): { label: string; class: string } {\n if (aqi <= 20) {\n return {\n label: $t('weather.aqiGood'),\n class: 'text-green-600 bg-green-50',\n }\n }\n if (aqi <= 40) {\n return {\n label: $t('weather.aqiFair'),\n class: 'text-lime-600 bg-lime-50',\n }\n }\n if (aqi <= 60) {\n return {\n label: $t('weather.aqiModerate'),\n class: 'text-yellow-600 bg-yellow-50',\n }\n }\n if (aqi <= 80) {\n return {\n label: $t('weather.aqiPoor'),\n class: 'text-orange-600 bg-orange-50',\n }\n }\n if (aqi <= 100) {\n return {\n label: $t('weather.aqiVeryPoor'),\n class: 'text-red-600 bg-red-50',\n }\n }\n return {\n label: $t('weather.aqiHazardous'),\n class: 'text-purple-700 bg-purple-50',\n }\n }\n\n /** Formats wind direction degrees to compass label */\n function windDirectionLabel(deg: number): string {\n const dirs = ['N', 'NE', 'E', 'SE', 'S', 'SO', 'O', 'NO']\n return dirs[Math.round(deg / 45) % 8]\n }\n</script>\n\n<template>\n <div\n v-if=\"data\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <!-- Header -->\n <div\n class=\"px-sm py-6 bg-surface-1 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:temp-cold-line\" class=\"text-16\" />\n <strong class=\"font-bold truncate\">\n {{ data.locationName }}\n </strong>\n <span class=\"ml-auto shrink-0 capitalize\">\n {{ headerDate }}\n </span>\n </div>\n\n <!-- Current conditions (only when real-time current is available AND first day is selected) -->\n <div\n v-if=\"data.current && selectedDayIndex === 0\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(data.current.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <span class=\"text-48 font-light leading-none text-word-1\">\n {{ $n(data.current.temperature, 'integer') }}&deg;\n </span>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div class=\"flex items-center gap-6\">\n {{ $t('weather.feelsLike') }}\n {{ $n(data.current.feelsLike, 'integer') }}&deg;\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:water-percent-line\" class=\"text-14\" />\n {{ data.current.humidity }}%\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14\" />\n {{ $n(data.current.windSpeed, 'integer') }} km/h\n {{ windDirectionLabel(data.current.windDirection) }}\n </div>\n <div\n v-if=\"data.current.uvIndex > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(data.current.uvIndex, 'integer') }}\n </div>\n </div>\n </div>\n\n <!-- Day summary (when no real-time current, or a non-today day is selected) -->\n <div\n v-else-if=\"selectedDay\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(selectedDay.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <div class=\"flex flex-col leading-none\">\n <span class=\"text-48 font-light text-word-1\">\n {{ $n(selectedDay.maxTemp, 'integer') }}&deg;\n </span>\n <span class=\"text-20 text-word-3\">\n {{ $n(selectedDay.minTemp, 'integer') }}&deg;\n </span>\n </div>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div\n v-if=\"selectedDay.precipitationProbabilityMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:drop-line\" class=\"text-14\" />\n {{ selectedDay.precipitationProbabilityMax }}% ({{\n selectedDay.precipitationSum\n }}\n mm)\n </div>\n <div\n v-if=\"selectedDay.uvIndexMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(selectedDay.uvIndexMax, 'integer') }}\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-foggy-line\" class=\"text-14\" />\n {{ $d(safeDate(selectedDay.sunrise), 'time') }}\n –\n {{ $d(safeDate(selectedDay.sunset), 'time') }}\n </div>\n </div>\n </div>\n\n <!-- Hourly forecast (filtered to selected day) -->\n <transition>\n <div\n v-if=\"filteredHourly.length > 0\"\n class=\"border-t border-surface-3\">\n <div\n class=\"flex overflow-x-auto gap-2 px-sm py-8 light-scrollbar\">\n <div\n v-for=\"hour in filteredHourly\"\n :key=\"hour.time\"\n class=\"flex flex-col items-center gap-4 min-w-48 px-4 py-6 rounded-lg text-11 text-word-3 shrink-0\">\n <span class=\"text-word-3\">{{\n Number.isNaN(safeDate(hour.time).getTime())\n ? hour.time\n : $d(safeDate(hour.time), 'time')\n }}</span>\n <VvIcon\n :name=\"wmoToIcon(hour.weatherCode)\"\n class=\"text-20 text-word-2\" />\n <span class=\"font-medium text-word-1\">\n {{ $n(hour.temperature, 'integer') }}&deg;\n </span>\n <span\n v-if=\"hour.precipitationProbability > 0\"\n class=\"text-word-5 text-smaller\">\n {{ hour.precipitationProbability }}%\n </span>\n </div>\n </div>\n </div>\n </transition>\n\n <!-- Daily forecast (clickable, inside accordion) -->\n <transition>\n <VvAccordion\n v-if=\"data.daily.length > 1\"\n v-model=\"dailyOpen\"\n modifiers=\"details no-padding\"\n :title=\"$t('weather.dailyForecast')\"\n class=\"border-t border-surface-3\">\n <button\n v-for=\"(day, index) in data.daily\"\n :key=\"day.date\"\n type=\"button\"\n class=\"flex items-center gap-sm px-sm py-8 text-12 w-full text-left transition-colors cursor-pointer border-t border-surface-3\"\n :class=\"\n index === selectedDayIndex\n ? 'bg-surface-2'\n : 'hover:bg-surface-1'\n \"\n @click=\"selectedDayIndex = index\">\n <span class=\"w-48 shrink-0 text-word-3 capitalize\">\n {{ formatDay(day.date, index, data.daily.length) }}\n </span>\n <VvIcon\n :name=\"wmoToIcon(day.weatherCode)\"\n class=\"text-18 text-word-2 shrink-0\" />\n <span\n v-if=\"day.precipitationProbabilityMax > 0\"\n class=\"text-word-5 text-11 w-32 shrink-0\">\n {{ day.precipitationProbabilityMax }}%\n </span>\n <span v-else class=\"w-32 shrink-0\" />\n <div class=\"ml-auto flex items-center gap-8 text-word-3\">\n <span class=\"text-word-1 font-semibold\">\n {{ $n(day.maxTemp, 'integer') }}&deg;\n </span>\n <span>{{ $n(day.minTemp, 'integer') }}&deg;</span>\n </div>\n </button>\n </VvAccordion>\n </transition>\n\n <!-- Air quality -->\n <div\n v-if=\"data.airQuality\"\n class=\"border-t border-surface-3 px-sm py-8 flex items-center gap-8 text-12\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14 text-word-3\" />\n <span class=\"text-word-3\">{{ $t('weather.airQuality') }}</span>\n <span\n :class=\"[\n 'ml-auto px-8 py-2 text-11 font-semibold',\n aqiInfo(data.airQuality.aqi).class,\n ]\">\n {{ aqiInfo(data.airQuality.aqi).label }}\n ({{ data.airQuality.aqi }})\n </span>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuDI,IAAM,IAAQ,GAIR,EAAE,GAAG,GAAI,GAAG,GAAI,GAAG,MAAO,GAAQ,EAAE,UAAU,UAAU,CAAA,EAExD,KAAW,QACN,EAAM,KAChB,EAEK,IAAO,QAAe,GAAS,MAAM,MAAK,EAE1C,IAAmB,EAAI,EAAC,EAExB,IAAY,EAAI,CAAC,EAAK,OAAO,QAAO;AAE1C,IAAM,IAAmB,MAAU;AAC/B,GAAI,IAAQ,MACR,EAAU,QAAQ;IAEzB;EAED,IAAM,IAAc,QACV,EAAK,OAAO,QAAQ,EAAiB,OAC/C,EAGM,IAAiB,QAAe;AAClC,OAAI,CAAC,EAAK,OAAO,QAAQ,UAAU,CAAC,EAAY,MAC5C,QAAO,EAAK,OAAO,UAAU,EAAC;GAElC,IAAM,IAAU,EAAY,MAAM;AAClC,UAAO,EAAK,MAAM,OAAO,QAAQ,MAAM,EAAE,KAAK,WAAW,EAAQ,CAAA;IACpE;EAMD,SAAS,EAAS,GAAqB;GAEnC,IAAM,IAAS,IAAI,KAAK,EAAK;AAC7B,OAAI,CAAC,OAAO,MAAM,EAAO,SAAS,CAAC,CAC/B,QAAO;AAIX,OAAI,iCAAiC,KAAK,EAAM,EAAE;IAC9C,IAAM,IAAQ,IAAI,KAAK,EAAM,QAAQ,KAAK,IAAI,CAAA;AAC9C,QAAI,CAAC,OAAO,MAAM,EAAM,SAAS,CAAC,CAC9B,QAAO;;GAKf,IAAM,IAAW,EAAM,MAAM,mCAAkC;AAC/D,OAAI,GAAU;IACV,IAAM,oBAAQ,IAAI,MAAK;AAOvB,WANA,EAAM,SACF,OAAO,EAAS,GAAG,EACnB,OAAO,EAAS,GAAG,EACnB,OAAO,EAAS,MAAM,EAAE,EACxB,EACJ,EACO;;AAIX,OAAI,sBAAsB,KAAK,EAAM,EAAE;IACnC,IAAM,oBAAO,IAAI,KAAK,IAAQ,YAAW;AACzC,QAAI,CAAC,OAAO,MAAM,EAAK,SAAS,CAAC,CAC7B,QAAO;;AAKf,0BAAO,IAAI,KAAK,IAAG;;EAIvB,SAAS,EAAU,GAAsB;AA2CrC,UA1CI,MAAS,IACF,iBAEP,MAAS,IACF,0BAEP,MAAS,IACF,kBAEP,MAAS,IACF,cAEP,MAAS,MAAM,MAAS,KACjB,WAEP,KAAQ,MAAM,KAAQ,KACf,gBAEP,MAAS,MAAM,MAAS,KACjB,aAEP,KAAQ,MAAM,KAAQ,KACf,YAEP,MAAS,MAAM,MAAS,KACjB,gBAEP,KAAQ,MAAM,KAAQ,KACf,YAEP,KAAQ,MAAM,KAAQ,KACf,eAEP,MAAS,MAAM,MAAS,KACjB,iBAEP,MAAS,KACF,oBAEP,MAAS,MAAM,MAAS,KACjB,qBAEJ;;EAQX,SAAS,EAAU,GAAiB,GAAe,GAAuB;GACtE,IAAM,IAAS,EAAS,IAAU,YAAW;AAC7C,OAAI,CAAC,OAAO,MAAM,EAAO,SAAS,CAAC,EAAE;IACjC,IAAM,oBAAQ,IAAI,MAAK;AAQvB,WAPA,EAAM,SAAS,IAAI,GAAG,GAAG,EAAC,EACT,KAAK,OACjB,EAAO,SAAS,GAAG,EAAM,SAAS,IAAI,MAEvC,KAAa,IACN,EAAG,gBAAe,GAEtB,EAAG,GAAQ,cAAa;;AAEnC,OAAI,MAAU,KAAK,KAAS,EACxB,QAAO,EAAG,gBAAe;GAE7B,IAAM,oBAAW,IAAI,MAAK;AAE1B,UADA,EAAS,QAAQ,EAAS,SAAS,GAAG,EAAK,EACpC,EAAG,GAAU,cAAa;;EAGrC,IAAM,KAAa,QAAe;AAC9B,OAAI,EAAY,OAAO;IACnB,IAAM,IAAS,EAAS,EAAY,MAAM,OAAO,YAAW;AAC5D,QAAI,CAAC,OAAO,MAAM,EAAO,SAAS,CAAC,CAC/B,QAAO,EACH,EAAY,MAAM,MAClB,EAAiB,OACjB,EAAK,MAAM,MAAM,OACrB;;AAGR,UAAO,EAAU,mBAAS,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAC;IACzE;EAGD,SAAS,EAAQ,GAA+C;AA+B5D,UA9BI,KAAO,KACA;IACH,OAAO,EAAG,kBAAkB;IAC5B,OAAO;IACX,GAEA,KAAO,KACA;IACH,OAAO,EAAG,kBAAkB;IAC5B,OAAO;IACX,GAEA,KAAO,KACA;IACH,OAAO,EAAG,sBAAsB;IAChC,OAAO;IACX,GAEA,KAAO,KACA;IACH,OAAO,EAAG,kBAAkB;IAC5B,OAAO;IACX,GAEA,KAAO,MACA;IACH,OAAO,EAAG,sBAAsB;IAChC,OAAO;IACX,GAEG;IACH,OAAO,EAAG,uBAAuB;IACjC,OAAO;IACX;;EAIJ,SAAS,GAAmB,GAAqB;AAE7C,UAAO;IADO;IAAK;IAAM;IAAK;IAAM;IAAK;IAAM;IAAK;IAC7C,CAAK,KAAK,MAAM,IAAM,GAAG,GAAG;;;;UAM7B,EAAA,SAAA,GAAA,EADV,EAoLM,OApLN,GAoLM;IAhLF,EASM,OATN,GASM;KAPF,EAAmD,GAAA;MAA3C,MAAK;MAAoB,OAAM;;KACvC,EAES,UAFT,IAES,EADF,EAAA,MAAK,aAAY,EAAA,EAAA;KAExB,EAEO,QAFP,IAEO,EADA,GAAA,MAAU,EAAA,EAAA;;IAMX,EAAA,MAAK,WAAW,EAAA,UAAgB,KAAA,GAAA,EAD1C,EAgCM,OAhCN,IAgCM,CA7BF,EAOM,OAPN,IAOM,CANF,EAE2C,GAAA;KADtC,MAAM,EAAU,EAAA,MAAK,QAAQ,YAAW;KACzC,OAAM;2BACV,EAEO,QAFP,IAEO,EADA,EAAA,EAAE,CAAC,EAAA,MAAK,QAAQ,aAAW,UAAA,CAAA,GAAe,MACjD,EAAA,CAAA,CAAA,EAEJ,EAoBM,OApBN,IAoBM;KAnBF,EAGM,OAHN,IAGM,EAFC,EAAA,EAAE,CAAA,oBAAA,CAAA,GAAwB,MAC7B,EAAG,EAAA,EAAE,CAAC,EAAA,MAAK,QAAQ,WAAS,UAAA,CAAA,GAAe,MAC/C,EAAA;KACA,EAGM,OAHN,IAGM,CAFF,EAAuD,GAAA;MAA/C,MAAK;MAAwB,OAAM;WAAY,MACvD,EAAG,EAAA,MAAK,QAAQ,SAAQ,GAAG,MAC/B,EAAA,CAAA,CAAA;KACA,EAIM,OAJN,IAIM,CAHF,EAA+C,GAAA;MAAvC,MAAK;MAAgB,OAAM;WAAY,MAC/C,EAAG,EAAA,EAAE,CAAC,EAAA,MAAK,QAAQ,WAAS,UAAA,CAAA,GAAe,WAC3C,EAAG,GAAmB,EAAA,MAAK,QAAQ,cAAa,CAAA,EAAA,EAAA,CAAA,CAAA;KAG1C,EAAA,MAAK,QAAQ,UAAO,KAAA,GAAA,EAD9B,EAKM,OALN,GAKM,CAFF,EAA6C,GAAA;MAArC,MAAK;MAAc,OAAM;WAAY,SAC1C,EAAG,EAAA,EAAE,CAAC,EAAA,MAAK,QAAQ,SAAO,UAAA,CAAA,EAAA,EAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA;YAO1B,EAAA,SAAA,GAAA,EADf,EAuCM,OAvCN,GAuCM,CApCF,EAYM,OAZN,GAYM,CAXF,EAE2C,GAAA;KADtC,MAAM,EAAU,EAAA,MAAY,YAAW;KACxC,OAAM;2BACV,EAOM,OAPN,GAOM,CANF,EAEO,QAFP,GAEO,EADA,EAAA,EAAE,CAAC,EAAA,MAAY,SAAO,UAAA,CAAA,GAAe,MAC5C,EAAA,EACA,EAEO,QAFP,GAEO,EADA,EAAA,EAAE,CAAC,EAAA,MAAY,SAAO,UAAA,CAAA,GAAe,MAC5C,EAAA,CAAA,CAAA,CAAA,CAAA,EAGR,EAsBM,OAtBN,GAsBM;KApBQ,EAAA,MAAY,8BAA2B,KAAA,GAAA,EADjD,EAQM,OARN,GAQM,CALF,EAA8C,GAAA;MAAtC,MAAK;MAAe,OAAM;WAAY,MAC9C,EAAG,EAAA,MAAY,4BAA2B,GAAG,QAAG,EAC5C,EAAA,MAAY,iBAAgB,GAC9B,SAEN,EAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA;KAEU,EAAA,MAAY,aAAU,KAAA,GAAA,EADhC,EAKM,OALN,GAKM,CAFF,EAA6C,GAAA;MAArC,MAAK;MAAc,OAAM;WAAY,SAC1C,EAAG,EAAA,EAAE,CAAC,EAAA,MAAY,YAAU,UAAA,CAAA,EAAA,EAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA;KAEnC,EAKM,OALN,GAKM,CAJF,EAAmD,GAAA;MAA3C,MAAK;MAAoB,OAAM;WAAY,MACnD,EAAG,EAAA,EAAE,CAAC,EAAS,EAAA,MAAY,QAAO,EAAA,OAAA,CAAA,GAAa,QAE/C,EAAG,EAAA,EAAE,CAAC,EAAS,EAAA,MAAY,OAAM,EAAA,OAAA,CAAA,EAAA,EAAA,CAAA,CAAA;;IAM7C,EA6Ba,GAAA,MAAA;sBADH,CA1BI,EAAA,MAAe,SAAM,KAAA,GAAA,EAD/B,EA2BM,OA3BN,GA2BM,CAxBF,EAuBM,OAvBN,GAuBM,EAAA,EAAA,GAAA,EArBF,EAoBM,GAAA,MAAA,EAnBa,EAAA,QAAR,YADX,EAoBM,OAAA;MAlBD,KAAK,EAAK;MACX,OAAM;;MACN,EAIS,QAJT,GAIS,EAHL,OAAO,MAAM,EAAS,EAAK,KAAI,CAAE,SAAO,CAAA,GAAsC,EAAK,OAAuC,EAAA,EAAE,CAAC,EAAS,EAAK,KAAI,EAAA,OAAA,CAAA,EAAA,EAAA;MAInJ,EAEkC,GAAA;OAD7B,MAAM,EAAU,EAAK,YAAW;OACjC,OAAM;;MACV,EAEO,QAFP,GAEO,EADA,EAAA,EAAE,CAAC,EAAK,aAAW,UAAA,CAAA,GAAe,MACzC,EAAA;MAEU,EAAK,2BAAwB,KAAA,GAAA,EADvC,EAIO,QAJP,GAIO,EADA,EAAK,yBAAwB,GAAG,MACvC,EAAA,IAAA,EAAA,IAAA,GAAA;;;;IAOhB,EAsCa,GAAA,MAAA;sBADK,CAnCJ,EAAA,MAAK,MAAM,SAAM,KAAA,GAAA,EAD3B,GAoCc,GAAA;;kBAlCD,EAAA;+CAAS,QAAA;MAClB,WAAU;MACT,OAAO,EAAA,EAAE,CAAA,wBAAA;MACV,OAAM;;uBAEgC,EAAA,EAAA,GAAA,EADtC,EA6BS,GAAA,MAAA,EA5BkB,EAAA,MAAK,QAApB,GAAK,YADjB,EA6BS,UAAA;OA3BJ,KAAK,EAAI;OACV,MAAK;OACL,OAAK,EAAA,CAAC,2HAC2B,MAAU,EAAA,QAAA,iBAAA,qBAAA,CAAA;OAK1C,UAAK,MAAE,EAAA,QAAmB;;OAC3B,EAEO,QAFP,GAEO,EADA,EAAU,EAAI,MAAM,GAAO,EAAA,MAAK,MAAM,OAAM,CAAA,EAAA,EAAA;OAEnD,EAE2C,GAAA;QADtC,MAAM,EAAU,EAAI,YAAW;QAChC,OAAM;;OAEA,EAAI,8BAA2B,KAAA,GAAA,EADzC,EAIO,QAJP,GAIO,EADA,EAAI,4BAA2B,GAAG,MACzC,EAAA,KAAA,GAAA,EACA,EAAqC,QAArC,EAAqC;OACrC,EAKM,OALN,IAKM,CAJF,EAEO,QAFP,GAEO,EADA,EAAA,EAAE,CAAC,EAAI,SAAO,UAAA,CAAA,GAAe,MACpC,EAAA,EACA,EAAkD,QAAA,MAAA,EAAzC,EAAA,EAAE,CAAC,EAAI,SAAO,UAAA,CAAA,GAAe,KAAK,EAAA,CAAA,CAAA;;;;;;IAQjD,EAAA,MAAK,cAAA,GAAA,EADf,EAaM,OAbN,GAaM;KAVF,EAA2D,GAAA;MAAnD,MAAK;MAAgB,OAAM;;KACnC,EAA+D,QAA/D,GAA+D,EAAlC,EAAA,EAAE,CAAA,qBAAA,CAAA,EAAA,EAAA;KAC/B,EAOO,QAAA,EANF,OAAK,EAAA,CAAA,2CAAuF,EAAQ,EAAA,MAAK,WAAW,IAAG,CAAE,MAAA,CAAA,EAAA,EAAA,EAIvH,EAAQ,EAAA,MAAK,WAAW,IAAG,CAAE,MAAK,GAAG,OACvC,EAAG,EAAA,MAAK,WAAW,IAAG,GAAG,MAC9B,EAAA"}
1
+ {"version":3,"file":"PkToolShowWeather-DR6egBIP.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolShowWeather.vue","../../../../packages/components/src/chat/PkToolShowWeather.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n\n interface WeatherCurrent {\n temperature: number\n feelsLike: number\n humidity: number\n precipitation: number\n weatherCode: number\n windSpeed: number\n windDirection: number\n uvIndex: number\n }\n\n interface WeatherHourly {\n time: string\n temperature: number\n precipitationProbability: number\n precipitation: number\n weatherCode: number\n windSpeed: number\n }\n\n interface WeatherDaily {\n date: string\n maxTemp: number\n minTemp: number\n weatherCode: number\n precipitationSum: number\n precipitationProbabilityMax: number\n uvIndexMax: number\n sunrise: string\n sunset: string\n }\n\n interface AirQuality {\n aqi: number\n pm10: number\n pm25: number\n no2: number\n ozone: number\n }\n\n interface WeatherData {\n locationName: string\n latitude: number\n longitude: number\n timezone: string\n current: WeatherCurrent | null\n hourly: WeatherHourly[]\n daily: WeatherDaily[]\n airQuality: AirQuality | null\n }\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { t: $t, d: $d, n: $n } = useI18n({ useScope: 'global' })\n\n const toolPart = computed(() => {\n return props.part as { input?: WeatherData }\n })\n\n const data = computed(() => toolPart.value.input)\n\n const selectedDayIndex = ref(0)\n\n const dailyOpen = ref(!data.value?.current)\n\n watch(selectedDayIndex, (index) => {\n if (index > 0) {\n dailyOpen.value = true\n }\n })\n\n const selectedDay = computed(\n () => data.value?.daily?.[selectedDayIndex.value],\n )\n\n /** Hourly entries filtered to the selected day's date */\n const filteredHourly = computed(() => {\n if (!data.value?.hourly?.length || !selectedDay.value) {\n return data.value?.hourly ?? []\n }\n const dayDate = selectedDay.value.date\n return data.value.hourly.filter((h) => h.time.startsWith(dayDate))\n })\n\n /**\n * Parses a date/time string in various formats the LLM might produce.\n * Tries ISO, space-separated, time-only, and common human-readable formats.\n */\n function safeDate(value: string): Date {\n // 1. Try native parsing (works for ISO 8601 and many standard formats)\n const direct = new Date(value)\n if (!Number.isNaN(direct.getTime())) {\n return direct\n }\n\n // 2. Replace space separator → T (\"2024-04-15 14:00\" → \"2024-04-15T14:00\")\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/.test(value)) {\n const withT = new Date(value.replace(' ', 'T'))\n if (!Number.isNaN(withT.getTime())) {\n return withT\n }\n }\n\n // 3. Time-only (\"14:00\" or \"14:00:00\") → today at that hour\n const timeOnly = value.match(/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/)\n if (timeOnly) {\n const today = new Date()\n today.setHours(\n Number(timeOnly[1]),\n Number(timeOnly[2]),\n Number(timeOnly[3] ?? 0),\n 0,\n )\n return today\n }\n\n // 4. Date-only (\"2024-04-15\") → noon to avoid timezone edge\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const noon = new Date(value + 'T12:00:00')\n if (!Number.isNaN(noon.getTime())) {\n return noon\n }\n }\n\n // Last resort: return an obviously invalid sentinel so callers can handle it\n return new Date(NaN)\n }\n\n /** Maps WMO weather code to an Iconify wi: icon name */\n function wmoToIcon(code: number): string {\n if (code === 0) {\n return 'wi:day-sunny'\n }\n if (code === 1) {\n return 'wi:day-sunny-overcast'\n }\n if (code === 2) {\n return 'wi:day-cloudy'\n }\n if (code === 3) {\n return 'wi:cloudy'\n }\n if (code === 45 || code === 48) {\n return 'wi:fog'\n }\n if (code >= 51 && code <= 55) {\n return 'wi:sprinkle'\n }\n if (code === 56 || code === 57) {\n return 'wi:sleet'\n }\n if (code >= 61 && code <= 65) {\n return 'wi:rain'\n }\n if (code === 66 || code === 67) {\n return 'wi:rain-mix'\n }\n if (code >= 71 && code <= 77) {\n return 'wi:snow'\n }\n if (code >= 80 && code <= 82) {\n return 'wi:showers'\n }\n if (code === 85 || code === 86) {\n return 'wi:snow-wind'\n }\n if (code === 95) {\n return 'wi:thunderstorm'\n }\n if (code === 96 || code === 99) {\n return 'wi:storm-showers'\n }\n return 'wi:na'\n }\n\n /**\n * Returns a formatted day label for daily forecast.\n * When daily has 1-2 entries, use the date string directly.\n * Otherwise, use index-based computation from today.\n */\n function formatDay(isoDate: string, index: number, total: number): string {\n const parsed = safeDate(isoDate + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n const today = new Date()\n today.setHours(12, 0, 0, 0)\n const diffDays = Math.round(\n (parsed.getTime() - today.getTime()) / 86_400_000,\n )\n if (diffDays === 0) {\n return $t('weather.today')\n }\n return $d(parsed, 'weekday-day')\n }\n if (index === 0 && total <= 7) {\n return $t('weather.today')\n }\n const fallback = new Date()\n fallback.setDate(fallback.getDate() + index)\n return $d(fallback, 'weekday-day')\n }\n\n const headerDate = computed(() => {\n if (selectedDay.value) {\n const parsed = safeDate(selectedDay.value.date + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n return formatDay(\n selectedDay.value.date,\n selectedDayIndex.value,\n data.value.daily.length,\n )\n }\n }\n return formatDay(safeDate(new Date().toISOString().slice(0, 10)), 0, 1)\n })\n\n /** Returns AQI label and CSS class for European AQI */\n function aqiInfo(aqi: number): { label: string; class: string } {\n if (aqi <= 20) {\n return {\n label: $t('weather.aqiGood'),\n class: 'text-green-600 bg-green-50',\n }\n }\n if (aqi <= 40) {\n return {\n label: $t('weather.aqiFair'),\n class: 'text-lime-600 bg-lime-50',\n }\n }\n if (aqi <= 60) {\n return {\n label: $t('weather.aqiModerate'),\n class: 'text-yellow-600 bg-yellow-50',\n }\n }\n if (aqi <= 80) {\n return {\n label: $t('weather.aqiPoor'),\n class: 'text-orange-600 bg-orange-50',\n }\n }\n if (aqi <= 100) {\n return {\n label: $t('weather.aqiVeryPoor'),\n class: 'text-red-600 bg-red-50',\n }\n }\n return {\n label: $t('weather.aqiHazardous'),\n class: 'text-purple-700 bg-purple-50',\n }\n }\n\n /** Formats wind direction degrees to compass label */\n function windDirectionLabel(deg: number): string {\n const dirs = ['N', 'NE', 'E', 'SE', 'S', 'SO', 'O', 'NO']\n return dirs[Math.round(deg / 45) % 8]\n }\n</script>\n\n<template>\n <div\n v-if=\"data\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <!-- Header -->\n <div\n class=\"px-sm py-6 bg-surface-1 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:temp-cold-line\" class=\"text-16\" />\n <strong class=\"font-bold truncate\">\n {{ data.locationName }}\n </strong>\n <span class=\"ml-auto shrink-0 capitalize\">\n {{ headerDate }}\n </span>\n </div>\n\n <!-- Current conditions (only when real-time current is available AND first day is selected) -->\n <div\n v-if=\"data.current && selectedDayIndex === 0\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(data.current.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <span class=\"text-48 font-light leading-none text-word-1\">\n {{ $n(data.current.temperature, 'integer') }}&deg;\n </span>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div class=\"flex items-center gap-6\">\n {{ $t('weather.feelsLike') }}\n {{ $n(data.current.feelsLike, 'integer') }}&deg;\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:water-percent-line\" class=\"text-14\" />\n {{ data.current.humidity }}%\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14\" />\n {{ $n(data.current.windSpeed, 'integer') }} km/h\n {{ windDirectionLabel(data.current.windDirection) }}\n </div>\n <div\n v-if=\"data.current.uvIndex > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(data.current.uvIndex, 'integer') }}\n </div>\n </div>\n </div>\n\n <!-- Day summary (when no real-time current, or a non-today day is selected) -->\n <div\n v-else-if=\"selectedDay\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(selectedDay.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <div class=\"flex flex-col leading-none\">\n <span class=\"text-48 font-light text-word-1\">\n {{ $n(selectedDay.maxTemp, 'integer') }}&deg;\n </span>\n <span class=\"text-20 text-word-3\">\n {{ $n(selectedDay.minTemp, 'integer') }}&deg;\n </span>\n </div>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div\n v-if=\"selectedDay.precipitationProbabilityMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:drop-line\" class=\"text-14\" />\n {{ selectedDay.precipitationProbabilityMax }}% ({{\n selectedDay.precipitationSum\n }}\n mm)\n </div>\n <div\n v-if=\"selectedDay.uvIndexMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(selectedDay.uvIndexMax, 'integer') }}\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-foggy-line\" class=\"text-14\" />\n {{ $d(safeDate(selectedDay.sunrise), 'time') }}\n –\n {{ $d(safeDate(selectedDay.sunset), 'time') }}\n </div>\n </div>\n </div>\n\n <!-- Hourly forecast (filtered to selected day) -->\n <transition>\n <div\n v-if=\"filteredHourly.length > 0\"\n class=\"border-t border-surface-3\">\n <div\n class=\"flex overflow-x-auto gap-2 px-sm py-8 light-scrollbar\">\n <div\n v-for=\"hour in filteredHourly\"\n :key=\"hour.time\"\n class=\"flex flex-col items-center gap-4 min-w-48 px-4 py-6 rounded-lg text-11 text-word-3 shrink-0\">\n <span class=\"text-word-3\">{{\n Number.isNaN(safeDate(hour.time).getTime())\n ? hour.time\n : $d(safeDate(hour.time), 'time')\n }}</span>\n <VvIcon\n :name=\"wmoToIcon(hour.weatherCode)\"\n class=\"text-20 text-word-2\" />\n <span class=\"font-medium text-word-1\">\n {{ $n(hour.temperature, 'integer') }}&deg;\n </span>\n <span\n v-if=\"hour.precipitationProbability > 0\"\n class=\"text-word-5 text-smaller\">\n {{ hour.precipitationProbability }}%\n </span>\n </div>\n </div>\n </div>\n </transition>\n\n <!-- Daily forecast (clickable, inside accordion) -->\n <transition>\n <VvAccordion\n v-if=\"data.daily.length > 1\"\n v-model=\"dailyOpen\"\n modifiers=\"details no-padding\"\n :title=\"$t('weather.dailyForecast')\"\n class=\"border-t border-surface-3\">\n <button\n v-for=\"(day, index) in data.daily\"\n :key=\"day.date\"\n type=\"button\"\n class=\"flex items-center gap-sm px-sm py-8 text-12 w-full text-left transition-colors cursor-pointer border-t border-surface-3\"\n :class=\"\n index === selectedDayIndex\n ? 'bg-surface-2'\n : 'hover:bg-surface-1'\n \"\n @click=\"selectedDayIndex = index\">\n <span class=\"w-48 shrink-0 text-word-3 capitalize\">\n {{ formatDay(day.date, index, data.daily.length) }}\n </span>\n <VvIcon\n :name=\"wmoToIcon(day.weatherCode)\"\n class=\"text-18 text-word-2 shrink-0\" />\n <span\n v-if=\"day.precipitationProbabilityMax > 0\"\n class=\"text-word-5 text-11 w-32 shrink-0\">\n {{ day.precipitationProbabilityMax }}%\n </span>\n <span v-else class=\"w-32 shrink-0\" />\n <div class=\"ml-auto flex items-center gap-8 text-word-3\">\n <span class=\"text-word-1 font-semibold\">\n {{ $n(day.maxTemp, 'integer') }}&deg;\n </span>\n <span>{{ $n(day.minTemp, 'integer') }}&deg;</span>\n </div>\n </button>\n </VvAccordion>\n </transition>\n\n <!-- Air quality -->\n <div\n v-if=\"data.airQuality\"\n class=\"border-t border-surface-3 px-sm py-8 flex items-center gap-8 text-12\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14 text-word-3\" />\n <span class=\"text-word-3\">{{ $t('weather.airQuality') }}</span>\n <span\n :class=\"[\n 'ml-auto px-8 py-2 text-11 font-semibold',\n aqiInfo(data.airQuality.aqi).class,\n ]\">\n {{ aqiInfo(data.airQuality.aqi).label }}\n ({{ data.airQuality.aqi }})\n </span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n\n interface WeatherCurrent {\n temperature: number\n feelsLike: number\n humidity: number\n precipitation: number\n weatherCode: number\n windSpeed: number\n windDirection: number\n uvIndex: number\n }\n\n interface WeatherHourly {\n time: string\n temperature: number\n precipitationProbability: number\n precipitation: number\n weatherCode: number\n windSpeed: number\n }\n\n interface WeatherDaily {\n date: string\n maxTemp: number\n minTemp: number\n weatherCode: number\n precipitationSum: number\n precipitationProbabilityMax: number\n uvIndexMax: number\n sunrise: string\n sunset: string\n }\n\n interface AirQuality {\n aqi: number\n pm10: number\n pm25: number\n no2: number\n ozone: number\n }\n\n interface WeatherData {\n locationName: string\n latitude: number\n longitude: number\n timezone: string\n current: WeatherCurrent | null\n hourly: WeatherHourly[]\n daily: WeatherDaily[]\n airQuality: AirQuality | null\n }\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { t: $t, d: $d, n: $n } = useI18n({ useScope: 'global' })\n\n const toolPart = computed(() => {\n return props.part as { input?: WeatherData }\n })\n\n const data = computed(() => toolPart.value.input)\n\n const selectedDayIndex = ref(0)\n\n const dailyOpen = ref(!data.value?.current)\n\n watch(selectedDayIndex, (index) => {\n if (index > 0) {\n dailyOpen.value = true\n }\n })\n\n const selectedDay = computed(\n () => data.value?.daily?.[selectedDayIndex.value],\n )\n\n /** Hourly entries filtered to the selected day's date */\n const filteredHourly = computed(() => {\n if (!data.value?.hourly?.length || !selectedDay.value) {\n return data.value?.hourly ?? []\n }\n const dayDate = selectedDay.value.date\n return data.value.hourly.filter((h) => h.time.startsWith(dayDate))\n })\n\n /**\n * Parses a date/time string in various formats the LLM might produce.\n * Tries ISO, space-separated, time-only, and common human-readable formats.\n */\n function safeDate(value: string): Date {\n // 1. Try native parsing (works for ISO 8601 and many standard formats)\n const direct = new Date(value)\n if (!Number.isNaN(direct.getTime())) {\n return direct\n }\n\n // 2. Replace space separator → T (\"2024-04-15 14:00\" → \"2024-04-15T14:00\")\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/.test(value)) {\n const withT = new Date(value.replace(' ', 'T'))\n if (!Number.isNaN(withT.getTime())) {\n return withT\n }\n }\n\n // 3. Time-only (\"14:00\" or \"14:00:00\") → today at that hour\n const timeOnly = value.match(/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/)\n if (timeOnly) {\n const today = new Date()\n today.setHours(\n Number(timeOnly[1]),\n Number(timeOnly[2]),\n Number(timeOnly[3] ?? 0),\n 0,\n )\n return today\n }\n\n // 4. Date-only (\"2024-04-15\") → noon to avoid timezone edge\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const noon = new Date(value + 'T12:00:00')\n if (!Number.isNaN(noon.getTime())) {\n return noon\n }\n }\n\n // Last resort: return an obviously invalid sentinel so callers can handle it\n return new Date(NaN)\n }\n\n /** Maps WMO weather code to an Iconify wi: icon name */\n function wmoToIcon(code: number): string {\n if (code === 0) {\n return 'wi:day-sunny'\n }\n if (code === 1) {\n return 'wi:day-sunny-overcast'\n }\n if (code === 2) {\n return 'wi:day-cloudy'\n }\n if (code === 3) {\n return 'wi:cloudy'\n }\n if (code === 45 || code === 48) {\n return 'wi:fog'\n }\n if (code >= 51 && code <= 55) {\n return 'wi:sprinkle'\n }\n if (code === 56 || code === 57) {\n return 'wi:sleet'\n }\n if (code >= 61 && code <= 65) {\n return 'wi:rain'\n }\n if (code === 66 || code === 67) {\n return 'wi:rain-mix'\n }\n if (code >= 71 && code <= 77) {\n return 'wi:snow'\n }\n if (code >= 80 && code <= 82) {\n return 'wi:showers'\n }\n if (code === 85 || code === 86) {\n return 'wi:snow-wind'\n }\n if (code === 95) {\n return 'wi:thunderstorm'\n }\n if (code === 96 || code === 99) {\n return 'wi:storm-showers'\n }\n return 'wi:na'\n }\n\n /**\n * Returns a formatted day label for daily forecast.\n * When daily has 1-2 entries, use the date string directly.\n * Otherwise, use index-based computation from today.\n */\n function formatDay(isoDate: string, index: number, total: number): string {\n const parsed = safeDate(isoDate + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n const today = new Date()\n today.setHours(12, 0, 0, 0)\n const diffDays = Math.round(\n (parsed.getTime() - today.getTime()) / 86_400_000,\n )\n if (diffDays === 0) {\n return $t('weather.today')\n }\n return $d(parsed, 'weekday-day')\n }\n if (index === 0 && total <= 7) {\n return $t('weather.today')\n }\n const fallback = new Date()\n fallback.setDate(fallback.getDate() + index)\n return $d(fallback, 'weekday-day')\n }\n\n const headerDate = computed(() => {\n if (selectedDay.value) {\n const parsed = safeDate(selectedDay.value.date + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n return formatDay(\n selectedDay.value.date,\n selectedDayIndex.value,\n data.value.daily.length,\n )\n }\n }\n return formatDay(safeDate(new Date().toISOString().slice(0, 10)), 0, 1)\n })\n\n /** Returns AQI label and CSS class for European AQI */\n function aqiInfo(aqi: number): { label: string; class: string } {\n if (aqi <= 20) {\n return {\n label: $t('weather.aqiGood'),\n class: 'text-green-600 bg-green-50',\n }\n }\n if (aqi <= 40) {\n return {\n label: $t('weather.aqiFair'),\n class: 'text-lime-600 bg-lime-50',\n }\n }\n if (aqi <= 60) {\n return {\n label: $t('weather.aqiModerate'),\n class: 'text-yellow-600 bg-yellow-50',\n }\n }\n if (aqi <= 80) {\n return {\n label: $t('weather.aqiPoor'),\n class: 'text-orange-600 bg-orange-50',\n }\n }\n if (aqi <= 100) {\n return {\n label: $t('weather.aqiVeryPoor'),\n class: 'text-red-600 bg-red-50',\n }\n }\n return {\n label: $t('weather.aqiHazardous'),\n class: 'text-purple-700 bg-purple-50',\n }\n }\n\n /** Formats wind direction degrees to compass label */\n function windDirectionLabel(deg: number): string {\n const dirs = ['N', 'NE', 'E', 'SE', 'S', 'SO', 'O', 'NO']\n return dirs[Math.round(deg / 45) % 8]\n }\n</script>\n\n<template>\n <div\n v-if=\"data\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <!-- Header -->\n <div\n class=\"px-sm py-6 bg-surface-1 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:temp-cold-line\" class=\"text-16\" />\n <strong class=\"font-bold truncate\">\n {{ data.locationName }}\n </strong>\n <span class=\"ml-auto shrink-0 capitalize\">\n {{ headerDate }}\n </span>\n </div>\n\n <!-- Current conditions (only when real-time current is available AND first day is selected) -->\n <div\n v-if=\"data.current && selectedDayIndex === 0\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(data.current.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <span class=\"text-48 font-light leading-none text-word-1\">\n {{ $n(data.current.temperature, 'integer') }}&deg;\n </span>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div class=\"flex items-center gap-6\">\n {{ $t('weather.feelsLike') }}\n {{ $n(data.current.feelsLike, 'integer') }}&deg;\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:water-percent-line\" class=\"text-14\" />\n {{ data.current.humidity }}%\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14\" />\n {{ $n(data.current.windSpeed, 'integer') }} km/h\n {{ windDirectionLabel(data.current.windDirection) }}\n </div>\n <div\n v-if=\"data.current.uvIndex > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(data.current.uvIndex, 'integer') }}\n </div>\n </div>\n </div>\n\n <!-- Day summary (when no real-time current, or a non-today day is selected) -->\n <div\n v-else-if=\"selectedDay\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(selectedDay.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <div class=\"flex flex-col leading-none\">\n <span class=\"text-48 font-light text-word-1\">\n {{ $n(selectedDay.maxTemp, 'integer') }}&deg;\n </span>\n <span class=\"text-20 text-word-3\">\n {{ $n(selectedDay.minTemp, 'integer') }}&deg;\n </span>\n </div>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div\n v-if=\"selectedDay.precipitationProbabilityMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:drop-line\" class=\"text-14\" />\n {{ selectedDay.precipitationProbabilityMax }}% ({{\n selectedDay.precipitationSum\n }}\n mm)\n </div>\n <div\n v-if=\"selectedDay.uvIndexMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(selectedDay.uvIndexMax, 'integer') }}\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-foggy-line\" class=\"text-14\" />\n {{ $d(safeDate(selectedDay.sunrise), 'time') }}\n –\n {{ $d(safeDate(selectedDay.sunset), 'time') }}\n </div>\n </div>\n </div>\n\n <!-- Hourly forecast (filtered to selected day) -->\n <transition>\n <div\n v-if=\"filteredHourly.length > 0\"\n class=\"border-t border-surface-3\">\n <div\n class=\"flex overflow-x-auto gap-2 px-sm py-8 light-scrollbar\">\n <div\n v-for=\"hour in filteredHourly\"\n :key=\"hour.time\"\n class=\"flex flex-col items-center gap-4 min-w-48 px-4 py-6 rounded-lg text-11 text-word-3 shrink-0\">\n <span class=\"text-word-3\">{{\n Number.isNaN(safeDate(hour.time).getTime())\n ? hour.time\n : $d(safeDate(hour.time), 'time')\n }}</span>\n <VvIcon\n :name=\"wmoToIcon(hour.weatherCode)\"\n class=\"text-20 text-word-2\" />\n <span class=\"font-medium text-word-1\">\n {{ $n(hour.temperature, 'integer') }}&deg;\n </span>\n <span\n v-if=\"hour.precipitationProbability > 0\"\n class=\"text-word-5 text-smaller\">\n {{ hour.precipitationProbability }}%\n </span>\n </div>\n </div>\n </div>\n </transition>\n\n <!-- Daily forecast (clickable, inside accordion) -->\n <transition>\n <VvAccordion\n v-if=\"data.daily.length > 1\"\n v-model=\"dailyOpen\"\n modifiers=\"details no-padding\"\n :title=\"$t('weather.dailyForecast')\"\n class=\"border-t border-surface-3\">\n <button\n v-for=\"(day, index) in data.daily\"\n :key=\"day.date\"\n type=\"button\"\n class=\"flex items-center gap-sm px-sm py-8 text-12 w-full text-left transition-colors cursor-pointer border-t border-surface-3\"\n :class=\"\n index === selectedDayIndex\n ? 'bg-surface-2'\n : 'hover:bg-surface-1'\n \"\n @click=\"selectedDayIndex = index\">\n <span class=\"w-48 shrink-0 text-word-3 capitalize\">\n {{ formatDay(day.date, index, data.daily.length) }}\n </span>\n <VvIcon\n :name=\"wmoToIcon(day.weatherCode)\"\n class=\"text-18 text-word-2 shrink-0\" />\n <span\n v-if=\"day.precipitationProbabilityMax > 0\"\n class=\"text-word-5 text-11 w-32 shrink-0\">\n {{ day.precipitationProbabilityMax }}%\n </span>\n <span v-else class=\"w-32 shrink-0\" />\n <div class=\"ml-auto flex items-center gap-8 text-word-3\">\n <span class=\"text-word-1 font-semibold\">\n {{ $n(day.maxTemp, 'integer') }}&deg;\n </span>\n <span>{{ $n(day.minTemp, 'integer') }}&deg;</span>\n </div>\n </button>\n </VvAccordion>\n </transition>\n\n <!-- Air quality -->\n <div\n v-if=\"data.airQuality\"\n class=\"border-t border-surface-3 px-sm py-8 flex items-center gap-8 text-12\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14 text-word-3\" />\n <span class=\"text-word-3\">{{ $t('weather.airQuality') }}</span>\n <span\n :class=\"[\n 'ml-auto px-8 py-2 text-11 font-semibold',\n aqiInfo(data.airQuality.aqi).class,\n ]\">\n {{ aqiInfo(data.airQuality.aqi).label }}\n ({{ data.airQuality.aqi }})\n </span>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { C as e, E as t, Gt as n, V as r, _ as i, b as a, ft as o, h as s, v as c, w as l, x as u, yt as d, z as f } from "./vue.runtime.esm-bundler-BmggS4HU.js";
2
- import { i as p } from "./index.es-Z3Hu-QIb.js";
2
+ import { o as p } from "./index.es-BGFmj59S.js";
3
3
  import { n as m } from "./src-C_wl-KYN.js";
4
4
  import { t as h } from "./PkUrl-CI17WkYu.js";
5
5
  //#region ../../packages/components/src/chat/PkToolShowWebPages.vue?vue&type=script&setup=true&lang.ts
@@ -58,4 +58,4 @@ var g = {
58
58
  //#endregion
59
59
  export { w as default };
60
60
 
61
- //# sourceMappingURL=PkToolShowWebPages-BCqywwEK.js.map
61
+ //# sourceMappingURL=PkToolShowWebPages-D8RngE-F.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowWebPages-BCqywwEK.js","names":["$t"],"sources":["../../../../packages/components/src/chat/PkToolShowWebPages.vue","../../../../packages/components/src/chat/PkToolShowWebPages.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isSameSite } from 'utils'\n import PkUrl from '../PkUrl.vue'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n pages: Array<{\n title: string\n url: string\n image?: string\n description?: string\n }>\n }\n }\n return part\n })\n\n const showMore = ref(false)\n const pages = computed(() => {\n if (showMore.value) {\n return toolPart.value.input?.pages || []\n }\n return (toolPart.value.input?.pages || []).slice(0, 2)\n })\n const hasMore = computed(\n () => (toolPart.value.input?.pages || []).length > 2,\n )\n</script>\n\n<template>\n <ul v-if=\"pages?.length\" class=\"flex flex-col gap-xs w-full\">\n <li\n v-for=\"(page, index) in pages\"\n :key=\"index\"\n class=\"border border-surface-3 rounded-lg overflow-hidden hover:border-surface-4 transition-colors shrink-0\">\n <a\n :href=\"page.url\"\n :title=\"$t('action.open')\"\n :target=\"isSameSite(page.url) ? undefined : '_blank'\"\n :rel=\"isSameSite(page.url) ? undefined : 'noopener noreferrer'\"\n class=\"flex gap-8 p-8 hover:bg-surface-1 transition-colors\">\n <img\n v-if=\"page.image\"\n :src=\"page.image\"\n :alt=\"page.title\"\n class=\"w-48 h-48 object-contain rounded shrink-0 border border-surface-3 bg-surface\" />\n <div class=\"flex flex-col gap-4 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate\">\n {{ page.title }}\n </strong>\n <span\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-12\" />\n <PkUrl :url=\"page.url\" />\n </span>\n <p\n v-if=\"page.description\"\n class=\"text-12 text-word-3 line-clamp-2\">\n {{ page.description }}\n </p>\n </div>\n </a>\n </li>\n <li v-if=\"hasMore\" class=\"text-center\">\n <button\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"\n showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\n \"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: (toolPart?.input?.pages?.length || 0) - 2,\n })\n }}\n </button>\n </li>\n </ul>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isSameSite } from 'utils'\n import PkUrl from '../PkUrl.vue'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n pages: Array<{\n title: string\n url: string\n image?: string\n description?: string\n }>\n }\n }\n return part\n })\n\n const showMore = ref(false)\n const pages = computed(() => {\n if (showMore.value) {\n return toolPart.value.input?.pages || []\n }\n return (toolPart.value.input?.pages || []).slice(0, 2)\n })\n const hasMore = computed(\n () => (toolPart.value.input?.pages || []).length > 2,\n )\n</script>\n\n<template>\n <ul v-if=\"pages?.length\" class=\"flex flex-col gap-xs w-full\">\n <li\n v-for=\"(page, index) in pages\"\n :key=\"index\"\n class=\"border border-surface-3 rounded-lg overflow-hidden hover:border-surface-4 transition-colors shrink-0\">\n <a\n :href=\"page.url\"\n :title=\"$t('action.open')\"\n :target=\"isSameSite(page.url) ? undefined : '_blank'\"\n :rel=\"isSameSite(page.url) ? undefined : 'noopener noreferrer'\"\n class=\"flex gap-8 p-8 hover:bg-surface-1 transition-colors\">\n <img\n v-if=\"page.image\"\n :src=\"page.image\"\n :alt=\"page.title\"\n class=\"w-48 h-48 object-contain rounded shrink-0 border border-surface-3 bg-surface\" />\n <div class=\"flex flex-col gap-4 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate\">\n {{ page.title }}\n </strong>\n <span\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-12\" />\n <PkUrl :url=\"page.url\" />\n </span>\n <p\n v-if=\"page.description\"\n class=\"text-12 text-word-3 line-clamp-2\">\n {{ page.description }}\n </p>\n </div>\n </a>\n </li>\n <li v-if=\"hasMore\" class=\"text-center\">\n <button\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"\n showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\n \"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: (toolPart?.input?.pages?.length || 0) - 2,\n })\n }}\n </button>\n </li>\n </ul>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EAKI,IAAM,IAAQ,GAIR,IAAW,QACA,EAAM,KAWtB,EAEK,IAAW,EAAI,GAAK,EACpB,IAAQ,QACN,EAAS,QACF,EAAS,MAAM,OAAO,SAAS,EAAC,IAEnC,EAAS,MAAM,OAAO,SAAS,EAAE,EAAE,MAAM,GAAG,EAAC,CACxD,EACK,IAAU,SACL,EAAS,MAAM,OAAO,SAAS,EAAE,EAAE,SAAS,EACvD;;;UAIU,EAAA,OAAO,UAAA,GAAA,EAAjB,EAmDK,MAnDL,GAmDK,EAAA,EAAA,GAAA,EAlDD,EA+BK,GAAA,MAAA,EA9BuB,EAAA,QAAhB,GAAM,YADlB,EA+BK,MAAA;IA7BA,KAAK;IACN,OAAM;OACN,EA0BI,KAAA;IAzBC,MAAM,EAAK;IACX,OAAOA,EAAAA,GAAE,cAAA;IACT,QAAQ,EAAA,EAAU,CAAC,EAAK,IAAG,GAAI,KAAA,IAAS;IACxC,KAAK,EAAA,EAAU,CAAC,EAAK,IAAG,GAAI,KAAA,IAAS;IACtC,OAAM;OAEI,EAAK,SAAA,GAAA,EADf,EAI2F,OAAA;;IAFtF,KAAK,EAAK;IACV,KAAK,EAAK;IACX,OAAM;gCACV,EAcM,OAdN,GAcM;IAbF,EAES,UAFT,GAES,EADF,EAAK,MAAK,EAAA,EAAA;IAEjB,EAIO,QAJP,GAIO,CAFH,EAAkD,GAAA;KAA1C,MAAK;KAAU,OAAM;QAC7B,EAAyB,GAAA,EAAjB,KAAK,EAAK,KAAA,EAAA,MAAA,GAAA,CAAA,MAAA,CAAA,CAAA,CAAA;IAGZ,EAAK,eAAA,GAAA,EADf,EAII,KAJJ,GAII,EADG,EAAK,YAAW,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;0BAKzB,EAAA,SAAA,GAAA,EAAV,EAiBK,MAjBL,GAiBK,CAhBD,EAeS,UAAA;IAdL,OAAM;IACL,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,QAAQ,CAAI,EAAA;OACpB,EAIsB,GAAA;IAHjB,MAA+B,EAAA,QAAQ,uBAAA;IAGxC,OAAM;4BAAY,MACtB,EACI,EAAA,QAAmCA,EAAAA,GAAE,kBAAA,GAA8CA,EAAAA,GAAE,mBAAA,EAAA,QAA4D,EAAA,OAAU,OAAO,OAAO,UAAM,KAAA,GAAA,CAAA,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA"}
1
+ {"version":3,"file":"PkToolShowWebPages-D8RngE-F.js","names":["$t"],"sources":["../../../../packages/components/src/chat/PkToolShowWebPages.vue","../../../../packages/components/src/chat/PkToolShowWebPages.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isSameSite } from 'utils'\n import PkUrl from '../PkUrl.vue'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n pages: Array<{\n title: string\n url: string\n image?: string\n description?: string\n }>\n }\n }\n return part\n })\n\n const showMore = ref(false)\n const pages = computed(() => {\n if (showMore.value) {\n return toolPart.value.input?.pages || []\n }\n return (toolPart.value.input?.pages || []).slice(0, 2)\n })\n const hasMore = computed(\n () => (toolPart.value.input?.pages || []).length > 2,\n )\n</script>\n\n<template>\n <ul v-if=\"pages?.length\" class=\"flex flex-col gap-xs w-full\">\n <li\n v-for=\"(page, index) in pages\"\n :key=\"index\"\n class=\"border border-surface-3 rounded-lg overflow-hidden hover:border-surface-4 transition-colors shrink-0\">\n <a\n :href=\"page.url\"\n :title=\"$t('action.open')\"\n :target=\"isSameSite(page.url) ? undefined : '_blank'\"\n :rel=\"isSameSite(page.url) ? undefined : 'noopener noreferrer'\"\n class=\"flex gap-8 p-8 hover:bg-surface-1 transition-colors\">\n <img\n v-if=\"page.image\"\n :src=\"page.image\"\n :alt=\"page.title\"\n class=\"w-48 h-48 object-contain rounded shrink-0 border border-surface-3 bg-surface\" />\n <div class=\"flex flex-col gap-4 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate\">\n {{ page.title }}\n </strong>\n <span\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-12\" />\n <PkUrl :url=\"page.url\" />\n </span>\n <p\n v-if=\"page.description\"\n class=\"text-12 text-word-3 line-clamp-2\">\n {{ page.description }}\n </p>\n </div>\n </a>\n </li>\n <li v-if=\"hasMore\" class=\"text-center\">\n <button\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"\n showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\n \"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: (toolPart?.input?.pages?.length || 0) - 2,\n })\n }}\n </button>\n </li>\n </ul>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isSameSite } from 'utils'\n import PkUrl from '../PkUrl.vue'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n pages: Array<{\n title: string\n url: string\n image?: string\n description?: string\n }>\n }\n }\n return part\n })\n\n const showMore = ref(false)\n const pages = computed(() => {\n if (showMore.value) {\n return toolPart.value.input?.pages || []\n }\n return (toolPart.value.input?.pages || []).slice(0, 2)\n })\n const hasMore = computed(\n () => (toolPart.value.input?.pages || []).length > 2,\n )\n</script>\n\n<template>\n <ul v-if=\"pages?.length\" class=\"flex flex-col gap-xs w-full\">\n <li\n v-for=\"(page, index) in pages\"\n :key=\"index\"\n class=\"border border-surface-3 rounded-lg overflow-hidden hover:border-surface-4 transition-colors shrink-0\">\n <a\n :href=\"page.url\"\n :title=\"$t('action.open')\"\n :target=\"isSameSite(page.url) ? undefined : '_blank'\"\n :rel=\"isSameSite(page.url) ? undefined : 'noopener noreferrer'\"\n class=\"flex gap-8 p-8 hover:bg-surface-1 transition-colors\">\n <img\n v-if=\"page.image\"\n :src=\"page.image\"\n :alt=\"page.title\"\n class=\"w-48 h-48 object-contain rounded shrink-0 border border-surface-3 bg-surface\" />\n <div class=\"flex flex-col gap-4 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate\">\n {{ page.title }}\n </strong>\n <span\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-12\" />\n <PkUrl :url=\"page.url\" />\n </span>\n <p\n v-if=\"page.description\"\n class=\"text-12 text-word-3 line-clamp-2\">\n {{ page.description }}\n </p>\n </div>\n </a>\n </li>\n <li v-if=\"hasMore\" class=\"text-center\">\n <button\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"\n showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\n \"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: (toolPart?.input?.pages?.length || 0) - 2,\n })\n }}\n </button>\n </li>\n </ul>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EAKI,IAAM,IAAQ,GAIR,IAAW,QACA,EAAM,KAWtB,EAEK,IAAW,EAAI,GAAK,EACpB,IAAQ,QACN,EAAS,QACF,EAAS,MAAM,OAAO,SAAS,EAAC,IAEnC,EAAS,MAAM,OAAO,SAAS,EAAE,EAAE,MAAM,GAAG,EAAC,CACxD,EACK,IAAU,SACL,EAAS,MAAM,OAAO,SAAS,EAAE,EAAE,SAAS,EACvD;;;UAIU,EAAA,OAAO,UAAA,GAAA,EAAjB,EAmDK,MAnDL,GAmDK,EAAA,EAAA,GAAA,EAlDD,EA+BK,GAAA,MAAA,EA9BuB,EAAA,QAAhB,GAAM,YADlB,EA+BK,MAAA;IA7BA,KAAK;IACN,OAAM;OACN,EA0BI,KAAA;IAzBC,MAAM,EAAK;IACX,OAAOA,EAAAA,GAAE,cAAA;IACT,QAAQ,EAAA,EAAU,CAAC,EAAK,IAAG,GAAI,KAAA,IAAS;IACxC,KAAK,EAAA,EAAU,CAAC,EAAK,IAAG,GAAI,KAAA,IAAS;IACtC,OAAM;OAEI,EAAK,SAAA,GAAA,EADf,EAI2F,OAAA;;IAFtF,KAAK,EAAK;IACV,KAAK,EAAK;IACX,OAAM;gCACV,EAcM,OAdN,GAcM;IAbF,EAES,UAFT,GAES,EADF,EAAK,MAAK,EAAA,EAAA;IAEjB,EAIO,QAJP,GAIO,CAFH,EAAkD,GAAA;KAA1C,MAAK;KAAU,OAAM;QAC7B,EAAyB,GAAA,EAAjB,KAAK,EAAK,KAAA,EAAA,MAAA,GAAA,CAAA,MAAA,CAAA,CAAA,CAAA;IAGZ,EAAK,eAAA,GAAA,EADf,EAII,KAJJ,GAII,EADG,EAAK,YAAW,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;0BAKzB,EAAA,SAAA,GAAA,EAAV,EAiBK,MAjBL,GAiBK,CAhBD,EAeS,UAAA;IAdL,OAAM;IACL,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,QAAQ,CAAI,EAAA;OACpB,EAIsB,GAAA;IAHjB,MAA+B,EAAA,QAAQ,uBAAA;IAGxC,OAAM;4BAAY,MACtB,EACI,EAAA,QAAmCA,EAAAA,GAAE,kBAAA,GAA8CA,EAAAA,GAAE,mBAAA,EAAA,QAA4D,EAAA,OAAU,OAAO,OAAO,UAAM,KAAA,GAAA,CAAA,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA"}