@8wave/ai-elements 0.98.0-beta.4 → 0.98.0-beta.5
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.
- package/dist/_chunks/{Document-CjCQXPDF.js → Document-DtFcn7Mq.js} +2 -2
- package/dist/_chunks/Document-DtFcn7Mq.js.map +1 -0
- package/dist/_chunks/{PkToolRequestConfirm-BGt2J3e5.js → PkToolRequestConfirm-FQ9nDFXy.js} +2 -2
- package/dist/_chunks/{PkToolRequestConfirm-BGt2J3e5.js.map → PkToolRequestConfirm-FQ9nDFXy.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestGeolocation-D9kfr6SV.js → PkToolRequestGeolocation-BJ9NJZIg.js} +2 -2
- package/dist/_chunks/{PkToolRequestGeolocation-D9kfr6SV.js.map → PkToolRequestGeolocation-BJ9NJZIg.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestOAuthConnection-BH2tXr0j.js → PkToolRequestOAuthConnection-DdvJ6n1K.js} +2 -2
- package/dist/_chunks/{PkToolRequestOAuthConnection-BH2tXr0j.js.map → PkToolRequestOAuthConnection-DdvJ6n1K.js.map} +1 -1
- package/dist/_chunks/{PkToolShowCalendarEvent-DZwH1Run.js → PkToolShowCalendarEvent-BYnhl2er.js} +2 -2
- package/dist/_chunks/{PkToolShowCalendarEvent-DZwH1Run.js.map → PkToolShowCalendarEvent-BYnhl2er.js.map} +1 -1
- package/dist/_chunks/{PkToolShowContactForm-DEQBTzf4.js → PkToolShowContactForm-upUNZtId.js} +3 -3
- package/dist/_chunks/{PkToolShowContactForm-DEQBTzf4.js.map → PkToolShowContactForm-upUNZtId.js.map} +1 -1
- package/dist/_chunks/{PkToolShowDiagram-siwsx1K7.js → PkToolShowDiagram-Jq_4KWiK.js} +2 -2
- package/dist/_chunks/{PkToolShowDiagram-siwsx1K7.js.map → PkToolShowDiagram-Jq_4KWiK.js.map} +1 -1
- package/dist/_chunks/{PkToolShowForm-AZwrmmM9.js → PkToolShowForm-BNV4Ja6g.js} +2 -2
- package/dist/_chunks/{PkToolShowForm-AZwrmmM9.js.map → PkToolShowForm-BNV4Ja6g.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSources-FnMkkdm6.js → PkToolShowSources-DwB6VXrK.js} +2 -2
- package/dist/_chunks/{PkToolShowSources-FnMkkdm6.js.map → PkToolShowSources-DwB6VXrK.js.map} +1 -1
- package/dist/_chunks/{PkToolShowWeather-COyrzQ7m.js → PkToolShowWeather-C2rPCQ_K.js} +2 -2
- package/dist/_chunks/{PkToolShowWeather-COyrzQ7m.js.map → PkToolShowWeather-C2rPCQ_K.js.map} +1 -1
- package/dist/_chunks/{index.es-eLFDkB2Y.js → index.es-u1dF1xg2.js} +2 -2
- package/dist/_chunks/{index.es-eLFDkB2Y.js.map → index.es-u1dF1xg2.js.map} +1 -1
- package/dist/_chunks/{vue-i18n-BBwABYBg.js → vue-i18n-kZs9Hfey.js} +650 -640
- package/dist/_chunks/vue-i18n-kZs9Hfey.js.map +1 -0
- package/dist/ai-elements.es.js +150 -121
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotMessages.js +1 -1
- package/dist-vue/PkChatbotViewChat.js +1 -1
- package/dist-vue/PkChatbotViewConversations.js +1 -1
- package/dist-vue/PkChatbotViewFiles.js +1 -1
- package/dist-vue/PkChatbotViewProfile.js +1 -1
- package/dist-vue/_chunks/{Document-B8gv0e2O.js → Document-CSdfKj_B.js} +2 -2
- package/dist-vue/_chunks/{Document-B8gv0e2O.js.map → Document-CSdfKj_B.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbot-CxpAotKB.js → PkChatbot-DHkhT0Jf.js} +6 -6
- package/dist-vue/_chunks/{PkChatbot-CxpAotKB.js.map → PkChatbot-DHkhT0Jf.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-DEzcxcqc.js → PkChatbotMessages-B4qfTNOJ.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotMessages-DEzcxcqc.js.map → PkChatbotMessages-B4qfTNOJ.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-QzWSymIg.js → PkChatbotViewChat-C08TsXiL.js} +4 -4
- package/dist-vue/_chunks/{PkChatbotViewChat-QzWSymIg.js.map → PkChatbotViewChat-C08TsXiL.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotViewConversations-BQZi5kSC.js +192 -0
- package/dist-vue/_chunks/PkChatbotViewConversations-BQZi5kSC.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewFiles-RHMwvWUS.js → PkChatbotViewFiles-TLKE_tRw.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotViewFiles-RHMwvWUS.js.map → PkChatbotViewFiles-TLKE_tRw.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewProfile-Bka6CUAa.js → PkChatbotViewProfile-B6ANZY96.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-Bka6CUAa.js.map → PkChatbotViewProfile-B6ANZY96.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSources-rGZz65c2.js → PkToolShowSources-CFh-i60o.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowSources-rGZz65c2.js.map → PkToolShowSources-CFh-i60o.js.map} +1 -1
- package/dist-vue/_chunks/{constants-BSe0uxSf.js → constants-BCJ_gKeL.js} +2 -2
- package/dist-vue/_chunks/{constants-BSe0uxSf.js.map → constants-BCJ_gKeL.js.map} +1 -1
- package/dist-vue/_chunks/{createChatbotApiClient-Bf5DbqVH.js → createChatbotApiClient-BOBdBxmf.js} +2 -2
- package/dist-vue/_chunks/{createChatbotApiClient-Bf5DbqVH.js.map → createChatbotApiClient-BOBdBxmf.js.map} +1 -1
- package/dist-vue/_chunks/{useChatbotStore-DeHwEN8W.js → useChatbotStore-BJ-EDmND.js} +3 -3
- package/dist-vue/_chunks/useChatbotStore-BJ-EDmND.js.map +1 -0
- package/dist-vue/api.js +1 -1
- package/dist-vue/apps/web-component/src/components/EmbeddedChatWidget.ce.d.ts +7 -0
- package/dist-vue/apps/web-component/src/components/FloatingChatWidget.ce.d.ts +7 -0
- package/dist-vue/apps/web-component/src/composables/useChatbotAuth.d.ts +11 -0
- package/dist-vue/apps/web-component/src/lib.d.ts +6 -0
- package/dist-vue/composables.js +118 -103
- package/dist-vue/composables.js.map +1 -1
- package/dist-vue/index.js +12 -10
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/packages/components/src/chat/PkChatbotMessages.d.ts +3 -134
- package/dist-vue/packages/components/src/chat/PkMessageParts.d.ts +3 -134
- package/dist-vue/packages/components/src/chat/constants.d.ts +1 -1
- package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +24 -24
- package/dist-vue/packages/models/src/schema/constants.d.ts +2 -0
- package/package.json +1 -1
- package/dist/_chunks/Document-CjCQXPDF.js.map +0 -1
- package/dist/_chunks/vue-i18n-BBwABYBg.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotViewConversations-O-wm_nu1.js +0 -192
- package/dist-vue/_chunks/PkChatbotViewConversations-O-wm_nu1.js.map +0 -1
- package/dist-vue/_chunks/useChatbotStore-DeHwEN8W.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PkChatbotViewConversations-BQZi5kSC.js","names":[],"sources":["../../../../packages/composable/src/chatbot/useConversationActions.ts","../../../../packages/components/src/chat/PkChatbotViewConversations.vue","../../../../packages/components/src/chat/PkChatbotViewConversations.vue"],"sourcesContent":["import { ref } from 'vue'\nimport type { Chat as Conversation } from 'models'\nimport { getTextPart, stripMarkdown } from 'utils'\nimport { useDialogConfirm } from '../useDialogConfirm'\n\n/**\n * Single-line preview for a conversation: explicit title, otherwise the\n * first user message. Used as list label and as search haystack.\n */\nexport function getConversationPreview(conversation: Conversation): string {\n return (\n stripMarkdown(conversation.title) ||\n stripMarkdown(\n getTextPart(\n // The list endpoint isn't Zod-parsed: don't trust the\n // declared types at runtime\n conversation.messages?.find(\n (message) => message.role === 'user',\n ),\n ),\n ) ||\n ''\n )\n}\n\n/**\n * Inline-rename and delete-with-confirm state shared by the conversation\n * lists (PkChatbotViewConversations, PkChatSidebar). Renames are optimistic:\n * on failure the next conversations load reconciles with the server state.\n */\nexport function useConversationActions(actions: {\n renameChatTitle: (chatId: string, title: string) => Promise<unknown>\n deleteChat: (chatId: string) => Promise<unknown>\n}) {\n const editingId = ref<string | null>(null)\n const editingTitle = ref('')\n\n const startEdit = (conversation: Conversation, fallbackTitle = '') => {\n editingId.value = conversation.id\n editingTitle.value = stripMarkdown(conversation.title) || fallbackTitle\n }\n\n const confirmEdit = async (id: string) => {\n const trimmed = editingTitle.value.trim()\n editingId.value = null\n if (trimmed && trimmed.length <= 255) {\n try {\n await actions.renameChatTitle(id, trimmed)\n } catch {\n // Revert optimistically: reload will pick up server state\n }\n }\n }\n\n const cancelEdit = () => {\n editingId.value = null\n }\n\n const onRenameKeydown = (event: KeyboardEvent, id: string) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n confirmEdit(id)\n } else if (event.key === 'Escape') {\n event.preventDefault()\n cancelEdit()\n }\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n const requestDelete = async (id: string) => {\n try {\n const proceed = await openDialogConfirm()\n if (!proceed) {\n return\n }\n await actions.deleteChat(id)\n } catch {\n // Silently fail: next load will reconcile\n }\n }\n\n return {\n editingId,\n editingTitle,\n startEdit,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete,\n }\n}\n","<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type { Chat as Conversation, UIChatMessage } from 'models'\n import { stripMarkdown, getTextPart } from 'utils'\n import {\n useChatbotStore,\n useConversationActions,\n useDialogConfirm,\n } from 'composables'\n import PkRelativeTime from '../PkRelativeTime.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { conversations, isLoadingConversations, localChatId } =\n storeToRefs(store)\n const { navigate, startNewChat, renameChatTitle, deleteChat } = store\n\n const {\n editingId,\n editingTitle,\n startEdit: startEditConversation,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete: handleDelete,\n } = useConversationActions({ renameChatTitle, deleteChat })\n\n const openConversation = (id: string) => {\n if (editingId.value) {\n return\n }\n localChatId.value = id\n navigate('chat')\n }\n\n const isCurrentConversation = (conversation: Conversation) => {\n return localChatId.value === conversation.id\n }\n\n const getLastMessage = (\n messages: UIChatMessage[],\n role: 'user' | 'assistant',\n ) => {\n return stripMarkdown(\n getTextPart(messages.filter((m) => m.role === role).slice(-1)[0]) ||\n '',\n )\n }\n\n const startEdit = (conversation: Conversation) => {\n startEditConversation(\n conversation,\n getLastMessage(conversation.messages, 'assistant'),\n )\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n\n // GDPR B.3 — bulk \"delete my conversations\", available to anonymous\n // sessions too (the host wires the endpoint through the profile context).\n // Hidden when the host application owns the session (external token or\n // host-session mode): the endpoint erases ALL conversations of the\n // session's user platform-wide, an account-scoped action that belongs to\n // the host, not to an embedded widget. Same gating as sign-out and\n // manage-profile in PkChatbotViewProfile.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canEraseConversations = computed(\n () =>\n !!profile?.eraseConversations &&\n !(profile.isExternalAuth?.value ?? false),\n )\n const isErasingConversations = ref(false)\n const handleEraseAll = async () => {\n const eraseConversations = profile?.eraseConversations\n if (!eraseConversations || !canEraseConversations.value) {\n return\n }\n const proceed = await openDialogConfirm({\n title: $t('action.deleteMyConversations'),\n questionLabel: $t('message.deleteMyConversationsConfirm'),\n })\n if (!proceed) {\n return\n }\n isErasingConversations.value = true\n try {\n await eraseConversations()\n conversations.value = []\n startNewChat()\n } catch {\n // Same policy as single delete: fail silently and reconcile\n // with the server state\n store.loadConversations(props.agentId)\n } finally {\n isErasingConversations.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingConversations && conversations.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 5\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-64\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"conversations.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 name=\"ri:chat-3-line\" class=\"w-28 h-28 text-word-4\" />\n </div>\n <div class=\"flex flex-col gap-4\">\n <strong class=\"text-16 font-semibold text-word-1\">\n {{ $t('message.noConversations') }}\n </strong>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('message.noConversationsHint') }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n class=\"mt-8\"\n :label=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n </div>\n\n <!-- Conversation list -->\n <ul\n v-else\n class=\"flex flex-col gap-8 overflow-y-auto p-16 pb-64 light-scrollbar\">\n <li v-for=\"conversation in conversations\" :key=\"conversation.id\">\n <button\n type=\"button\"\n class=\"w-full block rounded-md border border-surface-3 p-12 text-left text-14 leading-relaxed cursor-pointer transition-colors hover:border-surface-5 hover:bg-surface-1 focus-within:border-word\"\n :class=\"{\n 'border-brand bg-surface-1':\n isCurrentConversation(conversation),\n }\"\n @click=\"openConversation(conversation.id)\">\n <div class=\"flex gap-8\">\n <template v-if=\"editingId === conversation.id\">\n <VvInputText\n v-model=\"editingTitle\"\n class=\"flex-1\"\n modifiers=\"compact\"\n autofocus\n select-on-focus\n :maxlength=\"255\"\n :placeholder=\"$t('placeholder.insert')\"\n @click.stop\n @keydown=\"\n onRenameKeydown($event, conversation.id)\n \" />\n <VvButtonGroup modifiers=\"compact\">\n <VvButton\n icon=\"ri:save-line\"\n :title=\"$t('action.save')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"\n confirmEdit(conversation.id)\n \" />\n <VvButton\n icon=\"ri:close-line\"\n :title=\"$t('action.cancel')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"cancelEdit()\" />\n </VvButtonGroup>\n </template>\n <template v-else>\n <strong class=\"font-bold truncate block flex-1\">\n {{\n stripMarkdown(conversation.title) ||\n getLastMessage(\n conversation.messages,\n 'assistant',\n )\n }}\n </strong>\n <PkRelativeTime\n class=\"text-word-4 text-smaller shrink-0\"\n :date=\"conversation.lastMessageAt\" />\n <div @click.stop>\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:more-2-fill\"\n :title=\"$t('action.moreActions')\"\n modifiers=\"action-quiet-small\" />\n <template #items>\n <VvDropdownAction\n @click=\"startEdit(conversation)\">\n <VvIcon name=\"ri:pencil-line\" />\n {{ $t('action.renameChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n @click=\"\n handleDelete(conversation.id)\n \">\n <VvIcon name=\"ri:delete-bin-line\" />\n {{ $t('action.delete') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </div>\n </template>\n </div>\n <span\n v-if=\"editingId !== conversation.id\"\n class=\"text-12 text-word-4 line-clamp-2\">\n {{\n stripMarkdown(conversation.summary) ||\n getLastMessage(conversation.messages, 'user')\n }}\n </span>\n </button>\n </li>\n </ul>\n\n <!-- Sticky \"new chat\" CTA (hidden on empty state, which has its own CTA) -->\n <div\n v-if=\"conversations.length > 0\"\n class=\"absolute bottom-0 left-0 right-0 flex flex-col items-center gap-4 px-16 pb-16 pt-32 pointer-events-none\"\n :style=\"{\n background:\n 'linear-gradient(to top, var(--color-surface) 55%, transparent)',\n }\">\n <VvButton\n class=\"text-14 pointer-events-auto\"\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n :label=\"$t('action.startNewChat')\"\n @click.stop=\"startNewChat()\" />\n <VvButton\n v-if=\"canEraseConversations\"\n class=\"text-12 pointer-events-auto\"\n modifiers=\"action-quiet-danger\"\n icon=\"ri:delete-bin-line\"\n :loading=\"isErasingConversations\"\n :disabled=\"isErasingConversations\"\n :label=\"$t('action.deleteMyConversations')\"\n @click.stop=\"handleEraseAll()\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type { Chat as Conversation, UIChatMessage } from 'models'\n import { stripMarkdown, getTextPart } from 'utils'\n import {\n useChatbotStore,\n useConversationActions,\n useDialogConfirm,\n } from 'composables'\n import PkRelativeTime from '../PkRelativeTime.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { conversations, isLoadingConversations, localChatId } =\n storeToRefs(store)\n const { navigate, startNewChat, renameChatTitle, deleteChat } = store\n\n const {\n editingId,\n editingTitle,\n startEdit: startEditConversation,\n confirmEdit,\n cancelEdit,\n onRenameKeydown,\n requestDelete: handleDelete,\n } = useConversationActions({ renameChatTitle, deleteChat })\n\n const openConversation = (id: string) => {\n if (editingId.value) {\n return\n }\n localChatId.value = id\n navigate('chat')\n }\n\n const isCurrentConversation = (conversation: Conversation) => {\n return localChatId.value === conversation.id\n }\n\n const getLastMessage = (\n messages: UIChatMessage[],\n role: 'user' | 'assistant',\n ) => {\n return stripMarkdown(\n getTextPart(messages.filter((m) => m.role === role).slice(-1)[0]) ||\n '',\n )\n }\n\n const startEdit = (conversation: Conversation) => {\n startEditConversation(\n conversation,\n getLastMessage(conversation.messages, 'assistant'),\n )\n }\n\n const { openDialogConfirm } = useDialogConfirm()\n\n // GDPR B.3 — bulk \"delete my conversations\", available to anonymous\n // sessions too (the host wires the endpoint through the profile context).\n // Hidden when the host application owns the session (external token or\n // host-session mode): the endpoint erases ALL conversations of the\n // session's user platform-wide, an account-scoped action that belongs to\n // the host, not to an embedded widget. Same gating as sign-out and\n // manage-profile in PkChatbotViewProfile.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canEraseConversations = computed(\n () =>\n !!profile?.eraseConversations &&\n !(profile.isExternalAuth?.value ?? false),\n )\n const isErasingConversations = ref(false)\n const handleEraseAll = async () => {\n const eraseConversations = profile?.eraseConversations\n if (!eraseConversations || !canEraseConversations.value) {\n return\n }\n const proceed = await openDialogConfirm({\n title: $t('action.deleteMyConversations'),\n questionLabel: $t('message.deleteMyConversationsConfirm'),\n })\n if (!proceed) {\n return\n }\n isErasingConversations.value = true\n try {\n await eraseConversations()\n conversations.value = []\n startNewChat()\n } catch {\n // Same policy as single delete: fail silently and reconcile\n // with the server state\n store.loadConversations(props.agentId)\n } finally {\n isErasingConversations.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingConversations && conversations.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 5\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-64\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"conversations.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 name=\"ri:chat-3-line\" class=\"w-28 h-28 text-word-4\" />\n </div>\n <div class=\"flex flex-col gap-4\">\n <strong class=\"text-16 font-semibold text-word-1\">\n {{ $t('message.noConversations') }}\n </strong>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('message.noConversationsHint') }}\n </span>\n </div>\n <VvButton\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n class=\"mt-8\"\n :label=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n </div>\n\n <!-- Conversation list -->\n <ul\n v-else\n class=\"flex flex-col gap-8 overflow-y-auto p-16 pb-64 light-scrollbar\">\n <li v-for=\"conversation in conversations\" :key=\"conversation.id\">\n <button\n type=\"button\"\n class=\"w-full block rounded-md border border-surface-3 p-12 text-left text-14 leading-relaxed cursor-pointer transition-colors hover:border-surface-5 hover:bg-surface-1 focus-within:border-word\"\n :class=\"{\n 'border-brand bg-surface-1':\n isCurrentConversation(conversation),\n }\"\n @click=\"openConversation(conversation.id)\">\n <div class=\"flex gap-8\">\n <template v-if=\"editingId === conversation.id\">\n <VvInputText\n v-model=\"editingTitle\"\n class=\"flex-1\"\n modifiers=\"compact\"\n autofocus\n select-on-focus\n :maxlength=\"255\"\n :placeholder=\"$t('placeholder.insert')\"\n @click.stop\n @keydown=\"\n onRenameKeydown($event, conversation.id)\n \" />\n <VvButtonGroup modifiers=\"compact\">\n <VvButton\n icon=\"ri:save-line\"\n :title=\"$t('action.save')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"\n confirmEdit(conversation.id)\n \" />\n <VvButton\n icon=\"ri:close-line\"\n :title=\"$t('action.cancel')\"\n modifiers=\"action-quiet-small\"\n @click.stop=\"cancelEdit()\" />\n </VvButtonGroup>\n </template>\n <template v-else>\n <strong class=\"font-bold truncate block flex-1\">\n {{\n stripMarkdown(conversation.title) ||\n getLastMessage(\n conversation.messages,\n 'assistant',\n )\n }}\n </strong>\n <PkRelativeTime\n class=\"text-word-4 text-smaller shrink-0\"\n :date=\"conversation.lastMessageAt\" />\n <div @click.stop>\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:more-2-fill\"\n :title=\"$t('action.moreActions')\"\n modifiers=\"action-quiet-small\" />\n <template #items>\n <VvDropdownAction\n @click=\"startEdit(conversation)\">\n <VvIcon name=\"ri:pencil-line\" />\n {{ $t('action.renameChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n @click=\"\n handleDelete(conversation.id)\n \">\n <VvIcon name=\"ri:delete-bin-line\" />\n {{ $t('action.delete') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n </div>\n </template>\n </div>\n <span\n v-if=\"editingId !== conversation.id\"\n class=\"text-12 text-word-4 line-clamp-2\">\n {{\n stripMarkdown(conversation.summary) ||\n getLastMessage(conversation.messages, 'user')\n }}\n </span>\n </button>\n </li>\n </ul>\n\n <!-- Sticky \"new chat\" CTA (hidden on empty state, which has its own CTA) -->\n <div\n v-if=\"conversations.length > 0\"\n class=\"absolute bottom-0 left-0 right-0 flex flex-col items-center gap-4 px-16 pb-16 pt-32 pointer-events-none\"\n :style=\"{\n background:\n 'linear-gradient(to top, var(--color-surface) 55%, transparent)',\n }\">\n <VvButton\n class=\"text-14 pointer-events-auto\"\n modifiers=\"primary rounded\"\n icon=\"ri:add-line\"\n :label=\"$t('action.startNewChat')\"\n @click.stop=\"startNewChat()\" />\n <VvButton\n v-if=\"canEraseConversations\"\n class=\"text-12 pointer-events-auto\"\n modifiers=\"action-quiet-danger\"\n icon=\"ri:delete-bin-line\"\n :loading=\"isErasingConversations\"\n :disabled=\"isErasingConversations\"\n :label=\"$t('action.deleteMyConversations')\"\n @click.stop=\"handleEraseAll()\" />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as e, n as t, o as n } from "./Document-
|
|
2
|
-
import { t as r } from "./useChatbotStore-
|
|
1
|
+
import { a as e, n as t, o as n } from "./Document-CSdfKj_B.js";
|
|
2
|
+
import { t as r } from "./useChatbotStore-BJ-EDmND.js";
|
|
3
3
|
import { t as i } from "./useDialogConfirm-DaoekZdk.js";
|
|
4
4
|
import { VvButton as a, VvIcon as o } from "@volverjs/ui-vue/components";
|
|
5
5
|
import { Fragment as s, createBlock as c, createElementBlock as l, createElementVNode as u, createVNode as d, defineComponent as f, openBlock as p, reactive as m, ref as h, renderList as g, toDisplayString as _, unref as v } from "vue";
|
|
@@ -111,4 +111,4 @@ var S = { class: "flex flex-col flex-1 min-h-0 relative" }, C = {
|
|
|
111
111
|
//#endregion
|
|
112
112
|
export { P as t };
|
|
113
113
|
|
|
114
|
-
//# sourceMappingURL=PkChatbotViewFiles-
|
|
114
|
+
//# sourceMappingURL=PkChatbotViewFiles-TLKE_tRw.js.map
|
package/dist-vue/_chunks/{PkChatbotViewFiles-RHMwvWUS.js.map → PkChatbotViewFiles-TLKE_tRw.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbotViewFiles-RHMwvWUS.js","names":[],"sources":["../../../../packages/composable/src/useFileRow.ts","../../../../packages/components/src/chat/PkChatbotViewFiles.vue","../../../../packages/components/src/chat/PkChatbotViewFiles.vue"],"sourcesContent":["import { reactive } from 'vue'\nimport { useI18n } from 'vue-i18n'\nimport { isImageMime, isInlineViewable } from 'models'\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 thumbError = reactive(new Set<string>())\n const showThumb = (file: FileRowLike): boolean =>\n isImageMime(file.mimeType) && !thumbError.has(file.documentId)\n const thumbUrl = (file: FileRowLike): string =>\n urls.preview(file.documentId)\n const onThumbError = (file: FileRowLike): void => {\n thumbError.add(file.documentId)\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('userFiles.excludeTitle'),\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 -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-56\" />\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 v-else class=\"pk-chatbot-files\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"pk-chatbot-files__item\">\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 <button\n type=\"button\"\n class=\"pk-chatbot-files__main\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__name\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"pk-chatbot-files__date\">\n {{ formatDate(file.createdAt) }}\n </span>\n </button>\n <div class=\"pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet xs\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-danger xs\"\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 .pk-chatbot-files {\n display: flex;\n flex-direction: column;\n list-style: none;\n margin: 0;\n padding: var(--spacing-8);\n overflow-y: auto;\n\n &__item {\n display: flex;\n align-items: center;\n gap: var(--spacing-10);\n padding: var(--spacing-8) var(--spacing-10);\n border-radius: var(--rounded-md);\n transition: var(--transition-colors);\n\n // Separator between files\n & + & {\n border-top: 1px solid var(--color-surface-3);\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\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 background-color: var(--color-surface-1);\n color: var(--color-word-3);\n overflow: hidden;\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n text-align: left;\n background: none;\n border: 0;\n padding: 0;\n cursor: pointer;\n }\n\n &__name {\n font-size: var(--text-14);\n color: var(--color-word-1);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__date {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2);\n flex-shrink: 0;\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('userFiles.excludeTitle'),\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 -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-56\" />\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 v-else class=\"pk-chatbot-files\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"pk-chatbot-files__item\">\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 <button\n type=\"button\"\n class=\"pk-chatbot-files__main\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__name\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"pk-chatbot-files__date\">\n {{ formatDate(file.createdAt) }}\n </span>\n </button>\n <div class=\"pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet xs\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-danger xs\"\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 .pk-chatbot-files {\n display: flex;\n flex-direction: column;\n list-style: none;\n margin: 0;\n padding: var(--spacing-8);\n overflow-y: auto;\n\n &__item {\n display: flex;\n align-items: center;\n gap: var(--spacing-10);\n padding: var(--spacing-8) var(--spacing-10);\n border-radius: var(--rounded-md);\n transition: var(--transition-colors);\n\n // Separator between files\n & + & {\n border-top: 1px solid var(--color-surface-3);\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\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 background-color: var(--color-surface-1);\n color: var(--color-word-3);\n overflow: hidden;\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n text-align: left;\n background: none;\n border: 0;\n padding: 0;\n cursor: pointer;\n }\n\n &__name {\n font-size: var(--text-14);\n color: var(--color-word-1);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__date {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2);\n flex-shrink: 0;\n }\n }\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PkChatbotViewFiles-TLKE_tRw.js","names":[],"sources":["../../../../packages/composable/src/useFileRow.ts","../../../../packages/components/src/chat/PkChatbotViewFiles.vue","../../../../packages/components/src/chat/PkChatbotViewFiles.vue"],"sourcesContent":["import { reactive } from 'vue'\nimport { useI18n } from 'vue-i18n'\nimport { isImageMime, isInlineViewable } from 'models'\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 thumbError = reactive(new Set<string>())\n const showThumb = (file: FileRowLike): boolean =>\n isImageMime(file.mimeType) && !thumbError.has(file.documentId)\n const thumbUrl = (file: FileRowLike): string =>\n urls.preview(file.documentId)\n const onThumbError = (file: FileRowLike): void => {\n thumbError.add(file.documentId)\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('userFiles.excludeTitle'),\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 -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-56\" />\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 v-else class=\"pk-chatbot-files\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"pk-chatbot-files__item\">\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 <button\n type=\"button\"\n class=\"pk-chatbot-files__main\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__name\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"pk-chatbot-files__date\">\n {{ formatDate(file.createdAt) }}\n </span>\n </button>\n <div class=\"pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet xs\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-danger xs\"\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 .pk-chatbot-files {\n display: flex;\n flex-direction: column;\n list-style: none;\n margin: 0;\n padding: var(--spacing-8);\n overflow-y: auto;\n\n &__item {\n display: flex;\n align-items: center;\n gap: var(--spacing-10);\n padding: var(--spacing-8) var(--spacing-10);\n border-radius: var(--rounded-md);\n transition: var(--transition-colors);\n\n // Separator between files\n & + & {\n border-top: 1px solid var(--color-surface-3);\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\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 background-color: var(--color-surface-1);\n color: var(--color-word-3);\n overflow: hidden;\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n text-align: left;\n background: none;\n border: 0;\n padding: 0;\n cursor: pointer;\n }\n\n &__name {\n font-size: var(--text-14);\n color: var(--color-word-1);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__date {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2);\n flex-shrink: 0;\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('userFiles.excludeTitle'),\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 -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-56\" />\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 v-else class=\"pk-chatbot-files\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"pk-chatbot-files__item\">\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 <button\n type=\"button\"\n class=\"pk-chatbot-files__main\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__name\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"pk-chatbot-files__date\">\n {{ formatDate(file.createdAt) }}\n </span>\n </button>\n <div class=\"pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet xs\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-danger xs\"\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 .pk-chatbot-files {\n display: flex;\n flex-direction: column;\n list-style: none;\n margin: 0;\n padding: var(--spacing-8);\n overflow-y: auto;\n\n &__item {\n display: flex;\n align-items: center;\n gap: var(--spacing-10);\n padding: var(--spacing-8) var(--spacing-10);\n border-radius: var(--rounded-md);\n transition: var(--transition-colors);\n\n // Separator between files\n & + & {\n border-top: 1px solid var(--color-surface-3);\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\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 background-color: var(--color-surface-1);\n color: var(--color-word-3);\n overflow: hidden;\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n text-align: left;\n background: none;\n border: 0;\n padding: 0;\n cursor: pointer;\n }\n\n &__name {\n font-size: var(--text-14);\n color: var(--color-word-1);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__date {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2);\n flex-shrink: 0;\n }\n }\n</style>\n"],"mappings":"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"}
|
package/dist-vue/_chunks/{PkChatbotViewProfile-Bka6CUAa.js → PkChatbotViewProfile-B6ANZY96.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as e } from "./useChatbotStore-
|
|
1
|
+
import { t as e } from "./useChatbotStore-BJ-EDmND.js";
|
|
2
2
|
import { t } from "./PkAvatar-CyIgsndA.js";
|
|
3
3
|
import { t as n } from "./chatbotProfileContext-CgnFhNPn.js";
|
|
4
4
|
import { VvAlert as r, VvButton as i, VvIcon as a } from "@volverjs/ui-vue/components";
|
|
@@ -83,4 +83,4 @@ var x = { class: "flex flex-col flex-1 min-h-0 overflow-y-auto" }, S = { class:
|
|
|
83
83
|
//#endregion
|
|
84
84
|
export { N as t };
|
|
85
85
|
|
|
86
|
-
//# sourceMappingURL=PkChatbotViewProfile-
|
|
86
|
+
//# sourceMappingURL=PkChatbotViewProfile-B6ANZY96.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbotViewProfile-Bka6CUAa.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbotViewProfile.vue","../../../../packages/components/src/chat/PkChatbotViewProfile.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { useChatbotStore } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { name: agentName, agentInterface } = storeToRefs(store)\n\n const profile = inject(ChatbotProfileContextKey, undefined)\n const user = computed(() => profile?.user.value ?? null)\n const canManage = computed(() => profile?.canManageProfile.value ?? false)\n const profileUrl = computed(() => profile?.profileUrl.value)\n // External token mode: the host owns the auth lifecycle, so the widget\n // offers no sign-out nor profile management (the main site is not\n // accessible to externally authenticated users).\n const isExternalAuth = computed(\n () => profile?.isExternalAuth?.value ?? false,\n )\n\n const isSigningOut = ref(false)\n const errorMessage = ref<string>()\n\n async function onSignOut() {\n if (!profile) {\n return\n }\n isSigningOut.value = true\n try {\n await profile.signOut()\n } catch (error) {\n errorMessage.value =\n error instanceof Error ? error.message : $t('message.error')\n } finally {\n isSigningOut.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 overflow-y-auto\">\n <!-- Hero: user identity -->\n <div\n class=\"flex flex-col items-center gap-sm pt-lg px-lg pb-md bg-surface-1 border-b border-surface-3\">\n <PkAvatar\n modifiers=\"surface ring\"\n class=\"w-64 h-64\"\n :name=\"user?.name ?? agentName\"\n :img-src=\"user?.image ?? agentInterface?.logo\" />\n <div class=\"flex flex-col items-center gap-6 text-center\">\n <strong\n v-if=\"user?.name ?? agentName\"\n class=\"text-16 font-semibold text-word-1 leading-tight\">\n {{ user?.name ?? agentName }}\n </strong>\n <span\n v-if=\"user?.email\"\n class=\"text-12 text-word-3 leading-none\">\n {{ user.email }}\n </span>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"flex flex-col gap-sm p-md flex-1\">\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n :content=\"errorMessage\" />\n\n <!-- Manage profile link card -->\n <a\n v-if=\"canManage && profileUrl && !isExternalAuth\"\n :href=\"profileUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"flex items-center gap-12 p-12 rounded-lg border border-surface-3 hover:border-surface-5 hover:bg-surface-1 transition-colors cursor-pointer no-underline\">\n <div\n class=\"w-40 h-40 rounded-md bg-surface-2 border border-surface-3 flex items-center justify-center shrink-0\">\n <VvIcon\n name=\"ri:user-settings-line\"\n class=\"w-20 h-20 text-word-2\" />\n </div>\n <div class=\"flex flex-col flex-1 min-w-0 gap-1\">\n <span class=\"text-14 font-medium text-word-1 leading-tight\">\n {{ $t('action.manageProfile') }}\n </span>\n <span class=\"text-12 text-word-3 leading-tight\">\n {{ $t('label.manageProfileDescription') }}\n </span>\n </div>\n <VvIcon\n name=\"ri:arrow-right-s-line\"\n class=\"w-16 h-16 text-word-4 shrink-0\" />\n </a>\n </div>\n\n <!-- Footer: sign out -->\n <div v-if=\"!isExternalAuth\" class=\"px-md py-sm\">\n <VvButton\n modifiers=\"secondary-danger\"\n class=\"w-full\"\n icon=\"ri:logout-box-line\"\n :loading=\"isSigningOut\"\n :disabled=\"isSigningOut\"\n :label=\"$t('action.signOut')\"\n @click=\"onSignOut\" />\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { useChatbotStore } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { name: agentName, agentInterface } = storeToRefs(store)\n\n const profile = inject(ChatbotProfileContextKey, undefined)\n const user = computed(() => profile?.user.value ?? null)\n const canManage = computed(() => profile?.canManageProfile.value ?? false)\n const profileUrl = computed(() => profile?.profileUrl.value)\n // External token mode: the host owns the auth lifecycle, so the widget\n // offers no sign-out nor profile management (the main site is not\n // accessible to externally authenticated users).\n const isExternalAuth = computed(\n () => profile?.isExternalAuth?.value ?? false,\n )\n\n const isSigningOut = ref(false)\n const errorMessage = ref<string>()\n\n async function onSignOut() {\n if (!profile) {\n return\n }\n isSigningOut.value = true\n try {\n await profile.signOut()\n } catch (error) {\n errorMessage.value =\n error instanceof Error ? error.message : $t('message.error')\n } finally {\n isSigningOut.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 overflow-y-auto\">\n <!-- Hero: user identity -->\n <div\n class=\"flex flex-col items-center gap-sm pt-lg px-lg pb-md bg-surface-1 border-b border-surface-3\">\n <PkAvatar\n modifiers=\"surface ring\"\n class=\"w-64 h-64\"\n :name=\"user?.name ?? agentName\"\n :img-src=\"user?.image ?? agentInterface?.logo\" />\n <div class=\"flex flex-col items-center gap-6 text-center\">\n <strong\n v-if=\"user?.name ?? agentName\"\n class=\"text-16 font-semibold text-word-1 leading-tight\">\n {{ user?.name ?? agentName }}\n </strong>\n <span\n v-if=\"user?.email\"\n class=\"text-12 text-word-3 leading-none\">\n {{ user.email }}\n </span>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"flex flex-col gap-sm p-md flex-1\">\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n :content=\"errorMessage\" />\n\n <!-- Manage profile link card -->\n <a\n v-if=\"canManage && profileUrl && !isExternalAuth\"\n :href=\"profileUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"flex items-center gap-12 p-12 rounded-lg border border-surface-3 hover:border-surface-5 hover:bg-surface-1 transition-colors cursor-pointer no-underline\">\n <div\n class=\"w-40 h-40 rounded-md bg-surface-2 border border-surface-3 flex items-center justify-center shrink-0\">\n <VvIcon\n name=\"ri:user-settings-line\"\n class=\"w-20 h-20 text-word-2\" />\n </div>\n <div class=\"flex flex-col flex-1 min-w-0 gap-1\">\n <span class=\"text-14 font-medium text-word-1 leading-tight\">\n {{ $t('action.manageProfile') }}\n </span>\n <span class=\"text-12 text-word-3 leading-tight\">\n {{ $t('label.manageProfileDescription') }}\n </span>\n </div>\n <VvIcon\n name=\"ri:arrow-right-s-line\"\n class=\"w-16 h-16 text-word-4 shrink-0\" />\n </a>\n </div>\n\n <!-- Footer: sign out -->\n <div v-if=\"!isExternalAuth\" class=\"px-md py-sm\">\n <VvButton\n modifiers=\"secondary-danger\"\n class=\"w-full\"\n icon=\"ri:logout-box-line\"\n :loading=\"isSigningOut\"\n :disabled=\"isSigningOut\"\n :label=\"$t('action.signOut')\"\n @click=\"onSignOut\" />\n </div>\n\n <slot />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAQI,IAAM,IAAQ,GAER,EAAE,GAAG,MAAO,EAAQ,EAAE,UAAU,SAAS,CAAC,GAG1C,EAAE,MAAM,GAAW,sBAAmB,EAD9B,EAAgB,EAAM,OACoB,CAAK,GAEvD,IAAU,EAAO,GAA0B,KAAA,CAAS,GACpD,IAAO,QAAe,GAAS,KAAK,SAAS,IAAI,GACjD,IAAY,QAAe,GAAS,iBAAiB,SAAS,EAAK,GACnE,IAAa,QAAe,GAAS,WAAW,KAAK,GAIrD,IAAiB,QACb,GAAS,gBAAgB,SAAS,EAC5C,GAEM,IAAe,EAAI,EAAK,GACxB,IAAe,EAAY;EAEjC,eAAe,IAAY;GAClB,OAGL;MAAa,QAAQ;IACrB,IAAI;KACA,MAAM,EAAQ,QAAQ;IAC1B,SAAS,GAAO;KACZ,EAAa,QACT,aAAiB,QAAQ,EAAM,UAAU,EAAG,eAAe;IACnE,UAAU;KACN,EAAa,QAAQ;IACzB;GARqB;EASzB;;;eAIA,EAsEM,OAtEN,GAsEM;IApEF,EAmBM,OAnBN,GAmBM,CAjBF,EAIqD,GAAA;KAHjD,WAAU;KACV,OAAM;KACL,MAAM,EAAA,OAAM,QAAQ,EAAA,CAAA;KACpB,WAAS,EAAA,OAAM,SAAS,EAAA,CAAA,CAAc,EAAE;sCAC7C,EAWM,OAXN,GAWM,CATQ,EAAA,OAAM,QAAQ,EAAA,CAAA,KAAA,EAAA,GADxB,EAIS,UAJT,GAIS,EADF,EAAA,OAAM,QAAQ,EAAA,CAAA,CAAS,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAGpB,EAAA,OAAM,SAAA,EAAA,GADhB,EAIO,QAJP,GAIO,EADA,EAAA,MAAK,KAAK,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,CAAA,CAAA;IAMzB,EA+BM,OA/BN,GA+BM,CA7BQ,EAAA,SAAA,EAAA,GADV,EAG8B,GAAA;;KAD1B,WAAU;KACT,SAAS,EAAA;2CAIJ,EAAA,SAAa,EAAA,SAAU,CAAK,EAAA,SAAA,EAAA,GADtC,EAuBI,KAAA;;KArBC,MAAM,EAAA;KACP,QAAO;KACP,KAAI;KACJ,OAAM;;KACN,EAKM,OALN,GAKM,CAHF,EAEoC,GAAA;MADhC,MAAK;MACL,OAAM;;KAEd,EAOM,OAPN,GAOM,CANF,EAEO,QAFP,GAEO,EADA,EAAA,CAAA,CAAE,CAAA,sBAAA,CAAA,GAAA,CAAA,GAET,EAEO,QAFP,GAEO,EADA,EAAA,CAAA,CAAE,CAAA,gCAAA,CAAA,GAAA,CAAA,CAAA,CAAA;KAGb,EAE6C,GAAA;MADzC,MAAK;MACL,OAAM;;;IAKN,EAAA,qBAAA,EAAA,GAAZ,EASM,OATN,GASM,CARF,EAOyB,GAAA;KANrB,WAAU;KACV,OAAM;KACN,MAAK;KACJ,SAAS,EAAA;KACT,UAAU,EAAA;KACV,OAAO,EAAA,CAAA,CAAE,CAAA,gBAAA;KACT,SAAO;;;;;;IAGhB,EAAQ,EAAA,QAAA,SAAA"}
|
|
1
|
+
{"version":3,"file":"PkChatbotViewProfile-B6ANZY96.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbotViewProfile.vue","../../../../packages/components/src/chat/PkChatbotViewProfile.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { useChatbotStore } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { name: agentName, agentInterface } = storeToRefs(store)\n\n const profile = inject(ChatbotProfileContextKey, undefined)\n const user = computed(() => profile?.user.value ?? null)\n const canManage = computed(() => profile?.canManageProfile.value ?? false)\n const profileUrl = computed(() => profile?.profileUrl.value)\n // External token mode: the host owns the auth lifecycle, so the widget\n // offers no sign-out nor profile management (the main site is not\n // accessible to externally authenticated users).\n const isExternalAuth = computed(\n () => profile?.isExternalAuth?.value ?? false,\n )\n\n const isSigningOut = ref(false)\n const errorMessage = ref<string>()\n\n async function onSignOut() {\n if (!profile) {\n return\n }\n isSigningOut.value = true\n try {\n await profile.signOut()\n } catch (error) {\n errorMessage.value =\n error instanceof Error ? error.message : $t('message.error')\n } finally {\n isSigningOut.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 overflow-y-auto\">\n <!-- Hero: user identity -->\n <div\n class=\"flex flex-col items-center gap-sm pt-lg px-lg pb-md bg-surface-1 border-b border-surface-3\">\n <PkAvatar\n modifiers=\"surface ring\"\n class=\"w-64 h-64\"\n :name=\"user?.name ?? agentName\"\n :img-src=\"user?.image ?? agentInterface?.logo\" />\n <div class=\"flex flex-col items-center gap-6 text-center\">\n <strong\n v-if=\"user?.name ?? agentName\"\n class=\"text-16 font-semibold text-word-1 leading-tight\">\n {{ user?.name ?? agentName }}\n </strong>\n <span\n v-if=\"user?.email\"\n class=\"text-12 text-word-3 leading-none\">\n {{ user.email }}\n </span>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"flex flex-col gap-sm p-md flex-1\">\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n :content=\"errorMessage\" />\n\n <!-- Manage profile link card -->\n <a\n v-if=\"canManage && profileUrl && !isExternalAuth\"\n :href=\"profileUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"flex items-center gap-12 p-12 rounded-lg border border-surface-3 hover:border-surface-5 hover:bg-surface-1 transition-colors cursor-pointer no-underline\">\n <div\n class=\"w-40 h-40 rounded-md bg-surface-2 border border-surface-3 flex items-center justify-center shrink-0\">\n <VvIcon\n name=\"ri:user-settings-line\"\n class=\"w-20 h-20 text-word-2\" />\n </div>\n <div class=\"flex flex-col flex-1 min-w-0 gap-1\">\n <span class=\"text-14 font-medium text-word-1 leading-tight\">\n {{ $t('action.manageProfile') }}\n </span>\n <span class=\"text-12 text-word-3 leading-tight\">\n {{ $t('label.manageProfileDescription') }}\n </span>\n </div>\n <VvIcon\n name=\"ri:arrow-right-s-line\"\n class=\"w-16 h-16 text-word-4 shrink-0\" />\n </a>\n </div>\n\n <!-- Footer: sign out -->\n <div v-if=\"!isExternalAuth\" class=\"px-md py-sm\">\n <VvButton\n modifiers=\"secondary-danger\"\n class=\"w-full\"\n icon=\"ri:logout-box-line\"\n :loading=\"isSigningOut\"\n :disabled=\"isSigningOut\"\n :label=\"$t('action.signOut')\"\n @click=\"onSignOut\" />\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, inject, ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { useChatbotStore } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const store = useChatbotStore(props.agentId)\n const { name: agentName, agentInterface } = storeToRefs(store)\n\n const profile = inject(ChatbotProfileContextKey, undefined)\n const user = computed(() => profile?.user.value ?? null)\n const canManage = computed(() => profile?.canManageProfile.value ?? false)\n const profileUrl = computed(() => profile?.profileUrl.value)\n // External token mode: the host owns the auth lifecycle, so the widget\n // offers no sign-out nor profile management (the main site is not\n // accessible to externally authenticated users).\n const isExternalAuth = computed(\n () => profile?.isExternalAuth?.value ?? false,\n )\n\n const isSigningOut = ref(false)\n const errorMessage = ref<string>()\n\n async function onSignOut() {\n if (!profile) {\n return\n }\n isSigningOut.value = true\n try {\n await profile.signOut()\n } catch (error) {\n errorMessage.value =\n error instanceof Error ? error.message : $t('message.error')\n } finally {\n isSigningOut.value = false\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 overflow-y-auto\">\n <!-- Hero: user identity -->\n <div\n class=\"flex flex-col items-center gap-sm pt-lg px-lg pb-md bg-surface-1 border-b border-surface-3\">\n <PkAvatar\n modifiers=\"surface ring\"\n class=\"w-64 h-64\"\n :name=\"user?.name ?? agentName\"\n :img-src=\"user?.image ?? agentInterface?.logo\" />\n <div class=\"flex flex-col items-center gap-6 text-center\">\n <strong\n v-if=\"user?.name ?? agentName\"\n class=\"text-16 font-semibold text-word-1 leading-tight\">\n {{ user?.name ?? agentName }}\n </strong>\n <span\n v-if=\"user?.email\"\n class=\"text-12 text-word-3 leading-none\">\n {{ user.email }}\n </span>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"flex flex-col gap-sm p-md flex-1\">\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n :content=\"errorMessage\" />\n\n <!-- Manage profile link card -->\n <a\n v-if=\"canManage && profileUrl && !isExternalAuth\"\n :href=\"profileUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"flex items-center gap-12 p-12 rounded-lg border border-surface-3 hover:border-surface-5 hover:bg-surface-1 transition-colors cursor-pointer no-underline\">\n <div\n class=\"w-40 h-40 rounded-md bg-surface-2 border border-surface-3 flex items-center justify-center shrink-0\">\n <VvIcon\n name=\"ri:user-settings-line\"\n class=\"w-20 h-20 text-word-2\" />\n </div>\n <div class=\"flex flex-col flex-1 min-w-0 gap-1\">\n <span class=\"text-14 font-medium text-word-1 leading-tight\">\n {{ $t('action.manageProfile') }}\n </span>\n <span class=\"text-12 text-word-3 leading-tight\">\n {{ $t('label.manageProfileDescription') }}\n </span>\n </div>\n <VvIcon\n name=\"ri:arrow-right-s-line\"\n class=\"w-16 h-16 text-word-4 shrink-0\" />\n </a>\n </div>\n\n <!-- Footer: sign out -->\n <div v-if=\"!isExternalAuth\" class=\"px-md py-sm\">\n <VvButton\n modifiers=\"secondary-danger\"\n class=\"w-full\"\n icon=\"ri:logout-box-line\"\n :loading=\"isSigningOut\"\n :disabled=\"isSigningOut\"\n :label=\"$t('action.signOut')\"\n @click=\"onSignOut\" />\n </div>\n\n <slot />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAQI,IAAM,IAAQ,GAER,EAAE,GAAG,MAAO,EAAQ,EAAE,UAAU,SAAS,CAAC,GAG1C,EAAE,MAAM,GAAW,sBAAmB,EAD9B,EAAgB,EAAM,OACoB,CAAK,GAEvD,IAAU,EAAO,GAA0B,KAAA,CAAS,GACpD,IAAO,QAAe,GAAS,KAAK,SAAS,IAAI,GACjD,IAAY,QAAe,GAAS,iBAAiB,SAAS,EAAK,GACnE,IAAa,QAAe,GAAS,WAAW,KAAK,GAIrD,IAAiB,QACb,GAAS,gBAAgB,SAAS,EAC5C,GAEM,IAAe,EAAI,EAAK,GACxB,IAAe,EAAY;EAEjC,eAAe,IAAY;GAClB,OAGL;MAAa,QAAQ;IACrB,IAAI;KACA,MAAM,EAAQ,QAAQ;IAC1B,SAAS,GAAO;KACZ,EAAa,QACT,aAAiB,QAAQ,EAAM,UAAU,EAAG,eAAe;IACnE,UAAU;KACN,EAAa,QAAQ;IACzB;GARqB;EASzB;;;eAIA,EAsEM,OAtEN,GAsEM;IApEF,EAmBM,OAnBN,GAmBM,CAjBF,EAIqD,GAAA;KAHjD,WAAU;KACV,OAAM;KACL,MAAM,EAAA,OAAM,QAAQ,EAAA,CAAA;KACpB,WAAS,EAAA,OAAM,SAAS,EAAA,CAAA,CAAc,EAAE;sCAC7C,EAWM,OAXN,GAWM,CATQ,EAAA,OAAM,QAAQ,EAAA,CAAA,KAAA,EAAA,GADxB,EAIS,UAJT,GAIS,EADF,EAAA,OAAM,QAAQ,EAAA,CAAA,CAAS,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAGpB,EAAA,OAAM,SAAA,EAAA,GADhB,EAIO,QAJP,GAIO,EADA,EAAA,MAAK,KAAK,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,CAAA,CAAA;IAMzB,EA+BM,OA/BN,GA+BM,CA7BQ,EAAA,SAAA,EAAA,GADV,EAG8B,GAAA;;KAD1B,WAAU;KACT,SAAS,EAAA;2CAIJ,EAAA,SAAa,EAAA,SAAU,CAAK,EAAA,SAAA,EAAA,GADtC,EAuBI,KAAA;;KArBC,MAAM,EAAA;KACP,QAAO;KACP,KAAI;KACJ,OAAM;;KACN,EAKM,OALN,GAKM,CAHF,EAEoC,GAAA;MADhC,MAAK;MACL,OAAM;;KAEd,EAOM,OAPN,GAOM,CANF,EAEO,QAFP,GAEO,EADA,EAAA,CAAA,CAAE,CAAA,sBAAA,CAAA,GAAA,CAAA,GAET,EAEO,QAFP,GAEO,EADA,EAAA,CAAA,CAAE,CAAA,gCAAA,CAAA,GAAA,CAAA,CAAA,CAAA;KAGb,EAE6C,GAAA;MADzC,MAAK;MACL,OAAM;;;IAKN,EAAA,qBAAA,EAAA,GAAZ,EASM,OATN,GASM,CARF,EAOyB,GAAA;KANrB,WAAU;KACV,OAAM;KACN,MAAK;KACJ,SAAS,EAAA;KACT,UAAU,EAAA;KACV,OAAO,EAAA,CAAA,CAAE,CAAA,gBAAA;KACT,SAAO;;;;;;IAGhB,EAAQ,EAAA,QAAA,SAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
-
import { i as t } from "./Document-
|
|
2
|
+
import { i as t } from "./Document-CSdfKj_B.js";
|
|
3
3
|
import "./src-8QXYVWIx.js";
|
|
4
4
|
import { t as n } from "./markdown-CCMpGUA7.js";
|
|
5
5
|
import { t as r } from "./PkStreamingMarkdown-DEhbK-3c.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-
|
|
242
|
+
//# sourceMappingURL=PkToolShowSources-CFh-i60o.js.map
|
package/dist-vue/_chunks/{PkToolShowSources-rGZz65c2.js.map → PkToolShowSources-CFh-i60o.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkToolShowSources-rGZz65c2.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 ? 'line-md: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 ? 'line-md: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-CFh-i60o.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 ? 'line-md: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 ? 'line-md: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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"}
|
|
@@ -27,7 +27,7 @@ var l = (e, t, n) => o(e, t, {
|
|
|
27
27
|
return { sort: i(t).optional().default(t[0]) };
|
|
28
28
|
}
|
|
29
29
|
}, h = /* @__PURE__ */ function(e) {
|
|
30
|
-
return e.textEmbedding3Small = "openai/text-embedding-3-small", e.gpt5Nano = "openai/gpt-5-nano", e.gpt5Mini = "openai/gpt-5-mini", e.gpt51Thinking = "openai/gpt-5.1-thinking", e.gpt51Instant = "openai/gpt-5.1-instant", e.gpt52 = "openai/gpt-5.2", e.gpt52Chat = "openai/gpt-5.2-chat", e.gpt54nano = "openai/gpt-5.4-nano", e.gpt54mini = "openai/gpt-5.4-mini", e.gpt54 = "openai/gpt-5.4", e.gpt54Pro = "openai/gpt-5.4-pro", e.gpt55 = "openai/gpt-5.5", e.gpt55Pro = "openai/gpt-5.5-pro", e.gpt56Luna = "openai/gpt-5.6-luna", e.gpt56Terra = "openai/gpt-5.6-terra", e.gpt56Sol = "openai/gpt-5.6-sol", e.geminiEmbedding2 = "google/gemini-embedding-2", e.gemini25Flash = "google/gemini-2.5-flash", e.gemini25FlashLite = "google/gemini-2.5-flash-lite", e.gemini25Pro = "google/gemini-2.5-pro", e.gemini3Flash = "google/gemini-3-flash", e.gemini3ProPreview = "google/gemini-3-pro-preview", e.gemini31FlashLitePreview = "google/gemini-3.1-flash-lite-preview", e.gemini31ProPreview = "google/gemini-3.1-pro-preview", e.gemini31FlashLite = "google/gemini-3.1-flash-lite", e.gemini35Flash = "google/gemini-3.5-flash", e.gemini35FlashLite = "google/gemini-3.5-flash-lite", e.gemini36Flash = "google/gemini-3.6-flash", e.claudeHaiku45 = "anthropic/claude-haiku-4.5", e.claudeSonnet45 = "anthropic/claude-sonnet-4.5", e.claudeSonnet46 = "anthropic/claude-sonnet-4.6", e.claudeSonnet5 = "anthropic/claude-sonnet-5", e.claudeOpus46 = "anthropic/claude-opus-4.6", e.claudeOpus47 = "anthropic/claude-opus-4.7", e.claudeOpus48 = "anthropic/claude-opus-4.8", e.claudeFable5 = "anthropic/claude-fable-5", e.deepseek32 = "deepseek/deepseek-v3.2", e.deepseek32Thinking = "deepseek/deepseek-v3.2-thinking", e.deepseek4pro = "deepseek/deepseek-v4-pro", e.deepseek4Flash = "deepseek/deepseek-v4-flash", e.alibabaQwen36Plus = "alibaba/qwen3.6-plus", e.alibabaQwen37Max = "alibaba/qwen3.7-max", e.alibabaQwen37Plus = "alibaba/qwen3.7-plus", e.xaiGrok43 = "xai/grok-4.3", e.zaiGlm52 = "zai/glm-5.2", e.zaiGlm52Fast = "zai/glm-5.2-fast", e.metaMuseSpark11 = "meta/muse-spark-1.1", e;
|
|
30
|
+
return e.textEmbedding3Small = "openai/text-embedding-3-small", e.gpt5Nano = "openai/gpt-5-nano", e.gpt5Mini = "openai/gpt-5-mini", e.gpt51Thinking = "openai/gpt-5.1-thinking", e.gpt51Instant = "openai/gpt-5.1-instant", e.gpt52 = "openai/gpt-5.2", e.gpt52Chat = "openai/gpt-5.2-chat", e.gpt54nano = "openai/gpt-5.4-nano", e.gpt54mini = "openai/gpt-5.4-mini", e.gpt54 = "openai/gpt-5.4", e.gpt54Pro = "openai/gpt-5.4-pro", e.gpt55 = "openai/gpt-5.5", e.gpt55Pro = "openai/gpt-5.5-pro", e.gpt56Luna = "openai/gpt-5.6-luna", e.gpt56Terra = "openai/gpt-5.6-terra", e.gpt56Sol = "openai/gpt-5.6-sol", e.geminiEmbedding2 = "google/gemini-embedding-2", e.gemini25Flash = "google/gemini-2.5-flash", e.gemini25FlashLite = "google/gemini-2.5-flash-lite", e.gemini25Pro = "google/gemini-2.5-pro", e.gemini3Flash = "google/gemini-3-flash", e.gemini3ProPreview = "google/gemini-3-pro-preview", e.gemini31FlashLitePreview = "google/gemini-3.1-flash-lite-preview", e.gemini31ProPreview = "google/gemini-3.1-pro-preview", e.gemini31FlashLite = "google/gemini-3.1-flash-lite", e.gemini35Flash = "google/gemini-3.5-flash", e.gemini35FlashLite = "google/gemini-3.5-flash-lite", e.gemini36Flash = "google/gemini-3.6-flash", e.claudeHaiku45 = "anthropic/claude-haiku-4.5", e.claudeSonnet45 = "anthropic/claude-sonnet-4.5", e.claudeSonnet46 = "anthropic/claude-sonnet-4.6", e.claudeSonnet5 = "anthropic/claude-sonnet-5", e.claudeOpus46 = "anthropic/claude-opus-4.6", e.claudeOpus47 = "anthropic/claude-opus-4.7", e.claudeOpus48 = "anthropic/claude-opus-4.8", e.claudeOpus5 = "anthropic/claude-opus-5", e.claudeOpus5Fast = "anthropic/claude-opus-5-fast", e.claudeFable5 = "anthropic/claude-fable-5", e.deepseek32 = "deepseek/deepseek-v3.2", e.deepseek32Thinking = "deepseek/deepseek-v3.2-thinking", e.deepseek4pro = "deepseek/deepseek-v4-pro", e.deepseek4Flash = "deepseek/deepseek-v4-flash", e.alibabaQwen36Plus = "alibaba/qwen3.6-plus", e.alibabaQwen37Max = "alibaba/qwen3.7-max", e.alibabaQwen37Plus = "alibaba/qwen3.7-plus", e.xaiGrok43 = "xai/grok-4.3", e.zaiGlm52 = "zai/glm-5.2", e.zaiGlm52Fast = "zai/glm-5.2-fast", e.metaMuseSpark11 = "meta/muse-spark-1.1", e;
|
|
31
31
|
}({});
|
|
32
32
|
h.geminiEmbedding2;
|
|
33
33
|
var g = /* @__PURE__ */ function(e) {
|
|
@@ -42,4 +42,4 @@ var _ = /* @__PURE__ */ function(e) {
|
|
|
42
42
|
//#endregion
|
|
43
43
|
export { l as a, g as i, _ as n, d as o, h as r, m as s, v as t };
|
|
44
44
|
|
|
45
|
-
//# sourceMappingURL=constants-
|
|
45
|
+
//# sourceMappingURL=constants-BCJ_gKeL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants-BSe0uxSf.js","names":[],"sources":["../../../../packages/models/src/utils.ts","../../../../packages/models/src/schema/constants.ts"],"sourcesContent":["import * as z from 'zod'\n\n/**\n * Bidirectional wrapper for decode-time normalizations.\n *\n * Response serialization must never hit a unidirectional transform\n * (z.preprocess/.transform throw ZodEncodeError under zod encode), so\n * normalizing schemas are expressed as codecs: `decode` runs the\n * normalization (output validity is enforced by the output schema at\n * runtime), while `encode` is the identity for already-normalized data.\n */\nexport const decodeOnly = <Input extends z.ZodType, Output extends z.ZodType>(\n input: Input,\n output: Output,\n decode: (value: z.output<Input>) => z.input<Output>,\n) =>\n z.codec(input, output, {\n // Identity: already-normalized data is serialized unchanged. The\n // input schema re-validates at runtime, so the cast is type-only.\n encode: (value) => value as z.output<Input>,\n decode,\n })\n\nconst MAX_SCHEMA_SIZE_BYTES = 50 * 1024 // 50 KB\n\n/** Reusable Zod field for JSON Schema objects with a 50 KB size limit. */\nexport const jsonSchemaField = z\n .record(z.string(), z.unknown())\n .refine((val) => JSON.stringify(val).length <= MAX_SCHEMA_SIZE_BYTES, {\n message: `Schema must not exceed ${MAX_SCHEMA_SIZE_BYTES / 1024} KB`,\n })\n\nexport type QueryStringFilter<T extends string = string> = `filter[${T}]`\nexport type Querystring = {\n 'page[number]'?: number | string\n 'page[size]'?: number | string\n sort?: string[] | string\n [filterKeys: QueryStringFilter]: unknown\n [otherKeys: string]: unknown\n}\n\nexport const makeSortEnum = <const T extends string>(input: T) => {\n return [`${input}`, `-${input}`] as [T, `-${T}`]\n}\n\nexport const makeSortEnums = <const T extends string>(input: readonly T[]) => {\n return input.flatMap((i) => makeSortEnum(i))\n}\n\nexport const zodQs = {\n pagination: (defaultNumber = 1, defaultSize = 10) => {\n return {\n 'page[number]': z\n .number()\n .or(z.string())\n .transform((v) => Number(v))\n .default(defaultNumber),\n 'page[size]': z\n .number()\n .or(z.string())\n .transform((v) => Number(v))\n .default(defaultSize),\n }\n },\n filter: <const T extends string>(input: T) => {\n return { [`filter[${input}]`]: z.string().optional() } as Record<\n `filter[${T}]`,\n z.ZodOptional<z.ZodString | z.ZodArray<z.ZodString>>\n >\n },\n include: <const T extends string>(input: T) => {\n return { [`include[${input}]`]: z.string().optional() } as Record<\n `include[${T}]`,\n z.ZodOptional<z.ZodArray<z.ZodString> | z.ZodString>\n >\n },\n range: <const T extends string>(input: T) => {\n return {\n [`from[${input}]`]: z.string().optional(),\n [`to[${input}]`]: z.string().optional(),\n } as Record<`from[${T}]` | `to[${T}]`, z.ZodOptional<z.ZodString>>\n },\n has: <const T extends string>(input: T) => {\n return { [`has[${input}]`]: z.boolean().optional() } as Record<\n `has[${T}]`,\n z.ZodOptional<z.ZodBoolean>\n >\n },\n fullText: () => {\n return { [`filter[fullText]`]: z.string().optional() }\n },\n ids: () => {\n return { ids: z.union([z.string(), z.array(z.string())]).optional() }\n },\n deleted: () => {\n return { 'filter[isDeleted]': z.boolean().default(false) }\n },\n filters: <const T extends string>(input: readonly T[]) => {\n return input\n .map((i) => zodQs.filter(i))\n .reduce((a, b) => ({ ...a, ...b }))\n },\n sort: <const S extends string>(sort: S[]) => {\n const sortEnums = makeSortEnums(sort) as [S, ...`-${S}`[]]\n return {\n // @ts-expect-error: zod does not support tuple types\n sort: z.enum(sortEnums).optional().default(sortEnums[0]),\n }\n },\n}\n","export enum LanguageModel {\n // OpenAI\n textEmbedding3Small = 'openai/text-embedding-3-small',\n gpt5Nano = 'openai/gpt-5-nano',\n gpt5Mini = 'openai/gpt-5-mini',\n gpt51Thinking = 'openai/gpt-5.1-thinking',\n gpt51Instant = 'openai/gpt-5.1-instant',\n gpt52 = 'openai/gpt-5.2',\n gpt52Chat = 'openai/gpt-5.2-chat',\n gpt54nano = 'openai/gpt-5.4-nano',\n gpt54mini = 'openai/gpt-5.4-mini',\n gpt54 = 'openai/gpt-5.4',\n gpt54Pro = 'openai/gpt-5.4-pro',\n gpt55 = 'openai/gpt-5.5',\n gpt55Pro = 'openai/gpt-5.5-pro',\n gpt56Luna = 'openai/gpt-5.6-luna',\n gpt56Terra = 'openai/gpt-5.6-terra',\n gpt56Sol = 'openai/gpt-5.6-sol',\n // Google Gemini\n geminiEmbedding2 = 'google/gemini-embedding-2',\n gemini25Flash = 'google/gemini-2.5-flash',\n gemini25FlashLite = 'google/gemini-2.5-flash-lite',\n gemini25Pro = 'google/gemini-2.5-pro',\n gemini3Flash = 'google/gemini-3-flash',\n gemini3ProPreview = 'google/gemini-3-pro-preview',\n gemini31FlashLitePreview = 'google/gemini-3.1-flash-lite-preview',\n gemini31ProPreview = 'google/gemini-3.1-pro-preview',\n gemini31FlashLite = 'google/gemini-3.1-flash-lite',\n gemini35Flash = 'google/gemini-3.5-flash',\n gemini35FlashLite = 'google/gemini-3.5-flash-lite',\n gemini36Flash = 'google/gemini-3.6-flash',\n // Anthropic Claude\n claudeHaiku45 = 'anthropic/claude-haiku-4.5',\n claudeSonnet45 = 'anthropic/claude-sonnet-4.5',\n claudeSonnet46 = 'anthropic/claude-sonnet-4.6',\n claudeSonnet5 = 'anthropic/claude-sonnet-5',\n claudeOpus46 = 'anthropic/claude-opus-4.6',\n claudeOpus47 = 'anthropic/claude-opus-4.7',\n claudeOpus48 = 'anthropic/claude-opus-4.8',\n claudeFable5 = 'anthropic/claude-fable-5',\n // Deepseek\n deepseek32 = 'deepseek/deepseek-v3.2',\n deepseek32Thinking = 'deepseek/deepseek-v3.2-thinking',\n deepseek4pro = 'deepseek/deepseek-v4-pro',\n deepseek4Flash = 'deepseek/deepseek-v4-flash',\n // Alibaba\n alibabaQwen36Plus = 'alibaba/qwen3.6-plus',\n alibabaQwen37Max = 'alibaba/qwen3.7-max',\n alibabaQwen37Plus = 'alibaba/qwen3.7-plus',\n // xAI\n xaiGrok43 = 'xai/grok-4.3',\n // z.ai\n zaiGlm52 = 'zai/glm-5.2',\n zaiGlm52Fast = 'zai/glm-5.2-fast',\n // Meta\n metaMuseSpark11 = 'meta/muse-spark-1.1',\n}\n\nexport const EMBED_MODEL = LanguageModel.geminiEmbedding2\n\nexport enum LanguageModelBrand {\n openai = 'openai',\n google = 'google',\n anthropic = 'anthropic',\n deepseek = 'deepseek',\n xai = 'xai',\n zai = 'zai',\n meta = 'meta',\n alibaba = 'alibaba',\n}\n\n/**\n * Supported languages for documents and revised answers\n */\nexport enum SupportedLanguage {\n it = 'it',\n en = 'en',\n es = 'es',\n fr = 'fr',\n de = 'de',\n}\n\n/**\n * Language display names (i18n keys)\n */\nexport const LANGUAGE_NAMES: Record<SupportedLanguage, string> = {\n [SupportedLanguage.it]: 'language.italian',\n [SupportedLanguage.en]: 'language.english',\n [SupportedLanguage.es]: 'language.spanish',\n [SupportedLanguage.fr]: 'language.french',\n [SupportedLanguage.de]: 'language.german',\n}\n\n/**\n * Content extraction strategies for URL scraping\n */\nexport enum ExtractionStrategy {\n /** Use Mozilla Readability for content extraction (fast, free, no API calls) */\n READABILITY = 'readability',\n /** Use AI-based extraction (better for complex layouts, requires API calls) */\n AI = 'ai',\n}\n\n/**\n * Browser engine options for URL scraping\n */\nexport enum BrowserEngine {\n /** Use simple HTTP fetch (fast, lightweight, no JavaScript support) */\n FETCH = 'fetch',\n /** Use Puppeteer with headless Chrome (full browser, JavaScript support) */\n PUPPETEER = 'puppeteer',\n /** Delegate browser rendering to an external pdf-image-generator service */\n REMOTE = 'remote',\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"constants-BCJ_gKeL.js","names":[],"sources":["../../../../packages/models/src/utils.ts","../../../../packages/models/src/schema/constants.ts"],"sourcesContent":["import * as z from 'zod'\n\n/**\n * Bidirectional wrapper for decode-time normalizations.\n *\n * Response serialization must never hit a unidirectional transform\n * (z.preprocess/.transform throw ZodEncodeError under zod encode), so\n * normalizing schemas are expressed as codecs: `decode` runs the\n * normalization (output validity is enforced by the output schema at\n * runtime), while `encode` is the identity for already-normalized data.\n */\nexport const decodeOnly = <Input extends z.ZodType, Output extends z.ZodType>(\n input: Input,\n output: Output,\n decode: (value: z.output<Input>) => z.input<Output>,\n) =>\n z.codec(input, output, {\n // Identity: already-normalized data is serialized unchanged. The\n // input schema re-validates at runtime, so the cast is type-only.\n encode: (value) => value as z.output<Input>,\n decode,\n })\n\nconst MAX_SCHEMA_SIZE_BYTES = 50 * 1024 // 50 KB\n\n/** Reusable Zod field for JSON Schema objects with a 50 KB size limit. */\nexport const jsonSchemaField = z\n .record(z.string(), z.unknown())\n .refine((val) => JSON.stringify(val).length <= MAX_SCHEMA_SIZE_BYTES, {\n message: `Schema must not exceed ${MAX_SCHEMA_SIZE_BYTES / 1024} KB`,\n })\n\nexport type QueryStringFilter<T extends string = string> = `filter[${T}]`\nexport type Querystring = {\n 'page[number]'?: number | string\n 'page[size]'?: number | string\n sort?: string[] | string\n [filterKeys: QueryStringFilter]: unknown\n [otherKeys: string]: unknown\n}\n\nexport const makeSortEnum = <const T extends string>(input: T) => {\n return [`${input}`, `-${input}`] as [T, `-${T}`]\n}\n\nexport const makeSortEnums = <const T extends string>(input: readonly T[]) => {\n return input.flatMap((i) => makeSortEnum(i))\n}\n\nexport const zodQs = {\n pagination: (defaultNumber = 1, defaultSize = 10) => {\n return {\n 'page[number]': z\n .number()\n .or(z.string())\n .transform((v) => Number(v))\n .default(defaultNumber),\n 'page[size]': z\n .number()\n .or(z.string())\n .transform((v) => Number(v))\n .default(defaultSize),\n }\n },\n filter: <const T extends string>(input: T) => {\n return { [`filter[${input}]`]: z.string().optional() } as Record<\n `filter[${T}]`,\n z.ZodOptional<z.ZodString | z.ZodArray<z.ZodString>>\n >\n },\n include: <const T extends string>(input: T) => {\n return { [`include[${input}]`]: z.string().optional() } as Record<\n `include[${T}]`,\n z.ZodOptional<z.ZodArray<z.ZodString> | z.ZodString>\n >\n },\n range: <const T extends string>(input: T) => {\n return {\n [`from[${input}]`]: z.string().optional(),\n [`to[${input}]`]: z.string().optional(),\n } as Record<`from[${T}]` | `to[${T}]`, z.ZodOptional<z.ZodString>>\n },\n has: <const T extends string>(input: T) => {\n return { [`has[${input}]`]: z.boolean().optional() } as Record<\n `has[${T}]`,\n z.ZodOptional<z.ZodBoolean>\n >\n },\n fullText: () => {\n return { [`filter[fullText]`]: z.string().optional() }\n },\n ids: () => {\n return { ids: z.union([z.string(), z.array(z.string())]).optional() }\n },\n deleted: () => {\n return { 'filter[isDeleted]': z.boolean().default(false) }\n },\n filters: <const T extends string>(input: readonly T[]) => {\n return input\n .map((i) => zodQs.filter(i))\n .reduce((a, b) => ({ ...a, ...b }))\n },\n sort: <const S extends string>(sort: S[]) => {\n const sortEnums = makeSortEnums(sort) as [S, ...`-${S}`[]]\n return {\n // @ts-expect-error: zod does not support tuple types\n sort: z.enum(sortEnums).optional().default(sortEnums[0]),\n }\n },\n}\n","export enum LanguageModel {\n // OpenAI\n textEmbedding3Small = 'openai/text-embedding-3-small',\n gpt5Nano = 'openai/gpt-5-nano',\n gpt5Mini = 'openai/gpt-5-mini',\n gpt51Thinking = 'openai/gpt-5.1-thinking',\n gpt51Instant = 'openai/gpt-5.1-instant',\n gpt52 = 'openai/gpt-5.2',\n gpt52Chat = 'openai/gpt-5.2-chat',\n gpt54nano = 'openai/gpt-5.4-nano',\n gpt54mini = 'openai/gpt-5.4-mini',\n gpt54 = 'openai/gpt-5.4',\n gpt54Pro = 'openai/gpt-5.4-pro',\n gpt55 = 'openai/gpt-5.5',\n gpt55Pro = 'openai/gpt-5.5-pro',\n gpt56Luna = 'openai/gpt-5.6-luna',\n gpt56Terra = 'openai/gpt-5.6-terra',\n gpt56Sol = 'openai/gpt-5.6-sol',\n // Google Gemini\n geminiEmbedding2 = 'google/gemini-embedding-2',\n gemini25Flash = 'google/gemini-2.5-flash',\n gemini25FlashLite = 'google/gemini-2.5-flash-lite',\n gemini25Pro = 'google/gemini-2.5-pro',\n gemini3Flash = 'google/gemini-3-flash',\n gemini3ProPreview = 'google/gemini-3-pro-preview',\n gemini31FlashLitePreview = 'google/gemini-3.1-flash-lite-preview',\n gemini31ProPreview = 'google/gemini-3.1-pro-preview',\n gemini31FlashLite = 'google/gemini-3.1-flash-lite',\n gemini35Flash = 'google/gemini-3.5-flash',\n gemini35FlashLite = 'google/gemini-3.5-flash-lite',\n gemini36Flash = 'google/gemini-3.6-flash',\n // Anthropic Claude\n claudeHaiku45 = 'anthropic/claude-haiku-4.5',\n claudeSonnet45 = 'anthropic/claude-sonnet-4.5',\n claudeSonnet46 = 'anthropic/claude-sonnet-4.6',\n claudeSonnet5 = 'anthropic/claude-sonnet-5',\n claudeOpus46 = 'anthropic/claude-opus-4.6',\n claudeOpus47 = 'anthropic/claude-opus-4.7',\n claudeOpus48 = 'anthropic/claude-opus-4.8',\n claudeOpus5 = 'anthropic/claude-opus-5',\n claudeOpus5Fast = 'anthropic/claude-opus-5-fast',\n claudeFable5 = 'anthropic/claude-fable-5',\n // Deepseek\n deepseek32 = 'deepseek/deepseek-v3.2',\n deepseek32Thinking = 'deepseek/deepseek-v3.2-thinking',\n deepseek4pro = 'deepseek/deepseek-v4-pro',\n deepseek4Flash = 'deepseek/deepseek-v4-flash',\n // Alibaba\n alibabaQwen36Plus = 'alibaba/qwen3.6-plus',\n alibabaQwen37Max = 'alibaba/qwen3.7-max',\n alibabaQwen37Plus = 'alibaba/qwen3.7-plus',\n // xAI\n xaiGrok43 = 'xai/grok-4.3',\n // z.ai\n zaiGlm52 = 'zai/glm-5.2',\n zaiGlm52Fast = 'zai/glm-5.2-fast',\n // Meta\n metaMuseSpark11 = 'meta/muse-spark-1.1',\n}\n\nexport const EMBED_MODEL = LanguageModel.geminiEmbedding2\n\nexport enum LanguageModelBrand {\n openai = 'openai',\n google = 'google',\n anthropic = 'anthropic',\n deepseek = 'deepseek',\n xai = 'xai',\n zai = 'zai',\n meta = 'meta',\n alibaba = 'alibaba',\n}\n\n/**\n * Supported languages for documents and revised answers\n */\nexport enum SupportedLanguage {\n it = 'it',\n en = 'en',\n es = 'es',\n fr = 'fr',\n de = 'de',\n}\n\n/**\n * Language display names (i18n keys)\n */\nexport const LANGUAGE_NAMES: Record<SupportedLanguage, string> = {\n [SupportedLanguage.it]: 'language.italian',\n [SupportedLanguage.en]: 'language.english',\n [SupportedLanguage.es]: 'language.spanish',\n [SupportedLanguage.fr]: 'language.french',\n [SupportedLanguage.de]: 'language.german',\n}\n\n/**\n * Content extraction strategies for URL scraping\n */\nexport enum ExtractionStrategy {\n /** Use Mozilla Readability for content extraction (fast, free, no API calls) */\n READABILITY = 'readability',\n /** Use AI-based extraction (better for complex layouts, requires API calls) */\n AI = 'ai',\n}\n\n/**\n * Browser engine options for URL scraping\n */\nexport enum BrowserEngine {\n /** Use simple HTTP fetch (fast, lightweight, no JavaScript support) */\n FETCH = 'fetch',\n /** Use Puppeteer with headless Chrome (full browser, JavaScript support) */\n PUPPETEER = 'puppeteer',\n /** Delegate browser rendering to an external pdf-image-generator service */\n REMOTE = 'remote',\n}\n"],"mappings":"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"}
|
package/dist-vue/_chunks/{createChatbotApiClient-Bf5DbqVH.js → createChatbotApiClient-BOBdBxmf.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { S as e, _ as t, a as n, b as r, d as i, f as a, g as o, h as s, i as c, l, n as u, o as d, v as f, x as p } from "./schemas-DnUKIqgi.js";
|
|
2
2
|
import { i as m, n as ee, t as te } from "./Media-B_7Yj-ml.js";
|
|
3
|
-
import { a as ne, o as re, r as ie, s as h } from "./constants-
|
|
3
|
+
import { a as ne, o as re, r as ie, s as h } from "./constants-BCJ_gKeL.js";
|
|
4
4
|
import "./src-8QXYVWIx.js";
|
|
5
5
|
import { r as g, t as ae } from "./url-validation-uLMZ5-AU.js";
|
|
6
6
|
import { Ability as oe, buildMongoQueryMatcher as se, createAliasResolver as ce } from "@casl/ability";
|
|
@@ -922,4 +922,4 @@ function wt(e) {
|
|
|
922
922
|
//#endregion
|
|
923
923
|
export { L as a, M as c, be as d, ye as f, P as i, T as l, X as n, N as o, C as r, b as s, wt as t, Se as u };
|
|
924
924
|
|
|
925
|
-
//# sourceMappingURL=createChatbotApiClient-
|
|
925
|
+
//# sourceMappingURL=createChatbotApiClient-BOBdBxmf.js.map
|