@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":"PkToolShowDiagram-WregKAJO.js","names":["_hoisted_1","_hoisted_2","__unplugin_components_0","__unplugin_components_1","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createCommentVNode","_normalizeStyle","_createVNode","_withCtx","_Transition","_unref","_createBlock","_sfc_main","_style_0","__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_unref","_withCtx","_createBlock","_createCommentVNode","PkSvgViewer","_sfc_main","_style_0","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","normalizeStyle","onMounted","openBlock","init_runtime_core_esm_bundler","unref","watch","createCommentVNode","createBlock","createBaseVNode","init_reactivity_esm_bundler","ref","init_runtime_dom_esm_bundler","Transition","normalizeClass","createVNode","useI18n","toSnakeCase","useClipboard","Qi","Ae","VvButtonLocalized","_plugin_vue_export_helper_default","useLightbox","_hoisted_1$1","_hoisted_2$1","PkSvgViewer_default","props","__props","$t","svgRef","galleryRef","scale","translateX","translateY","startX","startY","svgDataUrl","svgWidth","svgHeight","svgStyle","computed","zoomIn","zoomOut","reset","openLightbox","lightbox","isDragging","onMouseDown","onMouseMove","onMouseUp","serializeSvg","svg","updateSvgExport","serialized","viewBox","init","_component_VvButton","_component_VvButtonGroup","createElementBlock","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","DiagramType","\"Sequence\"","\"Class\"","\"State\"","\"ER\"","\"XYChart\"","\"Flowchart\"","\"Graph\"","PkToolShowDiagram_default","$te","toolPart","svgError","svgContent","diagramType","code","diagramTypeLabel","diagramTypeIcon","renderDiagram","theme","THEMES","renderMermaidSVG","downloadSvg","blob","url","anchor","copyCode","copy","_component_VvIcon","copied"],"sources":["../../../../packages/components/src/PkSvgViewer.vue","../../../../packages/components/src/PkSvgViewer.vue","../../../../packages/components/src/PkSvgViewer.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { ref, onMounted, computed, watch } from 'vue'\n import { useLightbox } from './composables/useLightbox'\n import { useI18n } from 'vue-i18n'\n\n const props = defineProps<{\n svg?: string\n title?: string\n }>()\n\n const { t: $t } = useI18n()\n\n const svgRef = ref<HTMLDivElement | null>(null)\n const galleryRef = ref<HTMLElement>()\n\n const scale = ref(1)\n const translateX = ref(0)\n const translateY = ref(0)\n const startX = ref(0)\n const startY = ref(0)\n\n // PhotoSwipe integration\n const svgDataUrl = ref('')\n const svgWidth = ref(0)\n const svgHeight = ref(0)\n\n const { lightbox, init } = useLightbox({\n gallery: galleryRef,\n downloadable: true,\n openInNewTab: false,\n })\n\n const svgStyle = computed(() => ({\n transform: `translate(${translateX.value}px, ${translateY.value}px) scale(${scale.value})`,\n }))\n\n const zoomIn = () => {\n scale.value = Math.min(scale.value * 1.2, 10)\n }\n\n const zoomOut = () => {\n scale.value = Math.max(scale.value / 1.2, 0.1)\n }\n\n const reset = () => {\n scale.value = 1\n translateX.value = 0\n translateY.value = 0\n }\n\n const openLightbox = () => {\n if (svgDataUrl.value) {\n lightbox.value?.loadAndOpen(0)\n }\n }\n\n const isDragging = ref(false)\n\n // Pan logic\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault()\n isDragging.value = true\n startX.value = e.clientX - translateX.value\n startY.value = e.clientY - translateY.value\n }\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging.value) {\n return\n }\n translateX.value = e.clientX - startX.value\n translateY.value = e.clientY - startY.value\n }\n\n const onMouseUp = () => {\n isDragging.value = false\n }\n\n function serializeSvg(): string | null {\n const svg = svgRef.value?.querySelector('svg')\n if (!svg) {\n return null\n }\n return new XMLSerializer().serializeToString(svg)\n }\n\n function updateSvgExport() {\n const svg = svgRef.value?.querySelector('svg')\n const serialized = serializeSvg()\n if (!svg || !serialized) {\n return\n }\n\n const viewBox = svg.viewBox?.baseVal\n svgWidth.value =\n viewBox?.width || svg.getBoundingClientRect().width || 1200\n svgHeight.value =\n viewBox?.height || svg.getBoundingClientRect().height || 800\n svgDataUrl.value = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(serialized)}`\n }\n\n watch(\n () => props.svg,\n () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n },\n )\n\n onMounted(async () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n await init()\n })\n</script>\n\n<template>\n <div\n class=\"pk-svg-viewer\"\n :class=\"{\n 'pk-svg-viewer--dragging': isDragging,\n }\">\n <!-- Hidden Gallery for Lightbox -->\n <div ref=\"galleryRef\" class=\"hidden\">\n <a\n v-if=\"svgDataUrl\"\n :href=\"svgDataUrl\"\n :data-pswp-src=\"svgDataUrl\"\n :data-pswp-width=\"svgWidth\"\n :data-pswp-height=\"svgHeight\"\n :title=\"title\" />\n </div>\n\n <!-- SVG Stage -->\n <div\n class=\"pk-svg-viewer__stage\"\n @mousedown=\"onMouseDown\"\n @mousemove=\"onMouseMove\"\n @mouseup=\"onMouseUp\"\n @mouseleave=\"onMouseUp\">\n <div\n ref=\"svgRef\"\n :style=\"svgStyle\"\n class=\"pk-svg-viewer__content\" />\n </div>\n\n <!-- Floating Toolbar -->\n <div class=\"pk-svg-viewer__toolbar\">\n <VvButtonGroup\n modifiers=\"compact\"\n class=\"bg-surface-1 shadow-md overflow-hidden\">\n <transition name=\"fade\">\n <VvButton\n key=\"reset\"\n modifiers=\"action-small\"\n :disabled=\"\n scale === 1 && translateX === 0 && translateY === 0\n \"\n icon=\"ri:aspect-ratio-line\"\n :title=\"$t('action.reset')\"\n @click=\"reset\" />\n </transition>\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:zoom-out-line\"\n :title=\"$t('action.zoomOut')\"\n @click=\"zoomOut\" />\n <VvButton\n modifiers=\"action-small\"\n icon=\"ri:zoom-in-line\"\n :title=\"$t('action.zoomIn')\"\n @click=\"zoomIn\" />\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:fullscreen-line\"\n :title=\"$t('action.expand')\"\n @click=\"openLightbox\" />\n </VvButtonGroup>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-svg-viewer {\n position: relative;\n overflow: hidden;\n background-color: var(--color-surface-2);\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &__content {\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: grab;\n transition: transform 0.3s ease-out;\n transform-origin: center center;\n\n svg {\n max-width: 100%;\n max-height: 100%;\n height: auto;\n width: auto;\n }\n }\n\n &__toolbar {\n position: absolute;\n top: var(--spacing-8);\n right: var(--spacing-8);\n z-index: var(--z-dropdown);\n opacity: 0;\n transition: var(--transition-opacity);\n }\n\n &__stage {\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n }\n\n &:hover {\n .pk-svg-viewer__toolbar {\n opacity: 1;\n }\n }\n\n &--dragging {\n .pk-svg-viewer__toolbar {\n opacity: 0.5 !important;\n pointer-events: none;\n\n & * {\n pointer-events: none;\n }\n }\n\n .pk-svg-viewer__content {\n cursor: grabbing !important;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { ref, onMounted, computed, watch } from 'vue'\n import { useLightbox } from './composables/useLightbox'\n import { useI18n } from 'vue-i18n'\n\n const props = defineProps<{\n svg?: string\n title?: string\n }>()\n\n const { t: $t } = useI18n()\n\n const svgRef = ref<HTMLDivElement | null>(null)\n const galleryRef = ref<HTMLElement>()\n\n const scale = ref(1)\n const translateX = ref(0)\n const translateY = ref(0)\n const startX = ref(0)\n const startY = ref(0)\n\n // PhotoSwipe integration\n const svgDataUrl = ref('')\n const svgWidth = ref(0)\n const svgHeight = ref(0)\n\n const { lightbox, init } = useLightbox({\n gallery: galleryRef,\n downloadable: true,\n openInNewTab: false,\n })\n\n const svgStyle = computed(() => ({\n transform: `translate(${translateX.value}px, ${translateY.value}px) scale(${scale.value})`,\n }))\n\n const zoomIn = () => {\n scale.value = Math.min(scale.value * 1.2, 10)\n }\n\n const zoomOut = () => {\n scale.value = Math.max(scale.value / 1.2, 0.1)\n }\n\n const reset = () => {\n scale.value = 1\n translateX.value = 0\n translateY.value = 0\n }\n\n const openLightbox = () => {\n if (svgDataUrl.value) {\n lightbox.value?.loadAndOpen(0)\n }\n }\n\n const isDragging = ref(false)\n\n // Pan logic\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault()\n isDragging.value = true\n startX.value = e.clientX - translateX.value\n startY.value = e.clientY - translateY.value\n }\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging.value) {\n return\n }\n translateX.value = e.clientX - startX.value\n translateY.value = e.clientY - startY.value\n }\n\n const onMouseUp = () => {\n isDragging.value = false\n }\n\n function serializeSvg(): string | null {\n const svg = svgRef.value?.querySelector('svg')\n if (!svg) {\n return null\n }\n return new XMLSerializer().serializeToString(svg)\n }\n\n function updateSvgExport() {\n const svg = svgRef.value?.querySelector('svg')\n const serialized = serializeSvg()\n if (!svg || !serialized) {\n return\n }\n\n const viewBox = svg.viewBox?.baseVal\n svgWidth.value =\n viewBox?.width || svg.getBoundingClientRect().width || 1200\n svgHeight.value =\n viewBox?.height || svg.getBoundingClientRect().height || 800\n svgDataUrl.value = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(serialized)}`\n }\n\n watch(\n () => props.svg,\n () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n },\n )\n\n onMounted(async () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n await init()\n })\n</script>\n\n<template>\n <div\n class=\"pk-svg-viewer\"\n :class=\"{\n 'pk-svg-viewer--dragging': isDragging,\n }\">\n <!-- Hidden Gallery for Lightbox -->\n <div ref=\"galleryRef\" class=\"hidden\">\n <a\n v-if=\"svgDataUrl\"\n :href=\"svgDataUrl\"\n :data-pswp-src=\"svgDataUrl\"\n :data-pswp-width=\"svgWidth\"\n :data-pswp-height=\"svgHeight\"\n :title=\"title\" />\n </div>\n\n <!-- SVG Stage -->\n <div\n class=\"pk-svg-viewer__stage\"\n @mousedown=\"onMouseDown\"\n @mousemove=\"onMouseMove\"\n @mouseup=\"onMouseUp\"\n @mouseleave=\"onMouseUp\">\n <div\n ref=\"svgRef\"\n :style=\"svgStyle\"\n class=\"pk-svg-viewer__content\" />\n </div>\n\n <!-- Floating Toolbar -->\n <div class=\"pk-svg-viewer__toolbar\">\n <VvButtonGroup\n modifiers=\"compact\"\n class=\"bg-surface-1 shadow-md overflow-hidden\">\n <transition name=\"fade\">\n <VvButton\n key=\"reset\"\n modifiers=\"action-small\"\n :disabled=\"\n scale === 1 && translateX === 0 && translateY === 0\n \"\n icon=\"ri:aspect-ratio-line\"\n :title=\"$t('action.reset')\"\n @click=\"reset\" />\n </transition>\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:zoom-out-line\"\n :title=\"$t('action.zoomOut')\"\n @click=\"zoomOut\" />\n <VvButton\n modifiers=\"action-small\"\n icon=\"ri:zoom-in-line\"\n :title=\"$t('action.zoomIn')\"\n @click=\"zoomIn\" />\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:fullscreen-line\"\n :title=\"$t('action.expand')\"\n @click=\"openLightbox\" />\n </VvButtonGroup>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-svg-viewer {\n position: relative;\n overflow: hidden;\n background-color: var(--color-surface-2);\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &__content {\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: grab;\n transition: transform 0.3s ease-out;\n transform-origin: center center;\n\n svg {\n max-width: 100%;\n max-height: 100%;\n height: auto;\n width: auto;\n }\n }\n\n &__toolbar {\n position: absolute;\n top: var(--spacing-8);\n right: var(--spacing-8);\n z-index: var(--z-dropdown);\n opacity: 0;\n transition: var(--transition-opacity);\n }\n\n &__stage {\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n }\n\n &:hover {\n .pk-svg-viewer__toolbar {\n opacity: 1;\n }\n }\n\n &--dragging {\n .pk-svg-viewer__toolbar {\n opacity: 0.5 !important;\n pointer-events: none;\n\n & * {\n pointer-events: none;\n }\n }\n\n .pk-svg-viewer__content {\n cursor: grabbing !important;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { ref, onMounted, computed, watch } from 'vue'\n import { useLightbox } from './composables/useLightbox'\n import { useI18n } from 'vue-i18n'\n\n const props = defineProps<{\n svg?: string\n title?: string\n }>()\n\n const { t: $t } = useI18n()\n\n const svgRef = ref<HTMLDivElement | null>(null)\n const galleryRef = ref<HTMLElement>()\n\n const scale = ref(1)\n const translateX = ref(0)\n const translateY = ref(0)\n const startX = ref(0)\n const startY = ref(0)\n\n // PhotoSwipe integration\n const svgDataUrl = ref('')\n const svgWidth = ref(0)\n const svgHeight = ref(0)\n\n const { lightbox, init } = useLightbox({\n gallery: galleryRef,\n downloadable: true,\n openInNewTab: false,\n })\n\n const svgStyle = computed(() => ({\n transform: `translate(${translateX.value}px, ${translateY.value}px) scale(${scale.value})`,\n }))\n\n const zoomIn = () => {\n scale.value = Math.min(scale.value * 1.2, 10)\n }\n\n const zoomOut = () => {\n scale.value = Math.max(scale.value / 1.2, 0.1)\n }\n\n const reset = () => {\n scale.value = 1\n translateX.value = 0\n translateY.value = 0\n }\n\n const openLightbox = () => {\n if (svgDataUrl.value) {\n lightbox.value?.loadAndOpen(0)\n }\n }\n\n const isDragging = ref(false)\n\n // Pan logic\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault()\n isDragging.value = true\n startX.value = e.clientX - translateX.value\n startY.value = e.clientY - translateY.value\n }\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging.value) {\n return\n }\n translateX.value = e.clientX - startX.value\n translateY.value = e.clientY - startY.value\n }\n\n const onMouseUp = () => {\n isDragging.value = false\n }\n\n function serializeSvg(): string | null {\n const svg = svgRef.value?.querySelector('svg')\n if (!svg) {\n return null\n }\n return new XMLSerializer().serializeToString(svg)\n }\n\n function updateSvgExport() {\n const svg = svgRef.value?.querySelector('svg')\n const serialized = serializeSvg()\n if (!svg || !serialized) {\n return\n }\n\n const viewBox = svg.viewBox?.baseVal\n svgWidth.value =\n viewBox?.width || svg.getBoundingClientRect().width || 1200\n svgHeight.value =\n viewBox?.height || svg.getBoundingClientRect().height || 800\n svgDataUrl.value = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(serialized)}`\n }\n\n watch(\n () => props.svg,\n () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n },\n )\n\n onMounted(async () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n await init()\n })\n</script>\n\n<template>\n <div\n class=\"pk-svg-viewer\"\n :class=\"{\n 'pk-svg-viewer--dragging': isDragging,\n }\">\n <!-- Hidden Gallery for Lightbox -->\n <div ref=\"galleryRef\" class=\"hidden\">\n <a\n v-if=\"svgDataUrl\"\n :href=\"svgDataUrl\"\n :data-pswp-src=\"svgDataUrl\"\n :data-pswp-width=\"svgWidth\"\n :data-pswp-height=\"svgHeight\"\n :title=\"title\" />\n </div>\n\n <!-- SVG Stage -->\n <div\n class=\"pk-svg-viewer__stage\"\n @mousedown=\"onMouseDown\"\n @mousemove=\"onMouseMove\"\n @mouseup=\"onMouseUp\"\n @mouseleave=\"onMouseUp\">\n <div\n ref=\"svgRef\"\n :style=\"svgStyle\"\n class=\"pk-svg-viewer__content\" />\n </div>\n\n <!-- Floating Toolbar -->\n <div class=\"pk-svg-viewer__toolbar\">\n <VvButtonGroup\n modifiers=\"compact\"\n class=\"bg-surface-1 shadow-md overflow-hidden\">\n <transition name=\"fade\">\n <VvButton\n key=\"reset\"\n modifiers=\"action-small\"\n :disabled=\"\n scale === 1 && translateX === 0 && translateY === 0\n \"\n icon=\"ri:aspect-ratio-line\"\n :title=\"$t('action.reset')\"\n @click=\"reset\" />\n </transition>\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:zoom-out-line\"\n :title=\"$t('action.zoomOut')\"\n @click=\"zoomOut\" />\n <VvButton\n modifiers=\"action-small\"\n icon=\"ri:zoom-in-line\"\n :title=\"$t('action.zoomIn')\"\n @click=\"zoomIn\" />\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:fullscreen-line\"\n :title=\"$t('action.expand')\"\n @click=\"openLightbox\" />\n </VvButtonGroup>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-svg-viewer {\n position: relative;\n overflow: hidden;\n background-color: var(--color-surface-2);\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &__content {\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: grab;\n transition: transform 0.3s ease-out;\n transform-origin: center center;\n\n svg {\n max-width: 100%;\n max-height: 100%;\n height: auto;\n width: auto;\n }\n }\n\n &__toolbar {\n position: absolute;\n top: var(--spacing-8);\n right: var(--spacing-8);\n z-index: var(--z-dropdown);\n opacity: 0;\n transition: var(--transition-opacity);\n }\n\n &__stage {\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n }\n\n &:hover {\n .pk-svg-viewer__toolbar {\n opacity: 1;\n }\n }\n\n &--dragging {\n .pk-svg-viewer__toolbar {\n opacity: 0.5 !important;\n pointer-events: none;\n\n & * {\n pointer-events: none;\n }\n }\n\n .pk-svg-viewer__content {\n cursor: grabbing !important;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkToolShowDiagram-iQRTxxLw.js","names":["_hoisted_1","_hoisted_2","__unplugin_components_0","__unplugin_components_1","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createCommentVNode","_normalizeStyle","_createVNode","_withCtx","_Transition","_unref","_createBlock","_sfc_main","_style_0","__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_unref","_withCtx","_createBlock","_createCommentVNode","PkSvgViewer","_sfc_main","_style_0","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","normalizeStyle","onMounted","openBlock","init_runtime_core_esm_bundler","unref","watch","createCommentVNode","createBlock","createBaseVNode","init_reactivity_esm_bundler","ref","init_runtime_dom_esm_bundler","Transition","normalizeClass","createVNode","useI18n","toSnakeCase","useClipboard","au","Ce","VvButtonLocalized","_plugin_vue_export_helper_default","useLightbox","_hoisted_1$1","_hoisted_2$1","PkSvgViewer_default","props","__props","$t","svgRef","galleryRef","scale","translateX","translateY","startX","startY","svgDataUrl","svgWidth","svgHeight","svgStyle","computed","zoomIn","zoomOut","reset","openLightbox","lightbox","isDragging","onMouseDown","onMouseMove","onMouseUp","serializeSvg","svg","updateSvgExport","serialized","viewBox","init","_component_VvButton","_component_VvButtonGroup","createElementBlock","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","DiagramType","\"Sequence\"","\"Class\"","\"State\"","\"ER\"","\"XYChart\"","\"Flowchart\"","\"Graph\"","PkToolShowDiagram_default","$te","toolPart","svgError","svgContent","diagramType","code","diagramTypeLabel","diagramTypeIcon","renderDiagram","theme","THEMES","renderMermaidSVG","downloadSvg","blob","url","anchor","copyCode","copy","_component_VvIcon","copied"],"sources":["../../../../packages/components/src/PkSvgViewer.vue","../../../../packages/components/src/PkSvgViewer.vue","../../../../packages/components/src/PkSvgViewer.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { ref, onMounted, computed, watch } from 'vue'\n import { useLightbox } from './composables/useLightbox'\n import { useI18n } from 'vue-i18n'\n\n const props = defineProps<{\n svg?: string\n title?: string\n }>()\n\n const { t: $t } = useI18n()\n\n const svgRef = ref<HTMLDivElement | null>(null)\n const galleryRef = ref<HTMLElement>()\n\n const scale = ref(1)\n const translateX = ref(0)\n const translateY = ref(0)\n const startX = ref(0)\n const startY = ref(0)\n\n // PhotoSwipe integration\n const svgDataUrl = ref('')\n const svgWidth = ref(0)\n const svgHeight = ref(0)\n\n const { lightbox, init } = useLightbox({\n gallery: galleryRef,\n downloadable: true,\n openInNewTab: false,\n })\n\n const svgStyle = computed(() => ({\n transform: `translate(${translateX.value}px, ${translateY.value}px) scale(${scale.value})`,\n }))\n\n const zoomIn = () => {\n scale.value = Math.min(scale.value * 1.2, 10)\n }\n\n const zoomOut = () => {\n scale.value = Math.max(scale.value / 1.2, 0.1)\n }\n\n const reset = () => {\n scale.value = 1\n translateX.value = 0\n translateY.value = 0\n }\n\n const openLightbox = () => {\n if (svgDataUrl.value) {\n lightbox.value?.loadAndOpen(0)\n }\n }\n\n const isDragging = ref(false)\n\n // Pan logic\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault()\n isDragging.value = true\n startX.value = e.clientX - translateX.value\n startY.value = e.clientY - translateY.value\n }\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging.value) {\n return\n }\n translateX.value = e.clientX - startX.value\n translateY.value = e.clientY - startY.value\n }\n\n const onMouseUp = () => {\n isDragging.value = false\n }\n\n function serializeSvg(): string | null {\n const svg = svgRef.value?.querySelector('svg')\n if (!svg) {\n return null\n }\n return new XMLSerializer().serializeToString(svg)\n }\n\n function updateSvgExport() {\n const svg = svgRef.value?.querySelector('svg')\n const serialized = serializeSvg()\n if (!svg || !serialized) {\n return\n }\n\n const viewBox = svg.viewBox?.baseVal\n svgWidth.value =\n viewBox?.width || svg.getBoundingClientRect().width || 1200\n svgHeight.value =\n viewBox?.height || svg.getBoundingClientRect().height || 800\n svgDataUrl.value = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(serialized)}`\n }\n\n watch(\n () => props.svg,\n () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n },\n )\n\n onMounted(async () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n await init()\n })\n</script>\n\n<template>\n <div\n class=\"pk-svg-viewer\"\n :class=\"{\n 'pk-svg-viewer--dragging': isDragging,\n }\">\n <!-- Hidden Gallery for Lightbox -->\n <div ref=\"galleryRef\" class=\"hidden\">\n <a\n v-if=\"svgDataUrl\"\n :href=\"svgDataUrl\"\n :data-pswp-src=\"svgDataUrl\"\n :data-pswp-width=\"svgWidth\"\n :data-pswp-height=\"svgHeight\"\n :title=\"title\" />\n </div>\n\n <!-- SVG Stage -->\n <div\n class=\"pk-svg-viewer__stage\"\n @mousedown=\"onMouseDown\"\n @mousemove=\"onMouseMove\"\n @mouseup=\"onMouseUp\"\n @mouseleave=\"onMouseUp\">\n <div\n ref=\"svgRef\"\n :style=\"svgStyle\"\n class=\"pk-svg-viewer__content\" />\n </div>\n\n <!-- Floating Toolbar -->\n <div class=\"pk-svg-viewer__toolbar\">\n <VvButtonGroup\n modifiers=\"compact\"\n class=\"bg-surface-1 shadow-md overflow-hidden\">\n <transition name=\"fade\">\n <VvButton\n key=\"reset\"\n modifiers=\"action-small\"\n :disabled=\"\n scale === 1 && translateX === 0 && translateY === 0\n \"\n icon=\"ri:aspect-ratio-line\"\n :title=\"$t('action.reset')\"\n @click=\"reset\" />\n </transition>\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:zoom-out-line\"\n :title=\"$t('action.zoomOut')\"\n @click=\"zoomOut\" />\n <VvButton\n modifiers=\"action-small\"\n icon=\"ri:zoom-in-line\"\n :title=\"$t('action.zoomIn')\"\n @click=\"zoomIn\" />\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:fullscreen-line\"\n :title=\"$t('action.expand')\"\n @click=\"openLightbox\" />\n </VvButtonGroup>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-svg-viewer {\n position: relative;\n overflow: hidden;\n background-color: var(--color-surface-2);\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &__content {\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: grab;\n transition: transform 0.3s ease-out;\n transform-origin: center center;\n\n svg {\n max-width: 100%;\n max-height: 100%;\n height: auto;\n width: auto;\n }\n }\n\n &__toolbar {\n position: absolute;\n top: var(--spacing-8);\n right: var(--spacing-8);\n z-index: var(--z-dropdown);\n opacity: 0;\n transition: var(--transition-opacity);\n }\n\n &__stage {\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n }\n\n &:hover {\n .pk-svg-viewer__toolbar {\n opacity: 1;\n }\n }\n\n &--dragging {\n .pk-svg-viewer__toolbar {\n opacity: 0.5 !important;\n pointer-events: none;\n\n & * {\n pointer-events: none;\n }\n }\n\n .pk-svg-viewer__content {\n cursor: grabbing !important;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { ref, onMounted, computed, watch } from 'vue'\n import { useLightbox } from './composables/useLightbox'\n import { useI18n } from 'vue-i18n'\n\n const props = defineProps<{\n svg?: string\n title?: string\n }>()\n\n const { t: $t } = useI18n()\n\n const svgRef = ref<HTMLDivElement | null>(null)\n const galleryRef = ref<HTMLElement>()\n\n const scale = ref(1)\n const translateX = ref(0)\n const translateY = ref(0)\n const startX = ref(0)\n const startY = ref(0)\n\n // PhotoSwipe integration\n const svgDataUrl = ref('')\n const svgWidth = ref(0)\n const svgHeight = ref(0)\n\n const { lightbox, init } = useLightbox({\n gallery: galleryRef,\n downloadable: true,\n openInNewTab: false,\n })\n\n const svgStyle = computed(() => ({\n transform: `translate(${translateX.value}px, ${translateY.value}px) scale(${scale.value})`,\n }))\n\n const zoomIn = () => {\n scale.value = Math.min(scale.value * 1.2, 10)\n }\n\n const zoomOut = () => {\n scale.value = Math.max(scale.value / 1.2, 0.1)\n }\n\n const reset = () => {\n scale.value = 1\n translateX.value = 0\n translateY.value = 0\n }\n\n const openLightbox = () => {\n if (svgDataUrl.value) {\n lightbox.value?.loadAndOpen(0)\n }\n }\n\n const isDragging = ref(false)\n\n // Pan logic\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault()\n isDragging.value = true\n startX.value = e.clientX - translateX.value\n startY.value = e.clientY - translateY.value\n }\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging.value) {\n return\n }\n translateX.value = e.clientX - startX.value\n translateY.value = e.clientY - startY.value\n }\n\n const onMouseUp = () => {\n isDragging.value = false\n }\n\n function serializeSvg(): string | null {\n const svg = svgRef.value?.querySelector('svg')\n if (!svg) {\n return null\n }\n return new XMLSerializer().serializeToString(svg)\n }\n\n function updateSvgExport() {\n const svg = svgRef.value?.querySelector('svg')\n const serialized = serializeSvg()\n if (!svg || !serialized) {\n return\n }\n\n const viewBox = svg.viewBox?.baseVal\n svgWidth.value =\n viewBox?.width || svg.getBoundingClientRect().width || 1200\n svgHeight.value =\n viewBox?.height || svg.getBoundingClientRect().height || 800\n svgDataUrl.value = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(serialized)}`\n }\n\n watch(\n () => props.svg,\n () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n },\n )\n\n onMounted(async () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n await init()\n })\n</script>\n\n<template>\n <div\n class=\"pk-svg-viewer\"\n :class=\"{\n 'pk-svg-viewer--dragging': isDragging,\n }\">\n <!-- Hidden Gallery for Lightbox -->\n <div ref=\"galleryRef\" class=\"hidden\">\n <a\n v-if=\"svgDataUrl\"\n :href=\"svgDataUrl\"\n :data-pswp-src=\"svgDataUrl\"\n :data-pswp-width=\"svgWidth\"\n :data-pswp-height=\"svgHeight\"\n :title=\"title\" />\n </div>\n\n <!-- SVG Stage -->\n <div\n class=\"pk-svg-viewer__stage\"\n @mousedown=\"onMouseDown\"\n @mousemove=\"onMouseMove\"\n @mouseup=\"onMouseUp\"\n @mouseleave=\"onMouseUp\">\n <div\n ref=\"svgRef\"\n :style=\"svgStyle\"\n class=\"pk-svg-viewer__content\" />\n </div>\n\n <!-- Floating Toolbar -->\n <div class=\"pk-svg-viewer__toolbar\">\n <VvButtonGroup\n modifiers=\"compact\"\n class=\"bg-surface-1 shadow-md overflow-hidden\">\n <transition name=\"fade\">\n <VvButton\n key=\"reset\"\n modifiers=\"action-small\"\n :disabled=\"\n scale === 1 && translateX === 0 && translateY === 0\n \"\n icon=\"ri:aspect-ratio-line\"\n :title=\"$t('action.reset')\"\n @click=\"reset\" />\n </transition>\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:zoom-out-line\"\n :title=\"$t('action.zoomOut')\"\n @click=\"zoomOut\" />\n <VvButton\n modifiers=\"action-small\"\n icon=\"ri:zoom-in-line\"\n :title=\"$t('action.zoomIn')\"\n @click=\"zoomIn\" />\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:fullscreen-line\"\n :title=\"$t('action.expand')\"\n @click=\"openLightbox\" />\n </VvButtonGroup>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-svg-viewer {\n position: relative;\n overflow: hidden;\n background-color: var(--color-surface-2);\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &__content {\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: grab;\n transition: transform 0.3s ease-out;\n transform-origin: center center;\n\n svg {\n max-width: 100%;\n max-height: 100%;\n height: auto;\n width: auto;\n }\n }\n\n &__toolbar {\n position: absolute;\n top: var(--spacing-8);\n right: var(--spacing-8);\n z-index: var(--z-dropdown);\n opacity: 0;\n transition: var(--transition-opacity);\n }\n\n &__stage {\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n }\n\n &:hover {\n .pk-svg-viewer__toolbar {\n opacity: 1;\n }\n }\n\n &--dragging {\n .pk-svg-viewer__toolbar {\n opacity: 0.5 !important;\n pointer-events: none;\n\n & * {\n pointer-events: none;\n }\n }\n\n .pk-svg-viewer__content {\n cursor: grabbing !important;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { ref, onMounted, computed, watch } from 'vue'\n import { useLightbox } from './composables/useLightbox'\n import { useI18n } from 'vue-i18n'\n\n const props = defineProps<{\n svg?: string\n title?: string\n }>()\n\n const { t: $t } = useI18n()\n\n const svgRef = ref<HTMLDivElement | null>(null)\n const galleryRef = ref<HTMLElement>()\n\n const scale = ref(1)\n const translateX = ref(0)\n const translateY = ref(0)\n const startX = ref(0)\n const startY = ref(0)\n\n // PhotoSwipe integration\n const svgDataUrl = ref('')\n const svgWidth = ref(0)\n const svgHeight = ref(0)\n\n const { lightbox, init } = useLightbox({\n gallery: galleryRef,\n downloadable: true,\n openInNewTab: false,\n })\n\n const svgStyle = computed(() => ({\n transform: `translate(${translateX.value}px, ${translateY.value}px) scale(${scale.value})`,\n }))\n\n const zoomIn = () => {\n scale.value = Math.min(scale.value * 1.2, 10)\n }\n\n const zoomOut = () => {\n scale.value = Math.max(scale.value / 1.2, 0.1)\n }\n\n const reset = () => {\n scale.value = 1\n translateX.value = 0\n translateY.value = 0\n }\n\n const openLightbox = () => {\n if (svgDataUrl.value) {\n lightbox.value?.loadAndOpen(0)\n }\n }\n\n const isDragging = ref(false)\n\n // Pan logic\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault()\n isDragging.value = true\n startX.value = e.clientX - translateX.value\n startY.value = e.clientY - translateY.value\n }\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging.value) {\n return\n }\n translateX.value = e.clientX - startX.value\n translateY.value = e.clientY - startY.value\n }\n\n const onMouseUp = () => {\n isDragging.value = false\n }\n\n function serializeSvg(): string | null {\n const svg = svgRef.value?.querySelector('svg')\n if (!svg) {\n return null\n }\n return new XMLSerializer().serializeToString(svg)\n }\n\n function updateSvgExport() {\n const svg = svgRef.value?.querySelector('svg')\n const serialized = serializeSvg()\n if (!svg || !serialized) {\n return\n }\n\n const viewBox = svg.viewBox?.baseVal\n svgWidth.value =\n viewBox?.width || svg.getBoundingClientRect().width || 1200\n svgHeight.value =\n viewBox?.height || svg.getBoundingClientRect().height || 800\n svgDataUrl.value = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(serialized)}`\n }\n\n watch(\n () => props.svg,\n () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n },\n )\n\n onMounted(async () => {\n if (props.svg && svgRef.value) {\n svgRef.value.innerHTML = props.svg\n updateSvgExport()\n }\n await init()\n })\n</script>\n\n<template>\n <div\n class=\"pk-svg-viewer\"\n :class=\"{\n 'pk-svg-viewer--dragging': isDragging,\n }\">\n <!-- Hidden Gallery for Lightbox -->\n <div ref=\"galleryRef\" class=\"hidden\">\n <a\n v-if=\"svgDataUrl\"\n :href=\"svgDataUrl\"\n :data-pswp-src=\"svgDataUrl\"\n :data-pswp-width=\"svgWidth\"\n :data-pswp-height=\"svgHeight\"\n :title=\"title\" />\n </div>\n\n <!-- SVG Stage -->\n <div\n class=\"pk-svg-viewer__stage\"\n @mousedown=\"onMouseDown\"\n @mousemove=\"onMouseMove\"\n @mouseup=\"onMouseUp\"\n @mouseleave=\"onMouseUp\">\n <div\n ref=\"svgRef\"\n :style=\"svgStyle\"\n class=\"pk-svg-viewer__content\" />\n </div>\n\n <!-- Floating Toolbar -->\n <div class=\"pk-svg-viewer__toolbar\">\n <VvButtonGroup\n modifiers=\"compact\"\n class=\"bg-surface-1 shadow-md overflow-hidden\">\n <transition name=\"fade\">\n <VvButton\n key=\"reset\"\n modifiers=\"action-small\"\n :disabled=\"\n scale === 1 && translateX === 0 && translateY === 0\n \"\n icon=\"ri:aspect-ratio-line\"\n :title=\"$t('action.reset')\"\n @click=\"reset\" />\n </transition>\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:zoom-out-line\"\n :title=\"$t('action.zoomOut')\"\n @click=\"zoomOut\" />\n <VvButton\n modifiers=\"action-small\"\n icon=\"ri:zoom-in-line\"\n :title=\"$t('action.zoomIn')\"\n @click=\"zoomIn\" />\n <VvButton\n v-if=\"svgDataUrl\"\n modifiers=\"action-small\"\n icon=\"ri:fullscreen-line\"\n :title=\"$t('action.expand')\"\n @click=\"openLightbox\" />\n </VvButtonGroup>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-svg-viewer {\n position: relative;\n overflow: hidden;\n background-color: var(--color-surface-2);\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &__content {\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: grab;\n transition: transform 0.3s ease-out;\n transform-origin: center center;\n\n svg {\n max-width: 100%;\n max-height: 100%;\n height: auto;\n width: auto;\n }\n }\n\n &__toolbar {\n position: absolute;\n top: var(--spacing-8);\n right: var(--spacing-8);\n z-index: var(--z-dropdown);\n opacity: 0;\n transition: var(--transition-opacity);\n }\n\n &__stage {\n width: 100%;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n }\n\n &:hover {\n .pk-svg-viewer__toolbar {\n opacity: 1;\n }\n }\n\n &--dragging {\n .pk-svg-viewer__toolbar {\n opacity: 0.5 !important;\n pointer-events: none;\n\n & * {\n pointer-events: none;\n }\n }\n\n .pk-svg-viewer__content {\n cursor: grabbing !important;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n"],"mappings":"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"}
@@ -1,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, T as s, Tt as c, b as l, h as u, it as d, m as f, v as p, x as m } from "./vue.runtime.esm-bundler-DKVd2gci.js";
2
2
  import { i as h } from "./dist-DxtbpUdI.js";
3
- import { a as g, d as _, t as v, u as y } from "./index.es-dTcgyeM4.js";
4
- import { t as b } from "./VvButtonLocalized-DV-zbH9b.js";
3
+ import { a as g, p as _, t as v, u as y } from "./index.es-D7jsz1j2.js";
4
+ import { t as b } from "./VvButtonLocalized-PfbIEwav.js";
5
5
  import { n as x } from "./useShortUrlResolver-qFJHKHwE.js";
6
6
  //#region ../../packages/components/src/chat/PkToolShowEmail.vue?vue&type=script&setup=true&lang.ts
7
7
  a(), n(), d();
@@ -88,4 +88,4 @@ var S = { class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-s
88
88
  //#endregion
89
89
  export { j as default };
90
90
 
91
- //# sourceMappingURL=PkToolShowEmail-C7mUCqGL.js.map
91
+ //# sourceMappingURL=PkToolShowEmail-1pyuWnuN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowEmail-C7mUCqGL.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","_sfc_main","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","guardReactiveProps","normalizeProps","createTextVNode","createBaseVNode","init_reactivity_esm_bundler","computed","createElementBlock","createVNode","useClipboard","Qi","ru","Ae","ra","VvButtonLocalized","useShortUrlResolver","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","PkToolShowEmail_default","props","__props","toolPart","subject","resolveText","body","openInMailApp","mailtoLink","openInOutlook","url","openInGmail","openInYahooMail","_component_VvIcon","_component_VvButton","_component_VvDropdownAction","_component_VvDropdown","_component_VvButtonGroup","_ctx","copied","_cache","copy"],"sources":["../../../../packages/components/src/chat/PkToolShowEmail.vue","../../../../packages/components/src/chat/PkToolShowEmail.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useShortUrlResolver } from 'composables'\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 subject: string\n body: string\n }\n }\n return part\n })\n\n // Emails leave the app (mailto/webmail/clipboard), so expand short urls to\n // their real target rather than keeping the fragile `/api/r/` redirect.\n const { resolveText } = useShortUrlResolver()\n const subject = resolveText(() => toolPart.value.input?.subject)\n const body = resolveText(() => toolPart.value.input?.body)\n\n const openInMailApp = () => {\n if (!toolPart.value.input) {\n return\n }\n const mailtoLink = `mailto:?subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(mailtoLink)\n }\n\n const openInOutlook = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://outlook.office.com/mail/deeplink/compose?to=&subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\n }\n\n const openInGmail = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://mail.google.com/mail/u/0/?tf=cm&to=&su=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\n }\n\n const openInYahooMail = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://compose.mail.yahoo.com/?to=&subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\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\">\n <VvIcon name=\"ri:mail-line\" class=\"text-16\" />\n <strong class=\"font-bold\">\n {{ $t('label.email') }}\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=\"copy(`${subject}\\n\\n${body}`)\" />\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:mail-send-line\"\n modifiers=\"action-quiet-small\"\n :label=\"$t('action.send')\" />\n <template #items>\n <VvDropdownAction @click=\"openInOutlook\">\n <VvIcon name=\"mdi:outlook\" />\n {{ $t('action.openInOutlook') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInGmail\">\n <VvIcon name=\"mdi:gmail\" />\n {{ $t('action.openInGmail') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInYahooMail\">\n <VvIcon name=\"mdi:yahoo\" />\n {{ $t('action.openInYahooMail') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInMailApp\">\n <VvIcon name=\"ri:mail-open-line\" />\n {{ $t('action.openInMailApp') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </VvButtonGroup>\n </div>\n <div class=\"border-b border-surface-3 p-sm\">\n <div class=\"text-12 text-word-4 mb-4\">\n {{ $t('label.subject') }}\n </div>\n <div class=\"text-word-2 font-semibold\">\n {{ subject }}\n </div>\n </div>\n <div class=\"p-sm\">\n <div class=\"text-12 text-word-4 mb-4\">\n {{ $t('label.body') }}\n </div>\n <pre class=\"text-word-2 whitespace-pre-wrap break-words\">{{\n body\n }}</pre>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useShortUrlResolver } from 'composables'\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 subject: string\n body: string\n }\n }\n return part\n })\n\n // Emails leave the app (mailto/webmail/clipboard), so expand short urls to\n // their real target rather than keeping the fragile `/api/r/` redirect.\n const { resolveText } = useShortUrlResolver()\n const subject = resolveText(() => toolPart.value.input?.subject)\n const body = resolveText(() => toolPart.value.input?.body)\n\n const openInMailApp = () => {\n if (!toolPart.value.input) {\n return\n }\n const mailtoLink = `mailto:?subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(mailtoLink)\n }\n\n const openInOutlook = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://outlook.office.com/mail/deeplink/compose?to=&subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\n }\n\n const openInGmail = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://mail.google.com/mail/u/0/?tf=cm&to=&su=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\n }\n\n const openInYahooMail = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://compose.mail.yahoo.com/?to=&subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\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\">\n <VvIcon name=\"ri:mail-line\" class=\"text-16\" />\n <strong class=\"font-bold\">\n {{ $t('label.email') }}\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=\"copy(`${subject}\\n\\n${body}`)\" />\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:mail-send-line\"\n modifiers=\"action-quiet-small\"\n :label=\"$t('action.send')\" />\n <template #items>\n <VvDropdownAction @click=\"openInOutlook\">\n <VvIcon name=\"mdi:outlook\" />\n {{ $t('action.openInOutlook') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInGmail\">\n <VvIcon name=\"mdi:gmail\" />\n {{ $t('action.openInGmail') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInYahooMail\">\n <VvIcon name=\"mdi:yahoo\" />\n {{ $t('action.openInYahooMail') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInMailApp\">\n <VvIcon name=\"ri:mail-open-line\" />\n {{ $t('action.openInMailApp') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </VvButtonGroup>\n </div>\n <div class=\"border-b border-surface-3 p-sm\">\n <div class=\"text-12 text-word-4 mb-4\">\n {{ $t('label.subject') }}\n </div>\n <div class=\"text-word-2 font-semibold\">\n {{ subject }}\n </div>\n </div>\n <div class=\"p-sm\">\n <div class=\"text-12 text-word-4 mb-4\">\n {{ $t('label.body') }}\n </div>\n <pre class=\"text-word-2 whitespace-pre-wrap break-words\">{{\n body\n }}</pre>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkToolShowEmail-1pyuWnuN.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","_sfc_main","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","guardReactiveProps","normalizeProps","createTextVNode","createBaseVNode","init_reactivity_esm_bundler","computed","createElementBlock","createVNode","useClipboard","au","su","Ce","na","VvButtonLocalized","useShortUrlResolver","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","PkToolShowEmail_default","props","__props","toolPart","subject","resolveText","body","openInMailApp","mailtoLink","openInOutlook","url","openInGmail","openInYahooMail","_component_VvIcon","_component_VvButton","_component_VvDropdownAction","_component_VvDropdown","_component_VvButtonGroup","_ctx","copied","_cache","copy"],"sources":["../../../../packages/components/src/chat/PkToolShowEmail.vue","../../../../packages/components/src/chat/PkToolShowEmail.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useShortUrlResolver } from 'composables'\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 subject: string\n body: string\n }\n }\n return part\n })\n\n // Emails leave the app (mailto/webmail/clipboard), so expand short urls to\n // their real target rather than keeping the fragile `/api/r/` redirect.\n const { resolveText } = useShortUrlResolver()\n const subject = resolveText(() => toolPart.value.input?.subject)\n const body = resolveText(() => toolPart.value.input?.body)\n\n const openInMailApp = () => {\n if (!toolPart.value.input) {\n return\n }\n const mailtoLink = `mailto:?subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(mailtoLink)\n }\n\n const openInOutlook = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://outlook.office.com/mail/deeplink/compose?to=&subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\n }\n\n const openInGmail = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://mail.google.com/mail/u/0/?tf=cm&to=&su=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\n }\n\n const openInYahooMail = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://compose.mail.yahoo.com/?to=&subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\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\">\n <VvIcon name=\"ri:mail-line\" class=\"text-16\" />\n <strong class=\"font-bold\">\n {{ $t('label.email') }}\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=\"copy(`${subject}\\n\\n${body}`)\" />\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:mail-send-line\"\n modifiers=\"action-quiet-small\"\n :label=\"$t('action.send')\" />\n <template #items>\n <VvDropdownAction @click=\"openInOutlook\">\n <VvIcon name=\"mdi:outlook\" />\n {{ $t('action.openInOutlook') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInGmail\">\n <VvIcon name=\"mdi:gmail\" />\n {{ $t('action.openInGmail') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInYahooMail\">\n <VvIcon name=\"mdi:yahoo\" />\n {{ $t('action.openInYahooMail') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInMailApp\">\n <VvIcon name=\"ri:mail-open-line\" />\n {{ $t('action.openInMailApp') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </VvButtonGroup>\n </div>\n <div class=\"border-b border-surface-3 p-sm\">\n <div class=\"text-12 text-word-4 mb-4\">\n {{ $t('label.subject') }}\n </div>\n <div class=\"text-word-2 font-semibold\">\n {{ subject }}\n </div>\n </div>\n <div class=\"p-sm\">\n <div class=\"text-12 text-word-4 mb-4\">\n {{ $t('label.body') }}\n </div>\n <pre class=\"text-word-2 whitespace-pre-wrap break-words\">{{\n body\n }}</pre>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useShortUrlResolver } from 'composables'\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 subject: string\n body: string\n }\n }\n return part\n })\n\n // Emails leave the app (mailto/webmail/clipboard), so expand short urls to\n // their real target rather than keeping the fragile `/api/r/` redirect.\n const { resolveText } = useShortUrlResolver()\n const subject = resolveText(() => toolPart.value.input?.subject)\n const body = resolveText(() => toolPart.value.input?.body)\n\n const openInMailApp = () => {\n if (!toolPart.value.input) {\n return\n }\n const mailtoLink = `mailto:?subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(mailtoLink)\n }\n\n const openInOutlook = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://outlook.office.com/mail/deeplink/compose?to=&subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\n }\n\n const openInGmail = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://mail.google.com/mail/u/0/?tf=cm&to=&su=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\n }\n\n const openInYahooMail = () => {\n if (!toolPart.value.input) {\n return\n }\n const url = `https://compose.mail.yahoo.com/?to=&subject=${encodeURIComponent(\n subject.value,\n )}&body=${encodeURIComponent(body.value)}`\n window.open(url, '_blank')\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\">\n <VvIcon name=\"ri:mail-line\" class=\"text-16\" />\n <strong class=\"font-bold\">\n {{ $t('label.email') }}\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=\"copy(`${subject}\\n\\n${body}`)\" />\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:mail-send-line\"\n modifiers=\"action-quiet-small\"\n :label=\"$t('action.send')\" />\n <template #items>\n <VvDropdownAction @click=\"openInOutlook\">\n <VvIcon name=\"mdi:outlook\" />\n {{ $t('action.openInOutlook') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInGmail\">\n <VvIcon name=\"mdi:gmail\" />\n {{ $t('action.openInGmail') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInYahooMail\">\n <VvIcon name=\"mdi:yahoo\" />\n {{ $t('action.openInYahooMail') }}\n </VvDropdownAction>\n <VvDropdownAction @click=\"openInMailApp\">\n <VvIcon name=\"ri:mail-open-line\" />\n {{ $t('action.openInMailApp') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </VvButtonGroup>\n </div>\n <div class=\"border-b border-surface-3 p-sm\">\n <div class=\"text-12 text-word-4 mb-4\">\n {{ $t('label.subject') }}\n </div>\n <div class=\"text-word-2 font-semibold\">\n {{ subject }}\n </div>\n </div>\n <div class=\"p-sm\">\n <div class=\"text-12 text-word-4 mb-4\">\n {{ $t('label.body') }}\n </div>\n <pre class=\"text-word-2 whitespace-pre-wrap break-words\">{{\n body\n }}</pre>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -2,8 +2,8 @@ import { $ as e, B as t, C as n, Ct as r, Dt as i, Et as a, G as o, K as s, L as
2
2
  import { r as re } from "./vue-i18n-CooVjJjT.js";
3
3
  import { l as ie, t as ae, u as oe } from "./PkStreamingMarkdown-pEgm7m-B.js";
4
4
  import { g as O } from "./Agent-DU38SnNd.js";
5
- import { c as k, f as se, o as A, p as j, s as M, t as ce } from "./index.es-dTcgyeM4.js";
6
- import { t as le } from "./VvButtonLocalized-DV-zbH9b.js";
5
+ import { c as k, f as A, l as j, m as M, o as se, t as ce } from "./index.es-D7jsz1j2.js";
6
+ import { t as le } from "./VvButtonLocalized-PfbIEwav.js";
7
7
  //#region ../../packages/components/src/PkEditorRadioButtons.vue?vue&type=script&setup=true&lang.ts
8
8
  u(), r();
9
9
  //#endregion
@@ -39,7 +39,7 @@ var ue = /* @__PURE__ */ n({
39
39
  setup(n) {
40
40
  let r = n, a = s(n, "modelValue"), u = o(), d = b(() => `${r.name}-${u}`), f = b(() => [...r.modifiers ? Array.isArray(r.modifiers) ? r.modifiers : [r.modifiers] : [], r.segmented ? "segment" : "card"]);
41
41
  return (r, o) => {
42
- let s = M, u = k;
42
+ let s = j, u = k;
43
43
  return c(), g(u, {
44
44
  modelValue: a.value,
45
45
  "onUpdate:modelValue": o[0] ||= (e) => a.value = e,
@@ -103,7 +103,7 @@ var de = /* @__PURE__ */ n({
103
103
  setup(n) {
104
104
  let r = s(n, "modelValue");
105
105
  return (a, o) => {
106
- let s = A, u = j;
106
+ let s = M, u = A;
107
107
  return c(), g(u, {
108
108
  modelValue: r.value,
109
109
  "onUpdate:modelValue": o[0] ||= (e) => r.value = e,
@@ -379,4 +379,4 @@ var fe = {
379
379
  //#endregion
380
380
  export { z as default };
381
381
 
382
- //# sourceMappingURL=PkToolShowForm-D_2FmDwq.js.map
382
+ //# sourceMappingURL=PkToolShowForm-BRt9i4aW.js.map