@8wave/ai-elements 0.111.0 → 0.112.0-beta.3

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 (40) hide show
  1. package/dist/_chunks/{PkToolRequestGeolocation-BP5yJQIW.js → PkToolRequestGeolocation-65jeiJs8.js} +5 -5
  2. package/dist/_chunks/{PkToolRequestGeolocation-BP5yJQIW.js.map → PkToolRequestGeolocation-65jeiJs8.js.map} +1 -1
  3. package/dist/_chunks/{PkToolRequestOAuthConnection-4SqtPTCS.js → PkToolRequestOAuthConnection-bHpDAtzu.js} +5 -5
  4. package/dist/_chunks/{PkToolRequestOAuthConnection-4SqtPTCS.js.map → PkToolRequestOAuthConnection-bHpDAtzu.js.map} +1 -1
  5. package/dist/_chunks/{PkToolShowForm-C45JD6jS.js → PkToolShowForm-KuTsIy5P.js} +21 -21
  6. package/dist/_chunks/{PkToolShowForm-C45JD6jS.js.map → PkToolShowForm-KuTsIy5P.js.map} +1 -1
  7. package/dist/_chunks/{PkToolShowSources-DlBfHwkg.js → PkToolShowSources-CY_q7ANb.js} +4 -4
  8. package/dist/_chunks/{PkToolShowSources-DlBfHwkg.js.map → PkToolShowSources-CY_q7ANb.js.map} +1 -1
  9. package/dist/_chunks/{PkToolShowSuggestedReply-DzoRQESi.js → PkToolShowSuggestedReply-D98Atidw.js} +5 -5
  10. package/dist/_chunks/{PkToolShowSuggestedReply-DzoRQESi.js.map → PkToolShowSuggestedReply-D98Atidw.js.map} +1 -1
  11. package/dist/_chunks/{Document-Bmx6774B.js → markdown-BRD-hDrH.js} +70 -72
  12. package/dist/_chunks/markdown-BRD-hDrH.js.map +1 -0
  13. package/dist/ai-elements.es.js +602 -590
  14. package/dist/ai-elements.es.js.map +1 -1
  15. package/dist-vue/PkChatbot.js +1 -1
  16. package/dist-vue/PkChatbotMessages.js +1 -1
  17. package/dist-vue/PkChatbotViewChat.js +1 -1
  18. package/dist-vue/PkChatbotViewFiles.js +1 -1
  19. package/dist-vue/_chunks/{Document-8umjeTVS.js → Document-9SUVPzht.js} +70 -72
  20. package/dist-vue/_chunks/Document-9SUVPzht.js.map +1 -0
  21. package/dist-vue/_chunks/{PkChatbot-CloRPTIX.js → PkChatbot-BBR2t9R9.js} +3 -3
  22. package/dist-vue/_chunks/{PkChatbot-CloRPTIX.js.map → PkChatbot-BBR2t9R9.js.map} +1 -1
  23. package/dist-vue/_chunks/{PkChatbotMessages-r9D-kFvX.js → PkChatbotMessages-9NluDSgJ.js} +3 -3
  24. package/dist-vue/_chunks/{PkChatbotMessages-r9D-kFvX.js.map → PkChatbotMessages-9NluDSgJ.js.map} +1 -1
  25. package/dist-vue/_chunks/{PkChatbotViewChat-DP0EUTDu.js → PkChatbotViewChat-C0fxsO-G.js} +3 -3
  26. package/dist-vue/_chunks/{PkChatbotViewChat-DP0EUTDu.js.map → PkChatbotViewChat-C0fxsO-G.js.map} +1 -1
  27. package/dist-vue/_chunks/{PkChatbotViewFiles-hzlJUf0f.js → PkChatbotViewFiles-DPpZHHvK.js} +2 -2
  28. package/dist-vue/_chunks/{PkChatbotViewFiles-hzlJUf0f.js.map → PkChatbotViewFiles-DPpZHHvK.js.map} +1 -1
  29. package/dist-vue/_chunks/{PkToolShowDiagram-BAdJMmt7.js → PkToolShowDiagram-CVHYhfwH.js} +2 -2
  30. package/dist-vue/_chunks/{PkToolShowDiagram-BAdJMmt7.js.map → PkToolShowDiagram-CVHYhfwH.js.map} +1 -1
  31. package/dist-vue/_chunks/{PkToolShowSources-Dlaz2Y77.js → PkToolShowSources-Qq3c8_ZQ.js} +2 -2
  32. package/dist-vue/_chunks/{PkToolShowSources-Dlaz2Y77.js.map → PkToolShowSources-Qq3c8_ZQ.js.map} +1 -1
  33. package/dist-vue/index.js +3136 -3116
  34. package/dist-vue/index.js.map +1 -1
  35. package/dist-vue/packages/components/src/chat/PkLanguageModelCombobox.d.ts +7 -0
  36. package/dist-vue/packages/models/src/schema/ModelProvider.d.ts +121 -8
  37. package/dist-vue/packages/models/src/schema/constants.d.ts +2 -1
  38. package/package.json +1 -1
  39. package/dist/_chunks/Document-Bmx6774B.js.map +0 -1
  40. package/dist-vue/_chunks/Document-8umjeTVS.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { a as e, n as t, o as n } from "./Document-8umjeTVS.js";
1
+ import { a as e, n as t, o as n } from "./Document-9SUVPzht.js";
2
2
  import { n as r } from "./useChatbotStore-BowmAdfD.js";
3
3
  import { t as i } from "./useDialogConfirm-KWz0ohCQ.js";
4
4
  import { t as a } from "./useImageFallback-5vQIO0pC.js";
@@ -108,4 +108,4 @@ var C = { class: "flex flex-col flex-1 min-h-0 relative" }, w = {
108
108
  //#endregion
109
109
  export { R as t };
110
110
 
111
- //# sourceMappingURL=PkChatbotViewFiles-hzlJUf0f.js.map
111
+ //# sourceMappingURL=PkChatbotViewFiles-DPpZHHvK.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkChatbotViewFiles-hzlJUf0f.js","names":[],"sources":["../../../../packages/composable/src/useFileRow.ts","../../../../packages/components/src/chat/PkChatbotViewFiles.vue","../../../../packages/components/src/chat/PkChatbotViewFiles.vue"],"sourcesContent":["import { useI18n } from 'vue-i18n'\nimport { isImageMime, isInlineViewable } from 'models'\nimport { useImageFallback } from './useImageFallback'\n\n/** Minimal shape a file-list row needs for the shared display helpers. */\nexport interface FileRowLike {\n documentId: string\n mimeType: string | null\n}\n\n/**\n * Shared display logic for the user-files list rows, used by both the in-app\n * table (PjUserFilesTable) and the embedded widget (PkChatbotViewFiles). The two\n * surfaces differ only in how they build URLs (authenticated store vs widget API\n * client), so the URL builders are injected.\n *\n * - `view(id, inline)` → opens/downloads the original file.\n * - `preview(id)` → small resized blob for the row thumbnail.\n */\nexport function useFileRow(urls: {\n view: (documentId: string, inline: boolean) => string\n preview: (documentId: string) => string\n}) {\n const { locale } = useI18n({ useScope: 'global' })\n\n // Image rows show a thumbnail; if it fails to load (auth/redirect, or the\n // preview isn't generated yet) we fall back to the file-type icon.\n const { canShowImage, onImageError } = useImageFallback()\n const thumbUrl = (file: FileRowLike): string =>\n urls.preview(file.documentId)\n const showThumb = (file: FileRowLike): boolean =>\n isImageMime(file.mimeType) && canShowImage(thumbUrl(file))\n const onThumbError = (file: FileRowLike): void => {\n onImageError(thumbUrl(file))\n }\n\n // Name click previews images/PDF inline; an explicit download forces an\n // attachment (inline=false → download SAS).\n const openFile = (file: FileRowLike, download = false): void => {\n const inline = !download && isInlineViewable(file.mimeType)\n window.open(urls.view(file.documentId, inline), '_blank', 'noopener')\n }\n\n const formatDate = (iso: string): string =>\n new Date(iso).toLocaleDateString(locale.value, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n\n return { showThumb, thumbUrl, onThumbError, openFile, formatDate }\n}\n","<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { fileIconForMime } from 'models'\n import {\n useChatbotStore,\n useDialogConfirm,\n useFileRow,\n type ChatbotFile,\n } from 'composables'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n const { openDialogConfirm } = useDialogConfirm()\n\n const store = useChatbotStore(props.agentId)\n const { chatFiles, isLoadingChatFiles } = storeToRefs(store)\n const { showThumb, thumbUrl, onThumbError, openFile, formatDate } =\n useFileRow({\n view: store.getChatFileUrl,\n preview: store.getChatFilePreviewUrl,\n })\n\n const deletingId = ref<string | null>(null)\n\n const handleExclude = async (file: ChatbotFile) => {\n const proceed = await openDialogConfirm({\n title: $t('confirmTitle.excludeUserFile'),\n questionLabel: $t('userFiles.excludeConfirm'),\n })\n if (!proceed) {\n return\n }\n deletingId.value = file.documentId\n try {\n await store.deleteChatFile(file.documentId)\n } catch {\n // surfaced by the host's error handling\n } finally {\n deletingId.value = null\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton, shaped like the rows it replaces -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"u-pick-list__list u-pick-list__list--fill\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"u-pick-list__row u-pick-list__row--skeleton\">\n <div class=\"u-pick-list__entry u-pick-list__entry--roomy\">\n <div class=\"vv-skeleton w-36 h-36 shrink-0 rounded-md\">\n <div class=\"vv-skeleton__item\" />\n </div>\n <div class=\"flex flex-col gap-6 flex-1 min-w-0\">\n <div\n class=\"vv-skeleton h-14 rounded\"\n :class=\"i % 2 ? 'w-192' : 'w-128'\">\n <div class=\"vv-skeleton__item\" />\n </div>\n <div class=\"vv-skeleton h-12 rounded w-96\">\n <div class=\"vv-skeleton__item\" />\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"chatFiles.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon\n name=\"ri:folder-open-line\"\n class=\"w-28 h-28 text-word-4\" />\n </div>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('userFiles.chatFilesEmpty') }}\n </span>\n </div>\n\n <!-- File list -->\n <ul\n v-else\n class=\"pk-chatbot-files u-pick-list__list u-pick-list__list--fill overflow-y-auto light-scrollbar\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"u-pick-list__row\">\n <button\n type=\"button\"\n class=\"u-pick-list__entry u-pick-list__entry--roomy\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__icon\">\n <img\n v-if=\"showThumb(file)\"\n :src=\"thumbUrl(file)\"\n loading=\"lazy\"\n decoding=\"async\"\n alt=\"\"\n class=\"pk-chatbot-files__thumb\"\n @error=\"onThumbError(file)\" />\n <VvIcon v-else :name=\"fileIconForMime(file.mimeType)\" />\n </span>\n <span class=\"flex flex-col min-w-0 flex-1\">\n <span class=\"u-pick-list__label\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"text-12 text-word-3\">\n {{ formatDate(file.createdAt) }}\n </span>\n </span>\n </button>\n <div class=\"u-pick-list__actions pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-small\"\n icon=\"ri:delete-bin-line\"\n :title=\"$t('userFiles.exclude')\"\n :loading=\"deletingId === file.documentId\"\n :disabled=\"deletingId === file.documentId\"\n @click=\"handleExclude(file)\" />\n </div>\n </li>\n </ul>\n </div>\n</template>\n\n<style lang=\"scss\">\n // The frame, the rows and the label come from `u-pick-list`; what is left\n // here is the file tile and the pair of row actions.\n .pk-chatbot-files {\n list-style: none;\n margin: 0;\n padding: 0;\n\n // The box exists to clip a thumbnail and to keep the labels of rows\n // with and without one on the same left edge: with a file icon in it\n // there is nothing to draw, so it stays transparent.\n &__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-36);\n height: var(--spacing-36);\n flex-shrink: 0;\n border-radius: var(--rounded-md);\n color: var(--color-word-3);\n overflow: hidden;\n\n svg {\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__actions {\n gap: var(--spacing-2);\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { fileIconForMime } from 'models'\n import {\n useChatbotStore,\n useDialogConfirm,\n useFileRow,\n type ChatbotFile,\n } from 'composables'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n const { openDialogConfirm } = useDialogConfirm()\n\n const store = useChatbotStore(props.agentId)\n const { chatFiles, isLoadingChatFiles } = storeToRefs(store)\n const { showThumb, thumbUrl, onThumbError, openFile, formatDate } =\n useFileRow({\n view: store.getChatFileUrl,\n preview: store.getChatFilePreviewUrl,\n })\n\n const deletingId = ref<string | null>(null)\n\n const handleExclude = async (file: ChatbotFile) => {\n const proceed = await openDialogConfirm({\n title: $t('confirmTitle.excludeUserFile'),\n questionLabel: $t('userFiles.excludeConfirm'),\n })\n if (!proceed) {\n return\n }\n deletingId.value = file.documentId\n try {\n await store.deleteChatFile(file.documentId)\n } catch {\n // surfaced by the host's error handling\n } finally {\n deletingId.value = null\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton, shaped like the rows it replaces -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"u-pick-list__list u-pick-list__list--fill\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"u-pick-list__row u-pick-list__row--skeleton\">\n <div class=\"u-pick-list__entry u-pick-list__entry--roomy\">\n <div class=\"vv-skeleton w-36 h-36 shrink-0 rounded-md\">\n <div class=\"vv-skeleton__item\" />\n </div>\n <div class=\"flex flex-col gap-6 flex-1 min-w-0\">\n <div\n class=\"vv-skeleton h-14 rounded\"\n :class=\"i % 2 ? 'w-192' : 'w-128'\">\n <div class=\"vv-skeleton__item\" />\n </div>\n <div class=\"vv-skeleton h-12 rounded w-96\">\n <div class=\"vv-skeleton__item\" />\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"chatFiles.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon\n name=\"ri:folder-open-line\"\n class=\"w-28 h-28 text-word-4\" />\n </div>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('userFiles.chatFilesEmpty') }}\n </span>\n </div>\n\n <!-- File list -->\n <ul\n v-else\n class=\"pk-chatbot-files u-pick-list__list u-pick-list__list--fill overflow-y-auto light-scrollbar\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"u-pick-list__row\">\n <button\n type=\"button\"\n class=\"u-pick-list__entry u-pick-list__entry--roomy\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__icon\">\n <img\n v-if=\"showThumb(file)\"\n :src=\"thumbUrl(file)\"\n loading=\"lazy\"\n decoding=\"async\"\n alt=\"\"\n class=\"pk-chatbot-files__thumb\"\n @error=\"onThumbError(file)\" />\n <VvIcon v-else :name=\"fileIconForMime(file.mimeType)\" />\n </span>\n <span class=\"flex flex-col min-w-0 flex-1\">\n <span class=\"u-pick-list__label\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"text-12 text-word-3\">\n {{ formatDate(file.createdAt) }}\n </span>\n </span>\n </button>\n <div class=\"u-pick-list__actions pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-small\"\n icon=\"ri:delete-bin-line\"\n :title=\"$t('userFiles.exclude')\"\n :loading=\"deletingId === file.documentId\"\n :disabled=\"deletingId === file.documentId\"\n @click=\"handleExclude(file)\" />\n </div>\n </li>\n </ul>\n </div>\n</template>\n\n<style lang=\"scss\">\n // The frame, the rows and the label come from `u-pick-list`; what is left\n // here is the file tile and the pair of row actions.\n .pk-chatbot-files {\n list-style: none;\n margin: 0;\n padding: 0;\n\n // The box exists to clip a thumbnail and to keep the labels of rows\n // with and without one on the same left edge: with a file icon in it\n // there is nothing to draw, so it stays transparent.\n &__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-36);\n height: var(--spacing-36);\n flex-shrink: 0;\n border-radius: var(--rounded-md);\n color: var(--color-word-3);\n overflow: hidden;\n\n svg {\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__actions {\n gap: var(--spacing-2);\n }\n }\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkChatbotViewFiles-DPpZHHvK.js","names":[],"sources":["../../../../packages/composable/src/useFileRow.ts","../../../../packages/components/src/chat/PkChatbotViewFiles.vue","../../../../packages/components/src/chat/PkChatbotViewFiles.vue"],"sourcesContent":["import { useI18n } from 'vue-i18n'\nimport { isImageMime, isInlineViewable } from 'models'\nimport { useImageFallback } from './useImageFallback'\n\n/** Minimal shape a file-list row needs for the shared display helpers. */\nexport interface FileRowLike {\n documentId: string\n mimeType: string | null\n}\n\n/**\n * Shared display logic for the user-files list rows, used by both the in-app\n * table (PjUserFilesTable) and the embedded widget (PkChatbotViewFiles). The two\n * surfaces differ only in how they build URLs (authenticated store vs widget API\n * client), so the URL builders are injected.\n *\n * - `view(id, inline)` → opens/downloads the original file.\n * - `preview(id)` → small resized blob for the row thumbnail.\n */\nexport function useFileRow(urls: {\n view: (documentId: string, inline: boolean) => string\n preview: (documentId: string) => string\n}) {\n const { locale } = useI18n({ useScope: 'global' })\n\n // Image rows show a thumbnail; if it fails to load (auth/redirect, or the\n // preview isn't generated yet) we fall back to the file-type icon.\n const { canShowImage, onImageError } = useImageFallback()\n const thumbUrl = (file: FileRowLike): string =>\n urls.preview(file.documentId)\n const showThumb = (file: FileRowLike): boolean =>\n isImageMime(file.mimeType) && canShowImage(thumbUrl(file))\n const onThumbError = (file: FileRowLike): void => {\n onImageError(thumbUrl(file))\n }\n\n // Name click previews images/PDF inline; an explicit download forces an\n // attachment (inline=false → download SAS).\n const openFile = (file: FileRowLike, download = false): void => {\n const inline = !download && isInlineViewable(file.mimeType)\n window.open(urls.view(file.documentId, inline), '_blank', 'noopener')\n }\n\n const formatDate = (iso: string): string =>\n new Date(iso).toLocaleDateString(locale.value, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n\n return { showThumb, thumbUrl, onThumbError, openFile, formatDate }\n}\n","<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { fileIconForMime } from 'models'\n import {\n useChatbotStore,\n useDialogConfirm,\n useFileRow,\n type ChatbotFile,\n } from 'composables'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n const { openDialogConfirm } = useDialogConfirm()\n\n const store = useChatbotStore(props.agentId)\n const { chatFiles, isLoadingChatFiles } = storeToRefs(store)\n const { showThumb, thumbUrl, onThumbError, openFile, formatDate } =\n useFileRow({\n view: store.getChatFileUrl,\n preview: store.getChatFilePreviewUrl,\n })\n\n const deletingId = ref<string | null>(null)\n\n const handleExclude = async (file: ChatbotFile) => {\n const proceed = await openDialogConfirm({\n title: $t('confirmTitle.excludeUserFile'),\n questionLabel: $t('userFiles.excludeConfirm'),\n })\n if (!proceed) {\n return\n }\n deletingId.value = file.documentId\n try {\n await store.deleteChatFile(file.documentId)\n } catch {\n // surfaced by the host's error handling\n } finally {\n deletingId.value = null\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton, shaped like the rows it replaces -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"u-pick-list__list u-pick-list__list--fill\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"u-pick-list__row u-pick-list__row--skeleton\">\n <div class=\"u-pick-list__entry u-pick-list__entry--roomy\">\n <div class=\"vv-skeleton w-36 h-36 shrink-0 rounded-md\">\n <div class=\"vv-skeleton__item\" />\n </div>\n <div class=\"flex flex-col gap-6 flex-1 min-w-0\">\n <div\n class=\"vv-skeleton h-14 rounded\"\n :class=\"i % 2 ? 'w-192' : 'w-128'\">\n <div class=\"vv-skeleton__item\" />\n </div>\n <div class=\"vv-skeleton h-12 rounded w-96\">\n <div class=\"vv-skeleton__item\" />\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"chatFiles.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon\n name=\"ri:folder-open-line\"\n class=\"w-28 h-28 text-word-4\" />\n </div>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('userFiles.chatFilesEmpty') }}\n </span>\n </div>\n\n <!-- File list -->\n <ul\n v-else\n class=\"pk-chatbot-files u-pick-list__list u-pick-list__list--fill overflow-y-auto light-scrollbar\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"u-pick-list__row\">\n <button\n type=\"button\"\n class=\"u-pick-list__entry u-pick-list__entry--roomy\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__icon\">\n <img\n v-if=\"showThumb(file)\"\n :src=\"thumbUrl(file)\"\n loading=\"lazy\"\n decoding=\"async\"\n alt=\"\"\n class=\"pk-chatbot-files__thumb\"\n @error=\"onThumbError(file)\" />\n <VvIcon v-else :name=\"fileIconForMime(file.mimeType)\" />\n </span>\n <span class=\"flex flex-col min-w-0 flex-1\">\n <span class=\"u-pick-list__label\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"text-12 text-word-3\">\n {{ formatDate(file.createdAt) }}\n </span>\n </span>\n </button>\n <div class=\"u-pick-list__actions pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-small\"\n icon=\"ri:delete-bin-line\"\n :title=\"$t('userFiles.exclude')\"\n :loading=\"deletingId === file.documentId\"\n :disabled=\"deletingId === file.documentId\"\n @click=\"handleExclude(file)\" />\n </div>\n </li>\n </ul>\n </div>\n</template>\n\n<style lang=\"scss\">\n // The frame, the rows and the label come from `u-pick-list`; what is left\n // here is the file tile and the pair of row actions.\n .pk-chatbot-files {\n list-style: none;\n margin: 0;\n padding: 0;\n\n // The box exists to clip a thumbnail and to keep the labels of rows\n // with and without one on the same left edge: with a file icon in it\n // there is nothing to draw, so it stays transparent.\n &__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-36);\n height: var(--spacing-36);\n flex-shrink: 0;\n border-radius: var(--rounded-md);\n color: var(--color-word-3);\n overflow: hidden;\n\n svg {\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__actions {\n gap: var(--spacing-2);\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { fileIconForMime } from 'models'\n import {\n useChatbotStore,\n useDialogConfirm,\n useFileRow,\n type ChatbotFile,\n } from 'composables'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n const { openDialogConfirm } = useDialogConfirm()\n\n const store = useChatbotStore(props.agentId)\n const { chatFiles, isLoadingChatFiles } = storeToRefs(store)\n const { showThumb, thumbUrl, onThumbError, openFile, formatDate } =\n useFileRow({\n view: store.getChatFileUrl,\n preview: store.getChatFilePreviewUrl,\n })\n\n const deletingId = ref<string | null>(null)\n\n const handleExclude = async (file: ChatbotFile) => {\n const proceed = await openDialogConfirm({\n title: $t('confirmTitle.excludeUserFile'),\n questionLabel: $t('userFiles.excludeConfirm'),\n })\n if (!proceed) {\n return\n }\n deletingId.value = file.documentId\n try {\n await store.deleteChatFile(file.documentId)\n } catch {\n // surfaced by the host's error handling\n } finally {\n deletingId.value = null\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton, shaped like the rows it replaces -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"u-pick-list__list u-pick-list__list--fill\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"u-pick-list__row u-pick-list__row--skeleton\">\n <div class=\"u-pick-list__entry u-pick-list__entry--roomy\">\n <div class=\"vv-skeleton w-36 h-36 shrink-0 rounded-md\">\n <div class=\"vv-skeleton__item\" />\n </div>\n <div class=\"flex flex-col gap-6 flex-1 min-w-0\">\n <div\n class=\"vv-skeleton h-14 rounded\"\n :class=\"i % 2 ? 'w-192' : 'w-128'\">\n <div class=\"vv-skeleton__item\" />\n </div>\n <div class=\"vv-skeleton h-12 rounded w-96\">\n <div class=\"vv-skeleton__item\" />\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"chatFiles.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon\n name=\"ri:folder-open-line\"\n class=\"w-28 h-28 text-word-4\" />\n </div>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('userFiles.chatFilesEmpty') }}\n </span>\n </div>\n\n <!-- File list -->\n <ul\n v-else\n class=\"pk-chatbot-files u-pick-list__list u-pick-list__list--fill overflow-y-auto light-scrollbar\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"u-pick-list__row\">\n <button\n type=\"button\"\n class=\"u-pick-list__entry u-pick-list__entry--roomy\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__icon\">\n <img\n v-if=\"showThumb(file)\"\n :src=\"thumbUrl(file)\"\n loading=\"lazy\"\n decoding=\"async\"\n alt=\"\"\n class=\"pk-chatbot-files__thumb\"\n @error=\"onThumbError(file)\" />\n <VvIcon v-else :name=\"fileIconForMime(file.mimeType)\" />\n </span>\n <span class=\"flex flex-col min-w-0 flex-1\">\n <span class=\"u-pick-list__label\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"text-12 text-word-3\">\n {{ formatDate(file.createdAt) }}\n </span>\n </span>\n </button>\n <div class=\"u-pick-list__actions pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-small\"\n icon=\"ri:delete-bin-line\"\n :title=\"$t('userFiles.exclude')\"\n :loading=\"deletingId === file.documentId\"\n :disabled=\"deletingId === file.documentId\"\n @click=\"handleExclude(file)\" />\n </div>\n </li>\n </ul>\n </div>\n</template>\n\n<style lang=\"scss\">\n // The frame, the rows and the label come from `u-pick-list`; what is left\n // here is the file tile and the pair of row actions.\n .pk-chatbot-files {\n list-style: none;\n margin: 0;\n padding: 0;\n\n // The box exists to clip a thumbnail and to keep the labels of rows\n // with and without one on the same left edge: with a file icon in it\n // there is nothing to draw, so it stays transparent.\n &__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-36);\n height: var(--spacing-36);\n flex-shrink: 0;\n border-radius: var(--rounded-md);\n color: var(--color-word-3);\n overflow: hidden;\n\n svg {\n width: var(--spacing-20);\n height: var(--spacing-20);\n }\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__actions {\n gap: var(--spacing-2);\n }\n }\n</style>\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import "./src-DT8FLE4_.js";
2
- import { a as e } from "./PkChatbotMessages-r9D-kFvX.js";
2
+ import { a as e } from "./PkChatbotMessages-9NluDSgJ.js";
3
3
  import { t } from "./PkSvgViewer-D-0M5bqg.js";
4
4
  import { VvButton as n, VvButtonGroup as r, VvIcon as i } from "@volverjs/ui-vue/components";
5
5
  import { computed as a, createBlock as o, createCommentVNode as s, createElementBlock as c, createElementVNode as l, createVNode as u, defineComponent as d, onMounted as f, openBlock as p, ref as m, toDisplayString as h, unref as g, watch as _, withCtx as v } from "vue";
@@ -119,4 +119,4 @@ var x = {
119
119
  //#endregion
120
120
  export { k as default };
121
121
 
122
- //# sourceMappingURL=PkToolShowDiagram-BAdJMmt7.js.map
122
+ //# sourceMappingURL=PkToolShowDiagram-CVHYhfwH.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowDiagram-BAdJMmt7.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue"],"sourcesContent":["<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-CVHYhfwH.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue"],"sourcesContent":["<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,5 +1,5 @@
1
1
  import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
2
- import { i as t } from "./Document-8umjeTVS.js";
2
+ import { i as t } from "./Document-9SUVPzht.js";
3
3
  import { t as n } from "./PkStreamingMarkdown-D0IOaETI.js";
4
4
  import "./src-DT8FLE4_.js";
5
5
  import { t as r } from "./markdown-CCMpGUA7.js";
@@ -239,4 +239,4 @@ var w = ["title"], T = {
239
239
  //#endregion
240
240
  export { E as i, X as n, j as r, Z as t };
241
241
 
242
- //# sourceMappingURL=PkToolShowSources-Dlaz2Y77.js.map
242
+ //# sourceMappingURL=PkToolShowSources-Qq3c8_ZQ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowSources-Dlaz2Y77.js","names":["$t"],"sources":["../../../../packages/components/src/chat/PkChunkPreview.vue","../../../../packages/components/src/chat/PkChunkPreview.vue","../../../../packages/components/src/chat/PkDocumentHeader.vue","../../../../packages/components/src/chat/PkDocumentHeader.vue","../../../../packages/components/src/chat/PkToolShowSources.vue","../../../../packages/components/src/chat/PkToolShowSources.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { stripMarkdown } from 'utils'\n\n const props = defineProps<{\n content: string\n similarity?: number\n maxSimilarity?: number\n expanded: boolean\n }>()\n\n const similarityPercent = computed(() => {\n if (props.similarity == null) {\n return null\n }\n const max =\n props.maxSimilarity && props.maxSimilarity > 0\n ? props.maxSimilarity\n : 1\n return Math.round((props.similarity / max) * 100)\n })\n</script>\n\n<template>\n <VvIcon name=\"ri:text-snippet\" class=\"shrink-0 text-12 text-word-4\" />\n <span class=\"text-10 text-word-2 truncate flex-1\" :title=\"content\">\n {{ stripMarkdown(content) }}\n </span>\n <span v-if=\"similarityPercent != null\" class=\"text-10 text-word-4 shrink-0\">\n {{ similarityPercent }}%\n </span>\n <VvIcon\n :name=\"expanded ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-14 text-word-4 shrink-0\" />\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { stripMarkdown } from 'utils'\n\n const props = defineProps<{\n content: string\n similarity?: number\n maxSimilarity?: number\n expanded: boolean\n }>()\n\n const similarityPercent = computed(() => {\n if (props.similarity == null) {\n return null\n }\n const max =\n props.maxSimilarity && props.maxSimilarity > 0\n ? props.maxSimilarity\n : 1\n return Math.round((props.similarity / max) * 100)\n })\n</script>\n\n<template>\n <VvIcon name=\"ri:text-snippet\" class=\"shrink-0 text-12 text-word-4\" />\n <span class=\"text-10 text-word-2 truncate flex-1\" :title=\"content\">\n {{ stripMarkdown(content) }}\n </span>\n <span v-if=\"similarityPercent != null\" class=\"text-10 text-word-4 shrink-0\">\n {{ similarityPercent }}%\n </span>\n <VvIcon\n :name=\"expanded ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-14 text-word-4 shrink-0\" />\n</template>\n","<script setup lang=\"ts\">\n import type { DocumentType } from 'models'\n import { getTypeIcon } from 'models'\n import PkUrl from '../PkUrl.vue'\n\n defineProps<{\n type: string\n name: string\n url?: string\n filename?: string\n }>()\n</script>\n\n<template>\n <VvIcon\n :name=\"getTypeIcon(type as DocumentType)\"\n class=\"shrink-0 text-14 text-word-3\" />\n <div class=\"flex flex-col gap-2 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate block\">\n {{ name }}\n </strong>\n <span v-if=\"filename\" class=\"text-10 text-word-4 truncate\">\n {{ filename }}\n </span>\n <span\n v-if=\"url\"\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-10\" />\n <PkUrl :url=\"url\" />\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import type { DocumentType } from 'models'\n import { getTypeIcon } from 'models'\n import PkUrl from '../PkUrl.vue'\n\n defineProps<{\n type: string\n name: string\n url?: string\n filename?: string\n }>()\n</script>\n\n<template>\n <VvIcon\n :name=\"getTypeIcon(type as DocumentType)\"\n class=\"shrink-0 text-14 text-word-3\" />\n <div class=\"flex flex-col gap-2 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate block\">\n {{ name }}\n </strong>\n <span v-if=\"filename\" class=\"text-10 text-word-4 truncate\">\n {{ filename }}\n </span>\n <span\n v-if=\"url\"\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-10\" />\n <PkUrl :url=\"url\" />\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive } from 'vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkChunkPreview from './PkChunkPreview.vue'\n import PkDocumentHeader from './PkDocumentHeader.vue'\n\n interface SourceItem {\n chunkId: string\n documentId: string\n documentName: string\n documentType: string\n language: string\n content: string\n chunkIndex?: number | null\n similarity?: number\n sourceUrl?: string\n sourceFilename?: string\n sourceDescription?: string\n sourceImageUrl?: string\n }\n\n const props = defineProps<{\n part: unknown\n onExpandContext?: (payload: {\n documentId: string\n chunkIndex: number\n }) => Promise<string>\n onDownload?: (documentId: string) => void\n }>()\n\n const toolPart = computed(() => {\n return props.part as {\n input?: {\n sources: SourceItem[]\n }\n }\n })\n\n const sources = computed(() => toolPart.value.input?.sources || [])\n\n const groupedSources = computed(() => {\n const groups = new Map<\n string,\n { document: SourceItem; chunks: SourceItem[] }\n >()\n for (const source of sources.value) {\n const existing = groups.get(source.documentId)\n if (existing) {\n existing.chunks.push(source)\n } else {\n groups.set(source.documentId, {\n document: source,\n chunks: [source],\n })\n }\n }\n return [...groups.values()]\n })\n\n const maxSimilarity = computed(() => {\n return Math.max(...sources.value.map((s) => s.similarity ?? 0), 0)\n })\n\n const expandedDocuments = reactive(new Set<string>())\n const expandedChunks = reactive(new Set<string>())\n const contextData = reactive(new Map<string, string>())\n const loadingContext = reactive(new Set<string>())\n\n const toggleDocument = (documentId: string) => {\n if (expandedDocuments.has(documentId)) {\n expandedDocuments.delete(documentId)\n } else {\n expandedDocuments.add(documentId)\n }\n }\n\n const toggleChunk = (chunkId: string) => {\n if (expandedChunks.has(chunkId)) {\n expandedChunks.delete(chunkId)\n } else {\n expandedChunks.add(chunkId)\n }\n }\n\n const handleExpandContext = async (source: SourceItem) => {\n if (source.chunkIndex == null || !props.onExpandContext) {\n return\n }\n loadingContext.add(source.chunkId)\n try {\n const content = await props.onExpandContext({\n documentId: source.documentId,\n chunkIndex: source.chunkIndex,\n })\n contextData.set(source.chunkId, content)\n } finally {\n loadingContext.delete(source.chunkId)\n }\n }\n\n const clearContext = (chunkId: string) => {\n contextData.delete(chunkId)\n }\n\n const showMore = ref(false)\n const visibleGroups = computed(() => {\n if (showMore.value) {\n return groupedSources.value\n }\n return groupedSources.value.slice(0, 3)\n })\n const hasMore = computed(() => groupedSources.value.length > 3)\n</script>\n\n<template>\n <div v-if=\"sources.length\" class=\"flex flex-col gap-xs w-full\">\n <div class=\"flex items-center gap-4 text-12 text-word-3 font-semibold\">\n <VvIcon name=\"ri:article-line\" class=\"text-14\" />\n {{ $t('label.sources') }}\n <span class=\"text-word-4 font-normal\">\n ({{ groupedSources.length }})\n </span>\n </div>\n\n <ul class=\"flex flex-col gap-xs\">\n <li\n v-for=\"group in visibleGroups\"\n :key=\"group.document.documentId\"\n class=\"border border-surface-3 rounded-xl overflow-hidden\">\n <!-- Document header -->\n <button\n class=\"flex items-center gap-8 p-8 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleDocument(group.document.documentId)\">\n <PkDocumentHeader\n :type=\"group.document.documentType\"\n :name=\"group.document.documentName\"\n :url=\"group.document.sourceUrl\"\n :filename=\"group.document.sourceFilename\" />\n <div class=\"flex items-center gap-4 shrink-0\">\n <span\n v-if=\"group.chunks.length > 1\"\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2\">\n {{ group.chunks.length }}\n </span>\n <span\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2 uppercase\">\n {{ group.document.language }}\n </span>\n <VvIcon\n :name=\"\n expandedDocuments.has(group.document.documentId)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"text-16 text-word-3\" />\n </div>\n </button>\n\n <!-- Expanded content -->\n <div\n v-if=\"expandedDocuments.has(group.document.documentId)\"\n class=\"border-t border-surface-3\">\n <PkStreamingMarkdown\n v-if=\"group.document.sourceDescription\"\n :markdown=\"group.document.sourceDescription\"\n class=\"px-8 py-4 text-10 text-word-3 bg-surface-1 border-b border-surface-3\" />\n\n <!-- Chunk excerpts -->\n <div\n v-for=\"chunk in group.chunks\"\n :key=\"chunk.chunkId\"\n class=\"border-b border-surface-3 last:border-b-0\">\n <button\n class=\"flex items-center gap-8 px-8 py-6 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleChunk(chunk.chunkId)\">\n <PkChunkPreview\n :content=\"chunk.content\"\n :similarity=\"chunk.similarity\"\n :max-similarity=\"maxSimilarity\"\n :expanded=\"expandedChunks.has(chunk.chunkId)\" />\n </button>\n\n <!-- Expanded chunk content -->\n <div\n v-if=\"expandedChunks.has(chunk.chunkId)\"\n class=\"bg-surface-1 border-t border-surface-3\">\n <div class=\"p-8 text-10 text-word-3\">\n <transition mode=\"out-in\">\n <!-- Context expansion -->\n <PkStreamingMarkdown\n v-if=\"contextData.has(chunk.chunkId)\"\n :markdown=\"\n contextData.get(chunk.chunkId)!\n \"\n class=\"wysiwyg\" />\n\n <!-- Chunk content -->\n <PkStreamingMarkdown\n v-else\n :markdown=\"chunk.content\"\n class=\"wysiwyg\" />\n </transition>\n </div>\n\n <!-- Actions bar -->\n <div\n class=\"flex items-center gap-8 px-8 py-6 border-t border-surface-3 bg-surface\">\n <!-- Expand / collapse context -->\n <button\n v-if=\"\n onExpandContext &&\n chunk.chunkIndex != null &&\n !contextData.has(chunk.chunkId)\n \"\n class=\"text-10 text-word-3 hover:text-word flex items-center gap-4 cursor-pointer\"\n :disabled=\"\n loadingContext.has(chunk.chunkId)\n \"\n @click=\"handleExpandContext(chunk)\">\n <VvIcon\n :name=\"\n loadingContext.has(chunk.chunkId)\n ? 'loading-loop'\n : 'ri:expand-up-down-line'\n \"\n class=\"text-12\" />\n {{ $t('label.expandContext') }}\n </button>\n <button\n v-else-if=\"contextData.has(chunk.chunkId)\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n @click=\"clearContext(chunk.chunkId)\">\n <VvIcon\n name=\"ri:collapse-vertical-line\"\n class=\"text-12\" />\n {{ $t('label.collapseContext') }}\n </button>\n\n <!-- Open source URL -->\n <a\n v-if=\"chunk.sourceUrl\"\n :href=\"chunk.sourceUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 ml-auto\">\n <VvIcon\n name=\"ri:external-link-line\"\n class=\"text-12\" />\n {{ $t('label.openSource') }}\n </a>\n\n <!-- Download file -->\n <button\n v-if=\"\n onDownload &&\n chunk.documentType === 'file'\n \"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n :class=\"{\n 'ml-auto': !chunk.sourceUrl,\n }\"\n @click=\"onDownload(chunk.documentId)\">\n <VvIcon\n name=\"ri:download-line\"\n class=\"text-12\" />\n {{ $t('label.downloadFile') }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </li>\n </ul>\n\n <!-- Show more -->\n <button\n v-if=\"hasMore\"\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: groupedSources.length - 3,\n })\n }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive } from 'vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkChunkPreview from './PkChunkPreview.vue'\n import PkDocumentHeader from './PkDocumentHeader.vue'\n\n interface SourceItem {\n chunkId: string\n documentId: string\n documentName: string\n documentType: string\n language: string\n content: string\n chunkIndex?: number | null\n similarity?: number\n sourceUrl?: string\n sourceFilename?: string\n sourceDescription?: string\n sourceImageUrl?: string\n }\n\n const props = defineProps<{\n part: unknown\n onExpandContext?: (payload: {\n documentId: string\n chunkIndex: number\n }) => Promise<string>\n onDownload?: (documentId: string) => void\n }>()\n\n const toolPart = computed(() => {\n return props.part as {\n input?: {\n sources: SourceItem[]\n }\n }\n })\n\n const sources = computed(() => toolPart.value.input?.sources || [])\n\n const groupedSources = computed(() => {\n const groups = new Map<\n string,\n { document: SourceItem; chunks: SourceItem[] }\n >()\n for (const source of sources.value) {\n const existing = groups.get(source.documentId)\n if (existing) {\n existing.chunks.push(source)\n } else {\n groups.set(source.documentId, {\n document: source,\n chunks: [source],\n })\n }\n }\n return [...groups.values()]\n })\n\n const maxSimilarity = computed(() => {\n return Math.max(...sources.value.map((s) => s.similarity ?? 0), 0)\n })\n\n const expandedDocuments = reactive(new Set<string>())\n const expandedChunks = reactive(new Set<string>())\n const contextData = reactive(new Map<string, string>())\n const loadingContext = reactive(new Set<string>())\n\n const toggleDocument = (documentId: string) => {\n if (expandedDocuments.has(documentId)) {\n expandedDocuments.delete(documentId)\n } else {\n expandedDocuments.add(documentId)\n }\n }\n\n const toggleChunk = (chunkId: string) => {\n if (expandedChunks.has(chunkId)) {\n expandedChunks.delete(chunkId)\n } else {\n expandedChunks.add(chunkId)\n }\n }\n\n const handleExpandContext = async (source: SourceItem) => {\n if (source.chunkIndex == null || !props.onExpandContext) {\n return\n }\n loadingContext.add(source.chunkId)\n try {\n const content = await props.onExpandContext({\n documentId: source.documentId,\n chunkIndex: source.chunkIndex,\n })\n contextData.set(source.chunkId, content)\n } finally {\n loadingContext.delete(source.chunkId)\n }\n }\n\n const clearContext = (chunkId: string) => {\n contextData.delete(chunkId)\n }\n\n const showMore = ref(false)\n const visibleGroups = computed(() => {\n if (showMore.value) {\n return groupedSources.value\n }\n return groupedSources.value.slice(0, 3)\n })\n const hasMore = computed(() => groupedSources.value.length > 3)\n</script>\n\n<template>\n <div v-if=\"sources.length\" class=\"flex flex-col gap-xs w-full\">\n <div class=\"flex items-center gap-4 text-12 text-word-3 font-semibold\">\n <VvIcon name=\"ri:article-line\" class=\"text-14\" />\n {{ $t('label.sources') }}\n <span class=\"text-word-4 font-normal\">\n ({{ groupedSources.length }})\n </span>\n </div>\n\n <ul class=\"flex flex-col gap-xs\">\n <li\n v-for=\"group in visibleGroups\"\n :key=\"group.document.documentId\"\n class=\"border border-surface-3 rounded-xl overflow-hidden\">\n <!-- Document header -->\n <button\n class=\"flex items-center gap-8 p-8 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleDocument(group.document.documentId)\">\n <PkDocumentHeader\n :type=\"group.document.documentType\"\n :name=\"group.document.documentName\"\n :url=\"group.document.sourceUrl\"\n :filename=\"group.document.sourceFilename\" />\n <div class=\"flex items-center gap-4 shrink-0\">\n <span\n v-if=\"group.chunks.length > 1\"\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2\">\n {{ group.chunks.length }}\n </span>\n <span\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2 uppercase\">\n {{ group.document.language }}\n </span>\n <VvIcon\n :name=\"\n expandedDocuments.has(group.document.documentId)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"text-16 text-word-3\" />\n </div>\n </button>\n\n <!-- Expanded content -->\n <div\n v-if=\"expandedDocuments.has(group.document.documentId)\"\n class=\"border-t border-surface-3\">\n <PkStreamingMarkdown\n v-if=\"group.document.sourceDescription\"\n :markdown=\"group.document.sourceDescription\"\n class=\"px-8 py-4 text-10 text-word-3 bg-surface-1 border-b border-surface-3\" />\n\n <!-- Chunk excerpts -->\n <div\n v-for=\"chunk in group.chunks\"\n :key=\"chunk.chunkId\"\n class=\"border-b border-surface-3 last:border-b-0\">\n <button\n class=\"flex items-center gap-8 px-8 py-6 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleChunk(chunk.chunkId)\">\n <PkChunkPreview\n :content=\"chunk.content\"\n :similarity=\"chunk.similarity\"\n :max-similarity=\"maxSimilarity\"\n :expanded=\"expandedChunks.has(chunk.chunkId)\" />\n </button>\n\n <!-- Expanded chunk content -->\n <div\n v-if=\"expandedChunks.has(chunk.chunkId)\"\n class=\"bg-surface-1 border-t border-surface-3\">\n <div class=\"p-8 text-10 text-word-3\">\n <transition mode=\"out-in\">\n <!-- Context expansion -->\n <PkStreamingMarkdown\n v-if=\"contextData.has(chunk.chunkId)\"\n :markdown=\"\n contextData.get(chunk.chunkId)!\n \"\n class=\"wysiwyg\" />\n\n <!-- Chunk content -->\n <PkStreamingMarkdown\n v-else\n :markdown=\"chunk.content\"\n class=\"wysiwyg\" />\n </transition>\n </div>\n\n <!-- Actions bar -->\n <div\n class=\"flex items-center gap-8 px-8 py-6 border-t border-surface-3 bg-surface\">\n <!-- Expand / collapse context -->\n <button\n v-if=\"\n onExpandContext &&\n chunk.chunkIndex != null &&\n !contextData.has(chunk.chunkId)\n \"\n class=\"text-10 text-word-3 hover:text-word flex items-center gap-4 cursor-pointer\"\n :disabled=\"\n loadingContext.has(chunk.chunkId)\n \"\n @click=\"handleExpandContext(chunk)\">\n <VvIcon\n :name=\"\n loadingContext.has(chunk.chunkId)\n ? 'loading-loop'\n : 'ri:expand-up-down-line'\n \"\n class=\"text-12\" />\n {{ $t('label.expandContext') }}\n </button>\n <button\n v-else-if=\"contextData.has(chunk.chunkId)\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n @click=\"clearContext(chunk.chunkId)\">\n <VvIcon\n name=\"ri:collapse-vertical-line\"\n class=\"text-12\" />\n {{ $t('label.collapseContext') }}\n </button>\n\n <!-- Open source URL -->\n <a\n v-if=\"chunk.sourceUrl\"\n :href=\"chunk.sourceUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 ml-auto\">\n <VvIcon\n name=\"ri:external-link-line\"\n class=\"text-12\" />\n {{ $t('label.openSource') }}\n </a>\n\n <!-- Download file -->\n <button\n v-if=\"\n onDownload &&\n chunk.documentType === 'file'\n \"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n :class=\"{\n 'ml-auto': !chunk.sourceUrl,\n }\"\n @click=\"onDownload(chunk.documentId)\">\n <VvIcon\n name=\"ri:download-line\"\n class=\"text-12\" />\n {{ $t('label.downloadFile') }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </li>\n </ul>\n\n <!-- Show more -->\n <button\n v-if=\"hasMore\"\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: groupedSources.length - 3,\n })\n }}\n </button>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkToolShowSources-Qq3c8_ZQ.js","names":["$t"],"sources":["../../../../packages/components/src/chat/PkChunkPreview.vue","../../../../packages/components/src/chat/PkChunkPreview.vue","../../../../packages/components/src/chat/PkDocumentHeader.vue","../../../../packages/components/src/chat/PkDocumentHeader.vue","../../../../packages/components/src/chat/PkToolShowSources.vue","../../../../packages/components/src/chat/PkToolShowSources.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { stripMarkdown } from 'utils'\n\n const props = defineProps<{\n content: string\n similarity?: number\n maxSimilarity?: number\n expanded: boolean\n }>()\n\n const similarityPercent = computed(() => {\n if (props.similarity == null) {\n return null\n }\n const max =\n props.maxSimilarity && props.maxSimilarity > 0\n ? props.maxSimilarity\n : 1\n return Math.round((props.similarity / max) * 100)\n })\n</script>\n\n<template>\n <VvIcon name=\"ri:text-snippet\" class=\"shrink-0 text-12 text-word-4\" />\n <span class=\"text-10 text-word-2 truncate flex-1\" :title=\"content\">\n {{ stripMarkdown(content) }}\n </span>\n <span v-if=\"similarityPercent != null\" class=\"text-10 text-word-4 shrink-0\">\n {{ similarityPercent }}%\n </span>\n <VvIcon\n :name=\"expanded ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-14 text-word-4 shrink-0\" />\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { stripMarkdown } from 'utils'\n\n const props = defineProps<{\n content: string\n similarity?: number\n maxSimilarity?: number\n expanded: boolean\n }>()\n\n const similarityPercent = computed(() => {\n if (props.similarity == null) {\n return null\n }\n const max =\n props.maxSimilarity && props.maxSimilarity > 0\n ? props.maxSimilarity\n : 1\n return Math.round((props.similarity / max) * 100)\n })\n</script>\n\n<template>\n <VvIcon name=\"ri:text-snippet\" class=\"shrink-0 text-12 text-word-4\" />\n <span class=\"text-10 text-word-2 truncate flex-1\" :title=\"content\">\n {{ stripMarkdown(content) }}\n </span>\n <span v-if=\"similarityPercent != null\" class=\"text-10 text-word-4 shrink-0\">\n {{ similarityPercent }}%\n </span>\n <VvIcon\n :name=\"expanded ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-14 text-word-4 shrink-0\" />\n</template>\n","<script setup lang=\"ts\">\n import type { DocumentType } from 'models'\n import { getTypeIcon } from 'models'\n import PkUrl from '../PkUrl.vue'\n\n defineProps<{\n type: string\n name: string\n url?: string\n filename?: string\n }>()\n</script>\n\n<template>\n <VvIcon\n :name=\"getTypeIcon(type as DocumentType)\"\n class=\"shrink-0 text-14 text-word-3\" />\n <div class=\"flex flex-col gap-2 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate block\">\n {{ name }}\n </strong>\n <span v-if=\"filename\" class=\"text-10 text-word-4 truncate\">\n {{ filename }}\n </span>\n <span\n v-if=\"url\"\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-10\" />\n <PkUrl :url=\"url\" />\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import type { DocumentType } from 'models'\n import { getTypeIcon } from 'models'\n import PkUrl from '../PkUrl.vue'\n\n defineProps<{\n type: string\n name: string\n url?: string\n filename?: string\n }>()\n</script>\n\n<template>\n <VvIcon\n :name=\"getTypeIcon(type as DocumentType)\"\n class=\"shrink-0 text-14 text-word-3\" />\n <div class=\"flex flex-col gap-2 min-w-0 flex-1\">\n <strong class=\"font-bold text-12 text-word-1 truncate block\">\n {{ name }}\n </strong>\n <span v-if=\"filename\" class=\"text-10 text-word-4 truncate\">\n {{ filename }}\n </span>\n <span\n v-if=\"url\"\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-10\" />\n <PkUrl :url=\"url\" />\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive } from 'vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkChunkPreview from './PkChunkPreview.vue'\n import PkDocumentHeader from './PkDocumentHeader.vue'\n\n interface SourceItem {\n chunkId: string\n documentId: string\n documentName: string\n documentType: string\n language: string\n content: string\n chunkIndex?: number | null\n similarity?: number\n sourceUrl?: string\n sourceFilename?: string\n sourceDescription?: string\n sourceImageUrl?: string\n }\n\n const props = defineProps<{\n part: unknown\n onExpandContext?: (payload: {\n documentId: string\n chunkIndex: number\n }) => Promise<string>\n onDownload?: (documentId: string) => void\n }>()\n\n const toolPart = computed(() => {\n return props.part as {\n input?: {\n sources: SourceItem[]\n }\n }\n })\n\n const sources = computed(() => toolPart.value.input?.sources || [])\n\n const groupedSources = computed(() => {\n const groups = new Map<\n string,\n { document: SourceItem; chunks: SourceItem[] }\n >()\n for (const source of sources.value) {\n const existing = groups.get(source.documentId)\n if (existing) {\n existing.chunks.push(source)\n } else {\n groups.set(source.documentId, {\n document: source,\n chunks: [source],\n })\n }\n }\n return [...groups.values()]\n })\n\n const maxSimilarity = computed(() => {\n return Math.max(...sources.value.map((s) => s.similarity ?? 0), 0)\n })\n\n const expandedDocuments = reactive(new Set<string>())\n const expandedChunks = reactive(new Set<string>())\n const contextData = reactive(new Map<string, string>())\n const loadingContext = reactive(new Set<string>())\n\n const toggleDocument = (documentId: string) => {\n if (expandedDocuments.has(documentId)) {\n expandedDocuments.delete(documentId)\n } else {\n expandedDocuments.add(documentId)\n }\n }\n\n const toggleChunk = (chunkId: string) => {\n if (expandedChunks.has(chunkId)) {\n expandedChunks.delete(chunkId)\n } else {\n expandedChunks.add(chunkId)\n }\n }\n\n const handleExpandContext = async (source: SourceItem) => {\n if (source.chunkIndex == null || !props.onExpandContext) {\n return\n }\n loadingContext.add(source.chunkId)\n try {\n const content = await props.onExpandContext({\n documentId: source.documentId,\n chunkIndex: source.chunkIndex,\n })\n contextData.set(source.chunkId, content)\n } finally {\n loadingContext.delete(source.chunkId)\n }\n }\n\n const clearContext = (chunkId: string) => {\n contextData.delete(chunkId)\n }\n\n const showMore = ref(false)\n const visibleGroups = computed(() => {\n if (showMore.value) {\n return groupedSources.value\n }\n return groupedSources.value.slice(0, 3)\n })\n const hasMore = computed(() => groupedSources.value.length > 3)\n</script>\n\n<template>\n <div v-if=\"sources.length\" class=\"flex flex-col gap-xs w-full\">\n <div class=\"flex items-center gap-4 text-12 text-word-3 font-semibold\">\n <VvIcon name=\"ri:article-line\" class=\"text-14\" />\n {{ $t('label.sources') }}\n <span class=\"text-word-4 font-normal\">\n ({{ groupedSources.length }})\n </span>\n </div>\n\n <ul class=\"flex flex-col gap-xs\">\n <li\n v-for=\"group in visibleGroups\"\n :key=\"group.document.documentId\"\n class=\"border border-surface-3 rounded-xl overflow-hidden\">\n <!-- Document header -->\n <button\n class=\"flex items-center gap-8 p-8 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleDocument(group.document.documentId)\">\n <PkDocumentHeader\n :type=\"group.document.documentType\"\n :name=\"group.document.documentName\"\n :url=\"group.document.sourceUrl\"\n :filename=\"group.document.sourceFilename\" />\n <div class=\"flex items-center gap-4 shrink-0\">\n <span\n v-if=\"group.chunks.length > 1\"\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2\">\n {{ group.chunks.length }}\n </span>\n <span\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2 uppercase\">\n {{ group.document.language }}\n </span>\n <VvIcon\n :name=\"\n expandedDocuments.has(group.document.documentId)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"text-16 text-word-3\" />\n </div>\n </button>\n\n <!-- Expanded content -->\n <div\n v-if=\"expandedDocuments.has(group.document.documentId)\"\n class=\"border-t border-surface-3\">\n <PkStreamingMarkdown\n v-if=\"group.document.sourceDescription\"\n :markdown=\"group.document.sourceDescription\"\n class=\"px-8 py-4 text-10 text-word-3 bg-surface-1 border-b border-surface-3\" />\n\n <!-- Chunk excerpts -->\n <div\n v-for=\"chunk in group.chunks\"\n :key=\"chunk.chunkId\"\n class=\"border-b border-surface-3 last:border-b-0\">\n <button\n class=\"flex items-center gap-8 px-8 py-6 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleChunk(chunk.chunkId)\">\n <PkChunkPreview\n :content=\"chunk.content\"\n :similarity=\"chunk.similarity\"\n :max-similarity=\"maxSimilarity\"\n :expanded=\"expandedChunks.has(chunk.chunkId)\" />\n </button>\n\n <!-- Expanded chunk content -->\n <div\n v-if=\"expandedChunks.has(chunk.chunkId)\"\n class=\"bg-surface-1 border-t border-surface-3\">\n <div class=\"p-8 text-10 text-word-3\">\n <transition mode=\"out-in\">\n <!-- Context expansion -->\n <PkStreamingMarkdown\n v-if=\"contextData.has(chunk.chunkId)\"\n :markdown=\"\n contextData.get(chunk.chunkId)!\n \"\n class=\"wysiwyg\" />\n\n <!-- Chunk content -->\n <PkStreamingMarkdown\n v-else\n :markdown=\"chunk.content\"\n class=\"wysiwyg\" />\n </transition>\n </div>\n\n <!-- Actions bar -->\n <div\n class=\"flex items-center gap-8 px-8 py-6 border-t border-surface-3 bg-surface\">\n <!-- Expand / collapse context -->\n <button\n v-if=\"\n onExpandContext &&\n chunk.chunkIndex != null &&\n !contextData.has(chunk.chunkId)\n \"\n class=\"text-10 text-word-3 hover:text-word flex items-center gap-4 cursor-pointer\"\n :disabled=\"\n loadingContext.has(chunk.chunkId)\n \"\n @click=\"handleExpandContext(chunk)\">\n <VvIcon\n :name=\"\n loadingContext.has(chunk.chunkId)\n ? 'loading-loop'\n : 'ri:expand-up-down-line'\n \"\n class=\"text-12\" />\n {{ $t('label.expandContext') }}\n </button>\n <button\n v-else-if=\"contextData.has(chunk.chunkId)\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n @click=\"clearContext(chunk.chunkId)\">\n <VvIcon\n name=\"ri:collapse-vertical-line\"\n class=\"text-12\" />\n {{ $t('label.collapseContext') }}\n </button>\n\n <!-- Open source URL -->\n <a\n v-if=\"chunk.sourceUrl\"\n :href=\"chunk.sourceUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 ml-auto\">\n <VvIcon\n name=\"ri:external-link-line\"\n class=\"text-12\" />\n {{ $t('label.openSource') }}\n </a>\n\n <!-- Download file -->\n <button\n v-if=\"\n onDownload &&\n chunk.documentType === 'file'\n \"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n :class=\"{\n 'ml-auto': !chunk.sourceUrl,\n }\"\n @click=\"onDownload(chunk.documentId)\">\n <VvIcon\n name=\"ri:download-line\"\n class=\"text-12\" />\n {{ $t('label.downloadFile') }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </li>\n </ul>\n\n <!-- Show more -->\n <button\n v-if=\"hasMore\"\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: groupedSources.length - 3,\n })\n }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive } from 'vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkChunkPreview from './PkChunkPreview.vue'\n import PkDocumentHeader from './PkDocumentHeader.vue'\n\n interface SourceItem {\n chunkId: string\n documentId: string\n documentName: string\n documentType: string\n language: string\n content: string\n chunkIndex?: number | null\n similarity?: number\n sourceUrl?: string\n sourceFilename?: string\n sourceDescription?: string\n sourceImageUrl?: string\n }\n\n const props = defineProps<{\n part: unknown\n onExpandContext?: (payload: {\n documentId: string\n chunkIndex: number\n }) => Promise<string>\n onDownload?: (documentId: string) => void\n }>()\n\n const toolPart = computed(() => {\n return props.part as {\n input?: {\n sources: SourceItem[]\n }\n }\n })\n\n const sources = computed(() => toolPart.value.input?.sources || [])\n\n const groupedSources = computed(() => {\n const groups = new Map<\n string,\n { document: SourceItem; chunks: SourceItem[] }\n >()\n for (const source of sources.value) {\n const existing = groups.get(source.documentId)\n if (existing) {\n existing.chunks.push(source)\n } else {\n groups.set(source.documentId, {\n document: source,\n chunks: [source],\n })\n }\n }\n return [...groups.values()]\n })\n\n const maxSimilarity = computed(() => {\n return Math.max(...sources.value.map((s) => s.similarity ?? 0), 0)\n })\n\n const expandedDocuments = reactive(new Set<string>())\n const expandedChunks = reactive(new Set<string>())\n const contextData = reactive(new Map<string, string>())\n const loadingContext = reactive(new Set<string>())\n\n const toggleDocument = (documentId: string) => {\n if (expandedDocuments.has(documentId)) {\n expandedDocuments.delete(documentId)\n } else {\n expandedDocuments.add(documentId)\n }\n }\n\n const toggleChunk = (chunkId: string) => {\n if (expandedChunks.has(chunkId)) {\n expandedChunks.delete(chunkId)\n } else {\n expandedChunks.add(chunkId)\n }\n }\n\n const handleExpandContext = async (source: SourceItem) => {\n if (source.chunkIndex == null || !props.onExpandContext) {\n return\n }\n loadingContext.add(source.chunkId)\n try {\n const content = await props.onExpandContext({\n documentId: source.documentId,\n chunkIndex: source.chunkIndex,\n })\n contextData.set(source.chunkId, content)\n } finally {\n loadingContext.delete(source.chunkId)\n }\n }\n\n const clearContext = (chunkId: string) => {\n contextData.delete(chunkId)\n }\n\n const showMore = ref(false)\n const visibleGroups = computed(() => {\n if (showMore.value) {\n return groupedSources.value\n }\n return groupedSources.value.slice(0, 3)\n })\n const hasMore = computed(() => groupedSources.value.length > 3)\n</script>\n\n<template>\n <div v-if=\"sources.length\" class=\"flex flex-col gap-xs w-full\">\n <div class=\"flex items-center gap-4 text-12 text-word-3 font-semibold\">\n <VvIcon name=\"ri:article-line\" class=\"text-14\" />\n {{ $t('label.sources') }}\n <span class=\"text-word-4 font-normal\">\n ({{ groupedSources.length }})\n </span>\n </div>\n\n <ul class=\"flex flex-col gap-xs\">\n <li\n v-for=\"group in visibleGroups\"\n :key=\"group.document.documentId\"\n class=\"border border-surface-3 rounded-xl overflow-hidden\">\n <!-- Document header -->\n <button\n class=\"flex items-center gap-8 p-8 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleDocument(group.document.documentId)\">\n <PkDocumentHeader\n :type=\"group.document.documentType\"\n :name=\"group.document.documentName\"\n :url=\"group.document.sourceUrl\"\n :filename=\"group.document.sourceFilename\" />\n <div class=\"flex items-center gap-4 shrink-0\">\n <span\n v-if=\"group.chunks.length > 1\"\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2\">\n {{ group.chunks.length }}\n </span>\n <span\n class=\"text-10 text-word-4 bg-surface-2 rounded px-4 py-2 uppercase\">\n {{ group.document.language }}\n </span>\n <VvIcon\n :name=\"\n expandedDocuments.has(group.document.documentId)\n ? 'ri:arrow-up-s-line'\n : 'ri:arrow-down-s-line'\n \"\n class=\"text-16 text-word-3\" />\n </div>\n </button>\n\n <!-- Expanded content -->\n <div\n v-if=\"expandedDocuments.has(group.document.documentId)\"\n class=\"border-t border-surface-3\">\n <PkStreamingMarkdown\n v-if=\"group.document.sourceDescription\"\n :markdown=\"group.document.sourceDescription\"\n class=\"px-8 py-4 text-10 text-word-3 bg-surface-1 border-b border-surface-3\" />\n\n <!-- Chunk excerpts -->\n <div\n v-for=\"chunk in group.chunks\"\n :key=\"chunk.chunkId\"\n class=\"border-b border-surface-3 last:border-b-0\">\n <button\n class=\"flex items-center gap-8 px-8 py-6 w-full text-left hover:bg-surface-1 transition-colors cursor-pointer\"\n @click=\"toggleChunk(chunk.chunkId)\">\n <PkChunkPreview\n :content=\"chunk.content\"\n :similarity=\"chunk.similarity\"\n :max-similarity=\"maxSimilarity\"\n :expanded=\"expandedChunks.has(chunk.chunkId)\" />\n </button>\n\n <!-- Expanded chunk content -->\n <div\n v-if=\"expandedChunks.has(chunk.chunkId)\"\n class=\"bg-surface-1 border-t border-surface-3\">\n <div class=\"p-8 text-10 text-word-3\">\n <transition mode=\"out-in\">\n <!-- Context expansion -->\n <PkStreamingMarkdown\n v-if=\"contextData.has(chunk.chunkId)\"\n :markdown=\"\n contextData.get(chunk.chunkId)!\n \"\n class=\"wysiwyg\" />\n\n <!-- Chunk content -->\n <PkStreamingMarkdown\n v-else\n :markdown=\"chunk.content\"\n class=\"wysiwyg\" />\n </transition>\n </div>\n\n <!-- Actions bar -->\n <div\n class=\"flex items-center gap-8 px-8 py-6 border-t border-surface-3 bg-surface\">\n <!-- Expand / collapse context -->\n <button\n v-if=\"\n onExpandContext &&\n chunk.chunkIndex != null &&\n !contextData.has(chunk.chunkId)\n \"\n class=\"text-10 text-word-3 hover:text-word flex items-center gap-4 cursor-pointer\"\n :disabled=\"\n loadingContext.has(chunk.chunkId)\n \"\n @click=\"handleExpandContext(chunk)\">\n <VvIcon\n :name=\"\n loadingContext.has(chunk.chunkId)\n ? 'loading-loop'\n : 'ri:expand-up-down-line'\n \"\n class=\"text-12\" />\n {{ $t('label.expandContext') }}\n </button>\n <button\n v-else-if=\"contextData.has(chunk.chunkId)\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n @click=\"clearContext(chunk.chunkId)\">\n <VvIcon\n name=\"ri:collapse-vertical-line\"\n class=\"text-12\" />\n {{ $t('label.collapseContext') }}\n </button>\n\n <!-- Open source URL -->\n <a\n v-if=\"chunk.sourceUrl\"\n :href=\"chunk.sourceUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 ml-auto\">\n <VvIcon\n name=\"ri:external-link-line\"\n class=\"text-12\" />\n {{ $t('label.openSource') }}\n </a>\n\n <!-- Download file -->\n <button\n v-if=\"\n onDownload &&\n chunk.documentType === 'file'\n \"\n class=\"text-10 text-word-3 hover:text-word-1 flex items-center gap-4 cursor-pointer\"\n :class=\"{\n 'ml-auto': !chunk.sourceUrl,\n }\"\n @click=\"onDownload(chunk.documentId)\">\n <VvIcon\n name=\"ri:download-line\"\n class=\"text-12\" />\n {{ $t('label.downloadFile') }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </li>\n </ul>\n\n <!-- Show more -->\n <button\n v-if=\"hasMore\"\n class=\"text-12 text-word-3 cursor-pointer hover:underline flex items-center gap-4 mx-auto\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: groupedSources.length - 3,\n })\n }}\n </button>\n </div>\n</template>\n"],"mappings":"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"}