@8wave/ai-elements 0.97.0 → 0.98.0-beta.2
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/README.md +20 -18
- package/dist/_chunks/{Document-njJL_3oo.js → Document-CjCQXPDF.js} +54 -54
- package/dist/_chunks/{Document-njJL_3oo.js.map → Document-CjCQXPDF.js.map} +1 -1
- package/dist/_chunks/{PkStreamingMarkdown-Bns7j3b7.js → PkStreamingMarkdown-Djw604Ny.js} +43 -43
- package/dist/_chunks/PkStreamingMarkdown-Djw604Ny.js.map +1 -0
- package/dist/_chunks/{PkToolShowArtifact-BHGuiacA.js → PkToolShowArtifact-BaGEr7O9.js} +2 -2
- package/dist/_chunks/{PkToolShowArtifact-BHGuiacA.js.map → PkToolShowArtifact-BaGEr7O9.js.map} +1 -1
- package/dist/_chunks/{PkToolShowContactForm-Cp6fDIOT.js → PkToolShowContactForm-DEQBTzf4.js} +4 -4
- package/dist/_chunks/{PkToolShowContactForm-Cp6fDIOT.js.map → PkToolShowContactForm-DEQBTzf4.js.map} +1 -1
- package/dist/_chunks/{PkToolShowForm-BqUkMxMK.js → PkToolShowForm-AZwrmmM9.js} +2 -2
- package/dist/_chunks/{PkToolShowForm-BqUkMxMK.js.map → PkToolShowForm-AZwrmmM9.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSources-Bg0QAcmF.js → PkToolShowSources-FnMkkdm6.js} +3 -3
- package/dist/_chunks/{PkToolShowSources-Bg0QAcmF.js.map → PkToolShowSources-FnMkkdm6.js.map} +1 -1
- package/dist/_chunks/{VvCheckbox.es-If2fZZst.js → VvCheckbox.es-D5d8k9yP.js} +2 -2
- package/dist/_chunks/{VvCheckbox.es-If2fZZst.js.map → VvCheckbox.es-D5d8k9yP.js.map} +1 -1
- package/dist/_chunks/{VvCheckboxGroup.es-A-IjEdQh.js → VvCheckboxGroup.es-DVnsOjX0.js} +2 -2
- package/dist/_chunks/{VvCheckboxGroup.es-A-IjEdQh.js.map → VvCheckboxGroup.es-DVnsOjX0.js.map} +1 -1
- package/dist/_chunks/{VvCombobox.es-DLRkNj0w.js → VvCombobox.es-CFA-K-cQ.js} +2 -2
- package/dist/_chunks/{VvCombobox.es-DLRkNj0w.js.map → VvCombobox.es-CFA-K-cQ.js.map} +1 -1
- package/dist/_chunks/{VvInputText.es-DV6kHrYx.js → VvInputText.es-csEQ-bSG.js} +2 -2
- package/dist/_chunks/{VvInputText.es-DV6kHrYx.js.map → VvInputText.es-csEQ-bSG.js.map} +1 -1
- package/dist/_chunks/{VvRadio.es-C_L52PYW.js → VvRadio.es-BpymhC73.js} +2 -2
- package/dist/_chunks/{VvRadio.es-C_L52PYW.js.map → VvRadio.es-BpymhC73.js.map} +1 -1
- package/dist/_chunks/{VvRadioGroup.es-B64sR46T.js → VvRadioGroup.es-CSNzOJKx.js} +2 -2
- package/dist/_chunks/{VvRadioGroup.es-B64sR46T.js.map → VvRadioGroup.es-CSNzOJKx.js.map} +1 -1
- package/dist/_chunks/{VvSelect.es-D_WR7HcC.js → VvSelect.es-CjKMd0nH.js} +2 -2
- package/dist/_chunks/{VvSelect.es-D_WR7HcC.js.map → VvSelect.es-CjKMd0nH.js.map} +1 -1
- package/dist/_chunks/{VvTextarea.es-DpF-slnp.js → VvTextarea.es-eEyRRpuT.js} +2 -2
- package/dist/_chunks/{VvTextarea.es-DpF-slnp.js.map → VvTextarea.es-eEyRRpuT.js.map} +1 -1
- package/dist/_chunks/index.es-BYny0uhz.js.map +1 -1
- package/dist/_chunks/{index.es-DN8D4nRq.js → index.es-eLFDkB2Y.js} +11 -11
- package/dist/_chunks/{index.es-DN8D4nRq.js.map → index.es-eLFDkB2Y.js.map} +1 -1
- package/dist/_chunks/{schemas-BsOP_5an.js → schemas-DnUKIqgi.js} +503 -503
- package/dist/_chunks/{schemas-BsOP_5an.js.map → schemas-DnUKIqgi.js.map} +1 -1
- package/dist/ai-elements.es.js +3836 -3743
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotAuth.js +1 -1
- package/dist-vue/PkChatbotEmptyState.js +1 -1
- package/dist-vue/PkChatbotFeedbackForm.js +1 -1
- package/dist-vue/PkChatbotInput.js +1 -1
- package/dist-vue/PkChatbotMessages.js +1 -1
- package/dist-vue/PkChatbotSteps.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-q67u4D3u.js → Document-B8gv0e2O.js} +15 -15
- package/dist-vue/_chunks/{Document-q67u4D3u.js.map → Document-B8gv0e2O.js.map} +1 -1
- package/dist-vue/_chunks/{Media-BmW9F_3T.js → Media-B_7Yj-ml.js} +26 -26
- package/dist-vue/_chunks/{Media-BmW9F_3T.js.map → Media-B_7Yj-ml.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbot-A1jKttsQ.js → PkChatbot-CneS8fVC.js} +7 -7
- package/dist-vue/_chunks/{PkChatbot-A1jKttsQ.js.map → PkChatbot-CneS8fVC.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotAuth-C0ATmt-9.js → PkChatbotAuth-C_L2xWuD.js} +4 -4
- package/dist-vue/_chunks/{PkChatbotAuth-C0ATmt-9.js.map → PkChatbotAuth-C_L2xWuD.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotEmptyState-BGTSWFSS.js → PkChatbotEmptyState-BGOC6vxr.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotEmptyState-BGTSWFSS.js.map → PkChatbotEmptyState-BGOC6vxr.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotFeedbackForm-Dhsa1c06.js → PkChatbotFeedbackForm-BSe40qlc.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotFeedbackForm-Dhsa1c06.js.map → PkChatbotFeedbackForm-BSe40qlc.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotInput-Be3J_Fgz.js → PkChatbotInput-DTazYlPd.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotInput-Be3J_Fgz.js.map → PkChatbotInput-DTazYlPd.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-Cbn8okze.js → PkChatbotMessages-DEzcxcqc.js} +9 -9
- package/dist-vue/_chunks/{PkChatbotMessages-Cbn8okze.js.map → PkChatbotMessages-DEzcxcqc.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotSteps-BhqYCNWy.js → PkChatbotSteps-BpqdFhsG.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotSteps-BhqYCNWy.js.map → PkChatbotSteps-BpqdFhsG.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-Cc8bZfAr.js → PkChatbotViewChat-C7Xv2Oer.js} +6 -6
- package/dist-vue/_chunks/{PkChatbotViewChat-Cc8bZfAr.js.map → PkChatbotViewChat-C7Xv2Oer.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewConversations-BsfA4A7O.js → PkChatbotViewConversations-YbHHc8oD.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewConversations-BsfA4A7O.js.map → PkChatbotViewConversations-YbHHc8oD.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewFiles-C6aouWQh.js → PkChatbotViewFiles-BPE0g2Jk.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotViewFiles-C6aouWQh.js.map → PkChatbotViewFiles-BPE0g2Jk.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewProfile-B_ZAXIrC.js → PkChatbotViewProfile-B_gWPgVY.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-B_ZAXIrC.js.map → PkChatbotViewProfile-B_gWPgVY.js.map} +1 -1
- package/dist-vue/_chunks/{PkStreamingMarkdown-CNLdk20r.js → PkStreamingMarkdown-DEhbK-3c.js} +43 -43
- package/dist-vue/_chunks/PkStreamingMarkdown-DEhbK-3c.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowArtifact-B2ilUNT0.js → PkToolShowArtifact-CTBwt4DN.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowArtifact-B2ilUNT0.js.map → PkToolShowArtifact-CTBwt4DN.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowContactForm-C3CXe6sh.js → PkToolShowContactForm-B6-MsExL.js} +4 -4
- package/dist-vue/_chunks/{PkToolShowContactForm-C3CXe6sh.js.map → PkToolShowContactForm-B6-MsExL.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowForm-DnzUxm2o.js → PkToolShowForm-C-mWyeka.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowForm-DnzUxm2o.js.map → PkToolShowForm-C-mWyeka.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSources-BF2-VGSg.js → PkToolShowSources-rGZz65c2.js} +3 -3
- package/dist-vue/_chunks/{PkToolShowSources-BF2-VGSg.js.map → PkToolShowSources-rGZz65c2.js.map} +1 -1
- package/dist-vue/_chunks/{constants-DtnBK4PW.js → constants-BSe0uxSf.js} +4 -4
- package/dist-vue/_chunks/{constants-DtnBK4PW.js.map → constants-BSe0uxSf.js.map} +1 -1
- package/dist-vue/_chunks/{createChatbotApiClient-BHfNaVk8.js → createChatbotApiClient-Bf5DbqVH.js} +125 -113
- package/dist-vue/_chunks/createChatbotApiClient-Bf5DbqVH.js.map +1 -0
- package/dist-vue/_chunks/{index.es-BODtrBxv.js → index.es-XfWQ82pb.js} +3 -3
- package/dist-vue/_chunks/{index.es-BODtrBxv.js.map → index.es-XfWQ82pb.js.map} +1 -1
- package/dist-vue/_chunks/{schemas-BsOP_5an.js → schemas-DnUKIqgi.js} +503 -503
- package/dist-vue/_chunks/{schemas-BsOP_5an.js.map → schemas-DnUKIqgi.js.map} +1 -1
- package/dist-vue/_chunks/{useChatbotStore-bbenzuMe.js → useChatbotStore-Cfx-qlC4.js} +778 -761
- package/dist-vue/_chunks/useChatbotStore-Cfx-qlC4.js.map +1 -0
- package/dist-vue/api.js +1 -1
- package/dist-vue/apps/web-component/src/components/EmbeddedChatWidget.ce.d.ts +6 -0
- package/dist-vue/apps/web-component/src/components/FloatingChatWidget.ce.d.ts +6 -0
- package/dist-vue/apps/web-component/src/composables/useChatbotAgent.d.ts +6 -0
- package/dist-vue/apps/web-component/src/lib.d.ts +2 -0
- package/dist-vue/composables.js +5 -3
- package/dist-vue/composables.js.map +1 -1
- package/dist-vue/index.js +23 -23
- package/dist-vue/packages/components/src/chat/PkChatbotMessages.d.ts +134 -3
- package/dist-vue/packages/components/src/chat/PkMessageParts.d.ts +134 -3
- package/dist-vue/packages/composable/src/chatbot/api/index.d.ts +1 -0
- package/dist-vue/packages/composable/src/chatbot/api/resolveShortUrls.d.ts +16 -0
- package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +24 -24
- package/dist-vue/packages/models/src/schema/McpServer.d.ts +4 -0
- package/package.json +3 -3
- package/dist/_chunks/PkStreamingMarkdown-Bns7j3b7.js.map +0 -1
- package/dist-vue/_chunks/PkStreamingMarkdown-CNLdk20r.js.map +0 -1
- package/dist-vue/_chunks/createChatbotApiClient-BHfNaVk8.js.map +0 -1
- package/dist-vue/_chunks/useChatbotStore-bbenzuMe.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbot-A1jKttsQ.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbot.vue","../../../../packages/components/src/chat/PkChatbot.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, inject, toRef, watch, watchEffect, useSlots } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type {\n AgentInterface,\n AgentModel,\n AgentFileUpload,\n AgentGatewayOptions,\n ChatMessageActions,\n RevisedAnswer,\n UIChatMessage,\n AgentTools,\n } from 'models'\n import {\n useChatbotStore,\n useLocalizedString,\n provideShortUrlResolver,\n } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotViewChat from './PkChatbotViewChat.vue'\n import PkChatbotViewConversations from './PkChatbotViewConversations.vue'\n import PkChatbotViewFiles from './PkChatbotViewFiles.vue'\n import PkChatbotViewProfile from './PkChatbotViewProfile.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n import { resolveContrastColor } from './utils'\n\n // Captured in script setup to avoid TS7022 circular inference in template\n const slots = useSlots()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = defineProps<{\n baseUrl: string\n basePath?: string\n agentId: string\n agentType?: 'reasoning' | 'chat'\n userId?: string\n name?: string\n model?: string\n agentModel?: AgentModel\n agentTools?: AgentTools\n agentInterface?: AgentInterface\n actions?: ChatMessageActions[]\n revisedAnswers?: RevisedAnswer[]\n headers?: Record<string, string>\n systemTheme?: 'light' | 'dark'\n agentFileUpload?: AgentFileUpload\n fallbackModels?: string[]\n gatewayOptions?: Partial<AgentGatewayOptions>\n externalContext?: Record<string, unknown>\n /** Layout modifier: `widget` (default) keeps the contained layout with header, `fullscreen` removes header/background for full-page usage (the page provides the chrome) */\n modifier?: 'widget' | 'fullscreen'\n /** Display name of the current user, used for the `{name}` placeholder in the fullscreen greeting */\n userName?: string\n /** Show the \"Files in this chat\" menu for hosts that don't provide the\n * profile context but whose user is authenticated (e.g. the backoffice\n * playground). The widget host relies on `canManageProfile` instead. */\n userFilesEnabled?: boolean\n }>()\n const emit = defineEmits<{\n 'message-update': [message: UIChatMessage]\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n error: [error: { type: string; title: string; status: number }]\n }>()\n\n const chatId = defineModel<string | undefined>('chatId', {\n type: String,\n default: undefined,\n })\n\n const store = useChatbotStore(props.agentId)\n\n // Resolve backend short-urls (`/api/r/<hash>`) back to their real target\n // for display in tool components. Batched + cached across the chat tree.\n provideShortUrlResolver((hashes) =>\n store.apiClient.resolveShortUrls(hashes),\n )\n\n const { messages, chat, localChatId, activeView, chatError, isDark } =\n storeToRefs(store)\n\n // Ref to store.agentId for watchers (avoids duplicate-key with agentId prop)\n const storeAgentId = toRef(store, 'agentId')\n\n const { startNewChat, stopGeneration, regenerate, navigate, sendMessage } =\n store\n\n // Optional profile-management context provided by the host widget.\n // Present only for authenticated, non-anonymous sessions.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canManageProfile = computed(\n () => profile?.canManageProfile.value ?? false,\n )\n // Anonymous users get a \"Sign in\" entry instead of the profile one,\n // provided the host filled the `view-auth` slot and the agent has at\n // least one auth method enabled.\n const canSignIn = computed(\n () =>\n !canManageProfile.value &&\n (profile?.canSignIn?.value ?? false) &&\n slots['view-auth'] !== undefined,\n )\n\n // Once the sign-in completes (the session becomes non-anonymous) leave\n // the auth view and return to the conversation.\n watch(canManageProfile, (canManage) => {\n if (canManage && activeView.value === 'auth') {\n navigate('chat')\n }\n })\n\n // Sync props → store reactively\n watchEffect(() => {\n store.baseUrl = props.baseUrl\n store.basePath = props.basePath\n store.name = props.name\n store.agentType = props.agentType\n store.userId = props.userId\n store.agentInterface = props.agentInterface\n store.agentModel = props.agentModel\n store.agentTools = props.agentTools\n store.headers = props.headers\n store.revisedAnswers = props.revisedAnswers\n store.model = props.model\n store.actionsInput = props.actions\n store.agentFileUpload = props.agentFileUpload\n store.fallbackModels = props.fallbackModels\n store.gatewayOptions = props.gatewayOptions\n store.externalContext = props.externalContext\n store.systemTheme = props.systemTheme\n store.hideWelcomeMessage = props.modifier === 'fullscreen'\n })\n\n // Sync chatId v-model ↔ store (bidirectional)\n // Parent → store: if the parent provides an external chatId, push it into\n // the store so the Chat instance picks it up.\n watch(\n () => chatId.value,\n (id) => {\n if (id && id !== store.localChatId) {\n store.localChatId = id\n }\n },\n { immediate: true },\n )\n // Store → parent: keep the v-model in sync whenever the store changes\n // the chatId internally (e.g. startNewChat).\n watch(localChatId, (id) => {\n chatId.value = id\n })\n\n // Emit structured errors from chat to the parent component\n watch(chatError, (error) => {\n if (!error) {\n return\n }\n try {\n const parsedError = JSON.parse(error.message) as {\n type: string\n title: string\n status: number\n }\n emit('error', parsedError)\n } catch {\n // ignore non-JSON errors\n }\n })\n\n // ui\n const isFullscreen = computed(() => props.modifier === 'fullscreen')\n const isReasoningAgent = computed(() => props.agentType === 'reasoning')\n const useColorsForAgentHeader = computed(\n () => props.agentInterface?.useColorsForAgentHeader,\n )\n const themeClass = computed(() => [\n 'theme',\n `theme--${isDark.value ? 'dark' : 'light'}`,\n ])\n const mainColor = computed(() => props.agentInterface?.mainColor)\n const contrastColor = computed(() =>\n resolveContrastColor(props.agentInterface?.textColor, mainColor.value),\n )\n const footerMessage = useLocalizedString(\n () => props.agentInterface?.footerMessage,\n )\n const title = computed(() => {\n if (activeView.value === 'conversations') {\n return $t('label.conversations')\n }\n if (activeView.value === 'profile') {\n return $t('label.profile')\n }\n if (activeView.value === 'auth') {\n return $t('label.authentication')\n }\n if (activeView.value === 'files') {\n return $t('userFiles.chatFilesTitle')\n }\n return props.name\n })\n\n // state\n const isReady = computed(() => chat.value.status === 'ready')\n const activeMessage = computed(() => {\n return messages.value?.[messages.value.length - 1]\n })\n\n // expose\n defineExpose({\n startNewChat,\n regenerate,\n stopGeneration,\n sendMessage,\n })\n\n // load chat, conversations and feedbacks on agent or chat change\n watch(\n [storeAgentId, localChatId],\n async ([newAgentId, newChatId]) => {\n await store.initialize(newAgentId, newChatId)\n },\n {\n immediate: true,\n },\n )\n // load conversations when navigating to conversations view\n watch(activeView, (newView) => {\n if (newView === 'conversations') {\n store.loadConversations(storeAgentId.value)\n } else if (newView === 'files') {\n store.loadChatFiles()\n }\n })\n // emit message updates for up-to-date context in parent components\n watch(\n activeMessage,\n (message) => {\n if (message) {\n emit('message-update', message)\n }\n },\n {\n deep: true,\n immediate: true,\n },\n )\n</script>\n\n<template>\n <div\n class=\"pk-chatbot\"\n :class=\"[themeClass, { 'pk-chatbot--fullscreen': isFullscreen }]\">\n <!-- #region title -->\n <div\n v-if=\"!isFullscreen\"\n class=\"pk-chatbot__header\"\n :style=\"\n useColorsForAgentHeader\n ? {\n backgroundColor: mainColor,\n }\n : undefined\n \">\n <div class=\"pk-chatbot__header-start\">\n <template v-if=\"activeView !== 'chat'\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:arrow-left-s-line',\n title: $t('action.goBack'),\n }\"\n @click=\"navigate('chat')\" />\n <VvIcon\n :name=\"\n activeView === 'conversations'\n ? 'ri:history-line'\n : activeView === 'files'\n ? 'ri:attachment-2'\n : 'ri:user-line'\n \"\n class=\"pk-chatbot__nav-icon\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \" />\n </template>\n <PkAvatar\n v-else\n modifiers=\"surface ring shrink-0\"\n class=\"pk-chatbot__avatar\"\n :name\n :img-src=\"agentInterface?.logo\" />\n <strong\n class=\"pk-chatbot__title\"\n :title=\"title\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \">\n {{ title }}\n </strong>\n </div>\n <VvButtonGroup class=\"pk-chatbot__header-end\" modifiers=\"compact\">\n <slot\n name=\"header-actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\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 v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:more-2-fill',\n disabled: !isReady,\n title: $t('action.moreActions'),\n }\" />\n <template #items>\n <VvDropdownAction @click=\"startNewChat()\">\n <VvIcon name=\"ri:edit-box-line\" />\n {{ $t('action.startNewChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"!isReasoningAgent\"\n @click=\"navigate('conversations')\">\n <VvIcon name=\"ri:history-line\" />\n {{ $t('action.viewRecentChats') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"\n (canManageProfile || userFilesEnabled) &&\n localChatId\n \"\n @click=\"navigate('files')\">\n <VvIcon name=\"ri:attachment-2\" />\n {{ $t('userFiles.chatFilesTitle') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"canManageProfile\"\n @click=\"navigate('profile')\">\n <VvIcon name=\"ri:user-line\" />\n {{ $t('label.profile') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-else-if=\"canSignIn\"\n @click=\"navigate('auth')\">\n <VvIcon name=\"ri:account-circle-line\" />\n {{ $t('action.signIn') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n <slot\n name=\"actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n </VvButtonGroup>\n </div>\n <!-- #endregion -->\n\n <div v-if=\"isReasoningAgent\">\n <div class=\"pk-chatbot__warning-body\">\n <VvAlert modifiers=\"callout\">\n <div class=\"pk-chatbot__warning-header\">\n <VvIcon\n name=\"ri:alert-line\"\n class=\"pk-chatbot__warning-icon\" />\n <strong class=\"pk-chatbot__warning-title\">\n {{ $t('message.warning') }}\n </strong>\n </div>\n <p class=\"pk-chatbot__warning-text\">\n {{ $t('message.reasoningAgent') }}\n </p>\n </VvAlert>\n </div>\n </div>\n\n <!-- #region views -->\n <PkChatbotViewChat\n v-if=\"activeView === 'chat'\"\n :agent-id\n :modifier\n :user-name\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\">\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewChat>\n <PkChatbotViewConversations\n v-else-if=\"activeView === 'conversations'\"\n :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewConversations>\n <PkChatbotViewFiles v-else-if=\"activeView === 'files'\" :agent-id />\n <div\n v-else-if=\"activeView === 'auth'\"\n class=\"pk-chatbot__view-auth flex-1 min-h-0 overflow-y-auto\">\n <!-- Sign-in form provided by the host widget (owns the auth client) -->\n <slot name=\"view-auth\" />\n </div>\n <PkChatbotViewProfile v-else :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewProfile>\n <!-- #endregion -->\n\n <!-- #region footer -->\n <div v-if=\"footerMessage\" class=\"pk-chatbot__footer\">\n <PkStreamingMarkdown :markdown=\"footerMessage\" class=\"wysiwyg\" />\n </div>\n <!-- #endregion -->\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot {\n display: flex;\n flex-direction: column;\n position: relative;\n height: 100%;\n background-color: var(--color-surface);\n\n &__header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-14);\n background-color: var(--color-surface-1);\n padding: var(--spacing-14);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__header-start {\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n min-width: 0;\n }\n\n &__nav-icon {\n font-size: var(--text-18);\n }\n\n &__avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n }\n\n &__title {\n font-size: var(--text-16);\n font-weight: var(--font-semibold);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__header-end {\n flex-shrink: 0;\n }\n\n &__warning-body {\n padding: var(--spacing-md);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__warning-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n margin-bottom: var(--spacing-4);\n }\n\n &__warning-icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n\n &__warning-title {\n font-weight: 600;\n font-size: var(--text-14);\n }\n\n &__warning-text {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__footer {\n padding: var(--spacing-md);\n border-top: 1px solid var(--color-surface-4);\n text-align: center;\n font-size: var(--text-12);\n color: var(--color-word-2);\n }\n\n // Modifier: fullscreen — layout full-page (Gemini-style): nessun\n // header interno, sfondo trasparente, input pill centrato.\n // Il chrome (sidebar, navigazione) è gestito dalla pagina ospite.\n &--fullscreen {\n background-color: transparent;\n\n .pk-chatbot__footer {\n border-top: none;\n padding: 0 var(--spacing-md) var(--spacing-sm) var(--spacing-md);\n }\n\n // Reading column aligned with the input (Gemini-style). The\n // constraint lives on the inner wrapper so the scroll container\n // spans the page and the scrollbar sits at its edge.\n .pk-chatbot-messages {\n // Symmetric gutters: a visible scrollbar would otherwise\n // shrink the content box on one side only, shifting the\n // column off the input axis\n scrollbar-gutter: stable both-edges;\n // Dissolve the content at both edges (works on any page\n // background, dots included — a surface-colored fade band\n // would cut through the pattern instead)\n mask-image: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--spacing-40),\n #000 calc(100% - var(--spacing-40)),\n transparent 100%\n );\n // Resting clearance so the first/last lines sit inside the\n // fully opaque zone\n padding-block: var(--spacing-48);\n }\n\n .pk-chatbot-messages__wrapper {\n width: 100%;\n max-width: 768px;\n margin-inline: auto;\n }\n\n .pk-chatbot-file-preview {\n background-color: var(--color-surface);\n }\n\n .pk-chatbot-input__notice {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-3);\n border-top: 1px solid var(--color-surface-2);\n padding: var(--spacing-10) var(--spacing-16);\n\n &::after {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-2);\n bottom: calc(-1 * var(--spacing-20));\n height: var(--spacing-20);\n left: -1px;\n right: -1px;\n }\n }\n\n .pk-chatbot-input {\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n\n &__form {\n border-radius: var(--rounded-xxxl); // pill shape\n padding: var(--spacing-12) var(--spacing-16);\n background-color: var(--color-surface-1);\n border-color: var(--color-surface-2);\n box-shadow: none;\n transition:\n border-color var(--duration-200) var(--ease-in-out),\n box-shadow var(--duration-200) var(--ease-in-out);\n\n &:has(:focus-within) {\n border-color: var(--color-surface-4);\n box-shadow: var(--shadow-sm);\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n }\n\n &__textarea {\n background-color: transparent;\n }\n\n // Touch target ≥44px: estende l'hit area dei pulsanti\n // (icona 20px + padding 4px ≈ 28px) senza ingrandire l'icona\n &__button {\n position: relative;\n\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-8));\n }\n }\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, inject, toRef, watch, watchEffect, useSlots } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type {\n AgentInterface,\n AgentModel,\n AgentFileUpload,\n AgentGatewayOptions,\n ChatMessageActions,\n RevisedAnswer,\n UIChatMessage,\n AgentTools,\n } from 'models'\n import {\n useChatbotStore,\n useLocalizedString,\n provideShortUrlResolver,\n } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotViewChat from './PkChatbotViewChat.vue'\n import PkChatbotViewConversations from './PkChatbotViewConversations.vue'\n import PkChatbotViewFiles from './PkChatbotViewFiles.vue'\n import PkChatbotViewProfile from './PkChatbotViewProfile.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n import { resolveContrastColor } from './utils'\n\n // Captured in script setup to avoid TS7022 circular inference in template\n const slots = useSlots()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = defineProps<{\n baseUrl: string\n basePath?: string\n agentId: string\n agentType?: 'reasoning' | 'chat'\n userId?: string\n name?: string\n model?: string\n agentModel?: AgentModel\n agentTools?: AgentTools\n agentInterface?: AgentInterface\n actions?: ChatMessageActions[]\n revisedAnswers?: RevisedAnswer[]\n headers?: Record<string, string>\n systemTheme?: 'light' | 'dark'\n agentFileUpload?: AgentFileUpload\n fallbackModels?: string[]\n gatewayOptions?: Partial<AgentGatewayOptions>\n externalContext?: Record<string, unknown>\n /** Layout modifier: `widget` (default) keeps the contained layout with header, `fullscreen` removes header/background for full-page usage (the page provides the chrome) */\n modifier?: 'widget' | 'fullscreen'\n /** Display name of the current user, used for the `{name}` placeholder in the fullscreen greeting */\n userName?: string\n /** Show the \"Files in this chat\" menu for hosts that don't provide the\n * profile context but whose user is authenticated (e.g. the backoffice\n * playground). The widget host relies on `canManageProfile` instead. */\n userFilesEnabled?: boolean\n }>()\n const emit = defineEmits<{\n 'message-update': [message: UIChatMessage]\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n error: [error: { type: string; title: string; status: number }]\n }>()\n\n const chatId = defineModel<string | undefined>('chatId', {\n type: String,\n default: undefined,\n })\n\n const store = useChatbotStore(props.agentId)\n\n // Resolve backend short-urls (`/api/r/<hash>`) back to their real target\n // for display in tool components. Batched + cached across the chat tree.\n provideShortUrlResolver((hashes) =>\n store.apiClient.resolveShortUrls(hashes),\n )\n\n const { messages, chat, localChatId, activeView, chatError, isDark } =\n storeToRefs(store)\n\n // Ref to store.agentId for watchers (avoids duplicate-key with agentId prop)\n const storeAgentId = toRef(store, 'agentId')\n\n const { startNewChat, stopGeneration, regenerate, navigate, sendMessage } =\n store\n\n // Optional profile-management context provided by the host widget.\n // Present only for authenticated, non-anonymous sessions.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canManageProfile = computed(\n () => profile?.canManageProfile.value ?? false,\n )\n // Anonymous users get a \"Sign in\" entry instead of the profile one,\n // provided the host filled the `view-auth` slot and the agent has at\n // least one auth method enabled.\n const canSignIn = computed(\n () =>\n !canManageProfile.value &&\n (profile?.canSignIn?.value ?? false) &&\n slots['view-auth'] !== undefined,\n )\n\n // Once the sign-in completes (the session becomes non-anonymous) leave\n // the auth view and return to the conversation.\n watch(canManageProfile, (canManage) => {\n if (canManage && activeView.value === 'auth') {\n navigate('chat')\n }\n })\n\n // Sync props → store reactively\n watchEffect(() => {\n store.baseUrl = props.baseUrl\n store.basePath = props.basePath\n store.name = props.name\n store.agentType = props.agentType\n store.userId = props.userId\n store.agentInterface = props.agentInterface\n store.agentModel = props.agentModel\n store.agentTools = props.agentTools\n store.headers = props.headers\n store.revisedAnswers = props.revisedAnswers\n store.model = props.model\n store.actionsInput = props.actions\n store.agentFileUpload = props.agentFileUpload\n store.fallbackModels = props.fallbackModels\n store.gatewayOptions = props.gatewayOptions\n store.externalContext = props.externalContext\n store.systemTheme = props.systemTheme\n store.hideWelcomeMessage = props.modifier === 'fullscreen'\n })\n\n // Sync chatId v-model ↔ store (bidirectional)\n // Parent → store: if the parent provides an external chatId, push it into\n // the store so the Chat instance picks it up.\n watch(\n () => chatId.value,\n (id) => {\n if (id && id !== store.localChatId) {\n store.localChatId = id\n }\n },\n { immediate: true },\n )\n // Store → parent: keep the v-model in sync whenever the store changes\n // the chatId internally (e.g. startNewChat).\n watch(localChatId, (id) => {\n chatId.value = id\n })\n\n // Emit structured errors from chat to the parent component\n watch(chatError, (error) => {\n if (!error) {\n return\n }\n try {\n const parsedError = JSON.parse(error.message) as {\n type: string\n title: string\n status: number\n }\n emit('error', parsedError)\n } catch {\n // ignore non-JSON errors\n }\n })\n\n // ui\n const isFullscreen = computed(() => props.modifier === 'fullscreen')\n const isReasoningAgent = computed(() => props.agentType === 'reasoning')\n const useColorsForAgentHeader = computed(\n () => props.agentInterface?.useColorsForAgentHeader,\n )\n const themeClass = computed(() => [\n 'theme',\n `theme--${isDark.value ? 'dark' : 'light'}`,\n ])\n const mainColor = computed(() => props.agentInterface?.mainColor)\n const contrastColor = computed(() =>\n resolveContrastColor(props.agentInterface?.textColor, mainColor.value),\n )\n const footerMessage = useLocalizedString(\n () => props.agentInterface?.footerMessage,\n )\n const title = computed(() => {\n if (activeView.value === 'conversations') {\n return $t('label.conversations')\n }\n if (activeView.value === 'profile') {\n return $t('label.profile')\n }\n if (activeView.value === 'auth') {\n return $t('label.authentication')\n }\n if (activeView.value === 'files') {\n return $t('userFiles.chatFilesTitle')\n }\n return props.name\n })\n\n // state\n const isReady = computed(() => chat.value.status === 'ready')\n const activeMessage = computed(() => {\n return messages.value?.[messages.value.length - 1]\n })\n\n // expose\n defineExpose({\n startNewChat,\n regenerate,\n stopGeneration,\n sendMessage,\n })\n\n // load chat, conversations and feedbacks on agent or chat change\n watch(\n [storeAgentId, localChatId],\n async ([newAgentId, newChatId]) => {\n await store.initialize(newAgentId, newChatId)\n },\n {\n immediate: true,\n },\n )\n // load conversations when navigating to conversations view\n watch(activeView, (newView) => {\n if (newView === 'conversations') {\n store.loadConversations(storeAgentId.value)\n } else if (newView === 'files') {\n store.loadChatFiles()\n }\n })\n // emit message updates for up-to-date context in parent components\n watch(\n activeMessage,\n (message) => {\n if (message) {\n emit('message-update', message)\n }\n },\n {\n deep: true,\n immediate: true,\n },\n )\n</script>\n\n<template>\n <div\n class=\"pk-chatbot\"\n :class=\"[themeClass, { 'pk-chatbot--fullscreen': isFullscreen }]\">\n <!-- #region title -->\n <div\n v-if=\"!isFullscreen\"\n class=\"pk-chatbot__header\"\n :style=\"\n useColorsForAgentHeader\n ? {\n backgroundColor: mainColor,\n }\n : undefined\n \">\n <div class=\"pk-chatbot__header-start\">\n <template v-if=\"activeView !== 'chat'\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:arrow-left-s-line',\n title: $t('action.goBack'),\n }\"\n @click=\"navigate('chat')\" />\n <VvIcon\n :name=\"\n activeView === 'conversations'\n ? 'ri:history-line'\n : activeView === 'files'\n ? 'ri:attachment-2'\n : 'ri:user-line'\n \"\n class=\"pk-chatbot__nav-icon\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \" />\n </template>\n <PkAvatar\n v-else\n modifiers=\"surface ring shrink-0\"\n class=\"pk-chatbot__avatar\"\n :name\n :img-src=\"agentInterface?.logo\" />\n <strong\n class=\"pk-chatbot__title\"\n :title=\"title\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \">\n {{ title }}\n </strong>\n </div>\n <VvButtonGroup class=\"pk-chatbot__header-end\" modifiers=\"compact\">\n <slot\n name=\"header-actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\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 v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:more-2-fill',\n disabled: !isReady,\n title: $t('action.moreActions'),\n }\" />\n <template #items>\n <VvDropdownAction @click=\"startNewChat()\">\n <VvIcon name=\"ri:edit-box-line\" />\n {{ $t('action.startNewChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"!isReasoningAgent\"\n @click=\"navigate('conversations')\">\n <VvIcon name=\"ri:history-line\" />\n {{ $t('action.viewRecentChats') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"\n (canManageProfile || userFilesEnabled) &&\n localChatId\n \"\n @click=\"navigate('files')\">\n <VvIcon name=\"ri:attachment-2\" />\n {{ $t('userFiles.chatFilesTitle') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"canManageProfile\"\n @click=\"navigate('profile')\">\n <VvIcon name=\"ri:user-line\" />\n {{ $t('label.profile') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-else-if=\"canSignIn\"\n @click=\"navigate('auth')\">\n <VvIcon name=\"ri:account-circle-line\" />\n {{ $t('action.signIn') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n <slot\n name=\"actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n </VvButtonGroup>\n </div>\n <!-- #endregion -->\n\n <div v-if=\"isReasoningAgent\">\n <div class=\"pk-chatbot__warning-body\">\n <VvAlert modifiers=\"callout\">\n <div class=\"pk-chatbot__warning-header\">\n <VvIcon\n name=\"ri:alert-line\"\n class=\"pk-chatbot__warning-icon\" />\n <strong class=\"pk-chatbot__warning-title\">\n {{ $t('message.warning') }}\n </strong>\n </div>\n <p class=\"pk-chatbot__warning-text\">\n {{ $t('message.reasoningAgent') }}\n </p>\n </VvAlert>\n </div>\n </div>\n\n <!-- #region views -->\n <PkChatbotViewChat\n v-if=\"activeView === 'chat'\"\n :agent-id\n :modifier\n :user-name\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\">\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewChat>\n <PkChatbotViewConversations\n v-else-if=\"activeView === 'conversations'\"\n :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewConversations>\n <PkChatbotViewFiles v-else-if=\"activeView === 'files'\" :agent-id />\n <div\n v-else-if=\"activeView === 'auth'\"\n class=\"pk-chatbot__view-auth flex-1 min-h-0 overflow-y-auto\">\n <!-- Sign-in form provided by the host widget (owns the auth client) -->\n <slot name=\"view-auth\" />\n </div>\n <PkChatbotViewProfile v-else :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewProfile>\n <!-- #endregion -->\n\n <!-- #region footer -->\n <div v-if=\"footerMessage\" class=\"pk-chatbot__footer\">\n <PkStreamingMarkdown :markdown=\"footerMessage\" class=\"wysiwyg\" />\n </div>\n <!-- #endregion -->\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot {\n display: flex;\n flex-direction: column;\n position: relative;\n height: 100%;\n background-color: var(--color-surface);\n\n &__header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-14);\n background-color: var(--color-surface-1);\n padding: var(--spacing-14);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__header-start {\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n min-width: 0;\n }\n\n &__nav-icon {\n font-size: var(--text-18);\n }\n\n &__avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n }\n\n &__title {\n font-size: var(--text-16);\n font-weight: var(--font-semibold);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__header-end {\n flex-shrink: 0;\n }\n\n &__warning-body {\n padding: var(--spacing-md);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__warning-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n margin-bottom: var(--spacing-4);\n }\n\n &__warning-icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n\n &__warning-title {\n font-weight: 600;\n font-size: var(--text-14);\n }\n\n &__warning-text {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__footer {\n padding: var(--spacing-md);\n border-top: 1px solid var(--color-surface-4);\n text-align: center;\n font-size: var(--text-12);\n color: var(--color-word-2);\n }\n\n // Modifier: fullscreen — layout full-page (Gemini-style): nessun\n // header interno, sfondo trasparente, input pill centrato.\n // Il chrome (sidebar, navigazione) è gestito dalla pagina ospite.\n &--fullscreen {\n background-color: transparent;\n\n .pk-chatbot__footer {\n border-top: none;\n padding: 0 var(--spacing-md) var(--spacing-sm) var(--spacing-md);\n }\n\n // Reading column aligned with the input (Gemini-style). The\n // constraint lives on the inner wrapper so the scroll container\n // spans the page and the scrollbar sits at its edge.\n .pk-chatbot-messages {\n // Symmetric gutters: a visible scrollbar would otherwise\n // shrink the content box on one side only, shifting the\n // column off the input axis\n scrollbar-gutter: stable both-edges;\n // Dissolve the content at both edges (works on any page\n // background, dots included — a surface-colored fade band\n // would cut through the pattern instead)\n mask-image: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--spacing-40),\n #000 calc(100% - var(--spacing-40)),\n transparent 100%\n );\n // Resting clearance so the first/last lines sit inside the\n // fully opaque zone\n padding-block: var(--spacing-48);\n }\n\n .pk-chatbot-messages__wrapper {\n width: 100%;\n max-width: 768px;\n margin-inline: auto;\n }\n\n .pk-chatbot-file-preview {\n background-color: var(--color-surface);\n }\n\n .pk-chatbot-input__notice {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-3);\n border-top: 1px solid var(--color-surface-2);\n padding: var(--spacing-10) var(--spacing-16);\n\n &::after {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-2);\n bottom: calc(-1 * var(--spacing-20));\n height: var(--spacing-20);\n left: -1px;\n right: -1px;\n }\n }\n\n .pk-chatbot-input {\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n\n &__form {\n border-radius: var(--rounded-xxxl); // pill shape\n padding: var(--spacing-12) var(--spacing-16);\n background-color: var(--color-surface-1);\n border-color: var(--color-surface-2);\n box-shadow: none;\n transition:\n border-color var(--duration-200) var(--ease-in-out),\n box-shadow var(--duration-200) var(--ease-in-out);\n\n &:has(:focus-within) {\n border-color: var(--color-surface-4);\n box-shadow: var(--shadow-sm);\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n }\n\n &__textarea {\n background-color: transparent;\n }\n\n // Touch target ≥44px: estende l'hit area dei pulsanti\n // (icona 20px + padding 4px ≈ 28px) senza ingrandire l'icona\n &__button {\n position: relative;\n\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-8));\n }\n }\n }\n }\n }\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PkChatbot-CneS8fVC.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbot.vue","../../../../packages/components/src/chat/PkChatbot.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, inject, toRef, watch, watchEffect, useSlots } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type {\n AgentInterface,\n AgentModel,\n AgentFileUpload,\n AgentGatewayOptions,\n ChatMessageActions,\n RevisedAnswer,\n UIChatMessage,\n AgentTools,\n } from 'models'\n import {\n useChatbotStore,\n useLocalizedString,\n provideShortUrlResolver,\n } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotViewChat from './PkChatbotViewChat.vue'\n import PkChatbotViewConversations from './PkChatbotViewConversations.vue'\n import PkChatbotViewFiles from './PkChatbotViewFiles.vue'\n import PkChatbotViewProfile from './PkChatbotViewProfile.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n import { resolveContrastColor } from './utils'\n\n // Captured in script setup to avoid TS7022 circular inference in template\n const slots = useSlots()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = defineProps<{\n baseUrl: string\n basePath?: string\n agentId: string\n agentType?: 'reasoning' | 'chat'\n userId?: string\n name?: string\n model?: string\n agentModel?: AgentModel\n agentTools?: AgentTools\n agentInterface?: AgentInterface\n actions?: ChatMessageActions[]\n revisedAnswers?: RevisedAnswer[]\n headers?: Record<string, string>\n systemTheme?: 'light' | 'dark'\n agentFileUpload?: AgentFileUpload\n fallbackModels?: string[]\n gatewayOptions?: Partial<AgentGatewayOptions>\n externalContext?: Record<string, unknown>\n /** Layout modifier: `widget` (default) keeps the contained layout with header, `fullscreen` removes header/background for full-page usage (the page provides the chrome) */\n modifier?: 'widget' | 'fullscreen'\n /** Display name of the current user, used for the `{name}` placeholder in the fullscreen greeting */\n userName?: string\n /** Show the \"Files in this chat\" menu for hosts that don't provide the\n * profile context but whose user is authenticated (e.g. the backoffice\n * playground). The widget host relies on `canManageProfile` instead. */\n userFilesEnabled?: boolean\n }>()\n const emit = defineEmits<{\n 'message-update': [message: UIChatMessage]\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n error: [error: { type: string; title: string; status: number }]\n }>()\n\n const chatId = defineModel<string | undefined>('chatId', {\n type: String,\n default: undefined,\n })\n\n const store = useChatbotStore(props.agentId)\n\n // Resolve backend short-urls (`/api/r/<hash>`) back to their real target\n // for display in tool components. Batched + cached across the chat tree.\n provideShortUrlResolver((hashes) =>\n store.apiClient.resolveShortUrls(hashes),\n )\n\n const { messages, chat, localChatId, activeView, chatError, isDark } =\n storeToRefs(store)\n\n // Ref to store.agentId for watchers (avoids duplicate-key with agentId prop)\n const storeAgentId = toRef(store, 'agentId')\n\n const { startNewChat, stopGeneration, regenerate, navigate, sendMessage } =\n store\n\n // Optional profile-management context provided by the host widget.\n // Present only for authenticated, non-anonymous sessions.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canManageProfile = computed(\n () => profile?.canManageProfile.value ?? false,\n )\n // Anonymous users get a \"Sign in\" entry instead of the profile one,\n // provided the host filled the `view-auth` slot and the agent has at\n // least one auth method enabled.\n const canSignIn = computed(\n () =>\n !canManageProfile.value &&\n (profile?.canSignIn?.value ?? false) &&\n slots['view-auth'] !== undefined,\n )\n\n // Once the sign-in completes (the session becomes non-anonymous) leave\n // the auth view and return to the conversation.\n watch(canManageProfile, (canManage) => {\n if (canManage && activeView.value === 'auth') {\n navigate('chat')\n }\n })\n\n // Sync props → store reactively\n watchEffect(() => {\n store.baseUrl = props.baseUrl\n store.basePath = props.basePath\n store.name = props.name\n store.agentType = props.agentType\n store.userId = props.userId\n store.agentInterface = props.agentInterface\n store.agentModel = props.agentModel\n store.agentTools = props.agentTools\n store.headers = props.headers\n store.revisedAnswers = props.revisedAnswers\n store.model = props.model\n store.actionsInput = props.actions\n store.agentFileUpload = props.agentFileUpload\n store.fallbackModels = props.fallbackModels\n store.gatewayOptions = props.gatewayOptions\n store.externalContext = props.externalContext\n store.systemTheme = props.systemTheme\n store.hideWelcomeMessage = props.modifier === 'fullscreen'\n })\n\n // Sync chatId v-model ↔ store (bidirectional)\n // Parent → store: if the parent provides an external chatId, push it into\n // the store so the Chat instance picks it up.\n watch(\n () => chatId.value,\n (id) => {\n if (id && id !== store.localChatId) {\n store.localChatId = id\n }\n },\n { immediate: true },\n )\n // Store → parent: keep the v-model in sync whenever the store changes\n // the chatId internally (e.g. startNewChat).\n watch(localChatId, (id) => {\n chatId.value = id\n })\n\n // Emit structured errors from chat to the parent component\n watch(chatError, (error) => {\n if (!error) {\n return\n }\n try {\n const parsedError = JSON.parse(error.message) as {\n type: string\n title: string\n status: number\n }\n emit('error', parsedError)\n } catch {\n // ignore non-JSON errors\n }\n })\n\n // ui\n const isFullscreen = computed(() => props.modifier === 'fullscreen')\n const isReasoningAgent = computed(() => props.agentType === 'reasoning')\n const useColorsForAgentHeader = computed(\n () => props.agentInterface?.useColorsForAgentHeader,\n )\n const themeClass = computed(() => [\n 'theme',\n `theme--${isDark.value ? 'dark' : 'light'}`,\n ])\n const mainColor = computed(() => props.agentInterface?.mainColor)\n const contrastColor = computed(() =>\n resolveContrastColor(props.agentInterface?.textColor, mainColor.value),\n )\n const footerMessage = useLocalizedString(\n () => props.agentInterface?.footerMessage,\n )\n const title = computed(() => {\n if (activeView.value === 'conversations') {\n return $t('label.conversations')\n }\n if (activeView.value === 'profile') {\n return $t('label.profile')\n }\n if (activeView.value === 'auth') {\n return $t('label.authentication')\n }\n if (activeView.value === 'files') {\n return $t('userFiles.chatFilesTitle')\n }\n return props.name\n })\n\n // state\n const isReady = computed(() => chat.value.status === 'ready')\n const activeMessage = computed(() => {\n return messages.value?.[messages.value.length - 1]\n })\n\n // expose\n defineExpose({\n startNewChat,\n regenerate,\n stopGeneration,\n sendMessage,\n })\n\n // load chat, conversations and feedbacks on agent or chat change\n watch(\n [storeAgentId, localChatId],\n async ([newAgentId, newChatId]) => {\n await store.initialize(newAgentId, newChatId)\n },\n {\n immediate: true,\n },\n )\n // load conversations when navigating to conversations view\n watch(activeView, (newView) => {\n if (newView === 'conversations') {\n store.loadConversations(storeAgentId.value)\n } else if (newView === 'files') {\n store.loadChatFiles()\n }\n })\n // emit message updates for up-to-date context in parent components\n watch(\n activeMessage,\n (message) => {\n if (message) {\n emit('message-update', message)\n }\n },\n {\n deep: true,\n immediate: true,\n },\n )\n</script>\n\n<template>\n <div\n class=\"pk-chatbot\"\n :class=\"[themeClass, { 'pk-chatbot--fullscreen': isFullscreen }]\">\n <!-- #region title -->\n <div\n v-if=\"!isFullscreen\"\n class=\"pk-chatbot__header\"\n :style=\"\n useColorsForAgentHeader\n ? {\n backgroundColor: mainColor,\n }\n : undefined\n \">\n <div class=\"pk-chatbot__header-start\">\n <template v-if=\"activeView !== 'chat'\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:arrow-left-s-line',\n title: $t('action.goBack'),\n }\"\n @click=\"navigate('chat')\" />\n <VvIcon\n :name=\"\n activeView === 'conversations'\n ? 'ri:history-line'\n : activeView === 'files'\n ? 'ri:attachment-2'\n : 'ri:user-line'\n \"\n class=\"pk-chatbot__nav-icon\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \" />\n </template>\n <PkAvatar\n v-else\n modifiers=\"surface ring shrink-0\"\n class=\"pk-chatbot__avatar\"\n :name\n :img-src=\"agentInterface?.logo\" />\n <strong\n class=\"pk-chatbot__title\"\n :title=\"title\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \">\n {{ title }}\n </strong>\n </div>\n <VvButtonGroup class=\"pk-chatbot__header-end\" modifiers=\"compact\">\n <slot\n name=\"header-actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\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 v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:more-2-fill',\n disabled: !isReady,\n title: $t('action.moreActions'),\n }\" />\n <template #items>\n <VvDropdownAction @click=\"startNewChat()\">\n <VvIcon name=\"ri:edit-box-line\" />\n {{ $t('action.startNewChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"!isReasoningAgent\"\n @click=\"navigate('conversations')\">\n <VvIcon name=\"ri:history-line\" />\n {{ $t('action.viewRecentChats') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"\n (canManageProfile || userFilesEnabled) &&\n localChatId\n \"\n @click=\"navigate('files')\">\n <VvIcon name=\"ri:attachment-2\" />\n {{ $t('userFiles.chatFilesTitle') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"canManageProfile\"\n @click=\"navigate('profile')\">\n <VvIcon name=\"ri:user-line\" />\n {{ $t('label.profile') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-else-if=\"canSignIn\"\n @click=\"navigate('auth')\">\n <VvIcon name=\"ri:account-circle-line\" />\n {{ $t('action.signIn') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n <slot\n name=\"actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n </VvButtonGroup>\n </div>\n <!-- #endregion -->\n\n <div v-if=\"isReasoningAgent\">\n <div class=\"pk-chatbot__warning-body\">\n <VvAlert modifiers=\"callout\">\n <div class=\"pk-chatbot__warning-header\">\n <VvIcon\n name=\"ri:alert-line\"\n class=\"pk-chatbot__warning-icon\" />\n <strong class=\"pk-chatbot__warning-title\">\n {{ $t('message.warning') }}\n </strong>\n </div>\n <p class=\"pk-chatbot__warning-text\">\n {{ $t('message.reasoningAgent') }}\n </p>\n </VvAlert>\n </div>\n </div>\n\n <!-- #region views -->\n <PkChatbotViewChat\n v-if=\"activeView === 'chat'\"\n :agent-id\n :modifier\n :user-name\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\">\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewChat>\n <PkChatbotViewConversations\n v-else-if=\"activeView === 'conversations'\"\n :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewConversations>\n <PkChatbotViewFiles v-else-if=\"activeView === 'files'\" :agent-id />\n <div\n v-else-if=\"activeView === 'auth'\"\n class=\"pk-chatbot__view-auth flex-1 min-h-0 overflow-y-auto\">\n <!-- Sign-in form provided by the host widget (owns the auth client) -->\n <slot name=\"view-auth\" />\n </div>\n <PkChatbotViewProfile v-else :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewProfile>\n <!-- #endregion -->\n\n <!-- #region footer -->\n <div v-if=\"footerMessage\" class=\"pk-chatbot__footer\">\n <PkStreamingMarkdown :markdown=\"footerMessage\" class=\"wysiwyg\" />\n </div>\n <!-- #endregion -->\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot {\n display: flex;\n flex-direction: column;\n position: relative;\n height: 100%;\n background-color: var(--color-surface);\n\n &__header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-14);\n background-color: var(--color-surface-1);\n padding: var(--spacing-14);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__header-start {\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n min-width: 0;\n }\n\n &__nav-icon {\n font-size: var(--text-18);\n }\n\n &__avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n }\n\n &__title {\n font-size: var(--text-16);\n font-weight: var(--font-semibold);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__header-end {\n flex-shrink: 0;\n }\n\n &__warning-body {\n padding: var(--spacing-md);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__warning-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n margin-bottom: var(--spacing-4);\n }\n\n &__warning-icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n\n &__warning-title {\n font-weight: 600;\n font-size: var(--text-14);\n }\n\n &__warning-text {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__footer {\n padding: var(--spacing-md);\n border-top: 1px solid var(--color-surface-4);\n text-align: center;\n font-size: var(--text-12);\n color: var(--color-word-2);\n }\n\n // Modifier: fullscreen — layout full-page (Gemini-style): nessun\n // header interno, sfondo trasparente, input pill centrato.\n // Il chrome (sidebar, navigazione) è gestito dalla pagina ospite.\n &--fullscreen {\n background-color: transparent;\n\n .pk-chatbot__footer {\n border-top: none;\n padding: 0 var(--spacing-md) var(--spacing-sm) var(--spacing-md);\n }\n\n // Reading column aligned with the input (Gemini-style). The\n // constraint lives on the inner wrapper so the scroll container\n // spans the page and the scrollbar sits at its edge.\n .pk-chatbot-messages {\n // Symmetric gutters: a visible scrollbar would otherwise\n // shrink the content box on one side only, shifting the\n // column off the input axis\n scrollbar-gutter: stable both-edges;\n // Dissolve the content at both edges (works on any page\n // background, dots included — a surface-colored fade band\n // would cut through the pattern instead)\n mask-image: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--spacing-40),\n #000 calc(100% - var(--spacing-40)),\n transparent 100%\n );\n // Resting clearance so the first/last lines sit inside the\n // fully opaque zone\n padding-block: var(--spacing-48);\n }\n\n .pk-chatbot-messages__wrapper {\n width: 100%;\n max-width: 768px;\n margin-inline: auto;\n }\n\n .pk-chatbot-file-preview {\n background-color: var(--color-surface);\n }\n\n .pk-chatbot-input__notice {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-3);\n border-top: 1px solid var(--color-surface-2);\n padding: var(--spacing-10) var(--spacing-16);\n\n &::after {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-2);\n bottom: calc(-1 * var(--spacing-20));\n height: var(--spacing-20);\n left: -1px;\n right: -1px;\n }\n }\n\n .pk-chatbot-input {\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n\n &__form {\n border-radius: var(--rounded-xxxl); // pill shape\n padding: var(--spacing-12) var(--spacing-16);\n background-color: var(--color-surface-1);\n border-color: var(--color-surface-2);\n box-shadow: none;\n transition:\n border-color var(--duration-200) var(--ease-in-out),\n box-shadow var(--duration-200) var(--ease-in-out);\n\n &:has(:focus-within) {\n border-color: var(--color-surface-4);\n box-shadow: var(--shadow-sm);\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n }\n\n &__textarea {\n background-color: transparent;\n }\n\n // Touch target ≥44px: estende l'hit area dei pulsanti\n // (icona 20px + padding 4px ≈ 28px) senza ingrandire l'icona\n &__button {\n position: relative;\n\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-8));\n }\n }\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, inject, toRef, watch, watchEffect, useSlots } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type {\n AgentInterface,\n AgentModel,\n AgentFileUpload,\n AgentGatewayOptions,\n ChatMessageActions,\n RevisedAnswer,\n UIChatMessage,\n AgentTools,\n } from 'models'\n import {\n useChatbotStore,\n useLocalizedString,\n provideShortUrlResolver,\n } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotViewChat from './PkChatbotViewChat.vue'\n import PkChatbotViewConversations from './PkChatbotViewConversations.vue'\n import PkChatbotViewFiles from './PkChatbotViewFiles.vue'\n import PkChatbotViewProfile from './PkChatbotViewProfile.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n import { resolveContrastColor } from './utils'\n\n // Captured in script setup to avoid TS7022 circular inference in template\n const slots = useSlots()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = defineProps<{\n baseUrl: string\n basePath?: string\n agentId: string\n agentType?: 'reasoning' | 'chat'\n userId?: string\n name?: string\n model?: string\n agentModel?: AgentModel\n agentTools?: AgentTools\n agentInterface?: AgentInterface\n actions?: ChatMessageActions[]\n revisedAnswers?: RevisedAnswer[]\n headers?: Record<string, string>\n systemTheme?: 'light' | 'dark'\n agentFileUpload?: AgentFileUpload\n fallbackModels?: string[]\n gatewayOptions?: Partial<AgentGatewayOptions>\n externalContext?: Record<string, unknown>\n /** Layout modifier: `widget` (default) keeps the contained layout with header, `fullscreen` removes header/background for full-page usage (the page provides the chrome) */\n modifier?: 'widget' | 'fullscreen'\n /** Display name of the current user, used for the `{name}` placeholder in the fullscreen greeting */\n userName?: string\n /** Show the \"Files in this chat\" menu for hosts that don't provide the\n * profile context but whose user is authenticated (e.g. the backoffice\n * playground). The widget host relies on `canManageProfile` instead. */\n userFilesEnabled?: boolean\n }>()\n const emit = defineEmits<{\n 'message-update': [message: UIChatMessage]\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n error: [error: { type: string; title: string; status: number }]\n }>()\n\n const chatId = defineModel<string | undefined>('chatId', {\n type: String,\n default: undefined,\n })\n\n const store = useChatbotStore(props.agentId)\n\n // Resolve backend short-urls (`/api/r/<hash>`) back to their real target\n // for display in tool components. Batched + cached across the chat tree.\n provideShortUrlResolver((hashes) =>\n store.apiClient.resolveShortUrls(hashes),\n )\n\n const { messages, chat, localChatId, activeView, chatError, isDark } =\n storeToRefs(store)\n\n // Ref to store.agentId for watchers (avoids duplicate-key with agentId prop)\n const storeAgentId = toRef(store, 'agentId')\n\n const { startNewChat, stopGeneration, regenerate, navigate, sendMessage } =\n store\n\n // Optional profile-management context provided by the host widget.\n // Present only for authenticated, non-anonymous sessions.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canManageProfile = computed(\n () => profile?.canManageProfile.value ?? false,\n )\n // Anonymous users get a \"Sign in\" entry instead of the profile one,\n // provided the host filled the `view-auth` slot and the agent has at\n // least one auth method enabled.\n const canSignIn = computed(\n () =>\n !canManageProfile.value &&\n (profile?.canSignIn?.value ?? false) &&\n slots['view-auth'] !== undefined,\n )\n\n // Once the sign-in completes (the session becomes non-anonymous) leave\n // the auth view and return to the conversation.\n watch(canManageProfile, (canManage) => {\n if (canManage && activeView.value === 'auth') {\n navigate('chat')\n }\n })\n\n // Sync props → store reactively\n watchEffect(() => {\n store.baseUrl = props.baseUrl\n store.basePath = props.basePath\n store.name = props.name\n store.agentType = props.agentType\n store.userId = props.userId\n store.agentInterface = props.agentInterface\n store.agentModel = props.agentModel\n store.agentTools = props.agentTools\n store.headers = props.headers\n store.revisedAnswers = props.revisedAnswers\n store.model = props.model\n store.actionsInput = props.actions\n store.agentFileUpload = props.agentFileUpload\n store.fallbackModels = props.fallbackModels\n store.gatewayOptions = props.gatewayOptions\n store.externalContext = props.externalContext\n store.systemTheme = props.systemTheme\n store.hideWelcomeMessage = props.modifier === 'fullscreen'\n })\n\n // Sync chatId v-model ↔ store (bidirectional)\n // Parent → store: if the parent provides an external chatId, push it into\n // the store so the Chat instance picks it up.\n watch(\n () => chatId.value,\n (id) => {\n if (id && id !== store.localChatId) {\n store.localChatId = id\n }\n },\n { immediate: true },\n )\n // Store → parent: keep the v-model in sync whenever the store changes\n // the chatId internally (e.g. startNewChat).\n watch(localChatId, (id) => {\n chatId.value = id\n })\n\n // Emit structured errors from chat to the parent component\n watch(chatError, (error) => {\n if (!error) {\n return\n }\n try {\n const parsedError = JSON.parse(error.message) as {\n type: string\n title: string\n status: number\n }\n emit('error', parsedError)\n } catch {\n // ignore non-JSON errors\n }\n })\n\n // ui\n const isFullscreen = computed(() => props.modifier === 'fullscreen')\n const isReasoningAgent = computed(() => props.agentType === 'reasoning')\n const useColorsForAgentHeader = computed(\n () => props.agentInterface?.useColorsForAgentHeader,\n )\n const themeClass = computed(() => [\n 'theme',\n `theme--${isDark.value ? 'dark' : 'light'}`,\n ])\n const mainColor = computed(() => props.agentInterface?.mainColor)\n const contrastColor = computed(() =>\n resolveContrastColor(props.agentInterface?.textColor, mainColor.value),\n )\n const footerMessage = useLocalizedString(\n () => props.agentInterface?.footerMessage,\n )\n const title = computed(() => {\n if (activeView.value === 'conversations') {\n return $t('label.conversations')\n }\n if (activeView.value === 'profile') {\n return $t('label.profile')\n }\n if (activeView.value === 'auth') {\n return $t('label.authentication')\n }\n if (activeView.value === 'files') {\n return $t('userFiles.chatFilesTitle')\n }\n return props.name\n })\n\n // state\n const isReady = computed(() => chat.value.status === 'ready')\n const activeMessage = computed(() => {\n return messages.value?.[messages.value.length - 1]\n })\n\n // expose\n defineExpose({\n startNewChat,\n regenerate,\n stopGeneration,\n sendMessage,\n })\n\n // load chat, conversations and feedbacks on agent or chat change\n watch(\n [storeAgentId, localChatId],\n async ([newAgentId, newChatId]) => {\n await store.initialize(newAgentId, newChatId)\n },\n {\n immediate: true,\n },\n )\n // load conversations when navigating to conversations view\n watch(activeView, (newView) => {\n if (newView === 'conversations') {\n store.loadConversations(storeAgentId.value)\n } else if (newView === 'files') {\n store.loadChatFiles()\n }\n })\n // emit message updates for up-to-date context in parent components\n watch(\n activeMessage,\n (message) => {\n if (message) {\n emit('message-update', message)\n }\n },\n {\n deep: true,\n immediate: true,\n },\n )\n</script>\n\n<template>\n <div\n class=\"pk-chatbot\"\n :class=\"[themeClass, { 'pk-chatbot--fullscreen': isFullscreen }]\">\n <!-- #region title -->\n <div\n v-if=\"!isFullscreen\"\n class=\"pk-chatbot__header\"\n :style=\"\n useColorsForAgentHeader\n ? {\n backgroundColor: mainColor,\n }\n : undefined\n \">\n <div class=\"pk-chatbot__header-start\">\n <template v-if=\"activeView !== 'chat'\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:arrow-left-s-line',\n title: $t('action.goBack'),\n }\"\n @click=\"navigate('chat')\" />\n <VvIcon\n :name=\"\n activeView === 'conversations'\n ? 'ri:history-line'\n : activeView === 'files'\n ? 'ri:attachment-2'\n : 'ri:user-line'\n \"\n class=\"pk-chatbot__nav-icon\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \" />\n </template>\n <PkAvatar\n v-else\n modifiers=\"surface ring shrink-0\"\n class=\"pk-chatbot__avatar\"\n :name\n :img-src=\"agentInterface?.logo\" />\n <strong\n class=\"pk-chatbot__title\"\n :title=\"title\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \">\n {{ title }}\n </strong>\n </div>\n <VvButtonGroup class=\"pk-chatbot__header-end\" modifiers=\"compact\">\n <slot\n name=\"header-actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\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 v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:more-2-fill',\n disabled: !isReady,\n title: $t('action.moreActions'),\n }\" />\n <template #items>\n <VvDropdownAction @click=\"startNewChat()\">\n <VvIcon name=\"ri:edit-box-line\" />\n {{ $t('action.startNewChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"!isReasoningAgent\"\n @click=\"navigate('conversations')\">\n <VvIcon name=\"ri:history-line\" />\n {{ $t('action.viewRecentChats') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"\n (canManageProfile || userFilesEnabled) &&\n localChatId\n \"\n @click=\"navigate('files')\">\n <VvIcon name=\"ri:attachment-2\" />\n {{ $t('userFiles.chatFilesTitle') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"canManageProfile\"\n @click=\"navigate('profile')\">\n <VvIcon name=\"ri:user-line\" />\n {{ $t('label.profile') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-else-if=\"canSignIn\"\n @click=\"navigate('auth')\">\n <VvIcon name=\"ri:account-circle-line\" />\n {{ $t('action.signIn') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n <slot\n name=\"actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n </VvButtonGroup>\n </div>\n <!-- #endregion -->\n\n <div v-if=\"isReasoningAgent\">\n <div class=\"pk-chatbot__warning-body\">\n <VvAlert modifiers=\"callout\">\n <div class=\"pk-chatbot__warning-header\">\n <VvIcon\n name=\"ri:alert-line\"\n class=\"pk-chatbot__warning-icon\" />\n <strong class=\"pk-chatbot__warning-title\">\n {{ $t('message.warning') }}\n </strong>\n </div>\n <p class=\"pk-chatbot__warning-text\">\n {{ $t('message.reasoningAgent') }}\n </p>\n </VvAlert>\n </div>\n </div>\n\n <!-- #region views -->\n <PkChatbotViewChat\n v-if=\"activeView === 'chat'\"\n :agent-id\n :modifier\n :user-name\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\">\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewChat>\n <PkChatbotViewConversations\n v-else-if=\"activeView === 'conversations'\"\n :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewConversations>\n <PkChatbotViewFiles v-else-if=\"activeView === 'files'\" :agent-id />\n <div\n v-else-if=\"activeView === 'auth'\"\n class=\"pk-chatbot__view-auth flex-1 min-h-0 overflow-y-auto\">\n <!-- Sign-in form provided by the host widget (owns the auth client) -->\n <slot name=\"view-auth\" />\n </div>\n <PkChatbotViewProfile v-else :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewProfile>\n <!-- #endregion -->\n\n <!-- #region footer -->\n <div v-if=\"footerMessage\" class=\"pk-chatbot__footer\">\n <PkStreamingMarkdown :markdown=\"footerMessage\" class=\"wysiwyg\" />\n </div>\n <!-- #endregion -->\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot {\n display: flex;\n flex-direction: column;\n position: relative;\n height: 100%;\n background-color: var(--color-surface);\n\n &__header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-14);\n background-color: var(--color-surface-1);\n padding: var(--spacing-14);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__header-start {\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n min-width: 0;\n }\n\n &__nav-icon {\n font-size: var(--text-18);\n }\n\n &__avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n }\n\n &__title {\n font-size: var(--text-16);\n font-weight: var(--font-semibold);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__header-end {\n flex-shrink: 0;\n }\n\n &__warning-body {\n padding: var(--spacing-md);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__warning-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n margin-bottom: var(--spacing-4);\n }\n\n &__warning-icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n\n &__warning-title {\n font-weight: 600;\n font-size: var(--text-14);\n }\n\n &__warning-text {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__footer {\n padding: var(--spacing-md);\n border-top: 1px solid var(--color-surface-4);\n text-align: center;\n font-size: var(--text-12);\n color: var(--color-word-2);\n }\n\n // Modifier: fullscreen — layout full-page (Gemini-style): nessun\n // header interno, sfondo trasparente, input pill centrato.\n // Il chrome (sidebar, navigazione) è gestito dalla pagina ospite.\n &--fullscreen {\n background-color: transparent;\n\n .pk-chatbot__footer {\n border-top: none;\n padding: 0 var(--spacing-md) var(--spacing-sm) var(--spacing-md);\n }\n\n // Reading column aligned with the input (Gemini-style). The\n // constraint lives on the inner wrapper so the scroll container\n // spans the page and the scrollbar sits at its edge.\n .pk-chatbot-messages {\n // Symmetric gutters: a visible scrollbar would otherwise\n // shrink the content box on one side only, shifting the\n // column off the input axis\n scrollbar-gutter: stable both-edges;\n // Dissolve the content at both edges (works on any page\n // background, dots included — a surface-colored fade band\n // would cut through the pattern instead)\n mask-image: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--spacing-40),\n #000 calc(100% - var(--spacing-40)),\n transparent 100%\n );\n // Resting clearance so the first/last lines sit inside the\n // fully opaque zone\n padding-block: var(--spacing-48);\n }\n\n .pk-chatbot-messages__wrapper {\n width: 100%;\n max-width: 768px;\n margin-inline: auto;\n }\n\n .pk-chatbot-file-preview {\n background-color: var(--color-surface);\n }\n\n .pk-chatbot-input__notice {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-3);\n border-top: 1px solid var(--color-surface-2);\n padding: var(--spacing-10) var(--spacing-16);\n\n &::after {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-2);\n bottom: calc(-1 * var(--spacing-20));\n height: var(--spacing-20);\n left: -1px;\n right: -1px;\n }\n }\n\n .pk-chatbot-input {\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n\n &__form {\n border-radius: var(--rounded-xxxl); // pill shape\n padding: var(--spacing-12) var(--spacing-16);\n background-color: var(--color-surface-1);\n border-color: var(--color-surface-2);\n box-shadow: none;\n transition:\n border-color var(--duration-200) var(--ease-in-out),\n box-shadow var(--duration-200) var(--ease-in-out);\n\n &:has(:focus-within) {\n border-color: var(--color-surface-4);\n box-shadow: var(--shadow-sm);\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n }\n\n &__textarea {\n background-color: transparent;\n }\n\n // Touch target ≥44px: estende l'hit area dei pulsanti\n // (icona 20px + padding 4px ≈ 28px) senza ingrandire l'icona\n &__button {\n position: relative;\n\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-8));\n }\n }\n }\n }\n }\n</style>\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as a, n as o } from "./Media-
|
|
3
|
-
import { t as s } from "./index.es-
|
|
1
|
+
import { g as e, n as t, o as n, u as r, v as i } from "./schemas-DnUKIqgi.js";
|
|
2
|
+
import { i as a, n as o } from "./Media-B_7Yj-ml.js";
|
|
3
|
+
import { t as s } from "./index.es-XfWQ82pb.js";
|
|
4
4
|
import { VvAlert as ee, VvButton as te, VvButtonGroup as ne, VvIcon as re } from "@volverjs/ui-vue/components";
|
|
5
5
|
import { Fragment as c, Transition as l, computed as u, createBlock as d, createCommentVNode as f, createElementBlock as p, createElementVNode as m, createTextVNode as h, createVNode as g, defineComponent as _, mergeModels as v, openBlock as y, ref as b, resolveDynamicComponent as x, toDisplayString as S, unref as C, useModel as w, watch as T, withCtx as E } from "vue";
|
|
6
6
|
import { useI18n as ie } from "vue-i18n";
|
|
@@ -344,4 +344,4 @@ var A = k.extend({
|
|
|
344
344
|
//#endregion
|
|
345
345
|
export { K as t };
|
|
346
346
|
|
|
347
|
-
//# sourceMappingURL=PkChatbotAuth-
|
|
347
|
+
//# sourceMappingURL=PkChatbotAuth-C_L2xWuD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbotAuth-C0ATmt-9.js","names":[],"sources":["../../../../packages/models/src/schema/User.ts","../../../../packages/components/src/chat/PkChatbotAuth.vue","../../../../packages/components/src/chat/PkChatbotAuth.vue"],"sourcesContent":["import * as z from 'zod'\nimport { MediaSchema } from './Media'\n\nconst RoleSchema = z.enum(['user', 'admin'])\nconst PasswordSchema = z.string().min(8)\n\nexport const UserSchema = z.object({\n id: z.string(),\n name: z.string(),\n email: z.string(),\n emailVerified: z.boolean(),\n image: z.string().nullable(),\n media: MediaSchema.optional().nullable(),\n createdAt: z.coerce.date(),\n updatedAt: z.coerce.date(),\n banned: z.boolean().nullable(),\n banReason: z.string().nullable(),\n banExpires: z.coerce.date().nullable(),\n isAnonymous: z.boolean().nullable(),\n role: RoleSchema,\n})\n\nexport type User = z.infer<typeof UserSchema>\n\n/**\n * Trimmed user shape for org-scoped lookups (avatars, chat logs, member\n * lists). Served by GET /:audience/users/:id, which only resolves users\n * visible to the caller's active organization.\n */\nexport const UserPreviewSchema = z.object({\n id: z.string(),\n name: z.string(),\n email: z.string().nullable(),\n image: z.string().nullable(),\n isAnonymous: z.boolean().nullable(),\n banned: z.boolean().nullable(),\n})\n\nexport type UserPreview = z.infer<typeof UserPreviewSchema>\n\nexport const PasswordChangeSchema = z\n .object({\n password: PasswordSchema,\n passwordConfirmation: PasswordSchema,\n })\n .refine((data) => data.password === data.passwordConfirmation, {\n params: {\n i18n: 'errors.passwordsDoNotMatch',\n },\n path: ['passwordConfirmation'],\n })\n\nexport type PasswordChange = z.infer<typeof PasswordChangeSchema>\n\nexport const PasswordChangeWithCurrentSchema = PasswordChangeSchema.extend({\n currentPassword: PasswordSchema,\n})\n\nexport type PasswordChangeWithCurrent = z.infer<\n typeof PasswordChangeWithCurrentSchema\n>\n\nexport const UserCreateSchema = PasswordChangeSchema.extend({\n name: z.string().min(1),\n email: z.email(),\n media: MediaSchema.optional(),\n role: RoleSchema.optional().default('user'),\n})\n\nexport type UserCreate = z.infer<typeof UserCreateSchema>\n\nexport const UserUpdateSchema = z.object({\n id: z.string(),\n name: z.string(),\n email: z.string(),\n emailVerified: z.boolean().optional(),\n image: z.string().nullish(),\n role: RoleSchema,\n media: MediaSchema.optional().nullable(),\n banned: z.boolean().nullish(),\n banReason: z.string().nullish(),\n banExpires: z.string().nullish(),\n isAnonymous: z.boolean().nullish(),\n})\n\nexport type UserUpdate = z.infer<typeof UserUpdateSchema>\n\nexport const SignUpSchema = PasswordChangeSchema.extend({\n name: z.string().min(1),\n email: z.email(),\n rememberMe: z.boolean().optional(),\n})\n\nexport type SignUp = z.infer<typeof SignUpSchema>\n","<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { SignUpSchema } from 'models'\n import type { AgentBranding, AuthMethods } from 'models'\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = withDefaults(\n defineProps<{\n branding?: AgentBranding\n authMethods?: AuthMethods\n loading?: boolean\n }>(),\n {\n branding: undefined,\n authMethods: undefined,\n loading: false,\n },\n )\n\n const errorMessage = defineModel<string>('errorMessage')\n /**\n * Non-error feedback (e.g. \"check your inbox\" after sign-up / reset).\n * When set, a dedicated success screen replaces the form.\n */\n const success = defineModel<{ title: string; message: string }>('success')\n\n const emit = defineEmits<{\n (\n e: 'sign-in',\n payload: {\n email: string\n password: string\n rememberMe: boolean\n },\n ): void\n (\n e: 'sign-up',\n payload: { email: string; password: string; name: string },\n ): void\n (e: 'request-reset', payload: { email: string }): void\n (e: 'social', provider: 'google' | 'microsoft'): void\n (e: 'update:errorMessage', message?: string): void\n (e: 'update:success', value?: { title: string; message: string }): void\n }>()\n\n type Mode = 'sign-in' | 'sign-up' | 'reset'\n const mode = ref<Mode>('sign-in')\n watch(mode, () => {\n errorMessage.value = undefined\n success.value = undefined\n })\n\n /** Returns to the sign-in form, clearing any success feedback. */\n function backToSignIn() {\n success.value = undefined\n mode.value = 'sign-in'\n }\n\n const showSocialButtons = computed(\n () => props.authMethods?.google || props.authMethods?.microsoft,\n )\n\n const signInSchema = z.object({\n email: z.email(),\n password: z.string().min(8).max(64),\n rememberMe: z.boolean().default(false),\n })\n\n const resetSchema = z.object({\n email: z.email(),\n })\n\n const { VvForm: VvSignInForm, VvFormField: VvSignInField } = useForm(\n signInSchema,\n { lazyLoad: true },\n )\n const {\n VvForm: VvSignUpForm,\n VvFormField: VvSignUpField,\n clear: clearSignUp,\n } = useForm(SignUpSchema, { lazyLoad: true })\n const {\n VvForm: VvResetForm,\n VvFormField: VvResetField,\n clear: clearReset,\n } = useForm(resetSchema, { lazyLoad: true })\n\n // Empty the sign-up / reset forms once their submission succeeds so a\n // later \"back to sign in\" doesn't reveal stale input.\n watch(success, (value) => {\n if (value) {\n clearSignUp()\n clearReset()\n }\n })\n\n // Move focus to the success card once the flip animation has finished\n // (the out-in transition mounts it only after the form leaves), so\n // screen-reader/keyboard users are announced the result.\n const successCard = ref<HTMLElement>()\n function focusSuccessCard() {\n if (success.value) {\n successCard.value?.focus()\n }\n }\n\n function onSignIn(data: {\n email: string\n password: string\n rememberMe?: boolean\n }) {\n emit('sign-in', {\n email: data.email,\n password: data.password,\n rememberMe: data.rememberMe ?? false,\n })\n }\n\n function onSignUp(data: { name: string; email: string; password: string }) {\n emit('sign-up', {\n name: data.name.trim(),\n email: data.email,\n password: data.password,\n })\n }\n\n function onReset(data: { email: string }) {\n emit('request-reset', { email: data.email })\n }\n\n function handleSocial(provider: 'google' | 'microsoft') {\n emit('social', provider)\n }\n</script>\n\n<template>\n <div\n class=\"pk-chatbot-auth flex flex-col items-center justify-center w-full min-h-full p-lg overflow-y-auto\">\n <div class=\"pk-chatbot-auth__flip w-full max-w-384 mx-auto my-auto\">\n <Transition\n name=\"pk-flip\"\n mode=\"out-in\"\n @after-enter=\"focusSuccessCard\">\n <!-- Success screen (sign-up / reset feedback) -->\n <div\n v-if=\"success\"\n ref=\"successCard\"\n key=\"success\"\n tabindex=\"-1\"\n class=\"pk-chatbot-auth__flip-card outline-none flex flex-col text-center leading-normal\">\n <div\n class=\"pk-chatbot-auth__success-icon w-64 h-64 border border-surface-3 rounded-full bg-surface-0 flex items-center justify-center overflow-hidden bg-surface mx-auto mb-md\">\n <VvIcon\n name=\"ri:mail-check-line\"\n aria-hidden=\"true\"\n class=\"text-success w-32 h-32\" />\n </div>\n <h1 class=\"font-bold text-18 mb-xs\">\n {{ success.title }}\n </h1>\n <p class=\"mb-lg text-16 text-word-3\">\n {{ success.message }}\n </p>\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n icon=\"chevron-left\"\n :label=\"$t('action.backToSignIn')\"\n @click=\"backToSignIn\" />\n </div>\n\n <!-- Sign-in / sign-up / reset forms -->\n <div\n v-else\n key=\"form\"\n class=\"pk-chatbot-auth__flip-card flex flex-col\">\n <!-- Form Container -->\n <div class=\"flex flex-col\">\n <!-- Relative Header Logo -->\n <div\n v-if=\"branding?.interface.logo\"\n class=\"flex justify-center mb-md\">\n <img\n :src=\"branding.interface.logo\"\n :alt=\"branding.name\"\n class=\"w-64 h-64 border border-surface-3 rounded-full bg-surface-0 overflow-hidden bg-surface object-contain\" />\n </div>\n\n <div class=\"text-center leading-normal mb-lg\">\n <h1\n v-if=\"branding?.name\"\n class=\"font-bold text-20 m-0\">\n {{ branding.name }}\n </h1>\n <component\n :is=\"branding?.name ? 'h2' : 'h1'\"\n class=\"text-word-3 mb-0\">\n <template v-if=\"mode === 'sign-in'\">\n {{ $t('auth.signInTitle') }}\n </template>\n <template v-else-if=\"mode === 'sign-up'\">\n {{ $t('auth.signUpTitle') }}\n </template>\n <template v-else-if=\"mode === 'reset'\">\n {{ $t('auth.resetTitle') }}\n </template>\n </component>\n </div>\n\n <!-- Error message -->\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n class=\"mb-md\"\n :content=\"errorMessage\" />\n\n <!-- Sign In -->\n <VvSignInForm\n v-if=\"mode === 'sign-in'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onSignIn\">\n <div class=\"mb-md\">\n <VvSignInField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvSignInField\n name=\"password\"\n type=\"password\"\n autocomplete=\"current-password\"\n :label=\"$t('label.password')\"\n floating />\n <div class=\"mb-md\">\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n :label=\"$t('action.forgetPassword')\"\n @click=\"mode = 'reset'\" />\n </div>\n <VvSignInField\n name=\"rememberMe\"\n type=\"checkbox\"\n :label=\"$t('label.rememberMe')\"\n switch />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.signIn')\" />\n </VvSignInForm>\n\n <!-- Sign Up -->\n <VvSignUpForm\n v-else-if=\"mode === 'sign-up'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onSignUp\">\n <div class=\"mb-md\">\n <VvSignUpField\n name=\"name\"\n type=\"text\"\n autocomplete=\"name\"\n :label=\"$t('label.name')\"\n floating />\n <VvSignUpField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvSignUpField\n name=\"password\"\n type=\"password\"\n autocomplete=\"new-password\"\n :label=\"$t('label.password')\"\n floating />\n <VvSignUpField\n name=\"passwordConfirmation\"\n type=\"password\"\n autocomplete=\"new-password\"\n :label=\"$t('label.confirmPassword')\"\n floating />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.continue')\" />\n </VvSignUpForm>\n\n <!-- Reset Password -->\n <VvResetForm\n v-else-if=\"mode === 'reset'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onReset\">\n <VvResetField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvButtonGroup modifiers=\"vertical\">\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.submit')\" />\n </VvButtonGroup>\n </VvResetForm>\n\n <!-- Social providers (shared by sign-in and sign-up) -->\n <template\n v-if=\"\n (mode === 'sign-in' || mode === 'sign-up') &&\n showSocialButtons\n \">\n <div class=\"hr mt-xs\">\n <span>{{ $t('label.or') }}</span>\n </div>\n <VvButtonGroup\n modifiers=\"vertical\"\n class=\"gap-xs mt-xs\">\n <VvButton\n v-if=\"authMethods?.google\"\n type=\"button\"\n modifiers=\"secondary\"\n icon=\"logos:google-icon\"\n :disabled=\"loading\"\n :label=\"\n $t('action.signInWithSocialProvider', {\n provider: 'Google',\n })\n \"\n @click=\"handleSocial('google')\" />\n <VvButton\n v-if=\"authMethods?.microsoft\"\n type=\"button\"\n modifiers=\"secondary\"\n icon=\"logos:microsoft-icon\"\n :disabled=\"loading\"\n :label=\"\n $t('action.signInWithSocialProvider', {\n provider: 'Microsoft',\n })\n \"\n @click=\"handleSocial('microsoft')\" />\n </VvButtonGroup>\n </template>\n </div>\n\n <!-- Footer below Form -->\n <div class=\"pt-lg text-center text-word-3\">\n <template v-if=\"mode === 'sign-in'\">\n {{ $t('auth.noAccount') }}\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n class=\"font-semibold\"\n :label=\"$t('action.signUp')\"\n @click=\"mode = 'sign-up'\" />\n </template>\n <template v-else-if=\"mode === 'sign-up'\">\n {{ $t('auth.haveAccount') }}\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n class=\"font-semibold\"\n :label=\"$t('action.signIn')\"\n @click=\"mode = 'sign-in'\" />\n </template>\n <template v-else>\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n icon=\"chevron-left\"\n :label=\"$t('action.backToSignIn')\"\n @click=\"mode = 'sign-in'\" />\n </template>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-auth {\n // Flip-card reveal between the form and the success screen, mirroring\n // the classic login in apps/frontend.\n &__flip {\n display: grid;\n transform-style: preserve-3d;\n perspective: 1000px;\n }\n\n &__flip-card {\n grid-area: 1 / 1;\n backface-visibility: hidden;\n transform-style: preserve-3d;\n }\n\n // Subtle pop on the success check icon once the card has flipped in.\n &__success-icon {\n animation: pk-chatbot-auth-check-pop 0.4s ease-out 0.3s backwards;\n }\n }\n\n .pk-flip-enter-active {\n animation: pk-chatbot-auth-flip-in 0.6s ease-out;\n }\n\n .pk-flip-leave-active {\n animation: pk-chatbot-auth-flip-out 0.6s ease-in;\n }\n\n @keyframes pk-chatbot-auth-flip-in {\n 0% {\n transform: rotateY(-180deg);\n opacity: 0;\n }\n\n 100% {\n transform: rotateY(0deg);\n opacity: 1;\n }\n }\n\n @keyframes pk-chatbot-auth-flip-out {\n 0% {\n transform: rotateY(0deg);\n opacity: 1;\n }\n\n 100% {\n transform: rotateY(180deg);\n opacity: 0;\n }\n }\n\n @keyframes pk-chatbot-auth-check-pop {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n\n 60% {\n transform: scale(1.15);\n }\n\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { SignUpSchema } from 'models'\n import type { AgentBranding, AuthMethods } from 'models'\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = withDefaults(\n defineProps<{\n branding?: AgentBranding\n authMethods?: AuthMethods\n loading?: boolean\n }>(),\n {\n branding: undefined,\n authMethods: undefined,\n loading: false,\n },\n )\n\n const errorMessage = defineModel<string>('errorMessage')\n /**\n * Non-error feedback (e.g. \"check your inbox\" after sign-up / reset).\n * When set, a dedicated success screen replaces the form.\n */\n const success = defineModel<{ title: string; message: string }>('success')\n\n const emit = defineEmits<{\n (\n e: 'sign-in',\n payload: {\n email: string\n password: string\n rememberMe: boolean\n },\n ): void\n (\n e: 'sign-up',\n payload: { email: string; password: string; name: string },\n ): void\n (e: 'request-reset', payload: { email: string }): void\n (e: 'social', provider: 'google' | 'microsoft'): void\n (e: 'update:errorMessage', message?: string): void\n (e: 'update:success', value?: { title: string; message: string }): void\n }>()\n\n type Mode = 'sign-in' | 'sign-up' | 'reset'\n const mode = ref<Mode>('sign-in')\n watch(mode, () => {\n errorMessage.value = undefined\n success.value = undefined\n })\n\n /** Returns to the sign-in form, clearing any success feedback. */\n function backToSignIn() {\n success.value = undefined\n mode.value = 'sign-in'\n }\n\n const showSocialButtons = computed(\n () => props.authMethods?.google || props.authMethods?.microsoft,\n )\n\n const signInSchema = z.object({\n email: z.email(),\n password: z.string().min(8).max(64),\n rememberMe: z.boolean().default(false),\n })\n\n const resetSchema = z.object({\n email: z.email(),\n })\n\n const { VvForm: VvSignInForm, VvFormField: VvSignInField } = useForm(\n signInSchema,\n { lazyLoad: true },\n )\n const {\n VvForm: VvSignUpForm,\n VvFormField: VvSignUpField,\n clear: clearSignUp,\n } = useForm(SignUpSchema, { lazyLoad: true })\n const {\n VvForm: VvResetForm,\n VvFormField: VvResetField,\n clear: clearReset,\n } = useForm(resetSchema, { lazyLoad: true })\n\n // Empty the sign-up / reset forms once their submission succeeds so a\n // later \"back to sign in\" doesn't reveal stale input.\n watch(success, (value) => {\n if (value) {\n clearSignUp()\n clearReset()\n }\n })\n\n // Move focus to the success card once the flip animation has finished\n // (the out-in transition mounts it only after the form leaves), so\n // screen-reader/keyboard users are announced the result.\n const successCard = ref<HTMLElement>()\n function focusSuccessCard() {\n if (success.value) {\n successCard.value?.focus()\n }\n }\n\n function onSignIn(data: {\n email: string\n password: string\n rememberMe?: boolean\n }) {\n emit('sign-in', {\n email: data.email,\n password: data.password,\n rememberMe: data.rememberMe ?? false,\n })\n }\n\n function onSignUp(data: { name: string; email: string; password: string }) {\n emit('sign-up', {\n name: data.name.trim(),\n email: data.email,\n password: data.password,\n })\n }\n\n function onReset(data: { email: string }) {\n emit('request-reset', { email: data.email })\n }\n\n function handleSocial(provider: 'google' | 'microsoft') {\n emit('social', provider)\n }\n</script>\n\n<template>\n <div\n class=\"pk-chatbot-auth flex flex-col items-center justify-center w-full min-h-full p-lg overflow-y-auto\">\n <div class=\"pk-chatbot-auth__flip w-full max-w-384 mx-auto my-auto\">\n <Transition\n name=\"pk-flip\"\n mode=\"out-in\"\n @after-enter=\"focusSuccessCard\">\n <!-- Success screen (sign-up / reset feedback) -->\n <div\n v-if=\"success\"\n ref=\"successCard\"\n key=\"success\"\n tabindex=\"-1\"\n class=\"pk-chatbot-auth__flip-card outline-none flex flex-col text-center leading-normal\">\n <div\n class=\"pk-chatbot-auth__success-icon w-64 h-64 border border-surface-3 rounded-full bg-surface-0 flex items-center justify-center overflow-hidden bg-surface mx-auto mb-md\">\n <VvIcon\n name=\"ri:mail-check-line\"\n aria-hidden=\"true\"\n class=\"text-success w-32 h-32\" />\n </div>\n <h1 class=\"font-bold text-18 mb-xs\">\n {{ success.title }}\n </h1>\n <p class=\"mb-lg text-16 text-word-3\">\n {{ success.message }}\n </p>\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n icon=\"chevron-left\"\n :label=\"$t('action.backToSignIn')\"\n @click=\"backToSignIn\" />\n </div>\n\n <!-- Sign-in / sign-up / reset forms -->\n <div\n v-else\n key=\"form\"\n class=\"pk-chatbot-auth__flip-card flex flex-col\">\n <!-- Form Container -->\n <div class=\"flex flex-col\">\n <!-- Relative Header Logo -->\n <div\n v-if=\"branding?.interface.logo\"\n class=\"flex justify-center mb-md\">\n <img\n :src=\"branding.interface.logo\"\n :alt=\"branding.name\"\n class=\"w-64 h-64 border border-surface-3 rounded-full bg-surface-0 overflow-hidden bg-surface object-contain\" />\n </div>\n\n <div class=\"text-center leading-normal mb-lg\">\n <h1\n v-if=\"branding?.name\"\n class=\"font-bold text-20 m-0\">\n {{ branding.name }}\n </h1>\n <component\n :is=\"branding?.name ? 'h2' : 'h1'\"\n class=\"text-word-3 mb-0\">\n <template v-if=\"mode === 'sign-in'\">\n {{ $t('auth.signInTitle') }}\n </template>\n <template v-else-if=\"mode === 'sign-up'\">\n {{ $t('auth.signUpTitle') }}\n </template>\n <template v-else-if=\"mode === 'reset'\">\n {{ $t('auth.resetTitle') }}\n </template>\n </component>\n </div>\n\n <!-- Error message -->\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n class=\"mb-md\"\n :content=\"errorMessage\" />\n\n <!-- Sign In -->\n <VvSignInForm\n v-if=\"mode === 'sign-in'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onSignIn\">\n <div class=\"mb-md\">\n <VvSignInField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvSignInField\n name=\"password\"\n type=\"password\"\n autocomplete=\"current-password\"\n :label=\"$t('label.password')\"\n floating />\n <div class=\"mb-md\">\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n :label=\"$t('action.forgetPassword')\"\n @click=\"mode = 'reset'\" />\n </div>\n <VvSignInField\n name=\"rememberMe\"\n type=\"checkbox\"\n :label=\"$t('label.rememberMe')\"\n switch />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.signIn')\" />\n </VvSignInForm>\n\n <!-- Sign Up -->\n <VvSignUpForm\n v-else-if=\"mode === 'sign-up'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onSignUp\">\n <div class=\"mb-md\">\n <VvSignUpField\n name=\"name\"\n type=\"text\"\n autocomplete=\"name\"\n :label=\"$t('label.name')\"\n floating />\n <VvSignUpField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvSignUpField\n name=\"password\"\n type=\"password\"\n autocomplete=\"new-password\"\n :label=\"$t('label.password')\"\n floating />\n <VvSignUpField\n name=\"passwordConfirmation\"\n type=\"password\"\n autocomplete=\"new-password\"\n :label=\"$t('label.confirmPassword')\"\n floating />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.continue')\" />\n </VvSignUpForm>\n\n <!-- Reset Password -->\n <VvResetForm\n v-else-if=\"mode === 'reset'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onReset\">\n <VvResetField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvButtonGroup modifiers=\"vertical\">\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.submit')\" />\n </VvButtonGroup>\n </VvResetForm>\n\n <!-- Social providers (shared by sign-in and sign-up) -->\n <template\n v-if=\"\n (mode === 'sign-in' || mode === 'sign-up') &&\n showSocialButtons\n \">\n <div class=\"hr mt-xs\">\n <span>{{ $t('label.or') }}</span>\n </div>\n <VvButtonGroup\n modifiers=\"vertical\"\n class=\"gap-xs mt-xs\">\n <VvButton\n v-if=\"authMethods?.google\"\n type=\"button\"\n modifiers=\"secondary\"\n icon=\"logos:google-icon\"\n :disabled=\"loading\"\n :label=\"\n $t('action.signInWithSocialProvider', {\n provider: 'Google',\n })\n \"\n @click=\"handleSocial('google')\" />\n <VvButton\n v-if=\"authMethods?.microsoft\"\n type=\"button\"\n modifiers=\"secondary\"\n icon=\"logos:microsoft-icon\"\n :disabled=\"loading\"\n :label=\"\n $t('action.signInWithSocialProvider', {\n provider: 'Microsoft',\n })\n \"\n @click=\"handleSocial('microsoft')\" />\n </VvButtonGroup>\n </template>\n </div>\n\n <!-- Footer below Form -->\n <div class=\"pt-lg text-center text-word-3\">\n <template v-if=\"mode === 'sign-in'\">\n {{ $t('auth.noAccount') }}\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n class=\"font-semibold\"\n :label=\"$t('action.signUp')\"\n @click=\"mode = 'sign-up'\" />\n </template>\n <template v-else-if=\"mode === 'sign-up'\">\n {{ $t('auth.haveAccount') }}\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n class=\"font-semibold\"\n :label=\"$t('action.signIn')\"\n @click=\"mode = 'sign-in'\" />\n </template>\n <template v-else>\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n icon=\"chevron-left\"\n :label=\"$t('action.backToSignIn')\"\n @click=\"mode = 'sign-in'\" />\n </template>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-auth {\n // Flip-card reveal between the form and the success screen, mirroring\n // the classic login in apps/frontend.\n &__flip {\n display: grid;\n transform-style: preserve-3d;\n perspective: 1000px;\n }\n\n &__flip-card {\n grid-area: 1 / 1;\n backface-visibility: hidden;\n transform-style: preserve-3d;\n }\n\n // Subtle pop on the success check icon once the card has flipped in.\n &__success-icon {\n animation: pk-chatbot-auth-check-pop 0.4s ease-out 0.3s backwards;\n }\n }\n\n .pk-flip-enter-active {\n animation: pk-chatbot-auth-flip-in 0.6s ease-out;\n }\n\n .pk-flip-leave-active {\n animation: pk-chatbot-auth-flip-out 0.6s ease-in;\n }\n\n @keyframes pk-chatbot-auth-flip-in {\n 0% {\n transform: rotateY(-180deg);\n opacity: 0;\n }\n\n 100% {\n transform: rotateY(0deg);\n opacity: 1;\n }\n }\n\n @keyframes pk-chatbot-auth-flip-out {\n 0% {\n transform: rotateY(0deg);\n opacity: 1;\n }\n\n 100% {\n transform: rotateY(180deg);\n opacity: 0;\n }\n }\n\n @keyframes pk-chatbot-auth-check-pop {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n\n 60% {\n transform: scale(1.15);\n }\n\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PkChatbotAuth-C_L2xWuD.js","names":[],"sources":["../../../../packages/models/src/schema/User.ts","../../../../packages/components/src/chat/PkChatbotAuth.vue","../../../../packages/components/src/chat/PkChatbotAuth.vue"],"sourcesContent":["import * as z from 'zod'\nimport { MediaSchema } from './Media'\n\nconst RoleSchema = z.enum(['user', 'admin'])\nconst PasswordSchema = z.string().min(8)\n\nexport const UserSchema = z.object({\n id: z.string(),\n name: z.string(),\n email: z.string(),\n emailVerified: z.boolean(),\n image: z.string().nullable(),\n media: MediaSchema.optional().nullable(),\n createdAt: z.coerce.date(),\n updatedAt: z.coerce.date(),\n banned: z.boolean().nullable(),\n banReason: z.string().nullable(),\n banExpires: z.coerce.date().nullable(),\n isAnonymous: z.boolean().nullable(),\n role: RoleSchema,\n})\n\nexport type User = z.infer<typeof UserSchema>\n\n/**\n * Trimmed user shape for org-scoped lookups (avatars, chat logs, member\n * lists). Served by GET /:audience/users/:id, which only resolves users\n * visible to the caller's active organization.\n */\nexport const UserPreviewSchema = z.object({\n id: z.string(),\n name: z.string(),\n email: z.string().nullable(),\n image: z.string().nullable(),\n isAnonymous: z.boolean().nullable(),\n banned: z.boolean().nullable(),\n})\n\nexport type UserPreview = z.infer<typeof UserPreviewSchema>\n\nexport const PasswordChangeSchema = z\n .object({\n password: PasswordSchema,\n passwordConfirmation: PasswordSchema,\n })\n .refine((data) => data.password === data.passwordConfirmation, {\n params: {\n i18n: 'errors.passwordsDoNotMatch',\n },\n path: ['passwordConfirmation'],\n })\n\nexport type PasswordChange = z.infer<typeof PasswordChangeSchema>\n\nexport const PasswordChangeWithCurrentSchema = PasswordChangeSchema.extend({\n currentPassword: PasswordSchema,\n})\n\nexport type PasswordChangeWithCurrent = z.infer<\n typeof PasswordChangeWithCurrentSchema\n>\n\nexport const UserCreateSchema = PasswordChangeSchema.extend({\n name: z.string().min(1),\n email: z.email(),\n media: MediaSchema.optional(),\n role: RoleSchema.optional().default('user'),\n})\n\nexport type UserCreate = z.infer<typeof UserCreateSchema>\n\nexport const UserUpdateSchema = z.object({\n id: z.string(),\n name: z.string(),\n email: z.string(),\n emailVerified: z.boolean().optional(),\n image: z.string().nullish(),\n role: RoleSchema,\n media: MediaSchema.optional().nullable(),\n banned: z.boolean().nullish(),\n banReason: z.string().nullish(),\n banExpires: z.string().nullish(),\n isAnonymous: z.boolean().nullish(),\n})\n\nexport type UserUpdate = z.infer<typeof UserUpdateSchema>\n\nexport const SignUpSchema = PasswordChangeSchema.extend({\n name: z.string().min(1),\n email: z.email(),\n rememberMe: z.boolean().optional(),\n})\n\nexport type SignUp = z.infer<typeof SignUpSchema>\n","<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { SignUpSchema } from 'models'\n import type { AgentBranding, AuthMethods } from 'models'\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = withDefaults(\n defineProps<{\n branding?: AgentBranding\n authMethods?: AuthMethods\n loading?: boolean\n }>(),\n {\n branding: undefined,\n authMethods: undefined,\n loading: false,\n },\n )\n\n const errorMessage = defineModel<string>('errorMessage')\n /**\n * Non-error feedback (e.g. \"check your inbox\" after sign-up / reset).\n * When set, a dedicated success screen replaces the form.\n */\n const success = defineModel<{ title: string; message: string }>('success')\n\n const emit = defineEmits<{\n (\n e: 'sign-in',\n payload: {\n email: string\n password: string\n rememberMe: boolean\n },\n ): void\n (\n e: 'sign-up',\n payload: { email: string; password: string; name: string },\n ): void\n (e: 'request-reset', payload: { email: string }): void\n (e: 'social', provider: 'google' | 'microsoft'): void\n (e: 'update:errorMessage', message?: string): void\n (e: 'update:success', value?: { title: string; message: string }): void\n }>()\n\n type Mode = 'sign-in' | 'sign-up' | 'reset'\n const mode = ref<Mode>('sign-in')\n watch(mode, () => {\n errorMessage.value = undefined\n success.value = undefined\n })\n\n /** Returns to the sign-in form, clearing any success feedback. */\n function backToSignIn() {\n success.value = undefined\n mode.value = 'sign-in'\n }\n\n const showSocialButtons = computed(\n () => props.authMethods?.google || props.authMethods?.microsoft,\n )\n\n const signInSchema = z.object({\n email: z.email(),\n password: z.string().min(8).max(64),\n rememberMe: z.boolean().default(false),\n })\n\n const resetSchema = z.object({\n email: z.email(),\n })\n\n const { VvForm: VvSignInForm, VvFormField: VvSignInField } = useForm(\n signInSchema,\n { lazyLoad: true },\n )\n const {\n VvForm: VvSignUpForm,\n VvFormField: VvSignUpField,\n clear: clearSignUp,\n } = useForm(SignUpSchema, { lazyLoad: true })\n const {\n VvForm: VvResetForm,\n VvFormField: VvResetField,\n clear: clearReset,\n } = useForm(resetSchema, { lazyLoad: true })\n\n // Empty the sign-up / reset forms once their submission succeeds so a\n // later \"back to sign in\" doesn't reveal stale input.\n watch(success, (value) => {\n if (value) {\n clearSignUp()\n clearReset()\n }\n })\n\n // Move focus to the success card once the flip animation has finished\n // (the out-in transition mounts it only after the form leaves), so\n // screen-reader/keyboard users are announced the result.\n const successCard = ref<HTMLElement>()\n function focusSuccessCard() {\n if (success.value) {\n successCard.value?.focus()\n }\n }\n\n function onSignIn(data: {\n email: string\n password: string\n rememberMe?: boolean\n }) {\n emit('sign-in', {\n email: data.email,\n password: data.password,\n rememberMe: data.rememberMe ?? false,\n })\n }\n\n function onSignUp(data: { name: string; email: string; password: string }) {\n emit('sign-up', {\n name: data.name.trim(),\n email: data.email,\n password: data.password,\n })\n }\n\n function onReset(data: { email: string }) {\n emit('request-reset', { email: data.email })\n }\n\n function handleSocial(provider: 'google' | 'microsoft') {\n emit('social', provider)\n }\n</script>\n\n<template>\n <div\n class=\"pk-chatbot-auth flex flex-col items-center justify-center w-full min-h-full p-lg overflow-y-auto\">\n <div class=\"pk-chatbot-auth__flip w-full max-w-384 mx-auto my-auto\">\n <Transition\n name=\"pk-flip\"\n mode=\"out-in\"\n @after-enter=\"focusSuccessCard\">\n <!-- Success screen (sign-up / reset feedback) -->\n <div\n v-if=\"success\"\n ref=\"successCard\"\n key=\"success\"\n tabindex=\"-1\"\n class=\"pk-chatbot-auth__flip-card outline-none flex flex-col text-center leading-normal\">\n <div\n class=\"pk-chatbot-auth__success-icon w-64 h-64 border border-surface-3 rounded-full bg-surface-0 flex items-center justify-center overflow-hidden bg-surface mx-auto mb-md\">\n <VvIcon\n name=\"ri:mail-check-line\"\n aria-hidden=\"true\"\n class=\"text-success w-32 h-32\" />\n </div>\n <h1 class=\"font-bold text-18 mb-xs\">\n {{ success.title }}\n </h1>\n <p class=\"mb-lg text-16 text-word-3\">\n {{ success.message }}\n </p>\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n icon=\"chevron-left\"\n :label=\"$t('action.backToSignIn')\"\n @click=\"backToSignIn\" />\n </div>\n\n <!-- Sign-in / sign-up / reset forms -->\n <div\n v-else\n key=\"form\"\n class=\"pk-chatbot-auth__flip-card flex flex-col\">\n <!-- Form Container -->\n <div class=\"flex flex-col\">\n <!-- Relative Header Logo -->\n <div\n v-if=\"branding?.interface.logo\"\n class=\"flex justify-center mb-md\">\n <img\n :src=\"branding.interface.logo\"\n :alt=\"branding.name\"\n class=\"w-64 h-64 border border-surface-3 rounded-full bg-surface-0 overflow-hidden bg-surface object-contain\" />\n </div>\n\n <div class=\"text-center leading-normal mb-lg\">\n <h1\n v-if=\"branding?.name\"\n class=\"font-bold text-20 m-0\">\n {{ branding.name }}\n </h1>\n <component\n :is=\"branding?.name ? 'h2' : 'h1'\"\n class=\"text-word-3 mb-0\">\n <template v-if=\"mode === 'sign-in'\">\n {{ $t('auth.signInTitle') }}\n </template>\n <template v-else-if=\"mode === 'sign-up'\">\n {{ $t('auth.signUpTitle') }}\n </template>\n <template v-else-if=\"mode === 'reset'\">\n {{ $t('auth.resetTitle') }}\n </template>\n </component>\n </div>\n\n <!-- Error message -->\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n class=\"mb-md\"\n :content=\"errorMessage\" />\n\n <!-- Sign In -->\n <VvSignInForm\n v-if=\"mode === 'sign-in'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onSignIn\">\n <div class=\"mb-md\">\n <VvSignInField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvSignInField\n name=\"password\"\n type=\"password\"\n autocomplete=\"current-password\"\n :label=\"$t('label.password')\"\n floating />\n <div class=\"mb-md\">\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n :label=\"$t('action.forgetPassword')\"\n @click=\"mode = 'reset'\" />\n </div>\n <VvSignInField\n name=\"rememberMe\"\n type=\"checkbox\"\n :label=\"$t('label.rememberMe')\"\n switch />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.signIn')\" />\n </VvSignInForm>\n\n <!-- Sign Up -->\n <VvSignUpForm\n v-else-if=\"mode === 'sign-up'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onSignUp\">\n <div class=\"mb-md\">\n <VvSignUpField\n name=\"name\"\n type=\"text\"\n autocomplete=\"name\"\n :label=\"$t('label.name')\"\n floating />\n <VvSignUpField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvSignUpField\n name=\"password\"\n type=\"password\"\n autocomplete=\"new-password\"\n :label=\"$t('label.password')\"\n floating />\n <VvSignUpField\n name=\"passwordConfirmation\"\n type=\"password\"\n autocomplete=\"new-password\"\n :label=\"$t('label.confirmPassword')\"\n floating />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.continue')\" />\n </VvSignUpForm>\n\n <!-- Reset Password -->\n <VvResetForm\n v-else-if=\"mode === 'reset'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onReset\">\n <VvResetField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvButtonGroup modifiers=\"vertical\">\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.submit')\" />\n </VvButtonGroup>\n </VvResetForm>\n\n <!-- Social providers (shared by sign-in and sign-up) -->\n <template\n v-if=\"\n (mode === 'sign-in' || mode === 'sign-up') &&\n showSocialButtons\n \">\n <div class=\"hr mt-xs\">\n <span>{{ $t('label.or') }}</span>\n </div>\n <VvButtonGroup\n modifiers=\"vertical\"\n class=\"gap-xs mt-xs\">\n <VvButton\n v-if=\"authMethods?.google\"\n type=\"button\"\n modifiers=\"secondary\"\n icon=\"logos:google-icon\"\n :disabled=\"loading\"\n :label=\"\n $t('action.signInWithSocialProvider', {\n provider: 'Google',\n })\n \"\n @click=\"handleSocial('google')\" />\n <VvButton\n v-if=\"authMethods?.microsoft\"\n type=\"button\"\n modifiers=\"secondary\"\n icon=\"logos:microsoft-icon\"\n :disabled=\"loading\"\n :label=\"\n $t('action.signInWithSocialProvider', {\n provider: 'Microsoft',\n })\n \"\n @click=\"handleSocial('microsoft')\" />\n </VvButtonGroup>\n </template>\n </div>\n\n <!-- Footer below Form -->\n <div class=\"pt-lg text-center text-word-3\">\n <template v-if=\"mode === 'sign-in'\">\n {{ $t('auth.noAccount') }}\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n class=\"font-semibold\"\n :label=\"$t('action.signUp')\"\n @click=\"mode = 'sign-up'\" />\n </template>\n <template v-else-if=\"mode === 'sign-up'\">\n {{ $t('auth.haveAccount') }}\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n class=\"font-semibold\"\n :label=\"$t('action.signIn')\"\n @click=\"mode = 'sign-in'\" />\n </template>\n <template v-else>\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n icon=\"chevron-left\"\n :label=\"$t('action.backToSignIn')\"\n @click=\"mode = 'sign-in'\" />\n </template>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-auth {\n // Flip-card reveal between the form and the success screen, mirroring\n // the classic login in apps/frontend.\n &__flip {\n display: grid;\n transform-style: preserve-3d;\n perspective: 1000px;\n }\n\n &__flip-card {\n grid-area: 1 / 1;\n backface-visibility: hidden;\n transform-style: preserve-3d;\n }\n\n // Subtle pop on the success check icon once the card has flipped in.\n &__success-icon {\n animation: pk-chatbot-auth-check-pop 0.4s ease-out 0.3s backwards;\n }\n }\n\n .pk-flip-enter-active {\n animation: pk-chatbot-auth-flip-in 0.6s ease-out;\n }\n\n .pk-flip-leave-active {\n animation: pk-chatbot-auth-flip-out 0.6s ease-in;\n }\n\n @keyframes pk-chatbot-auth-flip-in {\n 0% {\n transform: rotateY(-180deg);\n opacity: 0;\n }\n\n 100% {\n transform: rotateY(0deg);\n opacity: 1;\n }\n }\n\n @keyframes pk-chatbot-auth-flip-out {\n 0% {\n transform: rotateY(0deg);\n opacity: 1;\n }\n\n 100% {\n transform: rotateY(180deg);\n opacity: 0;\n }\n }\n\n @keyframes pk-chatbot-auth-check-pop {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n\n 60% {\n transform: scale(1.15);\n }\n\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { SignUpSchema } from 'models'\n import type { AgentBranding, AuthMethods } from 'models'\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = withDefaults(\n defineProps<{\n branding?: AgentBranding\n authMethods?: AuthMethods\n loading?: boolean\n }>(),\n {\n branding: undefined,\n authMethods: undefined,\n loading: false,\n },\n )\n\n const errorMessage = defineModel<string>('errorMessage')\n /**\n * Non-error feedback (e.g. \"check your inbox\" after sign-up / reset).\n * When set, a dedicated success screen replaces the form.\n */\n const success = defineModel<{ title: string; message: string }>('success')\n\n const emit = defineEmits<{\n (\n e: 'sign-in',\n payload: {\n email: string\n password: string\n rememberMe: boolean\n },\n ): void\n (\n e: 'sign-up',\n payload: { email: string; password: string; name: string },\n ): void\n (e: 'request-reset', payload: { email: string }): void\n (e: 'social', provider: 'google' | 'microsoft'): void\n (e: 'update:errorMessage', message?: string): void\n (e: 'update:success', value?: { title: string; message: string }): void\n }>()\n\n type Mode = 'sign-in' | 'sign-up' | 'reset'\n const mode = ref<Mode>('sign-in')\n watch(mode, () => {\n errorMessage.value = undefined\n success.value = undefined\n })\n\n /** Returns to the sign-in form, clearing any success feedback. */\n function backToSignIn() {\n success.value = undefined\n mode.value = 'sign-in'\n }\n\n const showSocialButtons = computed(\n () => props.authMethods?.google || props.authMethods?.microsoft,\n )\n\n const signInSchema = z.object({\n email: z.email(),\n password: z.string().min(8).max(64),\n rememberMe: z.boolean().default(false),\n })\n\n const resetSchema = z.object({\n email: z.email(),\n })\n\n const { VvForm: VvSignInForm, VvFormField: VvSignInField } = useForm(\n signInSchema,\n { lazyLoad: true },\n )\n const {\n VvForm: VvSignUpForm,\n VvFormField: VvSignUpField,\n clear: clearSignUp,\n } = useForm(SignUpSchema, { lazyLoad: true })\n const {\n VvForm: VvResetForm,\n VvFormField: VvResetField,\n clear: clearReset,\n } = useForm(resetSchema, { lazyLoad: true })\n\n // Empty the sign-up / reset forms once their submission succeeds so a\n // later \"back to sign in\" doesn't reveal stale input.\n watch(success, (value) => {\n if (value) {\n clearSignUp()\n clearReset()\n }\n })\n\n // Move focus to the success card once the flip animation has finished\n // (the out-in transition mounts it only after the form leaves), so\n // screen-reader/keyboard users are announced the result.\n const successCard = ref<HTMLElement>()\n function focusSuccessCard() {\n if (success.value) {\n successCard.value?.focus()\n }\n }\n\n function onSignIn(data: {\n email: string\n password: string\n rememberMe?: boolean\n }) {\n emit('sign-in', {\n email: data.email,\n password: data.password,\n rememberMe: data.rememberMe ?? false,\n })\n }\n\n function onSignUp(data: { name: string; email: string; password: string }) {\n emit('sign-up', {\n name: data.name.trim(),\n email: data.email,\n password: data.password,\n })\n }\n\n function onReset(data: { email: string }) {\n emit('request-reset', { email: data.email })\n }\n\n function handleSocial(provider: 'google' | 'microsoft') {\n emit('social', provider)\n }\n</script>\n\n<template>\n <div\n class=\"pk-chatbot-auth flex flex-col items-center justify-center w-full min-h-full p-lg overflow-y-auto\">\n <div class=\"pk-chatbot-auth__flip w-full max-w-384 mx-auto my-auto\">\n <Transition\n name=\"pk-flip\"\n mode=\"out-in\"\n @after-enter=\"focusSuccessCard\">\n <!-- Success screen (sign-up / reset feedback) -->\n <div\n v-if=\"success\"\n ref=\"successCard\"\n key=\"success\"\n tabindex=\"-1\"\n class=\"pk-chatbot-auth__flip-card outline-none flex flex-col text-center leading-normal\">\n <div\n class=\"pk-chatbot-auth__success-icon w-64 h-64 border border-surface-3 rounded-full bg-surface-0 flex items-center justify-center overflow-hidden bg-surface mx-auto mb-md\">\n <VvIcon\n name=\"ri:mail-check-line\"\n aria-hidden=\"true\"\n class=\"text-success w-32 h-32\" />\n </div>\n <h1 class=\"font-bold text-18 mb-xs\">\n {{ success.title }}\n </h1>\n <p class=\"mb-lg text-16 text-word-3\">\n {{ success.message }}\n </p>\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n icon=\"chevron-left\"\n :label=\"$t('action.backToSignIn')\"\n @click=\"backToSignIn\" />\n </div>\n\n <!-- Sign-in / sign-up / reset forms -->\n <div\n v-else\n key=\"form\"\n class=\"pk-chatbot-auth__flip-card flex flex-col\">\n <!-- Form Container -->\n <div class=\"flex flex-col\">\n <!-- Relative Header Logo -->\n <div\n v-if=\"branding?.interface.logo\"\n class=\"flex justify-center mb-md\">\n <img\n :src=\"branding.interface.logo\"\n :alt=\"branding.name\"\n class=\"w-64 h-64 border border-surface-3 rounded-full bg-surface-0 overflow-hidden bg-surface object-contain\" />\n </div>\n\n <div class=\"text-center leading-normal mb-lg\">\n <h1\n v-if=\"branding?.name\"\n class=\"font-bold text-20 m-0\">\n {{ branding.name }}\n </h1>\n <component\n :is=\"branding?.name ? 'h2' : 'h1'\"\n class=\"text-word-3 mb-0\">\n <template v-if=\"mode === 'sign-in'\">\n {{ $t('auth.signInTitle') }}\n </template>\n <template v-else-if=\"mode === 'sign-up'\">\n {{ $t('auth.signUpTitle') }}\n </template>\n <template v-else-if=\"mode === 'reset'\">\n {{ $t('auth.resetTitle') }}\n </template>\n </component>\n </div>\n\n <!-- Error message -->\n <VvAlert\n v-if=\"errorMessage\"\n modifiers=\"danger\"\n class=\"mb-md\"\n :content=\"errorMessage\" />\n\n <!-- Sign In -->\n <VvSignInForm\n v-if=\"mode === 'sign-in'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onSignIn\">\n <div class=\"mb-md\">\n <VvSignInField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvSignInField\n name=\"password\"\n type=\"password\"\n autocomplete=\"current-password\"\n :label=\"$t('label.password')\"\n floating />\n <div class=\"mb-md\">\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n :label=\"$t('action.forgetPassword')\"\n @click=\"mode = 'reset'\" />\n </div>\n <VvSignInField\n name=\"rememberMe\"\n type=\"checkbox\"\n :label=\"$t('label.rememberMe')\"\n switch />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.signIn')\" />\n </VvSignInForm>\n\n <!-- Sign Up -->\n <VvSignUpForm\n v-else-if=\"mode === 'sign-up'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onSignUp\">\n <div class=\"mb-md\">\n <VvSignUpField\n name=\"name\"\n type=\"text\"\n autocomplete=\"name\"\n :label=\"$t('label.name')\"\n floating />\n <VvSignUpField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvSignUpField\n name=\"password\"\n type=\"password\"\n autocomplete=\"new-password\"\n :label=\"$t('label.password')\"\n floating />\n <VvSignUpField\n name=\"passwordConfirmation\"\n type=\"password\"\n autocomplete=\"new-password\"\n :label=\"$t('label.confirmPassword')\"\n floating />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.continue')\" />\n </VvSignUpForm>\n\n <!-- Reset Password -->\n <VvResetForm\n v-else-if=\"mode === 'reset'\"\n class=\"flex flex-col\"\n :readonly=\"loading\"\n @submit=\"onReset\">\n <VvResetField\n name=\"email\"\n type=\"email\"\n autocomplete=\"username\"\n :label=\"$t('label.email')\"\n floating />\n <VvButtonGroup modifiers=\"vertical\">\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading=\"loading\"\n :disabled=\"loading\"\n :label=\"$t('action.submit')\" />\n </VvButtonGroup>\n </VvResetForm>\n\n <!-- Social providers (shared by sign-in and sign-up) -->\n <template\n v-if=\"\n (mode === 'sign-in' || mode === 'sign-up') &&\n showSocialButtons\n \">\n <div class=\"hr mt-xs\">\n <span>{{ $t('label.or') }}</span>\n </div>\n <VvButtonGroup\n modifiers=\"vertical\"\n class=\"gap-xs mt-xs\">\n <VvButton\n v-if=\"authMethods?.google\"\n type=\"button\"\n modifiers=\"secondary\"\n icon=\"logos:google-icon\"\n :disabled=\"loading\"\n :label=\"\n $t('action.signInWithSocialProvider', {\n provider: 'Google',\n })\n \"\n @click=\"handleSocial('google')\" />\n <VvButton\n v-if=\"authMethods?.microsoft\"\n type=\"button\"\n modifiers=\"secondary\"\n icon=\"logos:microsoft-icon\"\n :disabled=\"loading\"\n :label=\"\n $t('action.signInWithSocialProvider', {\n provider: 'Microsoft',\n })\n \"\n @click=\"handleSocial('microsoft')\" />\n </VvButtonGroup>\n </template>\n </div>\n\n <!-- Footer below Form -->\n <div class=\"pt-lg text-center text-word-3\">\n <template v-if=\"mode === 'sign-in'\">\n {{ $t('auth.noAccount') }}\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n class=\"font-semibold\"\n :label=\"$t('action.signUp')\"\n @click=\"mode = 'sign-up'\" />\n </template>\n <template v-else-if=\"mode === 'sign-up'\">\n {{ $t('auth.haveAccount') }}\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n class=\"font-semibold\"\n :label=\"$t('action.signIn')\"\n @click=\"mode = 'sign-in'\" />\n </template>\n <template v-else>\n <VvButton\n type=\"button\"\n modifiers=\"link\"\n icon=\"chevron-left\"\n :label=\"$t('action.backToSignIn')\"\n @click=\"mode = 'sign-in'\" />\n </template>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-auth {\n // Flip-card reveal between the form and the success screen, mirroring\n // the classic login in apps/frontend.\n &__flip {\n display: grid;\n transform-style: preserve-3d;\n perspective: 1000px;\n }\n\n &__flip-card {\n grid-area: 1 / 1;\n backface-visibility: hidden;\n transform-style: preserve-3d;\n }\n\n // Subtle pop on the success check icon once the card has flipped in.\n &__success-icon {\n animation: pk-chatbot-auth-check-pop 0.4s ease-out 0.3s backwards;\n }\n }\n\n .pk-flip-enter-active {\n animation: pk-chatbot-auth-flip-in 0.6s ease-out;\n }\n\n .pk-flip-leave-active {\n animation: pk-chatbot-auth-flip-out 0.6s ease-in;\n }\n\n @keyframes pk-chatbot-auth-flip-in {\n 0% {\n transform: rotateY(-180deg);\n opacity: 0;\n }\n\n 100% {\n transform: rotateY(0deg);\n opacity: 1;\n }\n }\n\n @keyframes pk-chatbot-auth-flip-out {\n 0% {\n transform: rotateY(0deg);\n opacity: 1;\n }\n\n 100% {\n transform: rotateY(180deg);\n opacity: 0;\n }\n }\n\n @keyframes pk-chatbot-auth-check-pop {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n\n 60% {\n transform: scale(1.15);\n }\n\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n</style>\n"],"mappings":"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"}
|
package/dist-vue/_chunks/{PkChatbotEmptyState-BGTSWFSS.js → PkChatbotEmptyState-BGOC6vxr.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as e } from "./PkStreamingMarkdown-
|
|
1
|
+
import { t as e } from "./PkStreamingMarkdown-DEhbK-3c.js";
|
|
2
2
|
import { t } from "./PkAvatar-CyIgsndA.js";
|
|
3
3
|
import { createBlock as n, createCommentVNode as r, createElementBlock as i, defineComponent as a, openBlock as o } from "vue";
|
|
4
4
|
//#region ../../packages/components/src/chat/PkChatbotEmptyState.vue?vue&type=script&setup=true&lang.ts
|
|
@@ -27,4 +27,4 @@ var s = { class: "pk-chatbot-empty-state" }, c = /* @__PURE__ */ a({
|
|
|
27
27
|
//#endregion
|
|
28
28
|
export { c as t };
|
|
29
29
|
|
|
30
|
-
//# sourceMappingURL=PkChatbotEmptyState-
|
|
30
|
+
//# sourceMappingURL=PkChatbotEmptyState-BGOC6vxr.js.map
|
package/dist-vue/_chunks/{PkChatbotEmptyState-BGTSWFSS.js.map → PkChatbotEmptyState-BGOC6vxr.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbotEmptyState-
|
|
1
|
+
{"version":3,"file":"PkChatbotEmptyState-BGOC6vxr.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbotEmptyState.vue","../../../../packages/components/src/chat/PkChatbotEmptyState.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import PkAvatar from './PkAvatar.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n defineProps<{\n greeting?: string\n /** Agent avatar shown above the greeting where the input chip is hidden (mobile) */\n logo?: string | null\n name?: string\n }>()\n</script>\n\n<template>\n <div class=\"pk-chatbot-empty-state\">\n <PkAvatar\n v-if=\"logo || name\"\n class=\"pk-chatbot-empty-state__avatar\"\n modifiers=\"surface\"\n :img-src=\"logo\"\n :name=\"name\" />\n <PkStreamingMarkdown\n v-if=\"greeting\"\n :markdown=\"greeting\"\n :animation=\"null\"\n class=\"wysiwyg pk-chatbot-empty-state__greeting\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-empty-state {\n // Above the fullscreen ambient glow\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--spacing-sm);\n padding: var(--spacing-md);\n text-align: center;\n\n // Let the greeting wrap on a couple of lines (Gemini-like)\n @include media-breakpoint-down('xs', $breakpoints) {\n padding-inline: var(--spacing-56);\n }\n\n &__avatar {\n width: var(--spacing-64);\n height: var(--spacing-64);\n font-size: var(--text-20);\n border-radius: var(--rounded-full);\n\n // The agent identity lives in the input chip when that is visible\n @include media-breakpoint-up('md', $breakpoints) {\n display: none;\n }\n }\n\n &__greeting {\n font-size: var(--text-28);\n font-weight: var(--font-light);\n color: var(--color-word-1);\n text-wrap: balance;\n\n // Render the greeting markdown inline as a single heading line\n :first-child {\n margin-top: 0;\n }\n\n :last-child {\n margin-bottom: 0;\n }\n }\n\n // Enters fading in from below, in sync with the glow ignition\n &__avatar,\n &__greeting {\n animation: pk-chatbot-greeting-enter 600ms var(--ease-out) both;\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n }\n }\n\n @keyframes pk-chatbot-greeting-enter {\n from {\n opacity: 0;\n transform: translateY(var(--spacing-16));\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import PkAvatar from './PkAvatar.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n defineProps<{\n greeting?: string\n /** Agent avatar shown above the greeting where the input chip is hidden (mobile) */\n logo?: string | null\n name?: string\n }>()\n</script>\n\n<template>\n <div class=\"pk-chatbot-empty-state\">\n <PkAvatar\n v-if=\"logo || name\"\n class=\"pk-chatbot-empty-state__avatar\"\n modifiers=\"surface\"\n :img-src=\"logo\"\n :name=\"name\" />\n <PkStreamingMarkdown\n v-if=\"greeting\"\n :markdown=\"greeting\"\n :animation=\"null\"\n class=\"wysiwyg pk-chatbot-empty-state__greeting\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-empty-state {\n // Above the fullscreen ambient glow\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--spacing-sm);\n padding: var(--spacing-md);\n text-align: center;\n\n // Let the greeting wrap on a couple of lines (Gemini-like)\n @include media-breakpoint-down('xs', $breakpoints) {\n padding-inline: var(--spacing-56);\n }\n\n &__avatar {\n width: var(--spacing-64);\n height: var(--spacing-64);\n font-size: var(--text-20);\n border-radius: var(--rounded-full);\n\n // The agent identity lives in the input chip when that is visible\n @include media-breakpoint-up('md', $breakpoints) {\n display: none;\n }\n }\n\n &__greeting {\n font-size: var(--text-28);\n font-weight: var(--font-light);\n color: var(--color-word-1);\n text-wrap: balance;\n\n // Render the greeting markdown inline as a single heading line\n :first-child {\n margin-top: 0;\n }\n\n :last-child {\n margin-bottom: 0;\n }\n }\n\n // Enters fading in from below, in sync with the glow ignition\n &__avatar,\n &__greeting {\n animation: pk-chatbot-greeting-enter 600ms var(--ease-out) both;\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n }\n }\n\n @keyframes pk-chatbot-greeting-enter {\n from {\n opacity: 0;\n transform: translateY(var(--spacing-16));\n }\n }\n</style>\n"],"mappings":";;;;;;;;;;;;yBAaI,EAYM,OAZN,GAYM,CAVQ,EAAA,QAAQ,EAAA,QAAA,EAAA,GADlB,EAKmB,GAAA;;GAHf,OAAM;GACN,WAAU;GACT,WAAS,EAAA;GACT,MAAM,EAAA;iDAED,EAAA,YAAA,EAAA,GADV,EAIuD,GAAA;;GAFlD,UAAU,EAAA;GACV,WAAW;GACZ,OAAM"}
|