@8wave/ai-elements 0.119.0-beta.59 → 0.119.0-beta.62

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 (108) hide show
  1. package/README.md +3 -0
  2. package/dist/_chunks/{PkChatbotFilePreview-Bdi4_IFu.js → PkChatbotFilePreview-C0uMvUlV.js} +2 -2
  3. package/dist/_chunks/{PkChatbotFilePreview-Bdi4_IFu.js.map → PkChatbotFilePreview-C0uMvUlV.js.map} +1 -1
  4. package/dist/_chunks/{PkHeldImage-eNPFmmdh.js → PkHeldImage-CBvcNMEM.js} +2 -2
  5. package/dist/_chunks/{PkHeldImage-eNPFmmdh.js.map → PkHeldImage-CBvcNMEM.js.map} +1 -1
  6. package/dist/_chunks/{PkLazyImg-DHPPSq3N.js → PkLazyImg-COy3jb6W.js} +2 -2
  7. package/dist/_chunks/{PkLazyImg-DHPPSq3N.js.map → PkLazyImg-COy3jb6W.js.map} +1 -1
  8. package/dist/_chunks/{PkToolEditSpreadsheet-Ct9pdQJ1.js → PkToolEditSpreadsheet-C8PN08y2.js} +2 -2
  9. package/dist/_chunks/{PkToolEditSpreadsheet-Ct9pdQJ1.js.map → PkToolEditSpreadsheet-C8PN08y2.js.map} +1 -1
  10. package/dist/_chunks/{PkToolRequestGeolocation-DzhrY8bg.js → PkToolRequestGeolocation-Cu1TSjHl.js} +2 -2
  11. package/dist/_chunks/{PkToolRequestGeolocation-DzhrY8bg.js.map → PkToolRequestGeolocation-Cu1TSjHl.js.map} +1 -1
  12. package/dist/_chunks/{PkToolRequestOAuthConnection-CzRKRGoG.js → PkToolRequestOAuthConnection-Bc2UX4MZ.js} +3 -3
  13. package/dist/_chunks/{PkToolRequestOAuthConnection-CzRKRGoG.js.map → PkToolRequestOAuthConnection-Bc2UX4MZ.js.map} +1 -1
  14. package/dist/_chunks/{PkToolShowArtifact-D6ZTw5vh.js → PkToolShowArtifact-B1YYNKqc.js} +2 -2
  15. package/dist/_chunks/{PkToolShowArtifact-D6ZTw5vh.js.map → PkToolShowArtifact-B1YYNKqc.js.map} +1 -1
  16. package/dist/_chunks/{PkToolShowCalendarEvent-D8DMh3-P.js → PkToolShowCalendarEvent-r0SuzJDT.js} +3 -3
  17. package/dist/_chunks/{PkToolShowCalendarEvent-D8DMh3-P.js.map → PkToolShowCalendarEvent-r0SuzJDT.js.map} +1 -1
  18. package/dist/_chunks/{PkToolShowComparison-DLC22e1W.js → PkToolShowComparison-Bsh6iiIQ.js} +3 -3
  19. package/dist/_chunks/{PkToolShowComparison-DLC22e1W.js.map → PkToolShowComparison-Bsh6iiIQ.js.map} +1 -1
  20. package/dist/_chunks/{PkToolShowContactForm-CW7gKQ7h.js → PkToolShowContactForm-B2UzmoDw.js} +4 -4
  21. package/dist/_chunks/{PkToolShowContactForm-CW7gKQ7h.js.map → PkToolShowContactForm-B2UzmoDw.js.map} +1 -1
  22. package/dist/_chunks/{PkToolShowDiagram-WregKAJO.js → PkToolShowDiagram-iQRTxxLw.js} +3 -3
  23. package/dist/_chunks/{PkToolShowDiagram-WregKAJO.js.map → PkToolShowDiagram-iQRTxxLw.js.map} +1 -1
  24. package/dist/_chunks/{PkToolShowEmail-C7mUCqGL.js → PkToolShowEmail-1pyuWnuN.js} +3 -3
  25. package/dist/_chunks/{PkToolShowEmail-C7mUCqGL.js.map → PkToolShowEmail-1pyuWnuN.js.map} +1 -1
  26. package/dist/_chunks/{PkToolShowForm-D_2FmDwq.js → PkToolShowForm-BRt9i4aW.js} +5 -5
  27. package/dist/_chunks/{PkToolShowForm-D_2FmDwq.js.map → PkToolShowForm-BRt9i4aW.js.map} +1 -1
  28. package/dist/_chunks/{PkToolShowImageGallery-KmMO4UuQ.js → PkToolShowImageGallery-BZ3uOhQq.js} +3 -3
  29. package/dist/_chunks/{PkToolShowImageGallery-KmMO4UuQ.js.map → PkToolShowImageGallery-BZ3uOhQq.js.map} +1 -1
  30. package/dist/_chunks/{PkToolShowLocation-jRZtqoBO.js → PkToolShowLocation-BIAPTY0s.js} +3 -3
  31. package/dist/_chunks/{PkToolShowLocation-jRZtqoBO.js.map → PkToolShowLocation-BIAPTY0s.js.map} +1 -1
  32. package/dist/_chunks/{PkToolShowMessage-vw4XS4TR.js → PkToolShowMessage-BM3TU9SP.js} +3 -3
  33. package/dist/_chunks/{PkToolShowMessage-vw4XS4TR.js.map → PkToolShowMessage-BM3TU9SP.js.map} +1 -1
  34. package/dist/_chunks/{PkToolShowProductList-cld-FOFb.js → PkToolShowProductList-C4a7R9yQ.js} +4 -4
  35. package/dist/_chunks/{PkToolShowProductList-cld-FOFb.js.map → PkToolShowProductList-C4a7R9yQ.js.map} +1 -1
  36. package/dist/_chunks/{PkToolShowQrCode-BhcAUXSZ.js → PkToolShowQrCode-6zjcD50R.js} +3 -3
  37. package/dist/_chunks/{PkToolShowQrCode-BhcAUXSZ.js.map → PkToolShowQrCode-6zjcD50R.js.map} +1 -1
  38. package/dist/_chunks/{PkToolShowSources-DyzheirE.js → PkToolShowSources-CgZRJ1gU.js} +3 -3
  39. package/dist/_chunks/{PkToolShowSources-DyzheirE.js.map → PkToolShowSources-CgZRJ1gU.js.map} +1 -1
  40. package/dist/_chunks/{PkToolShowSuggestedReply-xUBuqStM.js → PkToolShowSuggestedReply-nHsdJfbX.js} +2 -2
  41. package/dist/_chunks/{PkToolShowSuggestedReply-xUBuqStM.js.map → PkToolShowSuggestedReply-nHsdJfbX.js.map} +1 -1
  42. package/dist/_chunks/{PkToolShowVideo-KDNxItWl.js → PkToolShowVideo-BxHrmc_4.js} +3 -3
  43. package/dist/_chunks/{PkToolShowVideo-KDNxItWl.js.map → PkToolShowVideo-BxHrmc_4.js.map} +1 -1
  44. package/dist/_chunks/{PkToolShowWeather-DTrZ7r1t.js → PkToolShowWeather-CNvVC6nZ.js} +2 -2
  45. package/dist/_chunks/{PkToolShowWeather-DTrZ7r1t.js.map → PkToolShowWeather-CNvVC6nZ.js.map} +1 -1
  46. package/dist/_chunks/{PkToolShowWebPages-D35B7GzS.js → PkToolShowWebPages-CRM3rPp6.js} +4 -4
  47. package/dist/_chunks/{PkToolShowWebPages-D35B7GzS.js.map → PkToolShowWebPages-CRM3rPp6.js.map} +1 -1
  48. package/dist/_chunks/{VvButtonLocalized-DV-zbH9b.js → VvButtonLocalized-PfbIEwav.js} +2 -2
  49. package/dist/_chunks/{VvButtonLocalized-DV-zbH9b.js.map → VvButtonLocalized-PfbIEwav.js.map} +1 -1
  50. package/dist/_chunks/{VvCheckbox.es-VtDFxWJM.js → VvCheckbox.es-CtWUn81l.js} +3 -3
  51. package/dist/_chunks/VvCheckbox.es-CtWUn81l.js.map +1 -0
  52. package/dist/_chunks/{VvCheckboxGroup.es-D7MifHZN.js → VvCheckboxGroup.es-DjcwsgD8.js} +3 -3
  53. package/dist/_chunks/VvCheckboxGroup.es-DjcwsgD8.js.map +1 -0
  54. package/dist/_chunks/{VvCombobox.es-Brn06uOj.js → VvCombobox.es-DKR6Fb2W.js} +339 -304
  55. package/dist/_chunks/VvCombobox.es-DKR6Fb2W.js.map +1 -0
  56. package/dist/_chunks/{VvInputText.es-Co9NrR4l.js → VvInputText.es-6IlsXl00.js} +315 -275
  57. package/dist/_chunks/VvInputText.es-6IlsXl00.js.map +1 -0
  58. package/dist/_chunks/{VvRadio.es-BxHgVeVy.js → VvRadio.es-GQuMOlYC.js} +3 -3
  59. package/dist/_chunks/VvRadio.es-GQuMOlYC.js.map +1 -0
  60. package/dist/_chunks/{VvRadioGroup.es-DZtXJGCi.js → VvRadioGroup.es-ct4eQCRN.js} +3 -3
  61. package/dist/_chunks/VvRadioGroup.es-ct4eQCRN.js.map +1 -0
  62. package/dist/_chunks/{VvSelect.es-BR8f0LUN.js → VvSelect.es-CQ2SG0HY.js} +3 -3
  63. package/dist/_chunks/VvSelect.es-CQ2SG0HY.js.map +1 -0
  64. package/dist/_chunks/{VvTextarea.es-TXhUgEuG.js → VvTextarea.es-BcMLwglK.js} +225 -190
  65. package/dist/_chunks/VvTextarea.es-BcMLwglK.js.map +1 -0
  66. package/dist/_chunks/{index.es-dTcgyeM4.js → index.es-D7jsz1j2.js} +254 -210
  67. package/dist/_chunks/index.es-D7jsz1j2.js.map +1 -0
  68. package/dist/_chunks/{index.es-DkFMpDsf.js → index.es-DmG03ICf.js} +10 -10
  69. package/dist/_chunks/{index.es-DkFMpDsf.js.map → index.es-DmG03ICf.js.map} +1 -1
  70. package/dist/ai-elements.es.js +1698 -1643
  71. package/dist/ai-elements.es.js.map +1 -1
  72. package/dist-vue/PkChatbot.js +1 -1
  73. package/dist-vue/PkChatbotAuth.js +1 -1
  74. package/dist-vue/PkChatbotFeedbackForm.js +1 -1
  75. package/dist-vue/PkChatbotMessages.js +1 -1
  76. package/dist-vue/PkChatbotViewChat.js +1 -1
  77. package/dist-vue/_chunks/{PkChatbot-Bgw5K9w1.js → PkChatbot-BRfoWvHX.js} +2 -2
  78. package/dist-vue/_chunks/{PkChatbot-Bgw5K9w1.js.map → PkChatbot-BRfoWvHX.js.map} +1 -1
  79. package/dist-vue/_chunks/{PkChatbotAuth-CHoEXxmg.js → PkChatbotAuth-BsxfxDgR.js} +2 -2
  80. package/dist-vue/_chunks/{PkChatbotAuth-CHoEXxmg.js.map → PkChatbotAuth-BsxfxDgR.js.map} +1 -1
  81. package/dist-vue/_chunks/{PkChatbotFeedbackForm-B5cYsWBr.js → PkChatbotFeedbackForm-pQc2JrQT.js} +2 -2
  82. package/dist-vue/_chunks/{PkChatbotFeedbackForm-B5cYsWBr.js.map → PkChatbotFeedbackForm-pQc2JrQT.js.map} +1 -1
  83. package/dist-vue/_chunks/{PkChatbotMessages-B2e1MQ_d.js → PkChatbotMessages-B6EEvqZy.js} +3 -3
  84. package/dist-vue/_chunks/{PkChatbotMessages-B2e1MQ_d.js.map → PkChatbotMessages-B6EEvqZy.js.map} +1 -1
  85. package/dist-vue/_chunks/{PkChatbotViewChat-DEpMIiic.js → PkChatbotViewChat-PdHUXyCz.js} +3 -3
  86. package/dist-vue/_chunks/{PkChatbotViewChat-DEpMIiic.js.map → PkChatbotViewChat-PdHUXyCz.js.map} +1 -1
  87. package/dist-vue/_chunks/{PkToolShowContactForm-BjE4mrDv.js → PkToolShowContactForm-CBuw96Vr.js} +2 -2
  88. package/dist-vue/_chunks/{PkToolShowContactForm-BjE4mrDv.js.map → PkToolShowContactForm-CBuw96Vr.js.map} +1 -1
  89. package/dist-vue/_chunks/{index.es-C1u3kP7i.js → index.es-CY6tu8qg.js} +2 -2
  90. package/dist-vue/_chunks/{index.es-C1u3kP7i.js.map → index.es-CY6tu8qg.js.map} +1 -1
  91. package/dist-vue/apps/web-component/src/composables/useChatbotAuth.d.ts +36 -0
  92. package/dist-vue/composables.js +217 -164
  93. package/dist-vue/composables.js.map +1 -1
  94. package/dist-vue/index.js +8 -8
  95. package/dist-vue/index.js.map +1 -1
  96. package/dist-vue/packages/auth/src/external-auth-plugin.d.ts +22 -0
  97. package/dist-vue/packages/auth/src/external-identity.d.ts +64 -0
  98. package/dist-vue/packages/models/src/schema/McpServer.d.ts +19 -3
  99. package/package.json +2 -2
  100. package/dist/_chunks/VvCheckbox.es-VtDFxWJM.js.map +0 -1
  101. package/dist/_chunks/VvCheckboxGroup.es-D7MifHZN.js.map +0 -1
  102. package/dist/_chunks/VvCombobox.es-Brn06uOj.js.map +0 -1
  103. package/dist/_chunks/VvInputText.es-Co9NrR4l.js.map +0 -1
  104. package/dist/_chunks/VvRadio.es-BxHgVeVy.js.map +0 -1
  105. package/dist/_chunks/VvRadioGroup.es-DZtXJGCi.js.map +0 -1
  106. package/dist/_chunks/VvSelect.es-BR8f0LUN.js.map +0 -1
  107. package/dist/_chunks/VvTextarea.es-TXhUgEuG.js.map +0 -1
  108. package/dist/_chunks/index.es-dTcgyeM4.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowCalendarEvent-D8DMh3-P.js","names":["$t","__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","__unplugin_components_3","__unplugin_components_4","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_withCtx","_unref","_normalizeProps","_guardReactiveProps","_createTextVNode","_createCommentVNode","_sfc_main","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","guardReactiveProps","normalizeProps","createCommentVNode","createTextVNode","createBaseVNode","init_reactivity_esm_bundler","computed","createElementBlock","createVNode","useI18n","isSameSite","useClipboard","Qi","ru","Ae","ra","VvButtonLocalized","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","PkToolShowCalendarEvent_default","props","__props","toolPart","dateDisplay","input","start","startDate","end","endDate","startDay","endDay","allDay","d","calendarStart","calendarDay","calendarMonth","toIcsDateTime","iso","escapeIcs","str","generateIcs","ics","title","location","additionalInfo","blob","url","a","openGoogleCalendar","params","openOutlook","copyEvent","lines","copy","_component_VvIcon","_component_VvButton","_component_VvDropdownAction","_component_VvDropdown","_component_VvButtonGroup","_ctx","copied","_cache"],"sources":["../../../../packages/components/src/chat/PkToolShowCalendarEvent.vue","../../../../packages/components/src/chat/PkToolShowCalendarEvent.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useClipboard } from '@vueuse/core'\n import { isSameSite } from 'utils'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n const { d } = useI18n()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title: string\n startDate: string\n endDate: string\n location?: string\n additionalInfo?: string\n allDay?: boolean\n }\n }\n return part\n })\n\n const dateDisplay = computed(() => {\n const input = toolPart.value.input\n if (!input) {\n return ''\n }\n const { startDate, endDate, allDay } = input\n const start = new Date(startDate)\n const end = new Date(endDate)\n const startDay = startDate.slice(0, 10)\n const endDay = endDate.slice(0, 10)\n if (allDay) {\n if (startDay === endDay) {\n return d(start, 'date-medium')\n }\n return `${d(start, 'date-medium')} - ${d(end, 'date-medium')}`\n }\n if (startDay === endDay) {\n return `${d(start, 'date-time-medium')} - ${d(end, 'time')}`\n }\n return `${d(start, 'date-time-medium')} - ${d(end, 'date-time-medium')}`\n })\n\n const calendarStart = computed(() =>\n toolPart.value.input ? new Date(toolPart.value.input.startDate) : null,\n )\n\n const calendarDay = computed(\n () => calendarStart.value?.getDate().toString() ?? '',\n )\n\n const calendarMonth = computed(() =>\n calendarStart.value ? d(calendarStart.value, 'month-short') : '',\n )\n\n // Floating time: remove dashes/colons without converting to UTC\n const toIcsDateTime = (iso: string) =>\n iso.replace(/[-:]/g, '').replace(/\\.\\d{3}$/, '')\n\n const escapeIcs = (str: string) =>\n str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/;/g, '\\\\;')\n .replace(/,/g, '\\\\,')\n .replace(/\\n/g, '\\\\n')\n\n const generateIcs = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const ics = [\n 'BEGIN:VCALENDAR',\n 'VERSION:2.0',\n 'PRODID:-//8wave//8bot//EN',\n 'BEGIN:VEVENT',\n `DTSTART:${toIcsDateTime(startDate)}`,\n `DTEND:${toIcsDateTime(endDate)}`,\n `SUMMARY:${escapeIcs(title)}`,\n location ? `LOCATION:${escapeIcs(location)}` : '',\n additionalInfo ? `DESCRIPTION:${escapeIcs(additionalInfo)}` : '',\n `DTSTAMP:${new Date()\n .toISOString()\n .replace(/[-:]/g, '')\n .replace(/\\.\\d{3}/, '')}`,\n `UID:${crypto.randomUUID()}@8bot.ai`,\n 'END:VEVENT',\n 'END:VCALENDAR',\n ]\n .filter(Boolean)\n .join('\\r\\n')\n\n const blob = new Blob([ics], { type: 'text/calendar;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `${title.replace(/[^a-zA-Z0-9\\s-]/g, '')}.ics`\n a.click()\n URL.revokeObjectURL(url)\n }\n\n const openGoogleCalendar = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const start = toIcsDateTime(startDate)\n const end = toIcsDateTime(endDate)\n const params = new URLSearchParams({\n action: 'TEMPLATE',\n text: title,\n dates: `${start}/${end}`,\n })\n if (location) {\n params.set('location', location)\n }\n if (additionalInfo) {\n params.set('details', additionalInfo)\n }\n window.open(\n `https://calendar.google.com/calendar/render?${params.toString()}`,\n '_blank',\n )\n }\n\n const openOutlook = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const params = new URLSearchParams({\n subject: title,\n startdt: startDate,\n enddt: endDate,\n })\n if (location) {\n params.set('location', location)\n }\n if (additionalInfo) {\n params.set('body', additionalInfo)\n }\n window.open(\n `https://outlook.office.com/calendar/0/action/compose?${params.toString()}`,\n '_blank',\n )\n }\n\n const copyEvent = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const lines = [input.title, dateDisplay.value]\n if (input.location) {\n lines.push(input.location)\n }\n if (input.additionalInfo) {\n lines.push(input.additionalInfo)\n }\n copy(lines.join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:calendar-event-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.calendarEvent') }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyEvent\" />\n <VvDropdown\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n icon=\"ri:calendar-line\"\n modifiers=\"action-quiet-small\"\n :label=\"$t('action.addToCalendar')\" />\n <template #items>\n <VvDropdownAction @click=\"openGoogleCalendar\">\n <VvIcon name=\"ri:google-line\" />\n Google Calendar\n </VvDropdownAction>\n <VvDropdownAction @click=\"openOutlook\">\n <VvIcon name=\"ri:microsoft-line\" />\n Outlook\n </VvDropdownAction>\n <VvDropdownAction @click=\"generateIcs\">\n <VvIcon name=\"ri:download-line\" />\n {{ $t('action.downloadIcs') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </VvButtonGroup>\n </div>\n <div class=\"p-sm flex gap-sm items-start\">\n <div\n class=\"shrink-0 w-44 rounded-xl overflow-hidden border border-surface-3 text-center\">\n <div\n class=\"bg-brand px-8 py-2 text-brand-contrast text-10 font-bold uppercase tracking-wide\">\n {{ calendarMonth }}\n </div>\n <div\n class=\"bg-surface-1 py-4 text-word-1 text-24 font-bold leading-none\">\n {{ calendarDay }}\n </div>\n </div>\n <div class=\"flex flex-col gap-4 min-w-0\">\n <p class=\"font-semibold text-word-1 text-14\">\n {{ toolPart.input.title }}\n </p>\n <div class=\"flex items-center gap-8 text-12 text-word-3\">\n <VvIcon name=\"ri:time-line\" class=\"text-14 shrink-0\" />\n <time :datetime=\"toolPart.input.startDate\">{{\n dateDisplay\n }}</time>\n </div>\n <div\n v-if=\"toolPart.input.location\"\n class=\"flex items-center gap-8 text-12 text-word-3\">\n <VvIcon name=\"ri:map-pin-line\" class=\"text-14 shrink-0\" />\n <a\n v-if=\"toolPart.input.location.startsWith('http')\"\n :href=\"toolPart.input.location\"\n :target=\"\n isSameSite(toolPart.input.location)\n ? undefined\n : '_blank'\n \"\n :rel=\"\n isSameSite(toolPart.input.location)\n ? undefined\n : 'noopener noreferrer'\n \"\n class=\"underline truncate\">\n {{ toolPart.input.location }}\n </a>\n <span v-else>{{ toolPart.input.location }}</span>\n </div>\n <div\n v-if=\"toolPart.input.additionalInfo\"\n class=\"flex items-start gap-8 text-12 text-word-3\">\n <VvIcon\n name=\"ri:sticky-note-line\"\n class=\"text-14 shrink-0 mt-1\" />\n <span>{{ toolPart.input.additionalInfo }}</span>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useClipboard } from '@vueuse/core'\n import { isSameSite } from 'utils'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n const { d } = useI18n()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title: string\n startDate: string\n endDate: string\n location?: string\n additionalInfo?: string\n allDay?: boolean\n }\n }\n return part\n })\n\n const dateDisplay = computed(() => {\n const input = toolPart.value.input\n if (!input) {\n return ''\n }\n const { startDate, endDate, allDay } = input\n const start = new Date(startDate)\n const end = new Date(endDate)\n const startDay = startDate.slice(0, 10)\n const endDay = endDate.slice(0, 10)\n if (allDay) {\n if (startDay === endDay) {\n return d(start, 'date-medium')\n }\n return `${d(start, 'date-medium')} - ${d(end, 'date-medium')}`\n }\n if (startDay === endDay) {\n return `${d(start, 'date-time-medium')} - ${d(end, 'time')}`\n }\n return `${d(start, 'date-time-medium')} - ${d(end, 'date-time-medium')}`\n })\n\n const calendarStart = computed(() =>\n toolPart.value.input ? new Date(toolPart.value.input.startDate) : null,\n )\n\n const calendarDay = computed(\n () => calendarStart.value?.getDate().toString() ?? '',\n )\n\n const calendarMonth = computed(() =>\n calendarStart.value ? d(calendarStart.value, 'month-short') : '',\n )\n\n // Floating time: remove dashes/colons without converting to UTC\n const toIcsDateTime = (iso: string) =>\n iso.replace(/[-:]/g, '').replace(/\\.\\d{3}$/, '')\n\n const escapeIcs = (str: string) =>\n str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/;/g, '\\\\;')\n .replace(/,/g, '\\\\,')\n .replace(/\\n/g, '\\\\n')\n\n const generateIcs = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const ics = [\n 'BEGIN:VCALENDAR',\n 'VERSION:2.0',\n 'PRODID:-//8wave//8bot//EN',\n 'BEGIN:VEVENT',\n `DTSTART:${toIcsDateTime(startDate)}`,\n `DTEND:${toIcsDateTime(endDate)}`,\n `SUMMARY:${escapeIcs(title)}`,\n location ? `LOCATION:${escapeIcs(location)}` : '',\n additionalInfo ? `DESCRIPTION:${escapeIcs(additionalInfo)}` : '',\n `DTSTAMP:${new Date()\n .toISOString()\n .replace(/[-:]/g, '')\n .replace(/\\.\\d{3}/, '')}`,\n `UID:${crypto.randomUUID()}@8bot.ai`,\n 'END:VEVENT',\n 'END:VCALENDAR',\n ]\n .filter(Boolean)\n .join('\\r\\n')\n\n const blob = new Blob([ics], { type: 'text/calendar;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `${title.replace(/[^a-zA-Z0-9\\s-]/g, '')}.ics`\n a.click()\n URL.revokeObjectURL(url)\n }\n\n const openGoogleCalendar = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const start = toIcsDateTime(startDate)\n const end = toIcsDateTime(endDate)\n const params = new URLSearchParams({\n action: 'TEMPLATE',\n text: title,\n dates: `${start}/${end}`,\n })\n if (location) {\n params.set('location', location)\n }\n if (additionalInfo) {\n params.set('details', additionalInfo)\n }\n window.open(\n `https://calendar.google.com/calendar/render?${params.toString()}`,\n '_blank',\n )\n }\n\n const openOutlook = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const params = new URLSearchParams({\n subject: title,\n startdt: startDate,\n enddt: endDate,\n })\n if (location) {\n params.set('location', location)\n }\n if (additionalInfo) {\n params.set('body', additionalInfo)\n }\n window.open(\n `https://outlook.office.com/calendar/0/action/compose?${params.toString()}`,\n '_blank',\n )\n }\n\n const copyEvent = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const lines = [input.title, dateDisplay.value]\n if (input.location) {\n lines.push(input.location)\n }\n if (input.additionalInfo) {\n lines.push(input.additionalInfo)\n }\n copy(lines.join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:calendar-event-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.calendarEvent') }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyEvent\" />\n <VvDropdown\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n icon=\"ri:calendar-line\"\n modifiers=\"action-quiet-small\"\n :label=\"$t('action.addToCalendar')\" />\n <template #items>\n <VvDropdownAction @click=\"openGoogleCalendar\">\n <VvIcon name=\"ri:google-line\" />\n Google Calendar\n </VvDropdownAction>\n <VvDropdownAction @click=\"openOutlook\">\n <VvIcon name=\"ri:microsoft-line\" />\n Outlook\n </VvDropdownAction>\n <VvDropdownAction @click=\"generateIcs\">\n <VvIcon name=\"ri:download-line\" />\n {{ $t('action.downloadIcs') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </VvButtonGroup>\n </div>\n <div class=\"p-sm flex gap-sm items-start\">\n <div\n class=\"shrink-0 w-44 rounded-xl overflow-hidden border border-surface-3 text-center\">\n <div\n class=\"bg-brand px-8 py-2 text-brand-contrast text-10 font-bold uppercase tracking-wide\">\n {{ calendarMonth }}\n </div>\n <div\n class=\"bg-surface-1 py-4 text-word-1 text-24 font-bold leading-none\">\n {{ calendarDay }}\n </div>\n </div>\n <div class=\"flex flex-col gap-4 min-w-0\">\n <p class=\"font-semibold text-word-1 text-14\">\n {{ toolPart.input.title }}\n </p>\n <div class=\"flex items-center gap-8 text-12 text-word-3\">\n <VvIcon name=\"ri:time-line\" class=\"text-14 shrink-0\" />\n <time :datetime=\"toolPart.input.startDate\">{{\n dateDisplay\n }}</time>\n </div>\n <div\n v-if=\"toolPart.input.location\"\n class=\"flex items-center gap-8 text-12 text-word-3\">\n <VvIcon name=\"ri:map-pin-line\" class=\"text-14 shrink-0\" />\n <a\n v-if=\"toolPart.input.location.startsWith('http')\"\n :href=\"toolPart.input.location\"\n :target=\"\n isSameSite(toolPart.input.location)\n ? undefined\n : '_blank'\n \"\n :rel=\"\n isSameSite(toolPart.input.location)\n ? undefined\n : 'noopener noreferrer'\n \"\n class=\"underline truncate\">\n {{ toolPart.input.location }}\n </a>\n <span v-else>{{ toolPart.input.location }}</span>\n </div>\n <div\n v-if=\"toolPart.input.additionalInfo\"\n class=\"flex items-start gap-8 text-12 text-word-3\">\n <VvIcon\n name=\"ri:sticky-note-line\"\n class=\"text-14 shrink-0 mt-1\" />\n <span>{{ toolPart.input.additionalInfo }}</span>\n </div>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkToolShowCalendarEvent-r0SuzJDT.js","names":["$t","__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","__unplugin_components_3","__unplugin_components_4","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_withCtx","_unref","_normalizeProps","_guardReactiveProps","_createTextVNode","_createCommentVNode","_sfc_main","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","guardReactiveProps","normalizeProps","createCommentVNode","createTextVNode","createBaseVNode","init_reactivity_esm_bundler","computed","createElementBlock","createVNode","useI18n","isSameSite","useClipboard","au","su","Ce","na","VvButtonLocalized","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","PkToolShowCalendarEvent_default","props","__props","toolPart","dateDisplay","input","start","startDate","end","endDate","startDay","endDay","allDay","d","calendarStart","calendarDay","calendarMonth","toIcsDateTime","iso","escapeIcs","str","generateIcs","ics","title","location","additionalInfo","blob","url","a","openGoogleCalendar","params","openOutlook","copyEvent","lines","copy","_component_VvIcon","_component_VvButton","_component_VvDropdownAction","_component_VvDropdown","_component_VvButtonGroup","_ctx","copied","_cache"],"sources":["../../../../packages/components/src/chat/PkToolShowCalendarEvent.vue","../../../../packages/components/src/chat/PkToolShowCalendarEvent.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useClipboard } from '@vueuse/core'\n import { isSameSite } from 'utils'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n const { d } = useI18n()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title: string\n startDate: string\n endDate: string\n location?: string\n additionalInfo?: string\n allDay?: boolean\n }\n }\n return part\n })\n\n const dateDisplay = computed(() => {\n const input = toolPart.value.input\n if (!input) {\n return ''\n }\n const { startDate, endDate, allDay } = input\n const start = new Date(startDate)\n const end = new Date(endDate)\n const startDay = startDate.slice(0, 10)\n const endDay = endDate.slice(0, 10)\n if (allDay) {\n if (startDay === endDay) {\n return d(start, 'date-medium')\n }\n return `${d(start, 'date-medium')} - ${d(end, 'date-medium')}`\n }\n if (startDay === endDay) {\n return `${d(start, 'date-time-medium')} - ${d(end, 'time')}`\n }\n return `${d(start, 'date-time-medium')} - ${d(end, 'date-time-medium')}`\n })\n\n const calendarStart = computed(() =>\n toolPart.value.input ? new Date(toolPart.value.input.startDate) : null,\n )\n\n const calendarDay = computed(\n () => calendarStart.value?.getDate().toString() ?? '',\n )\n\n const calendarMonth = computed(() =>\n calendarStart.value ? d(calendarStart.value, 'month-short') : '',\n )\n\n // Floating time: remove dashes/colons without converting to UTC\n const toIcsDateTime = (iso: string) =>\n iso.replace(/[-:]/g, '').replace(/\\.\\d{3}$/, '')\n\n const escapeIcs = (str: string) =>\n str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/;/g, '\\\\;')\n .replace(/,/g, '\\\\,')\n .replace(/\\n/g, '\\\\n')\n\n const generateIcs = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const ics = [\n 'BEGIN:VCALENDAR',\n 'VERSION:2.0',\n 'PRODID:-//8wave//8bot//EN',\n 'BEGIN:VEVENT',\n `DTSTART:${toIcsDateTime(startDate)}`,\n `DTEND:${toIcsDateTime(endDate)}`,\n `SUMMARY:${escapeIcs(title)}`,\n location ? `LOCATION:${escapeIcs(location)}` : '',\n additionalInfo ? `DESCRIPTION:${escapeIcs(additionalInfo)}` : '',\n `DTSTAMP:${new Date()\n .toISOString()\n .replace(/[-:]/g, '')\n .replace(/\\.\\d{3}/, '')}`,\n `UID:${crypto.randomUUID()}@8bot.ai`,\n 'END:VEVENT',\n 'END:VCALENDAR',\n ]\n .filter(Boolean)\n .join('\\r\\n')\n\n const blob = new Blob([ics], { type: 'text/calendar;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `${title.replace(/[^a-zA-Z0-9\\s-]/g, '')}.ics`\n a.click()\n URL.revokeObjectURL(url)\n }\n\n const openGoogleCalendar = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const start = toIcsDateTime(startDate)\n const end = toIcsDateTime(endDate)\n const params = new URLSearchParams({\n action: 'TEMPLATE',\n text: title,\n dates: `${start}/${end}`,\n })\n if (location) {\n params.set('location', location)\n }\n if (additionalInfo) {\n params.set('details', additionalInfo)\n }\n window.open(\n `https://calendar.google.com/calendar/render?${params.toString()}`,\n '_blank',\n )\n }\n\n const openOutlook = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const params = new URLSearchParams({\n subject: title,\n startdt: startDate,\n enddt: endDate,\n })\n if (location) {\n params.set('location', location)\n }\n if (additionalInfo) {\n params.set('body', additionalInfo)\n }\n window.open(\n `https://outlook.office.com/calendar/0/action/compose?${params.toString()}`,\n '_blank',\n )\n }\n\n const copyEvent = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const lines = [input.title, dateDisplay.value]\n if (input.location) {\n lines.push(input.location)\n }\n if (input.additionalInfo) {\n lines.push(input.additionalInfo)\n }\n copy(lines.join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:calendar-event-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.calendarEvent') }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyEvent\" />\n <VvDropdown\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n icon=\"ri:calendar-line\"\n modifiers=\"action-quiet-small\"\n :label=\"$t('action.addToCalendar')\" />\n <template #items>\n <VvDropdownAction @click=\"openGoogleCalendar\">\n <VvIcon name=\"ri:google-line\" />\n Google Calendar\n </VvDropdownAction>\n <VvDropdownAction @click=\"openOutlook\">\n <VvIcon name=\"ri:microsoft-line\" />\n Outlook\n </VvDropdownAction>\n <VvDropdownAction @click=\"generateIcs\">\n <VvIcon name=\"ri:download-line\" />\n {{ $t('action.downloadIcs') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </VvButtonGroup>\n </div>\n <div class=\"p-sm flex gap-sm items-start\">\n <div\n class=\"shrink-0 w-44 rounded-xl overflow-hidden border border-surface-3 text-center\">\n <div\n class=\"bg-brand px-8 py-2 text-brand-contrast text-10 font-bold uppercase tracking-wide\">\n {{ calendarMonth }}\n </div>\n <div\n class=\"bg-surface-1 py-4 text-word-1 text-24 font-bold leading-none\">\n {{ calendarDay }}\n </div>\n </div>\n <div class=\"flex flex-col gap-4 min-w-0\">\n <p class=\"font-semibold text-word-1 text-14\">\n {{ toolPart.input.title }}\n </p>\n <div class=\"flex items-center gap-8 text-12 text-word-3\">\n <VvIcon name=\"ri:time-line\" class=\"text-14 shrink-0\" />\n <time :datetime=\"toolPart.input.startDate\">{{\n dateDisplay\n }}</time>\n </div>\n <div\n v-if=\"toolPart.input.location\"\n class=\"flex items-center gap-8 text-12 text-word-3\">\n <VvIcon name=\"ri:map-pin-line\" class=\"text-14 shrink-0\" />\n <a\n v-if=\"toolPart.input.location.startsWith('http')\"\n :href=\"toolPart.input.location\"\n :target=\"\n isSameSite(toolPart.input.location)\n ? undefined\n : '_blank'\n \"\n :rel=\"\n isSameSite(toolPart.input.location)\n ? undefined\n : 'noopener noreferrer'\n \"\n class=\"underline truncate\">\n {{ toolPart.input.location }}\n </a>\n <span v-else>{{ toolPart.input.location }}</span>\n </div>\n <div\n v-if=\"toolPart.input.additionalInfo\"\n class=\"flex items-start gap-8 text-12 text-word-3\">\n <VvIcon\n name=\"ri:sticky-note-line\"\n class=\"text-14 shrink-0 mt-1\" />\n <span>{{ toolPart.input.additionalInfo }}</span>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useClipboard } from '@vueuse/core'\n import { isSameSite } from 'utils'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n const { d } = useI18n()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title: string\n startDate: string\n endDate: string\n location?: string\n additionalInfo?: string\n allDay?: boolean\n }\n }\n return part\n })\n\n const dateDisplay = computed(() => {\n const input = toolPart.value.input\n if (!input) {\n return ''\n }\n const { startDate, endDate, allDay } = input\n const start = new Date(startDate)\n const end = new Date(endDate)\n const startDay = startDate.slice(0, 10)\n const endDay = endDate.slice(0, 10)\n if (allDay) {\n if (startDay === endDay) {\n return d(start, 'date-medium')\n }\n return `${d(start, 'date-medium')} - ${d(end, 'date-medium')}`\n }\n if (startDay === endDay) {\n return `${d(start, 'date-time-medium')} - ${d(end, 'time')}`\n }\n return `${d(start, 'date-time-medium')} - ${d(end, 'date-time-medium')}`\n })\n\n const calendarStart = computed(() =>\n toolPart.value.input ? new Date(toolPart.value.input.startDate) : null,\n )\n\n const calendarDay = computed(\n () => calendarStart.value?.getDate().toString() ?? '',\n )\n\n const calendarMonth = computed(() =>\n calendarStart.value ? d(calendarStart.value, 'month-short') : '',\n )\n\n // Floating time: remove dashes/colons without converting to UTC\n const toIcsDateTime = (iso: string) =>\n iso.replace(/[-:]/g, '').replace(/\\.\\d{3}$/, '')\n\n const escapeIcs = (str: string) =>\n str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/;/g, '\\\\;')\n .replace(/,/g, '\\\\,')\n .replace(/\\n/g, '\\\\n')\n\n const generateIcs = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const ics = [\n 'BEGIN:VCALENDAR',\n 'VERSION:2.0',\n 'PRODID:-//8wave//8bot//EN',\n 'BEGIN:VEVENT',\n `DTSTART:${toIcsDateTime(startDate)}`,\n `DTEND:${toIcsDateTime(endDate)}`,\n `SUMMARY:${escapeIcs(title)}`,\n location ? `LOCATION:${escapeIcs(location)}` : '',\n additionalInfo ? `DESCRIPTION:${escapeIcs(additionalInfo)}` : '',\n `DTSTAMP:${new Date()\n .toISOString()\n .replace(/[-:]/g, '')\n .replace(/\\.\\d{3}/, '')}`,\n `UID:${crypto.randomUUID()}@8bot.ai`,\n 'END:VEVENT',\n 'END:VCALENDAR',\n ]\n .filter(Boolean)\n .join('\\r\\n')\n\n const blob = new Blob([ics], { type: 'text/calendar;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `${title.replace(/[^a-zA-Z0-9\\s-]/g, '')}.ics`\n a.click()\n URL.revokeObjectURL(url)\n }\n\n const openGoogleCalendar = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const start = toIcsDateTime(startDate)\n const end = toIcsDateTime(endDate)\n const params = new URLSearchParams({\n action: 'TEMPLATE',\n text: title,\n dates: `${start}/${end}`,\n })\n if (location) {\n params.set('location', location)\n }\n if (additionalInfo) {\n params.set('details', additionalInfo)\n }\n window.open(\n `https://calendar.google.com/calendar/render?${params.toString()}`,\n '_blank',\n )\n }\n\n const openOutlook = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const { title, startDate, endDate, location, additionalInfo } = input\n const params = new URLSearchParams({\n subject: title,\n startdt: startDate,\n enddt: endDate,\n })\n if (location) {\n params.set('location', location)\n }\n if (additionalInfo) {\n params.set('body', additionalInfo)\n }\n window.open(\n `https://outlook.office.com/calendar/0/action/compose?${params.toString()}`,\n '_blank',\n )\n }\n\n const copyEvent = () => {\n const input = toolPart.value.input\n if (!input) {\n return\n }\n const lines = [input.title, dateDisplay.value]\n if (input.location) {\n lines.push(input.location)\n }\n if (input.additionalInfo) {\n lines.push(input.additionalInfo)\n }\n copy(lines.join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:calendar-event-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.calendarEvent') }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyEvent\" />\n <VvDropdown\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n icon=\"ri:calendar-line\"\n modifiers=\"action-quiet-small\"\n :label=\"$t('action.addToCalendar')\" />\n <template #items>\n <VvDropdownAction @click=\"openGoogleCalendar\">\n <VvIcon name=\"ri:google-line\" />\n Google Calendar\n </VvDropdownAction>\n <VvDropdownAction @click=\"openOutlook\">\n <VvIcon name=\"ri:microsoft-line\" />\n Outlook\n </VvDropdownAction>\n <VvDropdownAction @click=\"generateIcs\">\n <VvIcon name=\"ri:download-line\" />\n {{ $t('action.downloadIcs') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </VvButtonGroup>\n </div>\n <div class=\"p-sm flex gap-sm items-start\">\n <div\n class=\"shrink-0 w-44 rounded-xl overflow-hidden border border-surface-3 text-center\">\n <div\n class=\"bg-brand px-8 py-2 text-brand-contrast text-10 font-bold uppercase tracking-wide\">\n {{ calendarMonth }}\n </div>\n <div\n class=\"bg-surface-1 py-4 text-word-1 text-24 font-bold leading-none\">\n {{ calendarDay }}\n </div>\n </div>\n <div class=\"flex flex-col gap-4 min-w-0\">\n <p class=\"font-semibold text-word-1 text-14\">\n {{ toolPart.input.title }}\n </p>\n <div class=\"flex items-center gap-8 text-12 text-word-3\">\n <VvIcon name=\"ri:time-line\" class=\"text-14 shrink-0\" />\n <time :datetime=\"toolPart.input.startDate\">{{\n dateDisplay\n }}</time>\n </div>\n <div\n v-if=\"toolPart.input.location\"\n class=\"flex items-center gap-8 text-12 text-word-3\">\n <VvIcon name=\"ri:map-pin-line\" class=\"text-14 shrink-0\" />\n <a\n v-if=\"toolPart.input.location.startsWith('http')\"\n :href=\"toolPart.input.location\"\n :target=\"\n isSameSite(toolPart.input.location)\n ? undefined\n : '_blank'\n \"\n :rel=\"\n isSameSite(toolPart.input.location)\n ? undefined\n : 'noopener noreferrer'\n \"\n class=\"underline truncate\">\n {{ toolPart.input.location }}\n </a>\n <span v-else>{{ toolPart.input.location }}</span>\n </div>\n <div\n v-if=\"toolPart.input.additionalInfo\"\n class=\"flex items-start gap-8 text-12 text-word-3\">\n <VvIcon\n name=\"ri:sticky-note-line\"\n class=\"text-14 shrink-0 mt-1\" />\n <span>{{ toolPart.input.additionalInfo }}</span>\n </div>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { $ as e, C as t, Ct as n, Dt as r, L as i, O as a, St as o, _ as s, b as c, d as l, h as u, it as d, m as f, v as p, wt as m, x as h, z as g } from "./vue.runtime.esm-bundler-DKVd2gci.js";
2
2
  import { i as _ } from "./dist-DxtbpUdI.js";
3
- import { a as v, t as y } from "./index.es-dTcgyeM4.js";
4
- import { t as b } from "./VvButtonLocalized-DV-zbH9b.js";
3
+ import { a as v, t as y } from "./index.es-D7jsz1j2.js";
4
+ import { t as b } from "./VvButtonLocalized-PfbIEwav.js";
5
5
  import { t as x } from "./_plugin-vue_export-helper-B3ysoDQm.js";
6
6
  //#region ../../packages/components/src/chat/PkToolShowComparison.vue?vue&type=script&setup=true&lang.ts
7
7
  a(), n(), d();
@@ -71,4 +71,4 @@ var S = {
71
71
  //#endregion
72
72
  export { N as default };
73
73
 
74
- //# sourceMappingURL=PkToolShowComparison-DLC22e1W.js.map
74
+ //# sourceMappingURL=PkToolShowComparison-Bsh6iiIQ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowComparison-DLC22e1W.js","names":["$t","__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_withCtx","_unref","_Fragment","_renderList","_normalizeClass","_createTextVNode","_createCommentVNode","_sfc_main","_style_0","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","createCommentVNode","createTextVNode","Fragment","createBaseVNode","init_reactivity_esm_bundler","computed","createElementBlock","normalizeClass","createVNode","renderList","useClipboard","Qi","Ae","VvButtonLocalized","_plugin_vue_export_helper_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","PkToolShowComparison_default","props","__props","toolPart","features","items","valueColorClass","value","copyTable","header","i","rows","f","_component_VvIcon","_component_VvButton","_component_VvButtonGroup","_ctx","copied","_cache","item","feature"],"sources":["../../../../packages/components/src/chat/PkToolShowComparison.vue","../../../../packages/components/src/chat/PkToolShowComparison.vue","../../../../packages/components/src/chat/PkToolShowComparison.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n // The tool contract asks for \"✓\" and \"✗\" on boolean features: the pair is\n // a verdict, so it wears one and the table reads by eye (DESIGN.md).\n const valueColorClass = (value?: string) => {\n if (value === '✓') {\n return 'text-success'\n }\n if (value === '✗') {\n return 'text-danger'\n }\n return 'text-word-2'\n }\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"pk-tool-comparison border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <!-- One row per feature, one column per item. In a narrow message\n each feature becomes a block that lists every item beside its\n value (`u-table--stack`), so a comparison still reads across. -->\n <div class=\"u-table-scroll\">\n <table class=\"u-table u-table--stack\" role=\"table\">\n <thead role=\"rowgroup\">\n <tr role=\"row\">\n <td></td>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"columnheader\"\n scope=\"col\"\n class=\"pk-tool-comparison__item\"\n :class=\"{\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n }\">\n {{ item.name }}\n <small\n v-if=\"item.highlighted\"\n class=\"pk-tool-comparison__recommended\">\n {{ $t('label.recommended') }}\n </small>\n </th>\n </tr>\n </thead>\n <tbody role=\"rowgroup\">\n <tr v-for=\"feature in features\" :key=\"feature\" role=\"row\">\n <th\n role=\"rowheader\"\n scope=\"row\"\n class=\"pk-tool-comparison__feature\">\n {{ feature.replace(/_/g, ' ') }}\n </th>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"cell\"\n :data-label=\"item.name\"\n :class=\"[\n valueColorClass(item.values[feature]),\n {\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n },\n ]\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n // Written under the block so it outweighs the `.u-table` rules.\n .pk-tool-comparison {\n // Stacked, the labels are the names of the items, longer than the\n // headings of a data table.\n --u-table-label-width: 8rem;\n\n // The items are what the table compares: their names are the titles\n // of the columns, not the small headings of a data table.\n & &__item {\n font-size: var(--text-14);\n color: var(--color-word-1);\n }\n\n & &__feature {\n font-weight: var(--font-medium);\n color: var(--color-word-3);\n }\n\n // The recommended item: a ground down its column and a word under its\n // name, no accent colour (DESIGN.md). Stacked, its name weighs more\n // than the others beside its value.\n & &__cell--highlighted {\n background-color: var(--color-surface-1);\n }\n\n &__recommended {\n display: block;\n font-size: var(--text-12);\n font-weight: var(--font-normal);\n color: var(--color-word-3);\n }\n\n @container (width < 30rem) {\n & &__cell--highlighted {\n background-color: transparent;\n\n &::before {\n font-weight: var(--font-semibold);\n color: var(--color-word-1);\n }\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n // The tool contract asks for \"✓\" and \"✗\" on boolean features: the pair is\n // a verdict, so it wears one and the table reads by eye (DESIGN.md).\n const valueColorClass = (value?: string) => {\n if (value === '✓') {\n return 'text-success'\n }\n if (value === '✗') {\n return 'text-danger'\n }\n return 'text-word-2'\n }\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"pk-tool-comparison border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <!-- One row per feature, one column per item. In a narrow message\n each feature becomes a block that lists every item beside its\n value (`u-table--stack`), so a comparison still reads across. -->\n <div class=\"u-table-scroll\">\n <table class=\"u-table u-table--stack\" role=\"table\">\n <thead role=\"rowgroup\">\n <tr role=\"row\">\n <td></td>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"columnheader\"\n scope=\"col\"\n class=\"pk-tool-comparison__item\"\n :class=\"{\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n }\">\n {{ item.name }}\n <small\n v-if=\"item.highlighted\"\n class=\"pk-tool-comparison__recommended\">\n {{ $t('label.recommended') }}\n </small>\n </th>\n </tr>\n </thead>\n <tbody role=\"rowgroup\">\n <tr v-for=\"feature in features\" :key=\"feature\" role=\"row\">\n <th\n role=\"rowheader\"\n scope=\"row\"\n class=\"pk-tool-comparison__feature\">\n {{ feature.replace(/_/g, ' ') }}\n </th>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"cell\"\n :data-label=\"item.name\"\n :class=\"[\n valueColorClass(item.values[feature]),\n {\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n },\n ]\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n // Written under the block so it outweighs the `.u-table` rules.\n .pk-tool-comparison {\n // Stacked, the labels are the names of the items, longer than the\n // headings of a data table.\n --u-table-label-width: 8rem;\n\n // The items are what the table compares: their names are the titles\n // of the columns, not the small headings of a data table.\n & &__item {\n font-size: var(--text-14);\n color: var(--color-word-1);\n }\n\n & &__feature {\n font-weight: var(--font-medium);\n color: var(--color-word-3);\n }\n\n // The recommended item: a ground down its column and a word under its\n // name, no accent colour (DESIGN.md). Stacked, its name weighs more\n // than the others beside its value.\n & &__cell--highlighted {\n background-color: var(--color-surface-1);\n }\n\n &__recommended {\n display: block;\n font-size: var(--text-12);\n font-weight: var(--font-normal);\n color: var(--color-word-3);\n }\n\n @container (width < 30rem) {\n & &__cell--highlighted {\n background-color: transparent;\n\n &::before {\n font-weight: var(--font-semibold);\n color: var(--color-word-1);\n }\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n // The tool contract asks for \"✓\" and \"✗\" on boolean features: the pair is\n // a verdict, so it wears one and the table reads by eye (DESIGN.md).\n const valueColorClass = (value?: string) => {\n if (value === '✓') {\n return 'text-success'\n }\n if (value === '✗') {\n return 'text-danger'\n }\n return 'text-word-2'\n }\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"pk-tool-comparison border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <!-- One row per feature, one column per item. In a narrow message\n each feature becomes a block that lists every item beside its\n value (`u-table--stack`), so a comparison still reads across. -->\n <div class=\"u-table-scroll\">\n <table class=\"u-table u-table--stack\" role=\"table\">\n <thead role=\"rowgroup\">\n <tr role=\"row\">\n <td></td>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"columnheader\"\n scope=\"col\"\n class=\"pk-tool-comparison__item\"\n :class=\"{\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n }\">\n {{ item.name }}\n <small\n v-if=\"item.highlighted\"\n class=\"pk-tool-comparison__recommended\">\n {{ $t('label.recommended') }}\n </small>\n </th>\n </tr>\n </thead>\n <tbody role=\"rowgroup\">\n <tr v-for=\"feature in features\" :key=\"feature\" role=\"row\">\n <th\n role=\"rowheader\"\n scope=\"row\"\n class=\"pk-tool-comparison__feature\">\n {{ feature.replace(/_/g, ' ') }}\n </th>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"cell\"\n :data-label=\"item.name\"\n :class=\"[\n valueColorClass(item.values[feature]),\n {\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n },\n ]\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n // Written under the block so it outweighs the `.u-table` rules.\n .pk-tool-comparison {\n // Stacked, the labels are the names of the items, longer than the\n // headings of a data table.\n --u-table-label-width: 8rem;\n\n // The items are what the table compares: their names are the titles\n // of the columns, not the small headings of a data table.\n & &__item {\n font-size: var(--text-14);\n color: var(--color-word-1);\n }\n\n & &__feature {\n font-weight: var(--font-medium);\n color: var(--color-word-3);\n }\n\n // The recommended item: a ground down its column and a word under its\n // name, no accent colour (DESIGN.md). Stacked, its name weighs more\n // than the others beside its value.\n & &__cell--highlighted {\n background-color: var(--color-surface-1);\n }\n\n &__recommended {\n display: block;\n font-size: var(--text-12);\n font-weight: var(--font-normal);\n color: var(--color-word-3);\n }\n\n @container (width < 30rem) {\n & &__cell--highlighted {\n background-color: transparent;\n\n &::before {\n font-weight: var(--font-semibold);\n color: var(--color-word-1);\n }\n }\n }\n }\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkToolShowComparison-Bsh6iiIQ.js","names":["$t","__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_withCtx","_unref","_Fragment","_renderList","_normalizeClass","_createTextVNode","_createCommentVNode","_sfc_main","_style_0","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","createCommentVNode","createTextVNode","Fragment","createBaseVNode","init_reactivity_esm_bundler","computed","createElementBlock","normalizeClass","createVNode","renderList","useClipboard","au","Ce","VvButtonLocalized","_plugin_vue_export_helper_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","PkToolShowComparison_default","props","__props","toolPart","features","items","valueColorClass","value","copyTable","header","i","rows","f","_component_VvIcon","_component_VvButton","_component_VvButtonGroup","_ctx","copied","_cache","item","feature"],"sources":["../../../../packages/components/src/chat/PkToolShowComparison.vue","../../../../packages/components/src/chat/PkToolShowComparison.vue","../../../../packages/components/src/chat/PkToolShowComparison.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n // The tool contract asks for \"✓\" and \"✗\" on boolean features: the pair is\n // a verdict, so it wears one and the table reads by eye (DESIGN.md).\n const valueColorClass = (value?: string) => {\n if (value === '✓') {\n return 'text-success'\n }\n if (value === '✗') {\n return 'text-danger'\n }\n return 'text-word-2'\n }\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"pk-tool-comparison border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <!-- One row per feature, one column per item. In a narrow message\n each feature becomes a block that lists every item beside its\n value (`u-table--stack`), so a comparison still reads across. -->\n <div class=\"u-table-scroll\">\n <table class=\"u-table u-table--stack\" role=\"table\">\n <thead role=\"rowgroup\">\n <tr role=\"row\">\n <td></td>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"columnheader\"\n scope=\"col\"\n class=\"pk-tool-comparison__item\"\n :class=\"{\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n }\">\n {{ item.name }}\n <small\n v-if=\"item.highlighted\"\n class=\"pk-tool-comparison__recommended\">\n {{ $t('label.recommended') }}\n </small>\n </th>\n </tr>\n </thead>\n <tbody role=\"rowgroup\">\n <tr v-for=\"feature in features\" :key=\"feature\" role=\"row\">\n <th\n role=\"rowheader\"\n scope=\"row\"\n class=\"pk-tool-comparison__feature\">\n {{ feature.replace(/_/g, ' ') }}\n </th>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"cell\"\n :data-label=\"item.name\"\n :class=\"[\n valueColorClass(item.values[feature]),\n {\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n },\n ]\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n // Written under the block so it outweighs the `.u-table` rules.\n .pk-tool-comparison {\n // Stacked, the labels are the names of the items, longer than the\n // headings of a data table.\n --u-table-label-width: 8rem;\n\n // The items are what the table compares: their names are the titles\n // of the columns, not the small headings of a data table.\n & &__item {\n font-size: var(--text-14);\n color: var(--color-word-1);\n }\n\n & &__feature {\n font-weight: var(--font-medium);\n color: var(--color-word-3);\n }\n\n // The recommended item: a ground down its column and a word under its\n // name, no accent colour (DESIGN.md). Stacked, its name weighs more\n // than the others beside its value.\n & &__cell--highlighted {\n background-color: var(--color-surface-1);\n }\n\n &__recommended {\n display: block;\n font-size: var(--text-12);\n font-weight: var(--font-normal);\n color: var(--color-word-3);\n }\n\n @container (width < 30rem) {\n & &__cell--highlighted {\n background-color: transparent;\n\n &::before {\n font-weight: var(--font-semibold);\n color: var(--color-word-1);\n }\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n // The tool contract asks for \"✓\" and \"✗\" on boolean features: the pair is\n // a verdict, so it wears one and the table reads by eye (DESIGN.md).\n const valueColorClass = (value?: string) => {\n if (value === '✓') {\n return 'text-success'\n }\n if (value === '✗') {\n return 'text-danger'\n }\n return 'text-word-2'\n }\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"pk-tool-comparison border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <!-- One row per feature, one column per item. In a narrow message\n each feature becomes a block that lists every item beside its\n value (`u-table--stack`), so a comparison still reads across. -->\n <div class=\"u-table-scroll\">\n <table class=\"u-table u-table--stack\" role=\"table\">\n <thead role=\"rowgroup\">\n <tr role=\"row\">\n <td></td>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"columnheader\"\n scope=\"col\"\n class=\"pk-tool-comparison__item\"\n :class=\"{\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n }\">\n {{ item.name }}\n <small\n v-if=\"item.highlighted\"\n class=\"pk-tool-comparison__recommended\">\n {{ $t('label.recommended') }}\n </small>\n </th>\n </tr>\n </thead>\n <tbody role=\"rowgroup\">\n <tr v-for=\"feature in features\" :key=\"feature\" role=\"row\">\n <th\n role=\"rowheader\"\n scope=\"row\"\n class=\"pk-tool-comparison__feature\">\n {{ feature.replace(/_/g, ' ') }}\n </th>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"cell\"\n :data-label=\"item.name\"\n :class=\"[\n valueColorClass(item.values[feature]),\n {\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n },\n ]\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n // Written under the block so it outweighs the `.u-table` rules.\n .pk-tool-comparison {\n // Stacked, the labels are the names of the items, longer than the\n // headings of a data table.\n --u-table-label-width: 8rem;\n\n // The items are what the table compares: their names are the titles\n // of the columns, not the small headings of a data table.\n & &__item {\n font-size: var(--text-14);\n color: var(--color-word-1);\n }\n\n & &__feature {\n font-weight: var(--font-medium);\n color: var(--color-word-3);\n }\n\n // The recommended item: a ground down its column and a word under its\n // name, no accent colour (DESIGN.md). Stacked, its name weighs more\n // than the others beside its value.\n & &__cell--highlighted {\n background-color: var(--color-surface-1);\n }\n\n &__recommended {\n display: block;\n font-size: var(--text-12);\n font-weight: var(--font-normal);\n color: var(--color-word-3);\n }\n\n @container (width < 30rem) {\n & &__cell--highlighted {\n background-color: transparent;\n\n &::before {\n font-weight: var(--font-semibold);\n color: var(--color-word-1);\n }\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n title?: string\n items: Array<{\n name: string\n values: Record<string, string>\n highlighted?: boolean\n }>\n }\n }\n return part\n })\n\n const features = computed(() => {\n const items = toolPart.value.input?.items\n if (!items || items.length === 0) {\n return []\n }\n return Object.keys(items[0].values)\n })\n\n // The tool contract asks for \"✓\" and \"✗\" on boolean features: the pair is\n // a verdict, so it wears one and the table reads by eye (DESIGN.md).\n const valueColorClass = (value?: string) => {\n if (value === '✓') {\n return 'text-success'\n }\n if (value === '✗') {\n return 'text-danger'\n }\n return 'text-word-2'\n }\n\n const copyTable = () => {\n const items = toolPart.value.input?.items\n if (!items) {\n return\n }\n const header = ['', ...items.map((i) => i.name)].join('\\t')\n const rows = features.value.map((f) =>\n [f.replace(/_/g, ' '), ...items.map((i) => i.values[f] ?? '')].join(\n '\\t',\n ),\n )\n copy([header, ...rows].join('\\n'))\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.items?.length\"\n class=\"pk-tool-comparison border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon name=\"ri:scales-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{\n toolPart.input.title ?? $t('label.comparison')\n }}</strong>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyTable\" />\n </VvButtonGroup>\n </div>\n <!-- One row per feature, one column per item. In a narrow message\n each feature becomes a block that lists every item beside its\n value (`u-table--stack`), so a comparison still reads across. -->\n <div class=\"u-table-scroll\">\n <table class=\"u-table u-table--stack\" role=\"table\">\n <thead role=\"rowgroup\">\n <tr role=\"row\">\n <td></td>\n <th\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"columnheader\"\n scope=\"col\"\n class=\"pk-tool-comparison__item\"\n :class=\"{\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n }\">\n {{ item.name }}\n <small\n v-if=\"item.highlighted\"\n class=\"pk-tool-comparison__recommended\">\n {{ $t('label.recommended') }}\n </small>\n </th>\n </tr>\n </thead>\n <tbody role=\"rowgroup\">\n <tr v-for=\"feature in features\" :key=\"feature\" role=\"row\">\n <th\n role=\"rowheader\"\n scope=\"row\"\n class=\"pk-tool-comparison__feature\">\n {{ feature.replace(/_/g, ' ') }}\n </th>\n <td\n v-for=\"item in toolPart.input.items\"\n :key=\"item.name\"\n role=\"cell\"\n :data-label=\"item.name\"\n :class=\"[\n valueColorClass(item.values[feature]),\n {\n 'pk-tool-comparison__cell--highlighted':\n item.highlighted,\n },\n ]\">\n {{ item.values[feature] ?? '-' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n // Written under the block so it outweighs the `.u-table` rules.\n .pk-tool-comparison {\n // Stacked, the labels are the names of the items, longer than the\n // headings of a data table.\n --u-table-label-width: 8rem;\n\n // The items are what the table compares: their names are the titles\n // of the columns, not the small headings of a data table.\n & &__item {\n font-size: var(--text-14);\n color: var(--color-word-1);\n }\n\n & &__feature {\n font-weight: var(--font-medium);\n color: var(--color-word-3);\n }\n\n // The recommended item: a ground down its column and a word under its\n // name, no accent colour (DESIGN.md). Stacked, its name weighs more\n // than the others beside its value.\n & &__cell--highlighted {\n background-color: var(--color-surface-1);\n }\n\n &__recommended {\n display: block;\n font-size: var(--text-12);\n font-weight: var(--font-normal);\n color: var(--color-word-3);\n }\n\n @container (width < 30rem) {\n & &__cell--highlighted {\n background-color: transparent;\n\n &::before {\n font-weight: var(--font-semibold);\n color: var(--color-word-1);\n }\n }\n }\n }\n</style>\n"],"mappings":"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"}
@@ -3,9 +3,9 @@ import { $ as r, C as i, Ct as a, Dt as o, L as s, O as c, St as l, X as u, _ as
3
3
  import { r as C } from "./vue-i18n-CooVjJjT.js";
4
4
  import { t as w } from "./PkStreamingMarkdown-pEgm7m-B.js";
5
5
  import { t as T } from "./src-CggZ5M7x.js";
6
- import { m as E, t as D } from "./index.es-dTcgyeM4.js";
7
- import { t as O } from "./VvButtonLocalized-DV-zbH9b.js";
8
- import { n as k, r as A, t as j } from "./index.es-DkFMpDsf.js";
6
+ import { s as E, t as D } from "./index.es-D7jsz1j2.js";
7
+ import { t as O } from "./VvButtonLocalized-PfbIEwav.js";
8
+ import { n as k, r as A, t as j } from "./index.es-DmG03ICf.js";
9
9
  //#region ../../packages/components/src/chat/PkToolShowContactForm.vue?vue&type=script&setup=true&lang.ts
10
10
  c(), _(), a(), y();
11
11
  var M = { class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-surface" }, N = { class: "px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40" }, P = { class: "font-bold" }, F = {
@@ -155,4 +155,4 @@ var M = { class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-s
155
155
  //#endregion
156
156
  export { K as default };
157
157
 
158
- //# sourceMappingURL=PkToolShowContactForm-CW7gKQ7h.js.map
158
+ //# sourceMappingURL=PkToolShowContactForm-B2UzmoDw.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowContactForm-CW7gKQ7h.js","names":["z.object","z.string","z.email","useForm","__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_unref","_Transition","_withCtx","_Fragment","_renderList","_createCommentVNode","_isRef","_createBlock","_createTextVNode","PkStreamingMarkdown","_sfc_main","object","string","email","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","watch","createCommentVNode","createTextVNode","isRef","Fragment","createBlock","createBaseVNode","init_reactivity_esm_bundler","computed","init_runtime_dom_esm_bundler","Transition","createElementBlock","createVNode","renderList","useI18n","PkStreamingMarkdown_default","formatPhoneNumber","xr","Ae","VvButtonLocalized","useLocalizedString","useSafeLocalStorage","Se","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","PkToolShowContactForm_default","__emit","$t","props","__props","toolPart","input","prefilled","value","field","localSubmitted","LeadFormSchema","emit","newInput","submitted","isSubmitted","submitLead","data","privacyNotice","_component_VvIcon","_component_VvAlert","_component_VvButton","VvForm","formData","_cache","$event","VvFormField"],"sources":["../../../../packages/components/src/chat/PkToolShowContactForm.vue","../../../../packages/components/src/chat/PkToolShowContactForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { useSafeLocalStorage, useLocalizedString } from 'composables'\n import type { LocalizedString } from 'models'\n import { formatPhoneNumber } from 'utils'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const props = defineProps<{\n part: unknown\n submitted?: boolean\n loading?: boolean\n error?: string\n readonly?: boolean\n privacyPolicyNotice?: LocalizedString\n /**\n * A conversation read back (the chat logs): the card is the record\n * of the form the agent showed, its prefilled values as text. Nothing\n * in the part says whether the visitor sent it, only the contacts do.\n */\n record?: boolean\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n type: string\n toolCallId: string\n input?: {\n name: string\n email: string\n phone?: string\n conversationContext?: Record<string, unknown>\n }\n }\n return part\n })\n\n const input = computed(() => toolPart.value.input)\n\n const prefilled = computed(() =>\n (\n [\n { name: 'name', label: $t('label.name') },\n { name: 'email', label: $t('label.email') },\n { name: 'phone', label: $t('label.phone') },\n ] as const\n )\n .map((field) => {\n const value = input.value?.[field.name]?.trim()\n return {\n ...field,\n // Written as the contacts page writes it\n value:\n field.name === 'phone' && value\n ? formatPhoneNumber(value) || value\n : value,\n }\n })\n .filter((field) => field.value),\n )\n\n const localSubmitted = useSafeLocalStorage<boolean>(\n computed(\n () =>\n `${toolPart.value.toolCallId}-${toolPart.value.type}-submitted`,\n ),\n false,\n )\n\n const LeadFormSchema = z.object({\n name: z.string().min(1).max(255).default(''),\n email: z.email().max(255).default(''),\n phone: z\n .string()\n .max(50)\n .regex(/^[+0-9\\s\\-.()]*$/, $t('validation.invalidPhoneNumber'))\n .optional(),\n })\n\n const emit = defineEmits<{\n submit: [data: Record<string, unknown>]\n }>()\n\n const { VvForm, VvFormField, formData } = useForm(LeadFormSchema, {\n lazyLoad: true,\n })\n\n // Pre-populate form with input values\n watch(\n input,\n (newInput) => {\n formData.value = {\n name: newInput?.name || '',\n email: newInput?.email || '',\n phone: newInput?.phone || '',\n }\n },\n { immediate: true },\n )\n\n watch(\n () => props.submitted,\n (submitted) => {\n if (submitted) {\n localSubmitted.value = true\n }\n },\n { immediate: true },\n )\n\n const isSubmitted = computed(() => {\n return props.submitted || localSubmitted.value\n })\n\n const submitLead = async (data: Record<string, unknown>): Promise<void> => {\n emit('submit', { ...data, metadata: input.value?.conversationContext })\n }\n\n const privacyNotice = useLocalizedString(\n () => props.privacyPolicyNotice,\n 'message.defaultPrivacyPolicyNotice',\n )\n</script>\n\n<template>\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:send-plane-2-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.contactUs') }}</strong>\n </div>\n\n <Transition mode=\"out-in\">\n <div v-if=\"record\" class=\"p-sm flex flex-col gap-sm text-14\">\n <p class=\"text-word-2\">\n {{\n prefilled.length\n ? $t('message.contactFormRecordPrefilled')\n : $t('message.contactFormRecord')\n }}\n </p>\n <dl\n v-if=\"prefilled.length\"\n class=\"flex flex-wrap gap-x-lg gap-y-sm\">\n <div\n v-for=\"field in prefilled\"\n :key=\"field.name\"\n class=\"flex flex-col gap-4 min-w-0\">\n <dt class=\"text-12 text-word-3\">{{ field.label }}</dt>\n <dd class=\"text-word-1 break-words\">\n {{ field.value }}\n </dd>\n </div>\n </dl>\n </div>\n <div v-else-if=\"!isSubmitted\" class=\"p-sm\">\n <VvForm v-model=\"formData\" :readonly @submit=\"submitLead\">\n <VvFormField\n name=\"name\"\n type=\"text\"\n :label=\"$t('label.name')\"\n :placeholder=\"$t('placeholder.name')\"\n icon=\"ri:user-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"email\"\n type=\"email\"\n :label=\"$t('label.email')\"\n :placeholder=\"$t('placeholder.email')\"\n icon=\"ri:mail-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"phone\"\n type=\"tel\"\n :label=\"$t('label.phone')\"\n :placeholder=\"$t('placeholder.phone')\"\n icon=\"ri:phone-line\"\n class=\"mb-md\"\n modifiers=\"compact\" />\n\n <!-- A send that failed says so: the reason reached the\n card and was never drawn -->\n <VvAlert v-if=\"error\" modifiers=\"danger\" class=\"mb-md\">\n {{ error }}\n </VvAlert>\n\n <div class=\"flex justify-between items-center gap-16 mt-xs\">\n <div\n class=\"text-smaller text-word-3 flex items-center gap-8\">\n <VvIcon\n name=\"ri:shield-check-line\"\n class=\"text-16 shrink-0\" />\n <PkStreamingMarkdown\n :markdown=\"privacyNotice\"\n class=\"wysiwyg\" />\n </div>\n <!-- The library's default button is the brand, and in\n the chat the brand is the agent's color, as on the\n rest of the card: `primary` drew it neutral dark -->\n <VvButton\n type=\"submit\"\n :loading\n :disabled=\"loading || readonly\"\n icon-position=\"right\"\n class=\"shrink-0\"\n :label=\"$t('action.submit')\" />\n </div>\n </VvForm>\n </div>\n <div v-else class=\"p-sm\">\n <!-- One confirmation row across every tool card: a green\n check, the title in weight, the body quiet. The check is\n the outcome, so it alone carries the colour (DESIGN.md). -->\n <div class=\"flex items-center gap-sm\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <div>\n <strong class=\"font-medium block text-word-1\">\n {{ $t('message.leadSubmittedTitle') }}\n </strong>\n <p class=\"text-word-3\">\n {{ $t('message.leadSubmittedMessage') }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { useSafeLocalStorage, useLocalizedString } from 'composables'\n import type { LocalizedString } from 'models'\n import { formatPhoneNumber } from 'utils'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const props = defineProps<{\n part: unknown\n submitted?: boolean\n loading?: boolean\n error?: string\n readonly?: boolean\n privacyPolicyNotice?: LocalizedString\n /**\n * A conversation read back (the chat logs): the card is the record\n * of the form the agent showed, its prefilled values as text. Nothing\n * in the part says whether the visitor sent it, only the contacts do.\n */\n record?: boolean\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n type: string\n toolCallId: string\n input?: {\n name: string\n email: string\n phone?: string\n conversationContext?: Record<string, unknown>\n }\n }\n return part\n })\n\n const input = computed(() => toolPart.value.input)\n\n const prefilled = computed(() =>\n (\n [\n { name: 'name', label: $t('label.name') },\n { name: 'email', label: $t('label.email') },\n { name: 'phone', label: $t('label.phone') },\n ] as const\n )\n .map((field) => {\n const value = input.value?.[field.name]?.trim()\n return {\n ...field,\n // Written as the contacts page writes it\n value:\n field.name === 'phone' && value\n ? formatPhoneNumber(value) || value\n : value,\n }\n })\n .filter((field) => field.value),\n )\n\n const localSubmitted = useSafeLocalStorage<boolean>(\n computed(\n () =>\n `${toolPart.value.toolCallId}-${toolPart.value.type}-submitted`,\n ),\n false,\n )\n\n const LeadFormSchema = z.object({\n name: z.string().min(1).max(255).default(''),\n email: z.email().max(255).default(''),\n phone: z\n .string()\n .max(50)\n .regex(/^[+0-9\\s\\-.()]*$/, $t('validation.invalidPhoneNumber'))\n .optional(),\n })\n\n const emit = defineEmits<{\n submit: [data: Record<string, unknown>]\n }>()\n\n const { VvForm, VvFormField, formData } = useForm(LeadFormSchema, {\n lazyLoad: true,\n })\n\n // Pre-populate form with input values\n watch(\n input,\n (newInput) => {\n formData.value = {\n name: newInput?.name || '',\n email: newInput?.email || '',\n phone: newInput?.phone || '',\n }\n },\n { immediate: true },\n )\n\n watch(\n () => props.submitted,\n (submitted) => {\n if (submitted) {\n localSubmitted.value = true\n }\n },\n { immediate: true },\n )\n\n const isSubmitted = computed(() => {\n return props.submitted || localSubmitted.value\n })\n\n const submitLead = async (data: Record<string, unknown>): Promise<void> => {\n emit('submit', { ...data, metadata: input.value?.conversationContext })\n }\n\n const privacyNotice = useLocalizedString(\n () => props.privacyPolicyNotice,\n 'message.defaultPrivacyPolicyNotice',\n )\n</script>\n\n<template>\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:send-plane-2-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.contactUs') }}</strong>\n </div>\n\n <Transition mode=\"out-in\">\n <div v-if=\"record\" class=\"p-sm flex flex-col gap-sm text-14\">\n <p class=\"text-word-2\">\n {{\n prefilled.length\n ? $t('message.contactFormRecordPrefilled')\n : $t('message.contactFormRecord')\n }}\n </p>\n <dl\n v-if=\"prefilled.length\"\n class=\"flex flex-wrap gap-x-lg gap-y-sm\">\n <div\n v-for=\"field in prefilled\"\n :key=\"field.name\"\n class=\"flex flex-col gap-4 min-w-0\">\n <dt class=\"text-12 text-word-3\">{{ field.label }}</dt>\n <dd class=\"text-word-1 break-words\">\n {{ field.value }}\n </dd>\n </div>\n </dl>\n </div>\n <div v-else-if=\"!isSubmitted\" class=\"p-sm\">\n <VvForm v-model=\"formData\" :readonly @submit=\"submitLead\">\n <VvFormField\n name=\"name\"\n type=\"text\"\n :label=\"$t('label.name')\"\n :placeholder=\"$t('placeholder.name')\"\n icon=\"ri:user-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"email\"\n type=\"email\"\n :label=\"$t('label.email')\"\n :placeholder=\"$t('placeholder.email')\"\n icon=\"ri:mail-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"phone\"\n type=\"tel\"\n :label=\"$t('label.phone')\"\n :placeholder=\"$t('placeholder.phone')\"\n icon=\"ri:phone-line\"\n class=\"mb-md\"\n modifiers=\"compact\" />\n\n <!-- A send that failed says so: the reason reached the\n card and was never drawn -->\n <VvAlert v-if=\"error\" modifiers=\"danger\" class=\"mb-md\">\n {{ error }}\n </VvAlert>\n\n <div class=\"flex justify-between items-center gap-16 mt-xs\">\n <div\n class=\"text-smaller text-word-3 flex items-center gap-8\">\n <VvIcon\n name=\"ri:shield-check-line\"\n class=\"text-16 shrink-0\" />\n <PkStreamingMarkdown\n :markdown=\"privacyNotice\"\n class=\"wysiwyg\" />\n </div>\n <!-- The library's default button is the brand, and in\n the chat the brand is the agent's color, as on the\n rest of the card: `primary` drew it neutral dark -->\n <VvButton\n type=\"submit\"\n :loading\n :disabled=\"loading || readonly\"\n icon-position=\"right\"\n class=\"shrink-0\"\n :label=\"$t('action.submit')\" />\n </div>\n </VvForm>\n </div>\n <div v-else class=\"p-sm\">\n <!-- One confirmation row across every tool card: a green\n check, the title in weight, the body quiet. The check is\n the outcome, so it alone carries the colour (DESIGN.md). -->\n <div class=\"flex items-center gap-sm\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <div>\n <strong class=\"font-medium block text-word-1\">\n {{ $t('message.leadSubmittedTitle') }}\n </strong>\n <p class=\"text-word-3\">\n {{ $t('message.leadSubmittedMessage') }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkToolShowContactForm-B2UzmoDw.js","names":["z.object","z.string","z.email","useForm","__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_unref","_Transition","_withCtx","_Fragment","_renderList","_createCommentVNode","_isRef","_createBlock","_createTextVNode","PkStreamingMarkdown","_sfc_main","object","string","email","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","watch","createCommentVNode","createTextVNode","isRef","Fragment","createBlock","createBaseVNode","init_reactivity_esm_bundler","computed","init_runtime_dom_esm_bundler","Transition","createElementBlock","createVNode","renderList","useI18n","PkStreamingMarkdown_default","formatPhoneNumber","eo","Ce","VvButtonLocalized","useLocalizedString","useSafeLocalStorage","Se","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","PkToolShowContactForm_default","__emit","$t","props","__props","toolPart","input","prefilled","value","field","localSubmitted","LeadFormSchema","emit","newInput","submitted","isSubmitted","submitLead","data","privacyNotice","_component_VvIcon","_component_VvAlert","_component_VvButton","VvForm","formData","_cache","$event","VvFormField"],"sources":["../../../../packages/components/src/chat/PkToolShowContactForm.vue","../../../../packages/components/src/chat/PkToolShowContactForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { useSafeLocalStorage, useLocalizedString } from 'composables'\n import type { LocalizedString } from 'models'\n import { formatPhoneNumber } from 'utils'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const props = defineProps<{\n part: unknown\n submitted?: boolean\n loading?: boolean\n error?: string\n readonly?: boolean\n privacyPolicyNotice?: LocalizedString\n /**\n * A conversation read back (the chat logs): the card is the record\n * of the form the agent showed, its prefilled values as text. Nothing\n * in the part says whether the visitor sent it, only the contacts do.\n */\n record?: boolean\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n type: string\n toolCallId: string\n input?: {\n name: string\n email: string\n phone?: string\n conversationContext?: Record<string, unknown>\n }\n }\n return part\n })\n\n const input = computed(() => toolPart.value.input)\n\n const prefilled = computed(() =>\n (\n [\n { name: 'name', label: $t('label.name') },\n { name: 'email', label: $t('label.email') },\n { name: 'phone', label: $t('label.phone') },\n ] as const\n )\n .map((field) => {\n const value = input.value?.[field.name]?.trim()\n return {\n ...field,\n // Written as the contacts page writes it\n value:\n field.name === 'phone' && value\n ? formatPhoneNumber(value) || value\n : value,\n }\n })\n .filter((field) => field.value),\n )\n\n const localSubmitted = useSafeLocalStorage<boolean>(\n computed(\n () =>\n `${toolPart.value.toolCallId}-${toolPart.value.type}-submitted`,\n ),\n false,\n )\n\n const LeadFormSchema = z.object({\n name: z.string().min(1).max(255).default(''),\n email: z.email().max(255).default(''),\n phone: z\n .string()\n .max(50)\n .regex(/^[+0-9\\s\\-.()]*$/, $t('validation.invalidPhoneNumber'))\n .optional(),\n })\n\n const emit = defineEmits<{\n submit: [data: Record<string, unknown>]\n }>()\n\n const { VvForm, VvFormField, formData } = useForm(LeadFormSchema, {\n lazyLoad: true,\n })\n\n // Pre-populate form with input values\n watch(\n input,\n (newInput) => {\n formData.value = {\n name: newInput?.name || '',\n email: newInput?.email || '',\n phone: newInput?.phone || '',\n }\n },\n { immediate: true },\n )\n\n watch(\n () => props.submitted,\n (submitted) => {\n if (submitted) {\n localSubmitted.value = true\n }\n },\n { immediate: true },\n )\n\n const isSubmitted = computed(() => {\n return props.submitted || localSubmitted.value\n })\n\n const submitLead = async (data: Record<string, unknown>): Promise<void> => {\n emit('submit', { ...data, metadata: input.value?.conversationContext })\n }\n\n const privacyNotice = useLocalizedString(\n () => props.privacyPolicyNotice,\n 'message.defaultPrivacyPolicyNotice',\n )\n</script>\n\n<template>\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:send-plane-2-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.contactUs') }}</strong>\n </div>\n\n <Transition mode=\"out-in\">\n <div v-if=\"record\" class=\"p-sm flex flex-col gap-sm text-14\">\n <p class=\"text-word-2\">\n {{\n prefilled.length\n ? $t('message.contactFormRecordPrefilled')\n : $t('message.contactFormRecord')\n }}\n </p>\n <dl\n v-if=\"prefilled.length\"\n class=\"flex flex-wrap gap-x-lg gap-y-sm\">\n <div\n v-for=\"field in prefilled\"\n :key=\"field.name\"\n class=\"flex flex-col gap-4 min-w-0\">\n <dt class=\"text-12 text-word-3\">{{ field.label }}</dt>\n <dd class=\"text-word-1 break-words\">\n {{ field.value }}\n </dd>\n </div>\n </dl>\n </div>\n <div v-else-if=\"!isSubmitted\" class=\"p-sm\">\n <VvForm v-model=\"formData\" :readonly @submit=\"submitLead\">\n <VvFormField\n name=\"name\"\n type=\"text\"\n :label=\"$t('label.name')\"\n :placeholder=\"$t('placeholder.name')\"\n icon=\"ri:user-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"email\"\n type=\"email\"\n :label=\"$t('label.email')\"\n :placeholder=\"$t('placeholder.email')\"\n icon=\"ri:mail-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"phone\"\n type=\"tel\"\n :label=\"$t('label.phone')\"\n :placeholder=\"$t('placeholder.phone')\"\n icon=\"ri:phone-line\"\n class=\"mb-md\"\n modifiers=\"compact\" />\n\n <!-- A send that failed says so: the reason reached the\n card and was never drawn -->\n <VvAlert v-if=\"error\" modifiers=\"danger\" class=\"mb-md\">\n {{ error }}\n </VvAlert>\n\n <div class=\"flex justify-between items-center gap-16 mt-xs\">\n <div\n class=\"text-smaller text-word-3 flex items-center gap-8\">\n <VvIcon\n name=\"ri:shield-check-line\"\n class=\"text-16 shrink-0\" />\n <PkStreamingMarkdown\n :markdown=\"privacyNotice\"\n class=\"wysiwyg\" />\n </div>\n <!-- The library's default button is the brand, and in\n the chat the brand is the agent's color, as on the\n rest of the card: `primary` drew it neutral dark -->\n <VvButton\n type=\"submit\"\n :loading\n :disabled=\"loading || readonly\"\n icon-position=\"right\"\n class=\"shrink-0\"\n :label=\"$t('action.submit')\" />\n </div>\n </VvForm>\n </div>\n <div v-else class=\"p-sm\">\n <!-- One confirmation row across every tool card: a green\n check, the title in weight, the body quiet. The check is\n the outcome, so it alone carries the colour (DESIGN.md). -->\n <div class=\"flex items-center gap-sm\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <div>\n <strong class=\"font-medium block text-word-1\">\n {{ $t('message.leadSubmittedTitle') }}\n </strong>\n <p class=\"text-word-3\">\n {{ $t('message.leadSubmittedMessage') }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { useSafeLocalStorage, useLocalizedString } from 'composables'\n import type { LocalizedString } from 'models'\n import { formatPhoneNumber } from 'utils'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const props = defineProps<{\n part: unknown\n submitted?: boolean\n loading?: boolean\n error?: string\n readonly?: boolean\n privacyPolicyNotice?: LocalizedString\n /**\n * A conversation read back (the chat logs): the card is the record\n * of the form the agent showed, its prefilled values as text. Nothing\n * in the part says whether the visitor sent it, only the contacts do.\n */\n record?: boolean\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n type: string\n toolCallId: string\n input?: {\n name: string\n email: string\n phone?: string\n conversationContext?: Record<string, unknown>\n }\n }\n return part\n })\n\n const input = computed(() => toolPart.value.input)\n\n const prefilled = computed(() =>\n (\n [\n { name: 'name', label: $t('label.name') },\n { name: 'email', label: $t('label.email') },\n { name: 'phone', label: $t('label.phone') },\n ] as const\n )\n .map((field) => {\n const value = input.value?.[field.name]?.trim()\n return {\n ...field,\n // Written as the contacts page writes it\n value:\n field.name === 'phone' && value\n ? formatPhoneNumber(value) || value\n : value,\n }\n })\n .filter((field) => field.value),\n )\n\n const localSubmitted = useSafeLocalStorage<boolean>(\n computed(\n () =>\n `${toolPart.value.toolCallId}-${toolPart.value.type}-submitted`,\n ),\n false,\n )\n\n const LeadFormSchema = z.object({\n name: z.string().min(1).max(255).default(''),\n email: z.email().max(255).default(''),\n phone: z\n .string()\n .max(50)\n .regex(/^[+0-9\\s\\-.()]*$/, $t('validation.invalidPhoneNumber'))\n .optional(),\n })\n\n const emit = defineEmits<{\n submit: [data: Record<string, unknown>]\n }>()\n\n const { VvForm, VvFormField, formData } = useForm(LeadFormSchema, {\n lazyLoad: true,\n })\n\n // Pre-populate form with input values\n watch(\n input,\n (newInput) => {\n formData.value = {\n name: newInput?.name || '',\n email: newInput?.email || '',\n phone: newInput?.phone || '',\n }\n },\n { immediate: true },\n )\n\n watch(\n () => props.submitted,\n (submitted) => {\n if (submitted) {\n localSubmitted.value = true\n }\n },\n { immediate: true },\n )\n\n const isSubmitted = computed(() => {\n return props.submitted || localSubmitted.value\n })\n\n const submitLead = async (data: Record<string, unknown>): Promise<void> => {\n emit('submit', { ...data, metadata: input.value?.conversationContext })\n }\n\n const privacyNotice = useLocalizedString(\n () => props.privacyPolicyNotice,\n 'message.defaultPrivacyPolicyNotice',\n )\n</script>\n\n<template>\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:send-plane-2-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.contactUs') }}</strong>\n </div>\n\n <Transition mode=\"out-in\">\n <div v-if=\"record\" class=\"p-sm flex flex-col gap-sm text-14\">\n <p class=\"text-word-2\">\n {{\n prefilled.length\n ? $t('message.contactFormRecordPrefilled')\n : $t('message.contactFormRecord')\n }}\n </p>\n <dl\n v-if=\"prefilled.length\"\n class=\"flex flex-wrap gap-x-lg gap-y-sm\">\n <div\n v-for=\"field in prefilled\"\n :key=\"field.name\"\n class=\"flex flex-col gap-4 min-w-0\">\n <dt class=\"text-12 text-word-3\">{{ field.label }}</dt>\n <dd class=\"text-word-1 break-words\">\n {{ field.value }}\n </dd>\n </div>\n </dl>\n </div>\n <div v-else-if=\"!isSubmitted\" class=\"p-sm\">\n <VvForm v-model=\"formData\" :readonly @submit=\"submitLead\">\n <VvFormField\n name=\"name\"\n type=\"text\"\n :label=\"$t('label.name')\"\n :placeholder=\"$t('placeholder.name')\"\n icon=\"ri:user-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"email\"\n type=\"email\"\n :label=\"$t('label.email')\"\n :placeholder=\"$t('placeholder.email')\"\n icon=\"ri:mail-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"phone\"\n type=\"tel\"\n :label=\"$t('label.phone')\"\n :placeholder=\"$t('placeholder.phone')\"\n icon=\"ri:phone-line\"\n class=\"mb-md\"\n modifiers=\"compact\" />\n\n <!-- A send that failed says so: the reason reached the\n card and was never drawn -->\n <VvAlert v-if=\"error\" modifiers=\"danger\" class=\"mb-md\">\n {{ error }}\n </VvAlert>\n\n <div class=\"flex justify-between items-center gap-16 mt-xs\">\n <div\n class=\"text-smaller text-word-3 flex items-center gap-8\">\n <VvIcon\n name=\"ri:shield-check-line\"\n class=\"text-16 shrink-0\" />\n <PkStreamingMarkdown\n :markdown=\"privacyNotice\"\n class=\"wysiwyg\" />\n </div>\n <!-- The library's default button is the brand, and in\n the chat the brand is the agent's color, as on the\n rest of the card: `primary` drew it neutral dark -->\n <VvButton\n type=\"submit\"\n :loading\n :disabled=\"loading || readonly\"\n icon-position=\"right\"\n class=\"shrink-0\"\n :label=\"$t('action.submit')\" />\n </div>\n </VvForm>\n </div>\n <div v-else class=\"p-sm\">\n <!-- One confirmation row across every tool card: a green\n check, the title in weight, the body quiet. The check is\n the outcome, so it alone carries the colour (DESIGN.md). -->\n <div class=\"flex items-center gap-sm\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <div>\n <strong class=\"font-medium block text-word-1\">\n {{ $t('message.leadSubmittedTitle') }}\n </strong>\n <p class=\"text-word-3\">\n {{ $t('message.leadSubmittedMessage') }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n"],"mappings":"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"}
@@ -3,8 +3,8 @@ import { r as x } from "./vue-i18n-CooVjJjT.js";
3
3
  import "./src-CggZ5M7x.js";
4
4
  import { r as S } from "./text-BHCGblDQ.js";
5
5
  import { i as C } from "./dist-DxtbpUdI.js";
6
- import { a as w, t as T } from "./index.es-dTcgyeM4.js";
7
- import { t as E } from "./VvButtonLocalized-DV-zbH9b.js";
6
+ import { a as w, t as T } from "./index.es-D7jsz1j2.js";
7
+ import { t as E } from "./VvButtonLocalized-PfbIEwav.js";
8
8
  import { t as D } from "./_plugin-vue_export-helper-B3ysoDQm.js";
9
9
  import { n as O } from "./useLightbox-3HiaRtzY.js";
10
10
  //#region ../../packages/components/src/PkSvgViewer.vue?vue&type=script&setup=true&lang.ts
@@ -241,4 +241,4 @@ var M = {
241
241
  //#endregion
242
242
  export { B as default };
243
243
 
244
- //# sourceMappingURL=PkToolShowDiagram-WregKAJO.js.map
244
+ //# sourceMappingURL=PkToolShowDiagram-iQRTxxLw.js.map