@8wave/ai-elements 0.99.0 → 0.100.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/PkLazyImg-BhLarH2R.js +34 -0
- package/dist/_chunks/PkLazyImg-BhLarH2R.js.map +1 -0
- package/{dist-vue/_chunks/PkStreamingMarkdown-CjBMQ18U.js → dist/_chunks/PkStreamingMarkdown-CHhH4FdH.js} +407 -385
- package/dist/_chunks/PkStreamingMarkdown-CHhH4FdH.js.map +1 -0
- package/dist/_chunks/{PkToolShowArtifact-bYYaMGxq.js → PkToolShowArtifact-BRYd-YSp.js} +2 -2
- package/dist/_chunks/{PkToolShowArtifact-bYYaMGxq.js.map → PkToolShowArtifact-BRYd-YSp.js.map} +1 -1
- package/dist/_chunks/{PkToolShowContactForm-DtjVW-n6.js → PkToolShowContactForm-vZTYDdfX.js} +2 -2
- package/dist/_chunks/{PkToolShowContactForm-DtjVW-n6.js.map → PkToolShowContactForm-vZTYDdfX.js.map} +1 -1
- package/dist/_chunks/{PkToolShowForm-BP8asXtz.js → PkToolShowForm-DMmZ34is.js} +2 -2
- package/dist/_chunks/{PkToolShowForm-BP8asXtz.js.map → PkToolShowForm-DMmZ34is.js.map} +1 -1
- package/dist/_chunks/{PkToolShowImageGallery-WQL3T4Hr.js → PkToolShowImageGallery-B7dClMBr.js} +28 -27
- package/dist/_chunks/PkToolShowImageGallery-B7dClMBr.js.map +1 -0
- package/dist/_chunks/{PkToolShowProductList-BbY4ni7Y.js → PkToolShowProductList-BGqOPGVe.js} +10 -8
- package/dist/_chunks/PkToolShowProductList-BGqOPGVe.js.map +1 -0
- package/dist/_chunks/{PkToolShowSources-D2tBcx7n.js → PkToolShowSources-DwhjDoSn.js} +2 -2
- package/dist/_chunks/{PkToolShowSources-D2tBcx7n.js.map → PkToolShowSources-DwhjDoSn.js.map} +1 -1
- package/dist/_chunks/PkToolShowWebPages-Co1LHj17.js +78 -0
- package/dist/_chunks/PkToolShowWebPages-Co1LHj17.js.map +1 -0
- package/dist/_chunks/useImageFallback-CaxDKce2.js +19 -0
- package/dist/_chunks/useImageFallback-CaxDKce2.js.map +1 -0
- package/dist/ai-elements.es.js +3049 -3015
- 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/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/PkChatbotViewFiles.js +1 -1
- package/dist-vue/PkChatbotViewProfile.js +1 -1
- package/dist-vue/_chunks/PkAvatar-paEJByEA.js +42 -0
- package/dist-vue/_chunks/PkAvatar-paEJByEA.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbot-Bf5uY3cO.js → PkChatbot-Cs3GOau6.js} +6 -6
- package/dist-vue/_chunks/{PkChatbot-Bf5uY3cO.js.map → PkChatbot-Cs3GOau6.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotAuth-DV83Fen2.js +349 -0
- package/dist-vue/_chunks/PkChatbotAuth-DV83Fen2.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotEmptyState-CPqLqXek.js → PkChatbotEmptyState-Cxv79prz.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotEmptyState-CPqLqXek.js.map → PkChatbotEmptyState-Cxv79prz.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotFilePreview-DyrszALJ.js.map +1 -1
- package/dist-vue/_chunks/{PkChatbotInput-jvb0vuak.js → PkChatbotInput-BSm3BI7D.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotInput-jvb0vuak.js.map → PkChatbotInput-BSm3BI7D.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-B3G12zah.js → PkChatbotMessages-BDRnBwzT.js} +11 -11
- package/dist-vue/_chunks/{PkChatbotMessages-B3G12zah.js.map → PkChatbotMessages-BDRnBwzT.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotSteps-BdU6YC_N.js → PkChatbotSteps-BdycCDRx.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotSteps-BdU6YC_N.js.map → PkChatbotSteps-BdycCDRx.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-c0Ecpzoc.js → PkChatbotViewChat-DwfZGp9d.js} +6 -6
- package/dist-vue/_chunks/{PkChatbotViewChat-c0Ecpzoc.js.map → PkChatbotViewChat-DwfZGp9d.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewFiles-BipRoidH.js → PkChatbotViewFiles-DwrKKSjU.js} +26 -25
- package/dist-vue/_chunks/PkChatbotViewFiles-DwrKKSjU.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewProfile-DuLwYpGZ.js → PkChatbotViewProfile-B7yqkE-h.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-DuLwYpGZ.js.map → PkChatbotViewProfile-B7yqkE-h.js.map} +1 -1
- package/dist-vue/_chunks/PkLazyImg-CDxX9xnq.js +34 -0
- package/dist-vue/_chunks/PkLazyImg-CDxX9xnq.js.map +1 -0
- package/{dist/_chunks/PkStreamingMarkdown-6Xx9n1ue.js → dist-vue/_chunks/PkStreamingMarkdown-B3OallmC.js} +331 -329
- package/dist-vue/_chunks/PkStreamingMarkdown-B3OallmC.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowArtifact-COwSXmF4.js → PkToolShowArtifact-N7we3fbM.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowArtifact-COwSXmF4.js.map → PkToolShowArtifact-N7we3fbM.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowContactForm-DRRtCCxB.js → PkToolShowContactForm-DgVlnLkC.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowContactForm-DRRtCCxB.js.map → PkToolShowContactForm-DgVlnLkC.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowForm-j7Ha-0X3.js → PkToolShowForm-dp0-LYl1.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowForm-j7Ha-0X3.js.map → PkToolShowForm-dp0-LYl1.js.map} +1 -1
- package/dist-vue/_chunks/PkToolShowImageGallery-CoC46nGb.js +75 -0
- package/dist-vue/_chunks/PkToolShowImageGallery-CoC46nGb.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowProductList-DXMN2UuE.js → PkToolShowProductList-C6YoC3sD.js} +25 -23
- package/dist-vue/_chunks/PkToolShowProductList-C6YoC3sD.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowSources-BVLw4W5l.js → PkToolShowSources-DJwpImlN.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowSources-BVLw4W5l.js.map → PkToolShowSources-DJwpImlN.js.map} +1 -1
- package/dist-vue/_chunks/PkToolShowWebPages-B9UeUWfJ.js +79 -0
- package/dist-vue/_chunks/PkToolShowWebPages-B9UeUWfJ.js.map +1 -0
- package/dist-vue/_chunks/useImageFallback-5vQIO0pC.js +18 -0
- package/dist-vue/_chunks/useImageFallback-5vQIO0pC.js.map +1 -0
- package/dist-vue/index.js +3952 -3966
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/locales.js +2 -2
- package/dist-vue/packages/components/src/PkLazyImg.d.ts +6 -1
- package/dist-vue/packages/components/src/chat/PkAvatar.d.ts +8 -1
- package/dist-vue/packages/components/src/chat/PkChatSidebarConversationItem.d.ts +2 -2
- package/dist-vue/packages/composable/src/index.d.ts +1 -0
- package/dist-vue/packages/composable/src/useImageFallback.d.ts +19 -0
- package/dist-vue/packages/models/src/schema/UserOverview.d.ts +96 -0
- package/dist-vue/packages/models/src/schema/index.d.ts +1 -0
- package/dist-vue/style.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/PkStreamingMarkdown-6Xx9n1ue.js.map +0 -1
- package/dist/_chunks/PkToolShowImageGallery-WQL3T4Hr.js.map +0 -1
- package/dist/_chunks/PkToolShowProductList-BbY4ni7Y.js.map +0 -1
- package/dist/_chunks/PkToolShowWebPages-DkMJrWmT.js +0 -76
- package/dist/_chunks/PkToolShowWebPages-DkMJrWmT.js.map +0 -1
- package/dist-vue/_chunks/PkAvatar-CyIgsndA.js +0 -31
- package/dist-vue/_chunks/PkAvatar-CyIgsndA.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotAuth-BGn2MDYD.js +0 -347
- package/dist-vue/_chunks/PkChatbotAuth-BGn2MDYD.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotViewFiles-BipRoidH.js.map +0 -1
- package/dist-vue/_chunks/PkStreamingMarkdown-CjBMQ18U.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowImageGallery-Bj7YlqbP.js +0 -74
- package/dist-vue/_chunks/PkToolShowImageGallery-Bj7YlqbP.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowProductList-DXMN2UuE.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowWebPages-CsACREOH.js +0 -77
- package/dist-vue/_chunks/PkToolShowWebPages-CsACREOH.js.map +0 -1
package/dist-vue/_chunks/{PkChatbotViewChat-c0Ecpzoc.js.map → PkChatbotViewChat-DwfZGp9d.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbotViewChat-c0Ecpzoc.js","names":[],"sources":["../../../../packages/composable/src/chatbot/useGreeting.ts","../../../../packages/components/src/chat/PkAuroraCanvas.vue","../../../../packages/components/src/chat/PkAuroraCanvas.vue","../../../../packages/components/src/chat/PkChatbotViewChat.vue","../../../../packages/components/src/chat/PkChatbotViewChat.vue"],"sourcesContent":["import { computed, toValue } from 'vue'\nimport type { ComputedRef, MaybeRefOrGetter } from 'vue'\nimport { useI18n } from 'vue-i18n'\nimport type { AgentInterface } from 'models'\n\n/**\n * Resolves the greeting shown in the fullscreen empty state.\n *\n * v1 behavior (docs/specs/public-chat-page.md): uses the first entry of the\n * `agentInterface.greeting` pool and supports only the `{name}` placeholder.\n * Fallback chain: `greeting[0]` → `initialMessage` → default welcome message.\n * Pool rotation and `{timeGreeting}` are planned follow-ups.\n */\nexport function useGreeting(\n agentInterface: MaybeRefOrGetter<AgentInterface | undefined>,\n userName: MaybeRefOrGetter<string | undefined>,\n): ComputedRef<string> {\n const { locale, t, te } = useI18n({ useScope: 'global' })\n\n return computed(() => {\n const ui = toValue(agentInterface)\n const template =\n ui?.greeting?.[0]?.[locale.value] ||\n ui?.initialMessage?.[locale.value] ||\n (te('message.defaultAgentWelcome')\n ? t('message.defaultAgentWelcome')\n : '')\n return resolveNamePlaceholder(template, toValue(userName))\n })\n}\n\nfunction resolveNamePlaceholder(template: string, name?: string): string {\n const result = name\n ? template.replace(/\\{name\\}/g, name)\n : // No name available: drop the placeholder along with any\n // adjacent punctuation (\", {name}!\" → \"\")\n template.replace(/[,\\s]*\\{name\\}[!?]?/g, '')\n return result.trim()\n}\n","<script setup lang=\"ts\">\n import {\n onBeforeUnmount,\n onMounted,\n onUnmounted,\n ref,\n useTemplateRef,\n } from 'vue'\n\n /**\n * Ambient animated \"aurora\" rendered with a raw WebGL fragment shader\n * (simplex noise blending two tints of the inherited `color`). No\n * dependencies; falls back to CSS blobs when WebGL is unavailable.\n * The accent comes from the CSS cascade: set `color` on the host.\n */\n\n const props = defineProps<{\n /** CSS selector (within the positioned parent) of the element the glow radiates from; defaults to the positioned parent box itself */\n sourceSelector?: string\n }>()\n\n const canvasEl = useTemplateRef<HTMLCanvasElement>('canvasEl')\n const failed = ref(false)\n\n const VERTEX_SHADER = `\nattribute vec2 a_position;\nvoid main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n}`\n\n // snoise: 2D simplex noise by Ian McEwan / Stefan Gustavson (MIT)\n const FRAGMENT_SHADER = `\nprecision mediump float;\nuniform vec2 u_resolution;\nuniform float u_time;\nuniform vec3 u_color;\nuniform vec2 u_center;\nuniform vec2 u_half;\nuniform float u_spread;\n\nvec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); }\n\nfloat snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)), 0.0);\n m = m * m;\n m = m * m;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\nvoid main() {\n vec2 px = gl_FragCoord.xy;\n vec2 rel = px - u_center;\n\n // Ignition: the ellipse lights up expanding from the center on mount\n float intro = smoothstep(0.0, 1.4, u_time);\n\n // Breathing: size and brightness oscillate gently but visibly\n float breath = 1.0\n + 0.12 * sin(u_time * 0.55)\n + 0.06 * snoise(vec2(u_time * 0.1, 3.7));\n float pulse = 1.0 + 0.2 * sin(u_time * 0.55 + 1.3);\n\n // Keep it simple (Gemini-style): one huge soft elliptical glow\n // centered on the pill\n vec2 radii = vec2(u_half.x * 0.95, u_half.y * 6.5)\n * u_spread\n * breath\n * mix(0.15, 1.0, intro);\n float r = length(rel / max(radii, vec2(1.0)));\n float glow = exp(-r * r * 2.2);\n\n // Never cut hard against the canvas bounds\n vec2 m = min(px, u_resolution - px);\n glow *= smoothstep(0.0, 32.0, min(m.x, m.y));\n\n // Dither: breaks the 8-bit banding rings of the very soft gradient\n // (no CSS blur on the canvas, the noise must fully mask the bands)\n glow = max(glow + (snoise(px * 0.9) - 0.5) * 0.03, 0.0);\n\n float alpha = glow * 0.2 * pulse * intro;\n\n // Premultiplied alpha (default canvas compositing)\n gl_FragColor = vec4(u_color * alpha, alpha);\n}`\n\n let gl: WebGLRenderingContext | null = null\n let rafId = 0\n let resizeObserver: ResizeObserver | null = null\n let timeLocation: WebGLUniformLocation | null = null\n let resolutionLocation: WebGLUniformLocation | null = null\n let centerLocation: WebGLUniformLocation | null = null\n let halfLocation: WebGLUniformLocation | null = null\n let spreadLocation: WebGLUniformLocation | null = null\n const startTime = performance.now()\n\n const compile = (type: number, source: string): WebGLShader | null => {\n if (!gl) {\n return null\n }\n const shader = gl.createShader(type)\n if (!shader) {\n return null\n }\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n return null\n }\n return shader\n }\n\n const parseInheritedColor = (): [number, number, number] | null => {\n if (!canvasEl.value) {\n return null\n }\n const raw = getComputedStyle(canvasEl.value).color\n const match = raw.match(/rgba?\\(([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/)\n if (!match) {\n return null\n }\n return [\n Number(match[1]) / 255,\n Number(match[2]) / 255,\n Number(match[3]) / 255,\n ]\n }\n\n const resize = () => {\n if (!gl || !canvasEl.value) {\n return\n }\n // The glow is blurred anyway: cap the DPR to keep the GPU cost low\n const dpr = Math.min(window.devicePixelRatio || 1, 1.5)\n const { clientWidth, clientHeight } = canvasEl.value\n canvasEl.value.width = Math.max(1, Math.round(clientWidth * dpr))\n canvasEl.value.height = Math.max(1, Math.round(clientHeight * dpr))\n gl.viewport(0, 0, canvasEl.value.width, canvasEl.value.height)\n\n // The glow radiates from the source element (by selector within the\n // positioned parent, or the parent box itself): measure it into\n // shader pixel coordinates (gl origin is bottom-left)\n const host = canvasEl.value.parentElement?.offsetParent\n const source =\n (props.sourceSelector\n ? host?.querySelector(props.sourceSelector)\n : host) ?? host\n const canvasRect = canvasEl.value.getBoundingClientRect()\n const sourceRect = source?.getBoundingClientRect()\n if (sourceRect && canvasRect.width > 0) {\n const scaleX = canvasEl.value.width / canvasRect.width\n const scaleY = canvasEl.value.height / canvasRect.height\n const centerX =\n (sourceRect.left + sourceRect.width / 2 - canvasRect.left) *\n scaleX\n const centerY =\n (canvasRect.bottom - (sourceRect.top + sourceRect.height / 2)) *\n scaleY\n const halfWidth = (sourceRect.width / 2) * scaleX\n const halfHeight = (sourceRect.height / 2) * scaleY\n gl.uniform2f(centerLocation, centerX, centerY)\n gl.uniform2f(halfLocation, halfWidth, halfHeight)\n // Optional responsive boost of the glow size (e.g. mobile)\n const spread = Number.parseFloat(\n getComputedStyle(canvasEl.value).getPropertyValue(\n '--aurora-spread',\n ),\n )\n gl.uniform1f(spreadLocation, Number.isNaN(spread) ? 1 : spread)\n }\n\n // Redraw before the next paint: setting the canvas size reallocates\n // the drawing buffer, and presenting it undrawn can flash as an\n // uninitialized (white) frame. With reduced motion (no loop) this\n // also keeps the settled static frame across resizes.\n drawFrame(prefersReducedMotion() ? 10 : undefined)\n }\n\n const prefersReducedMotion = () =>\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n const drawFrame = (timeSeconds?: number) => {\n if (!gl || !canvasEl.value) {\n return\n }\n gl.uniform1f(\n timeLocation,\n timeSeconds ?? (performance.now() - startTime) / 1000,\n )\n gl.uniform2f(\n resolutionLocation,\n canvasEl.value.width,\n canvasEl.value.height,\n )\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)\n }\n\n const loop = () => {\n drawFrame()\n rafId = requestAnimationFrame(loop)\n }\n\n onMounted(() => {\n const canvas = canvasEl.value\n gl = canvas?.getContext('webgl', { alpha: true }) ?? null\n const color = parseInheritedColor()\n if (!canvas || !gl || !color) {\n failed.value = true\n return\n }\n\n const vertex = compile(gl.VERTEX_SHADER, VERTEX_SHADER)\n const fragment = compile(gl.FRAGMENT_SHADER, FRAGMENT_SHADER)\n const program = gl.createProgram()\n if (!vertex || !fragment || !program) {\n failed.value = true\n return\n }\n gl.attachShader(program, vertex)\n gl.attachShader(program, fragment)\n gl.linkProgram(program)\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n failed.value = true\n return\n }\n gl.useProgram(program)\n\n // Fullscreen quad\n const buffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),\n gl.STATIC_DRAW,\n )\n const positionLocation = gl.getAttribLocation(program, 'a_position')\n gl.enableVertexAttribArray(positionLocation)\n gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)\n\n timeLocation = gl.getUniformLocation(program, 'u_time')\n resolutionLocation = gl.getUniformLocation(program, 'u_resolution')\n centerLocation = gl.getUniformLocation(program, 'u_center')\n halfLocation = gl.getUniformLocation(program, 'u_half')\n spreadLocation = gl.getUniformLocation(program, 'u_spread')\n gl.uniform3f(gl.getUniformLocation(program, 'u_color'), ...color)\n\n resizeObserver = new ResizeObserver(resize)\n resizeObserver.observe(canvas)\n resize()\n\n if (prefersReducedMotion()) {\n // Static frame: the color stays, the motion goes\n drawFrame(10)\n return\n }\n loop()\n })\n\n onBeforeUnmount(() => {\n cancelAnimationFrame(rafId)\n resizeObserver?.disconnect()\n })\n\n // Release the context (Chrome caps live WebGL contexts per page) only\n // after the compositor committed the removal: dropping the backing\n // buffer while the last frame is still on screen paints an undefined\n // (white) frame — a visible flash on the dark theme\n onUnmounted(() => {\n const context = gl\n gl = null\n setTimeout(() => {\n context?.getExtension('WEBGL_lose_context')?.loseContext()\n }, 150)\n })\n</script>\n\n<template>\n <div class=\"pk-aurora\" aria-hidden=\"true\">\n <canvas v-if=\"!failed\" ref=\"canvasEl\" class=\"pk-aurora__canvas\" />\n <div v-else class=\"pk-aurora__fallback\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-aurora {\n position: absolute;\n inset: 0;\n // The accent resolved by the canvas (and the CSS fallback)\n color: var(--chat-page-accent, var(--color-brand));\n pointer-events: none;\n\n &__canvas {\n width: 100%;\n height: 100%;\n // No CSS filter on purpose: a filtered accelerated canvas gets\n // its own compositor surface, which Chrome can present\n // uninitialized (a white flash) when the layer is created or\n // torn down. The glow is smoothed in the shader instead.\n }\n\n // CSS blobs when WebGL is unavailable\n &__fallback {\n position: absolute;\n inset: 0;\n background:\n radial-gradient(\n 45% 65% at 32% 38%,\n color-mix(in srgb, currentColor 26%, transparent),\n transparent 70%\n ),\n radial-gradient(\n 40% 55% at 68% 62%,\n color-mix(in srgb, currentColor 16%, transparent),\n transparent 70%\n );\n filter: blur(48px);\n animation: pk-aurora-drift 14s var(--ease-in-out) infinite alternate;\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n }\n }\n\n @keyframes pk-aurora-drift {\n from {\n transform: rotate(-10deg) scale(1);\n }\n\n to {\n transform: rotate(10deg) scale(1.2);\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import {\n onBeforeUnmount,\n onMounted,\n onUnmounted,\n ref,\n useTemplateRef,\n } from 'vue'\n\n /**\n * Ambient animated \"aurora\" rendered with a raw WebGL fragment shader\n * (simplex noise blending two tints of the inherited `color`). No\n * dependencies; falls back to CSS blobs when WebGL is unavailable.\n * The accent comes from the CSS cascade: set `color` on the host.\n */\n\n const props = defineProps<{\n /** CSS selector (within the positioned parent) of the element the glow radiates from; defaults to the positioned parent box itself */\n sourceSelector?: string\n }>()\n\n const canvasEl = useTemplateRef<HTMLCanvasElement>('canvasEl')\n const failed = ref(false)\n\n const VERTEX_SHADER = `\nattribute vec2 a_position;\nvoid main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n}`\n\n // snoise: 2D simplex noise by Ian McEwan / Stefan Gustavson (MIT)\n const FRAGMENT_SHADER = `\nprecision mediump float;\nuniform vec2 u_resolution;\nuniform float u_time;\nuniform vec3 u_color;\nuniform vec2 u_center;\nuniform vec2 u_half;\nuniform float u_spread;\n\nvec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); }\n\nfloat snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)), 0.0);\n m = m * m;\n m = m * m;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\nvoid main() {\n vec2 px = gl_FragCoord.xy;\n vec2 rel = px - u_center;\n\n // Ignition: the ellipse lights up expanding from the center on mount\n float intro = smoothstep(0.0, 1.4, u_time);\n\n // Breathing: size and brightness oscillate gently but visibly\n float breath = 1.0\n + 0.12 * sin(u_time * 0.55)\n + 0.06 * snoise(vec2(u_time * 0.1, 3.7));\n float pulse = 1.0 + 0.2 * sin(u_time * 0.55 + 1.3);\n\n // Keep it simple (Gemini-style): one huge soft elliptical glow\n // centered on the pill\n vec2 radii = vec2(u_half.x * 0.95, u_half.y * 6.5)\n * u_spread\n * breath\n * mix(0.15, 1.0, intro);\n float r = length(rel / max(radii, vec2(1.0)));\n float glow = exp(-r * r * 2.2);\n\n // Never cut hard against the canvas bounds\n vec2 m = min(px, u_resolution - px);\n glow *= smoothstep(0.0, 32.0, min(m.x, m.y));\n\n // Dither: breaks the 8-bit banding rings of the very soft gradient\n // (no CSS blur on the canvas, the noise must fully mask the bands)\n glow = max(glow + (snoise(px * 0.9) - 0.5) * 0.03, 0.0);\n\n float alpha = glow * 0.2 * pulse * intro;\n\n // Premultiplied alpha (default canvas compositing)\n gl_FragColor = vec4(u_color * alpha, alpha);\n}`\n\n let gl: WebGLRenderingContext | null = null\n let rafId = 0\n let resizeObserver: ResizeObserver | null = null\n let timeLocation: WebGLUniformLocation | null = null\n let resolutionLocation: WebGLUniformLocation | null = null\n let centerLocation: WebGLUniformLocation | null = null\n let halfLocation: WebGLUniformLocation | null = null\n let spreadLocation: WebGLUniformLocation | null = null\n const startTime = performance.now()\n\n const compile = (type: number, source: string): WebGLShader | null => {\n if (!gl) {\n return null\n }\n const shader = gl.createShader(type)\n if (!shader) {\n return null\n }\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n return null\n }\n return shader\n }\n\n const parseInheritedColor = (): [number, number, number] | null => {\n if (!canvasEl.value) {\n return null\n }\n const raw = getComputedStyle(canvasEl.value).color\n const match = raw.match(/rgba?\\(([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/)\n if (!match) {\n return null\n }\n return [\n Number(match[1]) / 255,\n Number(match[2]) / 255,\n Number(match[3]) / 255,\n ]\n }\n\n const resize = () => {\n if (!gl || !canvasEl.value) {\n return\n }\n // The glow is blurred anyway: cap the DPR to keep the GPU cost low\n const dpr = Math.min(window.devicePixelRatio || 1, 1.5)\n const { clientWidth, clientHeight } = canvasEl.value\n canvasEl.value.width = Math.max(1, Math.round(clientWidth * dpr))\n canvasEl.value.height = Math.max(1, Math.round(clientHeight * dpr))\n gl.viewport(0, 0, canvasEl.value.width, canvasEl.value.height)\n\n // The glow radiates from the source element (by selector within the\n // positioned parent, or the parent box itself): measure it into\n // shader pixel coordinates (gl origin is bottom-left)\n const host = canvasEl.value.parentElement?.offsetParent\n const source =\n (props.sourceSelector\n ? host?.querySelector(props.sourceSelector)\n : host) ?? host\n const canvasRect = canvasEl.value.getBoundingClientRect()\n const sourceRect = source?.getBoundingClientRect()\n if (sourceRect && canvasRect.width > 0) {\n const scaleX = canvasEl.value.width / canvasRect.width\n const scaleY = canvasEl.value.height / canvasRect.height\n const centerX =\n (sourceRect.left + sourceRect.width / 2 - canvasRect.left) *\n scaleX\n const centerY =\n (canvasRect.bottom - (sourceRect.top + sourceRect.height / 2)) *\n scaleY\n const halfWidth = (sourceRect.width / 2) * scaleX\n const halfHeight = (sourceRect.height / 2) * scaleY\n gl.uniform2f(centerLocation, centerX, centerY)\n gl.uniform2f(halfLocation, halfWidth, halfHeight)\n // Optional responsive boost of the glow size (e.g. mobile)\n const spread = Number.parseFloat(\n getComputedStyle(canvasEl.value).getPropertyValue(\n '--aurora-spread',\n ),\n )\n gl.uniform1f(spreadLocation, Number.isNaN(spread) ? 1 : spread)\n }\n\n // Redraw before the next paint: setting the canvas size reallocates\n // the drawing buffer, and presenting it undrawn can flash as an\n // uninitialized (white) frame. With reduced motion (no loop) this\n // also keeps the settled static frame across resizes.\n drawFrame(prefersReducedMotion() ? 10 : undefined)\n }\n\n const prefersReducedMotion = () =>\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n const drawFrame = (timeSeconds?: number) => {\n if (!gl || !canvasEl.value) {\n return\n }\n gl.uniform1f(\n timeLocation,\n timeSeconds ?? (performance.now() - startTime) / 1000,\n )\n gl.uniform2f(\n resolutionLocation,\n canvasEl.value.width,\n canvasEl.value.height,\n )\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)\n }\n\n const loop = () => {\n drawFrame()\n rafId = requestAnimationFrame(loop)\n }\n\n onMounted(() => {\n const canvas = canvasEl.value\n gl = canvas?.getContext('webgl', { alpha: true }) ?? null\n const color = parseInheritedColor()\n if (!canvas || !gl || !color) {\n failed.value = true\n return\n }\n\n const vertex = compile(gl.VERTEX_SHADER, VERTEX_SHADER)\n const fragment = compile(gl.FRAGMENT_SHADER, FRAGMENT_SHADER)\n const program = gl.createProgram()\n if (!vertex || !fragment || !program) {\n failed.value = true\n return\n }\n gl.attachShader(program, vertex)\n gl.attachShader(program, fragment)\n gl.linkProgram(program)\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n failed.value = true\n return\n }\n gl.useProgram(program)\n\n // Fullscreen quad\n const buffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),\n gl.STATIC_DRAW,\n )\n const positionLocation = gl.getAttribLocation(program, 'a_position')\n gl.enableVertexAttribArray(positionLocation)\n gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)\n\n timeLocation = gl.getUniformLocation(program, 'u_time')\n resolutionLocation = gl.getUniformLocation(program, 'u_resolution')\n centerLocation = gl.getUniformLocation(program, 'u_center')\n halfLocation = gl.getUniformLocation(program, 'u_half')\n spreadLocation = gl.getUniformLocation(program, 'u_spread')\n gl.uniform3f(gl.getUniformLocation(program, 'u_color'), ...color)\n\n resizeObserver = new ResizeObserver(resize)\n resizeObserver.observe(canvas)\n resize()\n\n if (prefersReducedMotion()) {\n // Static frame: the color stays, the motion goes\n drawFrame(10)\n return\n }\n loop()\n })\n\n onBeforeUnmount(() => {\n cancelAnimationFrame(rafId)\n resizeObserver?.disconnect()\n })\n\n // Release the context (Chrome caps live WebGL contexts per page) only\n // after the compositor committed the removal: dropping the backing\n // buffer while the last frame is still on screen paints an undefined\n // (white) frame — a visible flash on the dark theme\n onUnmounted(() => {\n const context = gl\n gl = null\n setTimeout(() => {\n context?.getExtension('WEBGL_lose_context')?.loseContext()\n }, 150)\n })\n</script>\n\n<template>\n <div class=\"pk-aurora\" aria-hidden=\"true\">\n <canvas v-if=\"!failed\" ref=\"canvasEl\" class=\"pk-aurora__canvas\" />\n <div v-else class=\"pk-aurora__fallback\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-aurora {\n position: absolute;\n inset: 0;\n // The accent resolved by the canvas (and the CSS fallback)\n color: var(--chat-page-accent, var(--color-brand));\n pointer-events: none;\n\n &__canvas {\n width: 100%;\n height: 100%;\n // No CSS filter on purpose: a filtered accelerated canvas gets\n // its own compositor surface, which Chrome can present\n // uninitialized (a white flash) when the layer is created or\n // torn down. The glow is smoothed in the shader instead.\n }\n\n // CSS blobs when WebGL is unavailable\n &__fallback {\n position: absolute;\n inset: 0;\n background:\n radial-gradient(\n 45% 65% at 32% 38%,\n color-mix(in srgb, currentColor 26%, transparent),\n transparent 70%\n ),\n radial-gradient(\n 40% 55% at 68% 62%,\n color-mix(in srgb, currentColor 16%, transparent),\n transparent 70%\n );\n filter: blur(48px);\n animation: pk-aurora-drift 14s var(--ease-in-out) infinite alternate;\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n }\n }\n\n @keyframes pk-aurora-drift {\n from {\n transform: rotate(-10deg) scale(1);\n }\n\n to {\n transform: rotate(10deg) scale(1.2);\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, useTemplateRef, defineAsyncComponent } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useDropZone } from '@vueuse/core'\n import { storeToRefs } from 'pinia'\n import PkAuroraCanvas from './PkAuroraCanvas.vue'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotMessages from './PkChatbotMessages.vue'\n import PkChatbotInput from './PkChatbotInput.vue'\n import PkChatbotEmptyState from './PkChatbotEmptyState.vue'\n import {\n useChatbotStore,\n useLocalizedString,\n useGreeting,\n } from 'composables'\n import type { UIChatMessage } from 'models'\n\n const PkToolShowForm = defineAsyncComponent(\n () => import('./PkToolShowForm.vue'),\n )\n const PkToolShowContactForm = defineAsyncComponent(\n () => import('./PkToolShowContactForm.vue'),\n )\n const PkToolShowSuggestedReply = defineAsyncComponent(\n () => import('./PkToolShowSuggestedReply.vue'),\n )\n const PkToolShowSources = defineAsyncComponent(\n () => import('./PkToolShowSources.vue'),\n )\n const PkToolRequestGeolocation = defineAsyncComponent(\n () => import('./PkToolRequestGeolocation.vue'),\n )\n const PkToolRequestOAuthConnection = defineAsyncComponent(\n () => import('./PkToolRequestOAuthConnection.vue'),\n )\n const PkToolShowLocation = defineAsyncComponent(\n () => import('./PkToolShowLocation.vue'),\n )\n const PkToolShowDiagram = defineAsyncComponent(\n () => import('./PkToolShowDiagram.vue'),\n )\n\n const props = defineProps<{\n agentId: string\n /** Mirrors the PkChatbot `modifier` prop: `fullscreen` shows a centered empty state (greeting) instead of the welcome message */\n modifier?: 'widget' | 'fullscreen'\n /** Display name of the current user, used for the `{name}` greeting placeholder */\n userName?: string\n }>()\n\n const emit = defineEmits<{\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n }>()\n\n const store = useChatbotStore(props.agentId)\n\n const {\n agentInterface,\n agentFileUpload,\n actions,\n revisedAnswers,\n messages,\n chat,\n messageFeedbacks,\n feedbackDialogMessage,\n isFeedbackSubmitting,\n isFeedbackSubmitted,\n feedbackSubmitError,\n isLeadSubmitted,\n isLoadingSubmitLead,\n submitLeadError,\n input,\n inputMessagePlaceholder,\n isConversationBlocked,\n baseUrl,\n pendingAttachments,\n isAttachmentLimitReached,\n fileLimitError,\n apiClient,\n isDark,\n } = storeToRefs(store)\n\n const {\n handleSubmit: storeHandleSubmit,\n stopGeneration,\n regenerate,\n onUpvote,\n onDownvote,\n onFeedback,\n onFeedbackSubmit,\n onLeadSubmit,\n startNewChat,\n addToolOutput,\n addToolApprovalResponse,\n handleFileSelect,\n clearFileLimitError,\n } = store\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const attachmentNotice = computed(() => {\n const err = fileLimitError.value\n if (!err) {\n return undefined\n }\n return err.reason === 'per-chat'\n ? $t('message.fileLimitPerChat', { max: err.max })\n : $t('message.fileLimitPerMessage', { max: err.max })\n })\n\n const dismissableNotice = useLocalizedString(\n () => agentInterface.value?.dismissableNotice,\n )\n\n // Fullscreen empty state: shown until the user sends the first message.\n // The synthetic welcome message is suppressed by the store in fullscreen\n // (`hideWelcomeMessage`), the greeting takes its place.\n const hasUserMessages = computed(() =>\n messages.value.some((message) => message.role === 'user'),\n )\n const showEmptyState = computed(\n () => props.modifier === 'fullscreen' && !hasUserMessages.value,\n )\n const greeting = useGreeting(agentInterface, () => props.userName)\n\n const chatViewEl = useTemplateRef<HTMLDivElement>('chatViewEl')\n\n const handleExpandSourceContext = async (payload: {\n documentId: string\n chunkIndex: number\n }) => {\n const result = await apiClient.value.expandSourceContext(\n props.agentId,\n payload.documentId,\n payload.chunkIndex,\n )\n return result.content\n }\n\n const handleDownloadSource = async (documentId: string) => {\n const result = await apiClient.value.downloadSourceDocument(\n props.agentId,\n documentId,\n )\n window.open(result.downloadUrl, '_blank')\n }\n\n const handleFileDrop = (files: File[] | null) => {\n if (!agentFileUpload.value?.enabled || !files) {\n return\n }\n for (const file of files) {\n handleFileSelect(file)\n }\n }\n\n const { isOverDropZone } = useDropZone(chatViewEl, {\n dataTypes: computed(\n () => agentFileUpload.value?.allowedMimeTypes ?? [],\n ),\n onDrop: handleFileDrop,\n })\n</script>\n\n<template>\n <div\n ref=\"chatViewEl\"\n class=\"pk-chatbot-view-chat\"\n :class=\"{\n 'pk-chatbot-view-chat--dragover':\n isOverDropZone && agentFileUpload?.enabled,\n 'pk-chatbot-view-chat--empty': showEmptyState,\n }\">\n <!-- #region empty state (fullscreen) / messages — crossfade out-in -->\n <Transition name=\"pk-chatbot-view-chat-fade\" mode=\"out-in\">\n <PkChatbotEmptyState\n v-if=\"showEmptyState\"\n :greeting=\"greeting\"\n :logo=\"agentInterface?.logo\"\n :name=\"store.name\" />\n <PkChatbotMessages\n v-else\n class=\"flex flex-col flex-1 min-h-0 p-md overflow-y-auto\"\n interactive\n :messages=\"messages\"\n :status=\"chat.status\"\n :error=\"chat.error\"\n :main-color=\"agentInterface?.mainColor\"\n :text-color=\"agentInterface?.textColor\"\n :revised-answers=\"revisedAnswers\"\n :actions=\"actions\"\n :message-feedbacks=\"messageFeedbacks\"\n :feedback-message-id=\"feedbackDialogMessage?.id\"\n :feedback-loading=\"isFeedbackSubmitting\"\n :feedback-submitted=\"isFeedbackSubmitted\"\n :feedback-error=\"feedbackSubmitError\"\n :show-extended-steps=\"agentInterface?.showExtendedSteps\"\n :is-dark=\"isDark\"\n :show-scroll-to-bottom=\"modifier === 'fullscreen'\"\n @feedback-submit=\"onFeedbackSubmit($event)\"\n @feedback-close=\"feedbackDialogMessage = undefined\"\n @regenerate=\"regenerate\"\n @auto-retry=\"regenerate\"\n @reset-chat=\"startNewChat\"\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\"\n @upvote=\"onUpvote\"\n @downvote=\"onDownvote\"\n @feedback=\"onFeedback\"\n @approval-respond=\"addToolApprovalResponse\"\n @confirm-respond=\"\n ({ toolCallId, confirmed }) =>\n addToolOutput({\n tool: 'requestConfirm',\n toolCallId,\n output: confirmed ? 'confirmed' : 'cancelled',\n })\n \">\n <template #tool-showContactForm=\"{ part }\">\n <PkToolShowContactForm\n :part\n :readonly=\"!baseUrl\"\n :submitted=\"isLeadSubmitted\"\n :loading=\"isLoadingSubmitLead\"\n :error=\"submitLeadError\"\n :privacy-policy-notice=\"\n agentInterface?.privacyPolicyNotice\n \"\n @submit=\"onLeadSubmit\" />\n </template>\n <template #tool-showSuggestedReply=\"{ part }\">\n <PkToolShowSuggestedReply\n :part\n @select=\"\n ($event) => {\n input = $event\n storeHandleSubmit()\n }\n \" />\n </template>\n <template #tool-showSources=\"{ part }\">\n <PkToolShowSources\n :part\n :on-expand-context=\"handleExpandSourceContext\"\n :on-download=\"handleDownloadSource\" />\n </template>\n <template #tool-showForm=\"{ part }\">\n <transition mode=\"out-in\">\n <PkToolShowForm\n :part\n @select=\"\n addToolOutput({\n tool: 'showForm',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </transition>\n </template>\n <template #tool-showMultipleChoice=\"{ part }\">\n <transition mode=\"out-in\">\n <PkToolShowForm\n :part\n allow-custom-answer\n @select=\"\n addToolOutput({\n tool: 'showMultipleChoice',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </transition>\n </template>\n <template #tool-requestOAuthConnection=\"{ part }\">\n <PkToolRequestOAuthConnection\n :part\n :resolve-connection=\"\n (serverName: string) =>\n apiClient.getOAuthAuthorizeUrl(\n props.agentId,\n serverName,\n )\n \"\n @connected=\"\n addToolOutput({\n tool: 'requestOAuthConnection',\n toolCallId: (part as any).toolCallId,\n output: {\n connected: true,\n mcpServerId: $event,\n },\n })\n \" />\n </template>\n <template #tool-requestGeolocation=\"{ part }\">\n <PkToolRequestGeolocation\n :part\n :reverse-geocode=\"\n (lat: number, lon: number) =>\n apiClient.reverseGeocode(lat, lon)\n \"\n @result=\"\n addToolOutput({\n tool: 'requestGeolocation',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </template>\n <template #tool-showLocation=\"{ part }\">\n <PkToolShowLocation\n :part\n :is-dark\n :main-color=\"agentInterface?.mainColor\"\n :forward-geocode=\"\n (query: string, lang?: string) =>\n apiClient.forwardGeocode(query, lang)\n \" />\n </template>\n <template #tool-showDiagram=\"{ part }\">\n <PkToolShowDiagram :part :is-dark />\n </template>\n </PkChatbotMessages>\n </Transition>\n <!-- #endregion -->\n\n <!-- #region input -->\n <div class=\"pk-chatbot-view-chat__input-stage\">\n <!-- Animated accent glow radiating from the input pill -->\n <Transition name=\"pk-chatbot-view-chat-fade\">\n <PkAuroraCanvas\n v-if=\"showEmptyState\"\n class=\"pk-chatbot-view-chat__aurora\"\n source-selector=\".pk-chatbot-input__form\" />\n </Transition>\n <div\n v-if=\"isConversationBlocked\"\n class=\"p-md border-t border-surface-3 text-center text-12 text-danger-darken-2 bg-surface-danger\">\n {{ $t('message.chatErrorConversationBlocked') }}\n </div>\n <PkChatbotInput\n v-else\n v-model=\"input\"\n v-model:pending-attachments=\"pendingAttachments\"\n :placeholder=\"inputMessagePlaceholder\"\n :dismissable-notice=\"\n dismissableNotice && chat.messages.length <= 1\n ? dismissableNotice\n : undefined\n \"\n :status=\"chat.status\"\n :max-message-length=\"agentInterface?.maxMessageLength\"\n :file-upload=\"agentFileUpload\"\n :attachment-limit-reached=\"isAttachmentLimitReached\"\n :attachment-notice=\"attachmentNotice\"\n @stop-generation=\"stopGeneration\"\n @submit=\"storeHandleSubmit\"\n @file-select=\"handleFileSelect\"\n @dismiss-attachment-notice=\"clearFileLimitError\">\n <!-- Fullscreen has no header: the agent identity lives in the\n input pill -->\n <template v-if=\"modifier === 'fullscreen'\" #prepend>\n <span class=\"pk-chatbot-view-chat__agent-chip\">\n <PkAvatar\n modifiers=\"surface\"\n class=\"pk-chatbot-view-chat__agent-chip-avatar\"\n :img-src=\"agentInterface?.logo\"\n :name=\"store.name\" />\n <strong\n v-if=\"store.name\"\n class=\"pk-chatbot-view-chat__agent-chip-name\">\n {{ store.name }}\n </strong>\n </span>\n </template>\n </PkChatbotInput>\n </div>\n <!-- #endregion -->\n <Transition>\n <div\n v-if=\"isOverDropZone && agentFileUpload?.enabled\"\n class=\"pk-chatbot-view-chat__drop-overlay\">\n <VvIcon\n name=\"ri:upload-cloud-2-line\"\n class=\"pk-chatbot-view-chat__drop-overlay-icon\" />\n <span>{{ $t('action.dropFile') }}</span>\n </div>\n </Transition>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-view-chat {\n position: relative;\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n\n // Empty state (fullscreen): center the greeting + input block\n // vertically in the available space\n &--empty {\n justify-content: center;\n gap: var(--spacing-md);\n\n // On phones the input docks to the bottom edge instead\n // (Gemini-like), with the greeting centered in the space above\n @include media-breakpoint-down('xs', $breakpoints) {\n justify-content: flex-start;\n\n .pk-chatbot-empty-state {\n flex: 1;\n justify-content: center;\n }\n }\n }\n\n // Anchors the glow to the input pill\n &__input-stage {\n position: relative;\n }\n\n // Radiates outwards from the pill, biased upwards (the pill itself\n // paints above, being later in the DOM)\n &__input-stage &__aurora {\n inset: calc(-1 * var(--spacing-224)) calc(-1 * var(--spacing-96))\n calc(-1 * var(--spacing-160));\n\n // With the input docked at the bottom the glow becomes a wide\n // dome filling the lower part of the screen (Gemini-like)\n @include media-breakpoint-down('xs', $breakpoints) {\n --aurora-spread: 1.6;\n\n // Tall enough that the glow tail never meets the canvas\n // edge fade (it would draw a visible horizontal band)\n inset: calc(-1 * var(--spacing-384) - var(--spacing-128))\n calc(-1 * var(--spacing-96)) calc(-1 * var(--spacing-160));\n }\n }\n\n // Agent identity inside the input pill (fullscreen has no header)\n &__agent-chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-6);\n flex-shrink: 0;\n padding: var(--spacing-4) var(--spacing-10) var(--spacing-4)\n var(--spacing-4);\n border: 1px solid var(--color-surface-3);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface);\n\n // Don't crowd the textarea on small screens\n @include media-breakpoint-down('md', $breakpoints) {\n display: none;\n }\n }\n\n &__agent-chip-avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n font-size: var(--text-10);\n border-radius: var(--rounded-full);\n }\n\n &__agent-chip-name {\n font-size: var(--text-12);\n font-weight: var(--font-semibold);\n color: var(--color-word-2);\n white-space: nowrap;\n }\n\n // Crossfade empty state ↔ messages: exit faster than enter so the\n // switch never feels like a snap\n &-fade-enter-active {\n transition: opacity 250ms var(--ease-out);\n }\n\n &-fade-leave-active {\n transition: opacity 150ms var(--ease-out);\n }\n\n &-fade-enter-from,\n &-fade-leave-to {\n opacity: 0;\n }\n\n @media (prefers-reduced-motion: reduce) {\n &-fade-enter-active,\n &-fade-leave-active {\n transition: none;\n }\n }\n\n &__drop-overlay {\n position: absolute;\n inset: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm)\n var(--spacing-sm);\n z-index: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-xs);\n background-color: color-mix(\n in srgb,\n var(--color-surface) 85%,\n transparent\n );\n border-radius: var(--rounded-xl);\n border: var(--spacing-2) dashed var(--color-surface-5);\n pointer-events: none;\n color: var(--color-word-3);\n\n &-icon {\n font-size: var(--spacing-32);\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, useTemplateRef, defineAsyncComponent } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useDropZone } from '@vueuse/core'\n import { storeToRefs } from 'pinia'\n import PkAuroraCanvas from './PkAuroraCanvas.vue'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotMessages from './PkChatbotMessages.vue'\n import PkChatbotInput from './PkChatbotInput.vue'\n import PkChatbotEmptyState from './PkChatbotEmptyState.vue'\n import {\n useChatbotStore,\n useLocalizedString,\n useGreeting,\n } from 'composables'\n import type { UIChatMessage } from 'models'\n\n const PkToolShowForm = defineAsyncComponent(\n () => import('./PkToolShowForm.vue'),\n )\n const PkToolShowContactForm = defineAsyncComponent(\n () => import('./PkToolShowContactForm.vue'),\n )\n const PkToolShowSuggestedReply = defineAsyncComponent(\n () => import('./PkToolShowSuggestedReply.vue'),\n )\n const PkToolShowSources = defineAsyncComponent(\n () => import('./PkToolShowSources.vue'),\n )\n const PkToolRequestGeolocation = defineAsyncComponent(\n () => import('./PkToolRequestGeolocation.vue'),\n )\n const PkToolRequestOAuthConnection = defineAsyncComponent(\n () => import('./PkToolRequestOAuthConnection.vue'),\n )\n const PkToolShowLocation = defineAsyncComponent(\n () => import('./PkToolShowLocation.vue'),\n )\n const PkToolShowDiagram = defineAsyncComponent(\n () => import('./PkToolShowDiagram.vue'),\n )\n\n const props = defineProps<{\n agentId: string\n /** Mirrors the PkChatbot `modifier` prop: `fullscreen` shows a centered empty state (greeting) instead of the welcome message */\n modifier?: 'widget' | 'fullscreen'\n /** Display name of the current user, used for the `{name}` greeting placeholder */\n userName?: string\n }>()\n\n const emit = defineEmits<{\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n }>()\n\n const store = useChatbotStore(props.agentId)\n\n const {\n agentInterface,\n agentFileUpload,\n actions,\n revisedAnswers,\n messages,\n chat,\n messageFeedbacks,\n feedbackDialogMessage,\n isFeedbackSubmitting,\n isFeedbackSubmitted,\n feedbackSubmitError,\n isLeadSubmitted,\n isLoadingSubmitLead,\n submitLeadError,\n input,\n inputMessagePlaceholder,\n isConversationBlocked,\n baseUrl,\n pendingAttachments,\n isAttachmentLimitReached,\n fileLimitError,\n apiClient,\n isDark,\n } = storeToRefs(store)\n\n const {\n handleSubmit: storeHandleSubmit,\n stopGeneration,\n regenerate,\n onUpvote,\n onDownvote,\n onFeedback,\n onFeedbackSubmit,\n onLeadSubmit,\n startNewChat,\n addToolOutput,\n addToolApprovalResponse,\n handleFileSelect,\n clearFileLimitError,\n } = store\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const attachmentNotice = computed(() => {\n const err = fileLimitError.value\n if (!err) {\n return undefined\n }\n return err.reason === 'per-chat'\n ? $t('message.fileLimitPerChat', { max: err.max })\n : $t('message.fileLimitPerMessage', { max: err.max })\n })\n\n const dismissableNotice = useLocalizedString(\n () => agentInterface.value?.dismissableNotice,\n )\n\n // Fullscreen empty state: shown until the user sends the first message.\n // The synthetic welcome message is suppressed by the store in fullscreen\n // (`hideWelcomeMessage`), the greeting takes its place.\n const hasUserMessages = computed(() =>\n messages.value.some((message) => message.role === 'user'),\n )\n const showEmptyState = computed(\n () => props.modifier === 'fullscreen' && !hasUserMessages.value,\n )\n const greeting = useGreeting(agentInterface, () => props.userName)\n\n const chatViewEl = useTemplateRef<HTMLDivElement>('chatViewEl')\n\n const handleExpandSourceContext = async (payload: {\n documentId: string\n chunkIndex: number\n }) => {\n const result = await apiClient.value.expandSourceContext(\n props.agentId,\n payload.documentId,\n payload.chunkIndex,\n )\n return result.content\n }\n\n const handleDownloadSource = async (documentId: string) => {\n const result = await apiClient.value.downloadSourceDocument(\n props.agentId,\n documentId,\n )\n window.open(result.downloadUrl, '_blank')\n }\n\n const handleFileDrop = (files: File[] | null) => {\n if (!agentFileUpload.value?.enabled || !files) {\n return\n }\n for (const file of files) {\n handleFileSelect(file)\n }\n }\n\n const { isOverDropZone } = useDropZone(chatViewEl, {\n dataTypes: computed(\n () => agentFileUpload.value?.allowedMimeTypes ?? [],\n ),\n onDrop: handleFileDrop,\n })\n</script>\n\n<template>\n <div\n ref=\"chatViewEl\"\n class=\"pk-chatbot-view-chat\"\n :class=\"{\n 'pk-chatbot-view-chat--dragover':\n isOverDropZone && agentFileUpload?.enabled,\n 'pk-chatbot-view-chat--empty': showEmptyState,\n }\">\n <!-- #region empty state (fullscreen) / messages — crossfade out-in -->\n <Transition name=\"pk-chatbot-view-chat-fade\" mode=\"out-in\">\n <PkChatbotEmptyState\n v-if=\"showEmptyState\"\n :greeting=\"greeting\"\n :logo=\"agentInterface?.logo\"\n :name=\"store.name\" />\n <PkChatbotMessages\n v-else\n class=\"flex flex-col flex-1 min-h-0 p-md overflow-y-auto\"\n interactive\n :messages=\"messages\"\n :status=\"chat.status\"\n :error=\"chat.error\"\n :main-color=\"agentInterface?.mainColor\"\n :text-color=\"agentInterface?.textColor\"\n :revised-answers=\"revisedAnswers\"\n :actions=\"actions\"\n :message-feedbacks=\"messageFeedbacks\"\n :feedback-message-id=\"feedbackDialogMessage?.id\"\n :feedback-loading=\"isFeedbackSubmitting\"\n :feedback-submitted=\"isFeedbackSubmitted\"\n :feedback-error=\"feedbackSubmitError\"\n :show-extended-steps=\"agentInterface?.showExtendedSteps\"\n :is-dark=\"isDark\"\n :show-scroll-to-bottom=\"modifier === 'fullscreen'\"\n @feedback-submit=\"onFeedbackSubmit($event)\"\n @feedback-close=\"feedbackDialogMessage = undefined\"\n @regenerate=\"regenerate\"\n @auto-retry=\"regenerate\"\n @reset-chat=\"startNewChat\"\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\"\n @upvote=\"onUpvote\"\n @downvote=\"onDownvote\"\n @feedback=\"onFeedback\"\n @approval-respond=\"addToolApprovalResponse\"\n @confirm-respond=\"\n ({ toolCallId, confirmed }) =>\n addToolOutput({\n tool: 'requestConfirm',\n toolCallId,\n output: confirmed ? 'confirmed' : 'cancelled',\n })\n \">\n <template #tool-showContactForm=\"{ part }\">\n <PkToolShowContactForm\n :part\n :readonly=\"!baseUrl\"\n :submitted=\"isLeadSubmitted\"\n :loading=\"isLoadingSubmitLead\"\n :error=\"submitLeadError\"\n :privacy-policy-notice=\"\n agentInterface?.privacyPolicyNotice\n \"\n @submit=\"onLeadSubmit\" />\n </template>\n <template #tool-showSuggestedReply=\"{ part }\">\n <PkToolShowSuggestedReply\n :part\n @select=\"\n ($event) => {\n input = $event\n storeHandleSubmit()\n }\n \" />\n </template>\n <template #tool-showSources=\"{ part }\">\n <PkToolShowSources\n :part\n :on-expand-context=\"handleExpandSourceContext\"\n :on-download=\"handleDownloadSource\" />\n </template>\n <template #tool-showForm=\"{ part }\">\n <transition mode=\"out-in\">\n <PkToolShowForm\n :part\n @select=\"\n addToolOutput({\n tool: 'showForm',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </transition>\n </template>\n <template #tool-showMultipleChoice=\"{ part }\">\n <transition mode=\"out-in\">\n <PkToolShowForm\n :part\n allow-custom-answer\n @select=\"\n addToolOutput({\n tool: 'showMultipleChoice',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </transition>\n </template>\n <template #tool-requestOAuthConnection=\"{ part }\">\n <PkToolRequestOAuthConnection\n :part\n :resolve-connection=\"\n (serverName: string) =>\n apiClient.getOAuthAuthorizeUrl(\n props.agentId,\n serverName,\n )\n \"\n @connected=\"\n addToolOutput({\n tool: 'requestOAuthConnection',\n toolCallId: (part as any).toolCallId,\n output: {\n connected: true,\n mcpServerId: $event,\n },\n })\n \" />\n </template>\n <template #tool-requestGeolocation=\"{ part }\">\n <PkToolRequestGeolocation\n :part\n :reverse-geocode=\"\n (lat: number, lon: number) =>\n apiClient.reverseGeocode(lat, lon)\n \"\n @result=\"\n addToolOutput({\n tool: 'requestGeolocation',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </template>\n <template #tool-showLocation=\"{ part }\">\n <PkToolShowLocation\n :part\n :is-dark\n :main-color=\"agentInterface?.mainColor\"\n :forward-geocode=\"\n (query: string, lang?: string) =>\n apiClient.forwardGeocode(query, lang)\n \" />\n </template>\n <template #tool-showDiagram=\"{ part }\">\n <PkToolShowDiagram :part :is-dark />\n </template>\n </PkChatbotMessages>\n </Transition>\n <!-- #endregion -->\n\n <!-- #region input -->\n <div class=\"pk-chatbot-view-chat__input-stage\">\n <!-- Animated accent glow radiating from the input pill -->\n <Transition name=\"pk-chatbot-view-chat-fade\">\n <PkAuroraCanvas\n v-if=\"showEmptyState\"\n class=\"pk-chatbot-view-chat__aurora\"\n source-selector=\".pk-chatbot-input__form\" />\n </Transition>\n <div\n v-if=\"isConversationBlocked\"\n class=\"p-md border-t border-surface-3 text-center text-12 text-danger-darken-2 bg-surface-danger\">\n {{ $t('message.chatErrorConversationBlocked') }}\n </div>\n <PkChatbotInput\n v-else\n v-model=\"input\"\n v-model:pending-attachments=\"pendingAttachments\"\n :placeholder=\"inputMessagePlaceholder\"\n :dismissable-notice=\"\n dismissableNotice && chat.messages.length <= 1\n ? dismissableNotice\n : undefined\n \"\n :status=\"chat.status\"\n :max-message-length=\"agentInterface?.maxMessageLength\"\n :file-upload=\"agentFileUpload\"\n :attachment-limit-reached=\"isAttachmentLimitReached\"\n :attachment-notice=\"attachmentNotice\"\n @stop-generation=\"stopGeneration\"\n @submit=\"storeHandleSubmit\"\n @file-select=\"handleFileSelect\"\n @dismiss-attachment-notice=\"clearFileLimitError\">\n <!-- Fullscreen has no header: the agent identity lives in the\n input pill -->\n <template v-if=\"modifier === 'fullscreen'\" #prepend>\n <span class=\"pk-chatbot-view-chat__agent-chip\">\n <PkAvatar\n modifiers=\"surface\"\n class=\"pk-chatbot-view-chat__agent-chip-avatar\"\n :img-src=\"agentInterface?.logo\"\n :name=\"store.name\" />\n <strong\n v-if=\"store.name\"\n class=\"pk-chatbot-view-chat__agent-chip-name\">\n {{ store.name }}\n </strong>\n </span>\n </template>\n </PkChatbotInput>\n </div>\n <!-- #endregion -->\n <Transition>\n <div\n v-if=\"isOverDropZone && agentFileUpload?.enabled\"\n class=\"pk-chatbot-view-chat__drop-overlay\">\n <VvIcon\n name=\"ri:upload-cloud-2-line\"\n class=\"pk-chatbot-view-chat__drop-overlay-icon\" />\n <span>{{ $t('action.dropFile') }}</span>\n </div>\n </Transition>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-view-chat {\n position: relative;\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n\n // Empty state (fullscreen): center the greeting + input block\n // vertically in the available space\n &--empty {\n justify-content: center;\n gap: var(--spacing-md);\n\n // On phones the input docks to the bottom edge instead\n // (Gemini-like), with the greeting centered in the space above\n @include media-breakpoint-down('xs', $breakpoints) {\n justify-content: flex-start;\n\n .pk-chatbot-empty-state {\n flex: 1;\n justify-content: center;\n }\n }\n }\n\n // Anchors the glow to the input pill\n &__input-stage {\n position: relative;\n }\n\n // Radiates outwards from the pill, biased upwards (the pill itself\n // paints above, being later in the DOM)\n &__input-stage &__aurora {\n inset: calc(-1 * var(--spacing-224)) calc(-1 * var(--spacing-96))\n calc(-1 * var(--spacing-160));\n\n // With the input docked at the bottom the glow becomes a wide\n // dome filling the lower part of the screen (Gemini-like)\n @include media-breakpoint-down('xs', $breakpoints) {\n --aurora-spread: 1.6;\n\n // Tall enough that the glow tail never meets the canvas\n // edge fade (it would draw a visible horizontal band)\n inset: calc(-1 * var(--spacing-384) - var(--spacing-128))\n calc(-1 * var(--spacing-96)) calc(-1 * var(--spacing-160));\n }\n }\n\n // Agent identity inside the input pill (fullscreen has no header)\n &__agent-chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-6);\n flex-shrink: 0;\n padding: var(--spacing-4) var(--spacing-10) var(--spacing-4)\n var(--spacing-4);\n border: 1px solid var(--color-surface-3);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface);\n\n // Don't crowd the textarea on small screens\n @include media-breakpoint-down('md', $breakpoints) {\n display: none;\n }\n }\n\n &__agent-chip-avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n font-size: var(--text-10);\n border-radius: var(--rounded-full);\n }\n\n &__agent-chip-name {\n font-size: var(--text-12);\n font-weight: var(--font-semibold);\n color: var(--color-word-2);\n white-space: nowrap;\n }\n\n // Crossfade empty state ↔ messages: exit faster than enter so the\n // switch never feels like a snap\n &-fade-enter-active {\n transition: opacity 250ms var(--ease-out);\n }\n\n &-fade-leave-active {\n transition: opacity 150ms var(--ease-out);\n }\n\n &-fade-enter-from,\n &-fade-leave-to {\n opacity: 0;\n }\n\n @media (prefers-reduced-motion: reduce) {\n &-fade-enter-active,\n &-fade-leave-active {\n transition: none;\n }\n }\n\n &__drop-overlay {\n position: absolute;\n inset: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm)\n var(--spacing-sm);\n z-index: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-xs);\n background-color: color-mix(\n in srgb,\n var(--color-surface) 85%,\n transparent\n );\n border-radius: var(--rounded-xl);\n border: var(--spacing-2) dashed var(--color-surface-5);\n pointer-events: none;\n color: var(--color-word-3);\n\n &-icon {\n font-size: var(--spacing-32);\n }\n }\n }\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PkChatbotViewChat-DwfZGp9d.js","names":[],"sources":["../../../../packages/composable/src/chatbot/useGreeting.ts","../../../../packages/components/src/chat/PkAuroraCanvas.vue","../../../../packages/components/src/chat/PkAuroraCanvas.vue","../../../../packages/components/src/chat/PkChatbotViewChat.vue","../../../../packages/components/src/chat/PkChatbotViewChat.vue"],"sourcesContent":["import { computed, toValue } from 'vue'\nimport type { ComputedRef, MaybeRefOrGetter } from 'vue'\nimport { useI18n } from 'vue-i18n'\nimport type { AgentInterface } from 'models'\n\n/**\n * Resolves the greeting shown in the fullscreen empty state.\n *\n * v1 behavior (docs/specs/public-chat-page.md): uses the first entry of the\n * `agentInterface.greeting` pool and supports only the `{name}` placeholder.\n * Fallback chain: `greeting[0]` → `initialMessage` → default welcome message.\n * Pool rotation and `{timeGreeting}` are planned follow-ups.\n */\nexport function useGreeting(\n agentInterface: MaybeRefOrGetter<AgentInterface | undefined>,\n userName: MaybeRefOrGetter<string | undefined>,\n): ComputedRef<string> {\n const { locale, t, te } = useI18n({ useScope: 'global' })\n\n return computed(() => {\n const ui = toValue(agentInterface)\n const template =\n ui?.greeting?.[0]?.[locale.value] ||\n ui?.initialMessage?.[locale.value] ||\n (te('message.defaultAgentWelcome')\n ? t('message.defaultAgentWelcome')\n : '')\n return resolveNamePlaceholder(template, toValue(userName))\n })\n}\n\nfunction resolveNamePlaceholder(template: string, name?: string): string {\n const result = name\n ? template.replace(/\\{name\\}/g, name)\n : // No name available: drop the placeholder along with any\n // adjacent punctuation (\", {name}!\" → \"\")\n template.replace(/[,\\s]*\\{name\\}[!?]?/g, '')\n return result.trim()\n}\n","<script setup lang=\"ts\">\n import {\n onBeforeUnmount,\n onMounted,\n onUnmounted,\n ref,\n useTemplateRef,\n } from 'vue'\n\n /**\n * Ambient animated \"aurora\" rendered with a raw WebGL fragment shader\n * (simplex noise blending two tints of the inherited `color`). No\n * dependencies; falls back to CSS blobs when WebGL is unavailable.\n * The accent comes from the CSS cascade: set `color` on the host.\n */\n\n const props = defineProps<{\n /** CSS selector (within the positioned parent) of the element the glow radiates from; defaults to the positioned parent box itself */\n sourceSelector?: string\n }>()\n\n const canvasEl = useTemplateRef<HTMLCanvasElement>('canvasEl')\n const failed = ref(false)\n\n const VERTEX_SHADER = `\nattribute vec2 a_position;\nvoid main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n}`\n\n // snoise: 2D simplex noise by Ian McEwan / Stefan Gustavson (MIT)\n const FRAGMENT_SHADER = `\nprecision mediump float;\nuniform vec2 u_resolution;\nuniform float u_time;\nuniform vec3 u_color;\nuniform vec2 u_center;\nuniform vec2 u_half;\nuniform float u_spread;\n\nvec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); }\n\nfloat snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)), 0.0);\n m = m * m;\n m = m * m;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\nvoid main() {\n vec2 px = gl_FragCoord.xy;\n vec2 rel = px - u_center;\n\n // Ignition: the ellipse lights up expanding from the center on mount\n float intro = smoothstep(0.0, 1.4, u_time);\n\n // Breathing: size and brightness oscillate gently but visibly\n float breath = 1.0\n + 0.12 * sin(u_time * 0.55)\n + 0.06 * snoise(vec2(u_time * 0.1, 3.7));\n float pulse = 1.0 + 0.2 * sin(u_time * 0.55 + 1.3);\n\n // Keep it simple (Gemini-style): one huge soft elliptical glow\n // centered on the pill\n vec2 radii = vec2(u_half.x * 0.95, u_half.y * 6.5)\n * u_spread\n * breath\n * mix(0.15, 1.0, intro);\n float r = length(rel / max(radii, vec2(1.0)));\n float glow = exp(-r * r * 2.2);\n\n // Never cut hard against the canvas bounds\n vec2 m = min(px, u_resolution - px);\n glow *= smoothstep(0.0, 32.0, min(m.x, m.y));\n\n // Dither: breaks the 8-bit banding rings of the very soft gradient\n // (no CSS blur on the canvas, the noise must fully mask the bands)\n glow = max(glow + (snoise(px * 0.9) - 0.5) * 0.03, 0.0);\n\n float alpha = glow * 0.2 * pulse * intro;\n\n // Premultiplied alpha (default canvas compositing)\n gl_FragColor = vec4(u_color * alpha, alpha);\n}`\n\n let gl: WebGLRenderingContext | null = null\n let rafId = 0\n let resizeObserver: ResizeObserver | null = null\n let timeLocation: WebGLUniformLocation | null = null\n let resolutionLocation: WebGLUniformLocation | null = null\n let centerLocation: WebGLUniformLocation | null = null\n let halfLocation: WebGLUniformLocation | null = null\n let spreadLocation: WebGLUniformLocation | null = null\n const startTime = performance.now()\n\n const compile = (type: number, source: string): WebGLShader | null => {\n if (!gl) {\n return null\n }\n const shader = gl.createShader(type)\n if (!shader) {\n return null\n }\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n return null\n }\n return shader\n }\n\n const parseInheritedColor = (): [number, number, number] | null => {\n if (!canvasEl.value) {\n return null\n }\n const raw = getComputedStyle(canvasEl.value).color\n const match = raw.match(/rgba?\\(([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/)\n if (!match) {\n return null\n }\n return [\n Number(match[1]) / 255,\n Number(match[2]) / 255,\n Number(match[3]) / 255,\n ]\n }\n\n const resize = () => {\n if (!gl || !canvasEl.value) {\n return\n }\n // The glow is blurred anyway: cap the DPR to keep the GPU cost low\n const dpr = Math.min(window.devicePixelRatio || 1, 1.5)\n const { clientWidth, clientHeight } = canvasEl.value\n canvasEl.value.width = Math.max(1, Math.round(clientWidth * dpr))\n canvasEl.value.height = Math.max(1, Math.round(clientHeight * dpr))\n gl.viewport(0, 0, canvasEl.value.width, canvasEl.value.height)\n\n // The glow radiates from the source element (by selector within the\n // positioned parent, or the parent box itself): measure it into\n // shader pixel coordinates (gl origin is bottom-left)\n const host = canvasEl.value.parentElement?.offsetParent\n const source =\n (props.sourceSelector\n ? host?.querySelector(props.sourceSelector)\n : host) ?? host\n const canvasRect = canvasEl.value.getBoundingClientRect()\n const sourceRect = source?.getBoundingClientRect()\n if (sourceRect && canvasRect.width > 0) {\n const scaleX = canvasEl.value.width / canvasRect.width\n const scaleY = canvasEl.value.height / canvasRect.height\n const centerX =\n (sourceRect.left + sourceRect.width / 2 - canvasRect.left) *\n scaleX\n const centerY =\n (canvasRect.bottom - (sourceRect.top + sourceRect.height / 2)) *\n scaleY\n const halfWidth = (sourceRect.width / 2) * scaleX\n const halfHeight = (sourceRect.height / 2) * scaleY\n gl.uniform2f(centerLocation, centerX, centerY)\n gl.uniform2f(halfLocation, halfWidth, halfHeight)\n // Optional responsive boost of the glow size (e.g. mobile)\n const spread = Number.parseFloat(\n getComputedStyle(canvasEl.value).getPropertyValue(\n '--aurora-spread',\n ),\n )\n gl.uniform1f(spreadLocation, Number.isNaN(spread) ? 1 : spread)\n }\n\n // Redraw before the next paint: setting the canvas size reallocates\n // the drawing buffer, and presenting it undrawn can flash as an\n // uninitialized (white) frame. With reduced motion (no loop) this\n // also keeps the settled static frame across resizes.\n drawFrame(prefersReducedMotion() ? 10 : undefined)\n }\n\n const prefersReducedMotion = () =>\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n const drawFrame = (timeSeconds?: number) => {\n if (!gl || !canvasEl.value) {\n return\n }\n gl.uniform1f(\n timeLocation,\n timeSeconds ?? (performance.now() - startTime) / 1000,\n )\n gl.uniform2f(\n resolutionLocation,\n canvasEl.value.width,\n canvasEl.value.height,\n )\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)\n }\n\n const loop = () => {\n drawFrame()\n rafId = requestAnimationFrame(loop)\n }\n\n onMounted(() => {\n const canvas = canvasEl.value\n gl = canvas?.getContext('webgl', { alpha: true }) ?? null\n const color = parseInheritedColor()\n if (!canvas || !gl || !color) {\n failed.value = true\n return\n }\n\n const vertex = compile(gl.VERTEX_SHADER, VERTEX_SHADER)\n const fragment = compile(gl.FRAGMENT_SHADER, FRAGMENT_SHADER)\n const program = gl.createProgram()\n if (!vertex || !fragment || !program) {\n failed.value = true\n return\n }\n gl.attachShader(program, vertex)\n gl.attachShader(program, fragment)\n gl.linkProgram(program)\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n failed.value = true\n return\n }\n gl.useProgram(program)\n\n // Fullscreen quad\n const buffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),\n gl.STATIC_DRAW,\n )\n const positionLocation = gl.getAttribLocation(program, 'a_position')\n gl.enableVertexAttribArray(positionLocation)\n gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)\n\n timeLocation = gl.getUniformLocation(program, 'u_time')\n resolutionLocation = gl.getUniformLocation(program, 'u_resolution')\n centerLocation = gl.getUniformLocation(program, 'u_center')\n halfLocation = gl.getUniformLocation(program, 'u_half')\n spreadLocation = gl.getUniformLocation(program, 'u_spread')\n gl.uniform3f(gl.getUniformLocation(program, 'u_color'), ...color)\n\n resizeObserver = new ResizeObserver(resize)\n resizeObserver.observe(canvas)\n resize()\n\n if (prefersReducedMotion()) {\n // Static frame: the color stays, the motion goes\n drawFrame(10)\n return\n }\n loop()\n })\n\n onBeforeUnmount(() => {\n cancelAnimationFrame(rafId)\n resizeObserver?.disconnect()\n })\n\n // Release the context (Chrome caps live WebGL contexts per page) only\n // after the compositor committed the removal: dropping the backing\n // buffer while the last frame is still on screen paints an undefined\n // (white) frame — a visible flash on the dark theme\n onUnmounted(() => {\n const context = gl\n gl = null\n setTimeout(() => {\n context?.getExtension('WEBGL_lose_context')?.loseContext()\n }, 150)\n })\n</script>\n\n<template>\n <div class=\"pk-aurora\" aria-hidden=\"true\">\n <canvas v-if=\"!failed\" ref=\"canvasEl\" class=\"pk-aurora__canvas\" />\n <div v-else class=\"pk-aurora__fallback\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-aurora {\n position: absolute;\n inset: 0;\n // The accent resolved by the canvas (and the CSS fallback)\n color: var(--chat-page-accent, var(--color-brand));\n pointer-events: none;\n\n &__canvas {\n width: 100%;\n height: 100%;\n // No CSS filter on purpose: a filtered accelerated canvas gets\n // its own compositor surface, which Chrome can present\n // uninitialized (a white flash) when the layer is created or\n // torn down. The glow is smoothed in the shader instead.\n }\n\n // CSS blobs when WebGL is unavailable\n &__fallback {\n position: absolute;\n inset: 0;\n background:\n radial-gradient(\n 45% 65% at 32% 38%,\n color-mix(in srgb, currentColor 26%, transparent),\n transparent 70%\n ),\n radial-gradient(\n 40% 55% at 68% 62%,\n color-mix(in srgb, currentColor 16%, transparent),\n transparent 70%\n );\n filter: blur(48px);\n animation: pk-aurora-drift 14s var(--ease-in-out) infinite alternate;\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n }\n }\n\n @keyframes pk-aurora-drift {\n from {\n transform: rotate(-10deg) scale(1);\n }\n\n to {\n transform: rotate(10deg) scale(1.2);\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import {\n onBeforeUnmount,\n onMounted,\n onUnmounted,\n ref,\n useTemplateRef,\n } from 'vue'\n\n /**\n * Ambient animated \"aurora\" rendered with a raw WebGL fragment shader\n * (simplex noise blending two tints of the inherited `color`). No\n * dependencies; falls back to CSS blobs when WebGL is unavailable.\n * The accent comes from the CSS cascade: set `color` on the host.\n */\n\n const props = defineProps<{\n /** CSS selector (within the positioned parent) of the element the glow radiates from; defaults to the positioned parent box itself */\n sourceSelector?: string\n }>()\n\n const canvasEl = useTemplateRef<HTMLCanvasElement>('canvasEl')\n const failed = ref(false)\n\n const VERTEX_SHADER = `\nattribute vec2 a_position;\nvoid main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n}`\n\n // snoise: 2D simplex noise by Ian McEwan / Stefan Gustavson (MIT)\n const FRAGMENT_SHADER = `\nprecision mediump float;\nuniform vec2 u_resolution;\nuniform float u_time;\nuniform vec3 u_color;\nuniform vec2 u_center;\nuniform vec2 u_half;\nuniform float u_spread;\n\nvec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); }\n\nfloat snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)), 0.0);\n m = m * m;\n m = m * m;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\nvoid main() {\n vec2 px = gl_FragCoord.xy;\n vec2 rel = px - u_center;\n\n // Ignition: the ellipse lights up expanding from the center on mount\n float intro = smoothstep(0.0, 1.4, u_time);\n\n // Breathing: size and brightness oscillate gently but visibly\n float breath = 1.0\n + 0.12 * sin(u_time * 0.55)\n + 0.06 * snoise(vec2(u_time * 0.1, 3.7));\n float pulse = 1.0 + 0.2 * sin(u_time * 0.55 + 1.3);\n\n // Keep it simple (Gemini-style): one huge soft elliptical glow\n // centered on the pill\n vec2 radii = vec2(u_half.x * 0.95, u_half.y * 6.5)\n * u_spread\n * breath\n * mix(0.15, 1.0, intro);\n float r = length(rel / max(radii, vec2(1.0)));\n float glow = exp(-r * r * 2.2);\n\n // Never cut hard against the canvas bounds\n vec2 m = min(px, u_resolution - px);\n glow *= smoothstep(0.0, 32.0, min(m.x, m.y));\n\n // Dither: breaks the 8-bit banding rings of the very soft gradient\n // (no CSS blur on the canvas, the noise must fully mask the bands)\n glow = max(glow + (snoise(px * 0.9) - 0.5) * 0.03, 0.0);\n\n float alpha = glow * 0.2 * pulse * intro;\n\n // Premultiplied alpha (default canvas compositing)\n gl_FragColor = vec4(u_color * alpha, alpha);\n}`\n\n let gl: WebGLRenderingContext | null = null\n let rafId = 0\n let resizeObserver: ResizeObserver | null = null\n let timeLocation: WebGLUniformLocation | null = null\n let resolutionLocation: WebGLUniformLocation | null = null\n let centerLocation: WebGLUniformLocation | null = null\n let halfLocation: WebGLUniformLocation | null = null\n let spreadLocation: WebGLUniformLocation | null = null\n const startTime = performance.now()\n\n const compile = (type: number, source: string): WebGLShader | null => {\n if (!gl) {\n return null\n }\n const shader = gl.createShader(type)\n if (!shader) {\n return null\n }\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n return null\n }\n return shader\n }\n\n const parseInheritedColor = (): [number, number, number] | null => {\n if (!canvasEl.value) {\n return null\n }\n const raw = getComputedStyle(canvasEl.value).color\n const match = raw.match(/rgba?\\(([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/)\n if (!match) {\n return null\n }\n return [\n Number(match[1]) / 255,\n Number(match[2]) / 255,\n Number(match[3]) / 255,\n ]\n }\n\n const resize = () => {\n if (!gl || !canvasEl.value) {\n return\n }\n // The glow is blurred anyway: cap the DPR to keep the GPU cost low\n const dpr = Math.min(window.devicePixelRatio || 1, 1.5)\n const { clientWidth, clientHeight } = canvasEl.value\n canvasEl.value.width = Math.max(1, Math.round(clientWidth * dpr))\n canvasEl.value.height = Math.max(1, Math.round(clientHeight * dpr))\n gl.viewport(0, 0, canvasEl.value.width, canvasEl.value.height)\n\n // The glow radiates from the source element (by selector within the\n // positioned parent, or the parent box itself): measure it into\n // shader pixel coordinates (gl origin is bottom-left)\n const host = canvasEl.value.parentElement?.offsetParent\n const source =\n (props.sourceSelector\n ? host?.querySelector(props.sourceSelector)\n : host) ?? host\n const canvasRect = canvasEl.value.getBoundingClientRect()\n const sourceRect = source?.getBoundingClientRect()\n if (sourceRect && canvasRect.width > 0) {\n const scaleX = canvasEl.value.width / canvasRect.width\n const scaleY = canvasEl.value.height / canvasRect.height\n const centerX =\n (sourceRect.left + sourceRect.width / 2 - canvasRect.left) *\n scaleX\n const centerY =\n (canvasRect.bottom - (sourceRect.top + sourceRect.height / 2)) *\n scaleY\n const halfWidth = (sourceRect.width / 2) * scaleX\n const halfHeight = (sourceRect.height / 2) * scaleY\n gl.uniform2f(centerLocation, centerX, centerY)\n gl.uniform2f(halfLocation, halfWidth, halfHeight)\n // Optional responsive boost of the glow size (e.g. mobile)\n const spread = Number.parseFloat(\n getComputedStyle(canvasEl.value).getPropertyValue(\n '--aurora-spread',\n ),\n )\n gl.uniform1f(spreadLocation, Number.isNaN(spread) ? 1 : spread)\n }\n\n // Redraw before the next paint: setting the canvas size reallocates\n // the drawing buffer, and presenting it undrawn can flash as an\n // uninitialized (white) frame. With reduced motion (no loop) this\n // also keeps the settled static frame across resizes.\n drawFrame(prefersReducedMotion() ? 10 : undefined)\n }\n\n const prefersReducedMotion = () =>\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n const drawFrame = (timeSeconds?: number) => {\n if (!gl || !canvasEl.value) {\n return\n }\n gl.uniform1f(\n timeLocation,\n timeSeconds ?? (performance.now() - startTime) / 1000,\n )\n gl.uniform2f(\n resolutionLocation,\n canvasEl.value.width,\n canvasEl.value.height,\n )\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)\n }\n\n const loop = () => {\n drawFrame()\n rafId = requestAnimationFrame(loop)\n }\n\n onMounted(() => {\n const canvas = canvasEl.value\n gl = canvas?.getContext('webgl', { alpha: true }) ?? null\n const color = parseInheritedColor()\n if (!canvas || !gl || !color) {\n failed.value = true\n return\n }\n\n const vertex = compile(gl.VERTEX_SHADER, VERTEX_SHADER)\n const fragment = compile(gl.FRAGMENT_SHADER, FRAGMENT_SHADER)\n const program = gl.createProgram()\n if (!vertex || !fragment || !program) {\n failed.value = true\n return\n }\n gl.attachShader(program, vertex)\n gl.attachShader(program, fragment)\n gl.linkProgram(program)\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n failed.value = true\n return\n }\n gl.useProgram(program)\n\n // Fullscreen quad\n const buffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),\n gl.STATIC_DRAW,\n )\n const positionLocation = gl.getAttribLocation(program, 'a_position')\n gl.enableVertexAttribArray(positionLocation)\n gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)\n\n timeLocation = gl.getUniformLocation(program, 'u_time')\n resolutionLocation = gl.getUniformLocation(program, 'u_resolution')\n centerLocation = gl.getUniformLocation(program, 'u_center')\n halfLocation = gl.getUniformLocation(program, 'u_half')\n spreadLocation = gl.getUniformLocation(program, 'u_spread')\n gl.uniform3f(gl.getUniformLocation(program, 'u_color'), ...color)\n\n resizeObserver = new ResizeObserver(resize)\n resizeObserver.observe(canvas)\n resize()\n\n if (prefersReducedMotion()) {\n // Static frame: the color stays, the motion goes\n drawFrame(10)\n return\n }\n loop()\n })\n\n onBeforeUnmount(() => {\n cancelAnimationFrame(rafId)\n resizeObserver?.disconnect()\n })\n\n // Release the context (Chrome caps live WebGL contexts per page) only\n // after the compositor committed the removal: dropping the backing\n // buffer while the last frame is still on screen paints an undefined\n // (white) frame — a visible flash on the dark theme\n onUnmounted(() => {\n const context = gl\n gl = null\n setTimeout(() => {\n context?.getExtension('WEBGL_lose_context')?.loseContext()\n }, 150)\n })\n</script>\n\n<template>\n <div class=\"pk-aurora\" aria-hidden=\"true\">\n <canvas v-if=\"!failed\" ref=\"canvasEl\" class=\"pk-aurora__canvas\" />\n <div v-else class=\"pk-aurora__fallback\" />\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-aurora {\n position: absolute;\n inset: 0;\n // The accent resolved by the canvas (and the CSS fallback)\n color: var(--chat-page-accent, var(--color-brand));\n pointer-events: none;\n\n &__canvas {\n width: 100%;\n height: 100%;\n // No CSS filter on purpose: a filtered accelerated canvas gets\n // its own compositor surface, which Chrome can present\n // uninitialized (a white flash) when the layer is created or\n // torn down. The glow is smoothed in the shader instead.\n }\n\n // CSS blobs when WebGL is unavailable\n &__fallback {\n position: absolute;\n inset: 0;\n background:\n radial-gradient(\n 45% 65% at 32% 38%,\n color-mix(in srgb, currentColor 26%, transparent),\n transparent 70%\n ),\n radial-gradient(\n 40% 55% at 68% 62%,\n color-mix(in srgb, currentColor 16%, transparent),\n transparent 70%\n );\n filter: blur(48px);\n animation: pk-aurora-drift 14s var(--ease-in-out) infinite alternate;\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n }\n }\n\n @keyframes pk-aurora-drift {\n from {\n transform: rotate(-10deg) scale(1);\n }\n\n to {\n transform: rotate(10deg) scale(1.2);\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, useTemplateRef, defineAsyncComponent } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useDropZone } from '@vueuse/core'\n import { storeToRefs } from 'pinia'\n import PkAuroraCanvas from './PkAuroraCanvas.vue'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotMessages from './PkChatbotMessages.vue'\n import PkChatbotInput from './PkChatbotInput.vue'\n import PkChatbotEmptyState from './PkChatbotEmptyState.vue'\n import {\n useChatbotStore,\n useLocalizedString,\n useGreeting,\n } from 'composables'\n import type { UIChatMessage } from 'models'\n\n const PkToolShowForm = defineAsyncComponent(\n () => import('./PkToolShowForm.vue'),\n )\n const PkToolShowContactForm = defineAsyncComponent(\n () => import('./PkToolShowContactForm.vue'),\n )\n const PkToolShowSuggestedReply = defineAsyncComponent(\n () => import('./PkToolShowSuggestedReply.vue'),\n )\n const PkToolShowSources = defineAsyncComponent(\n () => import('./PkToolShowSources.vue'),\n )\n const PkToolRequestGeolocation = defineAsyncComponent(\n () => import('./PkToolRequestGeolocation.vue'),\n )\n const PkToolRequestOAuthConnection = defineAsyncComponent(\n () => import('./PkToolRequestOAuthConnection.vue'),\n )\n const PkToolShowLocation = defineAsyncComponent(\n () => import('./PkToolShowLocation.vue'),\n )\n const PkToolShowDiagram = defineAsyncComponent(\n () => import('./PkToolShowDiagram.vue'),\n )\n\n const props = defineProps<{\n agentId: string\n /** Mirrors the PkChatbot `modifier` prop: `fullscreen` shows a centered empty state (greeting) instead of the welcome message */\n modifier?: 'widget' | 'fullscreen'\n /** Display name of the current user, used for the `{name}` greeting placeholder */\n userName?: string\n }>()\n\n const emit = defineEmits<{\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n }>()\n\n const store = useChatbotStore(props.agentId)\n\n const {\n agentInterface,\n agentFileUpload,\n actions,\n revisedAnswers,\n messages,\n chat,\n messageFeedbacks,\n feedbackDialogMessage,\n isFeedbackSubmitting,\n isFeedbackSubmitted,\n feedbackSubmitError,\n isLeadSubmitted,\n isLoadingSubmitLead,\n submitLeadError,\n input,\n inputMessagePlaceholder,\n isConversationBlocked,\n baseUrl,\n pendingAttachments,\n isAttachmentLimitReached,\n fileLimitError,\n apiClient,\n isDark,\n } = storeToRefs(store)\n\n const {\n handleSubmit: storeHandleSubmit,\n stopGeneration,\n regenerate,\n onUpvote,\n onDownvote,\n onFeedback,\n onFeedbackSubmit,\n onLeadSubmit,\n startNewChat,\n addToolOutput,\n addToolApprovalResponse,\n handleFileSelect,\n clearFileLimitError,\n } = store\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const attachmentNotice = computed(() => {\n const err = fileLimitError.value\n if (!err) {\n return undefined\n }\n return err.reason === 'per-chat'\n ? $t('message.fileLimitPerChat', { max: err.max })\n : $t('message.fileLimitPerMessage', { max: err.max })\n })\n\n const dismissableNotice = useLocalizedString(\n () => agentInterface.value?.dismissableNotice,\n )\n\n // Fullscreen empty state: shown until the user sends the first message.\n // The synthetic welcome message is suppressed by the store in fullscreen\n // (`hideWelcomeMessage`), the greeting takes its place.\n const hasUserMessages = computed(() =>\n messages.value.some((message) => message.role === 'user'),\n )\n const showEmptyState = computed(\n () => props.modifier === 'fullscreen' && !hasUserMessages.value,\n )\n const greeting = useGreeting(agentInterface, () => props.userName)\n\n const chatViewEl = useTemplateRef<HTMLDivElement>('chatViewEl')\n\n const handleExpandSourceContext = async (payload: {\n documentId: string\n chunkIndex: number\n }) => {\n const result = await apiClient.value.expandSourceContext(\n props.agentId,\n payload.documentId,\n payload.chunkIndex,\n )\n return result.content\n }\n\n const handleDownloadSource = async (documentId: string) => {\n const result = await apiClient.value.downloadSourceDocument(\n props.agentId,\n documentId,\n )\n window.open(result.downloadUrl, '_blank')\n }\n\n const handleFileDrop = (files: File[] | null) => {\n if (!agentFileUpload.value?.enabled || !files) {\n return\n }\n for (const file of files) {\n handleFileSelect(file)\n }\n }\n\n const { isOverDropZone } = useDropZone(chatViewEl, {\n dataTypes: computed(\n () => agentFileUpload.value?.allowedMimeTypes ?? [],\n ),\n onDrop: handleFileDrop,\n })\n</script>\n\n<template>\n <div\n ref=\"chatViewEl\"\n class=\"pk-chatbot-view-chat\"\n :class=\"{\n 'pk-chatbot-view-chat--dragover':\n isOverDropZone && agentFileUpload?.enabled,\n 'pk-chatbot-view-chat--empty': showEmptyState,\n }\">\n <!-- #region empty state (fullscreen) / messages — crossfade out-in -->\n <Transition name=\"pk-chatbot-view-chat-fade\" mode=\"out-in\">\n <PkChatbotEmptyState\n v-if=\"showEmptyState\"\n :greeting=\"greeting\"\n :logo=\"agentInterface?.logo\"\n :name=\"store.name\" />\n <PkChatbotMessages\n v-else\n class=\"flex flex-col flex-1 min-h-0 p-md overflow-y-auto\"\n interactive\n :messages=\"messages\"\n :status=\"chat.status\"\n :error=\"chat.error\"\n :main-color=\"agentInterface?.mainColor\"\n :text-color=\"agentInterface?.textColor\"\n :revised-answers=\"revisedAnswers\"\n :actions=\"actions\"\n :message-feedbacks=\"messageFeedbacks\"\n :feedback-message-id=\"feedbackDialogMessage?.id\"\n :feedback-loading=\"isFeedbackSubmitting\"\n :feedback-submitted=\"isFeedbackSubmitted\"\n :feedback-error=\"feedbackSubmitError\"\n :show-extended-steps=\"agentInterface?.showExtendedSteps\"\n :is-dark=\"isDark\"\n :show-scroll-to-bottom=\"modifier === 'fullscreen'\"\n @feedback-submit=\"onFeedbackSubmit($event)\"\n @feedback-close=\"feedbackDialogMessage = undefined\"\n @regenerate=\"regenerate\"\n @auto-retry=\"regenerate\"\n @reset-chat=\"startNewChat\"\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\"\n @upvote=\"onUpvote\"\n @downvote=\"onDownvote\"\n @feedback=\"onFeedback\"\n @approval-respond=\"addToolApprovalResponse\"\n @confirm-respond=\"\n ({ toolCallId, confirmed }) =>\n addToolOutput({\n tool: 'requestConfirm',\n toolCallId,\n output: confirmed ? 'confirmed' : 'cancelled',\n })\n \">\n <template #tool-showContactForm=\"{ part }\">\n <PkToolShowContactForm\n :part\n :readonly=\"!baseUrl\"\n :submitted=\"isLeadSubmitted\"\n :loading=\"isLoadingSubmitLead\"\n :error=\"submitLeadError\"\n :privacy-policy-notice=\"\n agentInterface?.privacyPolicyNotice\n \"\n @submit=\"onLeadSubmit\" />\n </template>\n <template #tool-showSuggestedReply=\"{ part }\">\n <PkToolShowSuggestedReply\n :part\n @select=\"\n ($event) => {\n input = $event\n storeHandleSubmit()\n }\n \" />\n </template>\n <template #tool-showSources=\"{ part }\">\n <PkToolShowSources\n :part\n :on-expand-context=\"handleExpandSourceContext\"\n :on-download=\"handleDownloadSource\" />\n </template>\n <template #tool-showForm=\"{ part }\">\n <transition mode=\"out-in\">\n <PkToolShowForm\n :part\n @select=\"\n addToolOutput({\n tool: 'showForm',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </transition>\n </template>\n <template #tool-showMultipleChoice=\"{ part }\">\n <transition mode=\"out-in\">\n <PkToolShowForm\n :part\n allow-custom-answer\n @select=\"\n addToolOutput({\n tool: 'showMultipleChoice',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </transition>\n </template>\n <template #tool-requestOAuthConnection=\"{ part }\">\n <PkToolRequestOAuthConnection\n :part\n :resolve-connection=\"\n (serverName: string) =>\n apiClient.getOAuthAuthorizeUrl(\n props.agentId,\n serverName,\n )\n \"\n @connected=\"\n addToolOutput({\n tool: 'requestOAuthConnection',\n toolCallId: (part as any).toolCallId,\n output: {\n connected: true,\n mcpServerId: $event,\n },\n })\n \" />\n </template>\n <template #tool-requestGeolocation=\"{ part }\">\n <PkToolRequestGeolocation\n :part\n :reverse-geocode=\"\n (lat: number, lon: number) =>\n apiClient.reverseGeocode(lat, lon)\n \"\n @result=\"\n addToolOutput({\n tool: 'requestGeolocation',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </template>\n <template #tool-showLocation=\"{ part }\">\n <PkToolShowLocation\n :part\n :is-dark\n :main-color=\"agentInterface?.mainColor\"\n :forward-geocode=\"\n (query: string, lang?: string) =>\n apiClient.forwardGeocode(query, lang)\n \" />\n </template>\n <template #tool-showDiagram=\"{ part }\">\n <PkToolShowDiagram :part :is-dark />\n </template>\n </PkChatbotMessages>\n </Transition>\n <!-- #endregion -->\n\n <!-- #region input -->\n <div class=\"pk-chatbot-view-chat__input-stage\">\n <!-- Animated accent glow radiating from the input pill -->\n <Transition name=\"pk-chatbot-view-chat-fade\">\n <PkAuroraCanvas\n v-if=\"showEmptyState\"\n class=\"pk-chatbot-view-chat__aurora\"\n source-selector=\".pk-chatbot-input__form\" />\n </Transition>\n <div\n v-if=\"isConversationBlocked\"\n class=\"p-md border-t border-surface-3 text-center text-12 text-danger-darken-2 bg-surface-danger\">\n {{ $t('message.chatErrorConversationBlocked') }}\n </div>\n <PkChatbotInput\n v-else\n v-model=\"input\"\n v-model:pending-attachments=\"pendingAttachments\"\n :placeholder=\"inputMessagePlaceholder\"\n :dismissable-notice=\"\n dismissableNotice && chat.messages.length <= 1\n ? dismissableNotice\n : undefined\n \"\n :status=\"chat.status\"\n :max-message-length=\"agentInterface?.maxMessageLength\"\n :file-upload=\"agentFileUpload\"\n :attachment-limit-reached=\"isAttachmentLimitReached\"\n :attachment-notice=\"attachmentNotice\"\n @stop-generation=\"stopGeneration\"\n @submit=\"storeHandleSubmit\"\n @file-select=\"handleFileSelect\"\n @dismiss-attachment-notice=\"clearFileLimitError\">\n <!-- Fullscreen has no header: the agent identity lives in the\n input pill -->\n <template v-if=\"modifier === 'fullscreen'\" #prepend>\n <span class=\"pk-chatbot-view-chat__agent-chip\">\n <PkAvatar\n modifiers=\"surface\"\n class=\"pk-chatbot-view-chat__agent-chip-avatar\"\n :img-src=\"agentInterface?.logo\"\n :name=\"store.name\" />\n <strong\n v-if=\"store.name\"\n class=\"pk-chatbot-view-chat__agent-chip-name\">\n {{ store.name }}\n </strong>\n </span>\n </template>\n </PkChatbotInput>\n </div>\n <!-- #endregion -->\n <Transition>\n <div\n v-if=\"isOverDropZone && agentFileUpload?.enabled\"\n class=\"pk-chatbot-view-chat__drop-overlay\">\n <VvIcon\n name=\"ri:upload-cloud-2-line\"\n class=\"pk-chatbot-view-chat__drop-overlay-icon\" />\n <span>{{ $t('action.dropFile') }}</span>\n </div>\n </Transition>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-view-chat {\n position: relative;\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n\n // Empty state (fullscreen): center the greeting + input block\n // vertically in the available space\n &--empty {\n justify-content: center;\n gap: var(--spacing-md);\n\n // On phones the input docks to the bottom edge instead\n // (Gemini-like), with the greeting centered in the space above\n @include media-breakpoint-down('xs', $breakpoints) {\n justify-content: flex-start;\n\n .pk-chatbot-empty-state {\n flex: 1;\n justify-content: center;\n }\n }\n }\n\n // Anchors the glow to the input pill\n &__input-stage {\n position: relative;\n }\n\n // Radiates outwards from the pill, biased upwards (the pill itself\n // paints above, being later in the DOM)\n &__input-stage &__aurora {\n inset: calc(-1 * var(--spacing-224)) calc(-1 * var(--spacing-96))\n calc(-1 * var(--spacing-160));\n\n // With the input docked at the bottom the glow becomes a wide\n // dome filling the lower part of the screen (Gemini-like)\n @include media-breakpoint-down('xs', $breakpoints) {\n --aurora-spread: 1.6;\n\n // Tall enough that the glow tail never meets the canvas\n // edge fade (it would draw a visible horizontal band)\n inset: calc(-1 * var(--spacing-384) - var(--spacing-128))\n calc(-1 * var(--spacing-96)) calc(-1 * var(--spacing-160));\n }\n }\n\n // Agent identity inside the input pill (fullscreen has no header)\n &__agent-chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-6);\n flex-shrink: 0;\n padding: var(--spacing-4) var(--spacing-10) var(--spacing-4)\n var(--spacing-4);\n border: 1px solid var(--color-surface-3);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface);\n\n // Don't crowd the textarea on small screens\n @include media-breakpoint-down('md', $breakpoints) {\n display: none;\n }\n }\n\n &__agent-chip-avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n font-size: var(--text-10);\n border-radius: var(--rounded-full);\n }\n\n &__agent-chip-name {\n font-size: var(--text-12);\n font-weight: var(--font-semibold);\n color: var(--color-word-2);\n white-space: nowrap;\n }\n\n // Crossfade empty state ↔ messages: exit faster than enter so the\n // switch never feels like a snap\n &-fade-enter-active {\n transition: opacity 250ms var(--ease-out);\n }\n\n &-fade-leave-active {\n transition: opacity 150ms var(--ease-out);\n }\n\n &-fade-enter-from,\n &-fade-leave-to {\n opacity: 0;\n }\n\n @media (prefers-reduced-motion: reduce) {\n &-fade-enter-active,\n &-fade-leave-active {\n transition: none;\n }\n }\n\n &__drop-overlay {\n position: absolute;\n inset: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm)\n var(--spacing-sm);\n z-index: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-xs);\n background-color: color-mix(\n in srgb,\n var(--color-surface) 85%,\n transparent\n );\n border-radius: var(--rounded-xl);\n border: var(--spacing-2) dashed var(--color-surface-5);\n pointer-events: none;\n color: var(--color-word-3);\n\n &-icon {\n font-size: var(--spacing-32);\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, useTemplateRef, defineAsyncComponent } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useDropZone } from '@vueuse/core'\n import { storeToRefs } from 'pinia'\n import PkAuroraCanvas from './PkAuroraCanvas.vue'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotMessages from './PkChatbotMessages.vue'\n import PkChatbotInput from './PkChatbotInput.vue'\n import PkChatbotEmptyState from './PkChatbotEmptyState.vue'\n import {\n useChatbotStore,\n useLocalizedString,\n useGreeting,\n } from 'composables'\n import type { UIChatMessage } from 'models'\n\n const PkToolShowForm = defineAsyncComponent(\n () => import('./PkToolShowForm.vue'),\n )\n const PkToolShowContactForm = defineAsyncComponent(\n () => import('./PkToolShowContactForm.vue'),\n )\n const PkToolShowSuggestedReply = defineAsyncComponent(\n () => import('./PkToolShowSuggestedReply.vue'),\n )\n const PkToolShowSources = defineAsyncComponent(\n () => import('./PkToolShowSources.vue'),\n )\n const PkToolRequestGeolocation = defineAsyncComponent(\n () => import('./PkToolRequestGeolocation.vue'),\n )\n const PkToolRequestOAuthConnection = defineAsyncComponent(\n () => import('./PkToolRequestOAuthConnection.vue'),\n )\n const PkToolShowLocation = defineAsyncComponent(\n () => import('./PkToolShowLocation.vue'),\n )\n const PkToolShowDiagram = defineAsyncComponent(\n () => import('./PkToolShowDiagram.vue'),\n )\n\n const props = defineProps<{\n agentId: string\n /** Mirrors the PkChatbot `modifier` prop: `fullscreen` shows a centered empty state (greeting) instead of the welcome message */\n modifier?: 'widget' | 'fullscreen'\n /** Display name of the current user, used for the `{name}` greeting placeholder */\n userName?: string\n }>()\n\n const emit = defineEmits<{\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n }>()\n\n const store = useChatbotStore(props.agentId)\n\n const {\n agentInterface,\n agentFileUpload,\n actions,\n revisedAnswers,\n messages,\n chat,\n messageFeedbacks,\n feedbackDialogMessage,\n isFeedbackSubmitting,\n isFeedbackSubmitted,\n feedbackSubmitError,\n isLeadSubmitted,\n isLoadingSubmitLead,\n submitLeadError,\n input,\n inputMessagePlaceholder,\n isConversationBlocked,\n baseUrl,\n pendingAttachments,\n isAttachmentLimitReached,\n fileLimitError,\n apiClient,\n isDark,\n } = storeToRefs(store)\n\n const {\n handleSubmit: storeHandleSubmit,\n stopGeneration,\n regenerate,\n onUpvote,\n onDownvote,\n onFeedback,\n onFeedbackSubmit,\n onLeadSubmit,\n startNewChat,\n addToolOutput,\n addToolApprovalResponse,\n handleFileSelect,\n clearFileLimitError,\n } = store\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const attachmentNotice = computed(() => {\n const err = fileLimitError.value\n if (!err) {\n return undefined\n }\n return err.reason === 'per-chat'\n ? $t('message.fileLimitPerChat', { max: err.max })\n : $t('message.fileLimitPerMessage', { max: err.max })\n })\n\n const dismissableNotice = useLocalizedString(\n () => agentInterface.value?.dismissableNotice,\n )\n\n // Fullscreen empty state: shown until the user sends the first message.\n // The synthetic welcome message is suppressed by the store in fullscreen\n // (`hideWelcomeMessage`), the greeting takes its place.\n const hasUserMessages = computed(() =>\n messages.value.some((message) => message.role === 'user'),\n )\n const showEmptyState = computed(\n () => props.modifier === 'fullscreen' && !hasUserMessages.value,\n )\n const greeting = useGreeting(agentInterface, () => props.userName)\n\n const chatViewEl = useTemplateRef<HTMLDivElement>('chatViewEl')\n\n const handleExpandSourceContext = async (payload: {\n documentId: string\n chunkIndex: number\n }) => {\n const result = await apiClient.value.expandSourceContext(\n props.agentId,\n payload.documentId,\n payload.chunkIndex,\n )\n return result.content\n }\n\n const handleDownloadSource = async (documentId: string) => {\n const result = await apiClient.value.downloadSourceDocument(\n props.agentId,\n documentId,\n )\n window.open(result.downloadUrl, '_blank')\n }\n\n const handleFileDrop = (files: File[] | null) => {\n if (!agentFileUpload.value?.enabled || !files) {\n return\n }\n for (const file of files) {\n handleFileSelect(file)\n }\n }\n\n const { isOverDropZone } = useDropZone(chatViewEl, {\n dataTypes: computed(\n () => agentFileUpload.value?.allowedMimeTypes ?? [],\n ),\n onDrop: handleFileDrop,\n })\n</script>\n\n<template>\n <div\n ref=\"chatViewEl\"\n class=\"pk-chatbot-view-chat\"\n :class=\"{\n 'pk-chatbot-view-chat--dragover':\n isOverDropZone && agentFileUpload?.enabled,\n 'pk-chatbot-view-chat--empty': showEmptyState,\n }\">\n <!-- #region empty state (fullscreen) / messages — crossfade out-in -->\n <Transition name=\"pk-chatbot-view-chat-fade\" mode=\"out-in\">\n <PkChatbotEmptyState\n v-if=\"showEmptyState\"\n :greeting=\"greeting\"\n :logo=\"agentInterface?.logo\"\n :name=\"store.name\" />\n <PkChatbotMessages\n v-else\n class=\"flex flex-col flex-1 min-h-0 p-md overflow-y-auto\"\n interactive\n :messages=\"messages\"\n :status=\"chat.status\"\n :error=\"chat.error\"\n :main-color=\"agentInterface?.mainColor\"\n :text-color=\"agentInterface?.textColor\"\n :revised-answers=\"revisedAnswers\"\n :actions=\"actions\"\n :message-feedbacks=\"messageFeedbacks\"\n :feedback-message-id=\"feedbackDialogMessage?.id\"\n :feedback-loading=\"isFeedbackSubmitting\"\n :feedback-submitted=\"isFeedbackSubmitted\"\n :feedback-error=\"feedbackSubmitError\"\n :show-extended-steps=\"agentInterface?.showExtendedSteps\"\n :is-dark=\"isDark\"\n :show-scroll-to-bottom=\"modifier === 'fullscreen'\"\n @feedback-submit=\"onFeedbackSubmit($event)\"\n @feedback-close=\"feedbackDialogMessage = undefined\"\n @regenerate=\"regenerate\"\n @auto-retry=\"regenerate\"\n @reset-chat=\"startNewChat\"\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\"\n @upvote=\"onUpvote\"\n @downvote=\"onDownvote\"\n @feedback=\"onFeedback\"\n @approval-respond=\"addToolApprovalResponse\"\n @confirm-respond=\"\n ({ toolCallId, confirmed }) =>\n addToolOutput({\n tool: 'requestConfirm',\n toolCallId,\n output: confirmed ? 'confirmed' : 'cancelled',\n })\n \">\n <template #tool-showContactForm=\"{ part }\">\n <PkToolShowContactForm\n :part\n :readonly=\"!baseUrl\"\n :submitted=\"isLeadSubmitted\"\n :loading=\"isLoadingSubmitLead\"\n :error=\"submitLeadError\"\n :privacy-policy-notice=\"\n agentInterface?.privacyPolicyNotice\n \"\n @submit=\"onLeadSubmit\" />\n </template>\n <template #tool-showSuggestedReply=\"{ part }\">\n <PkToolShowSuggestedReply\n :part\n @select=\"\n ($event) => {\n input = $event\n storeHandleSubmit()\n }\n \" />\n </template>\n <template #tool-showSources=\"{ part }\">\n <PkToolShowSources\n :part\n :on-expand-context=\"handleExpandSourceContext\"\n :on-download=\"handleDownloadSource\" />\n </template>\n <template #tool-showForm=\"{ part }\">\n <transition mode=\"out-in\">\n <PkToolShowForm\n :part\n @select=\"\n addToolOutput({\n tool: 'showForm',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </transition>\n </template>\n <template #tool-showMultipleChoice=\"{ part }\">\n <transition mode=\"out-in\">\n <PkToolShowForm\n :part\n allow-custom-answer\n @select=\"\n addToolOutput({\n tool: 'showMultipleChoice',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </transition>\n </template>\n <template #tool-requestOAuthConnection=\"{ part }\">\n <PkToolRequestOAuthConnection\n :part\n :resolve-connection=\"\n (serverName: string) =>\n apiClient.getOAuthAuthorizeUrl(\n props.agentId,\n serverName,\n )\n \"\n @connected=\"\n addToolOutput({\n tool: 'requestOAuthConnection',\n toolCallId: (part as any).toolCallId,\n output: {\n connected: true,\n mcpServerId: $event,\n },\n })\n \" />\n </template>\n <template #tool-requestGeolocation=\"{ part }\">\n <PkToolRequestGeolocation\n :part\n :reverse-geocode=\"\n (lat: number, lon: number) =>\n apiClient.reverseGeocode(lat, lon)\n \"\n @result=\"\n addToolOutput({\n tool: 'requestGeolocation',\n toolCallId: (part as any).toolCallId,\n output: $event,\n })\n \" />\n </template>\n <template #tool-showLocation=\"{ part }\">\n <PkToolShowLocation\n :part\n :is-dark\n :main-color=\"agentInterface?.mainColor\"\n :forward-geocode=\"\n (query: string, lang?: string) =>\n apiClient.forwardGeocode(query, lang)\n \" />\n </template>\n <template #tool-showDiagram=\"{ part }\">\n <PkToolShowDiagram :part :is-dark />\n </template>\n </PkChatbotMessages>\n </Transition>\n <!-- #endregion -->\n\n <!-- #region input -->\n <div class=\"pk-chatbot-view-chat__input-stage\">\n <!-- Animated accent glow radiating from the input pill -->\n <Transition name=\"pk-chatbot-view-chat-fade\">\n <PkAuroraCanvas\n v-if=\"showEmptyState\"\n class=\"pk-chatbot-view-chat__aurora\"\n source-selector=\".pk-chatbot-input__form\" />\n </Transition>\n <div\n v-if=\"isConversationBlocked\"\n class=\"p-md border-t border-surface-3 text-center text-12 text-danger-darken-2 bg-surface-danger\">\n {{ $t('message.chatErrorConversationBlocked') }}\n </div>\n <PkChatbotInput\n v-else\n v-model=\"input\"\n v-model:pending-attachments=\"pendingAttachments\"\n :placeholder=\"inputMessagePlaceholder\"\n :dismissable-notice=\"\n dismissableNotice && chat.messages.length <= 1\n ? dismissableNotice\n : undefined\n \"\n :status=\"chat.status\"\n :max-message-length=\"agentInterface?.maxMessageLength\"\n :file-upload=\"agentFileUpload\"\n :attachment-limit-reached=\"isAttachmentLimitReached\"\n :attachment-notice=\"attachmentNotice\"\n @stop-generation=\"stopGeneration\"\n @submit=\"storeHandleSubmit\"\n @file-select=\"handleFileSelect\"\n @dismiss-attachment-notice=\"clearFileLimitError\">\n <!-- Fullscreen has no header: the agent identity lives in the\n input pill -->\n <template v-if=\"modifier === 'fullscreen'\" #prepend>\n <span class=\"pk-chatbot-view-chat__agent-chip\">\n <PkAvatar\n modifiers=\"surface\"\n class=\"pk-chatbot-view-chat__agent-chip-avatar\"\n :img-src=\"agentInterface?.logo\"\n :name=\"store.name\" />\n <strong\n v-if=\"store.name\"\n class=\"pk-chatbot-view-chat__agent-chip-name\">\n {{ store.name }}\n </strong>\n </span>\n </template>\n </PkChatbotInput>\n </div>\n <!-- #endregion -->\n <Transition>\n <div\n v-if=\"isOverDropZone && agentFileUpload?.enabled\"\n class=\"pk-chatbot-view-chat__drop-overlay\">\n <VvIcon\n name=\"ri:upload-cloud-2-line\"\n class=\"pk-chatbot-view-chat__drop-overlay-icon\" />\n <span>{{ $t('action.dropFile') }}</span>\n </div>\n </Transition>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-view-chat {\n position: relative;\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n\n // Empty state (fullscreen): center the greeting + input block\n // vertically in the available space\n &--empty {\n justify-content: center;\n gap: var(--spacing-md);\n\n // On phones the input docks to the bottom edge instead\n // (Gemini-like), with the greeting centered in the space above\n @include media-breakpoint-down('xs', $breakpoints) {\n justify-content: flex-start;\n\n .pk-chatbot-empty-state {\n flex: 1;\n justify-content: center;\n }\n }\n }\n\n // Anchors the glow to the input pill\n &__input-stage {\n position: relative;\n }\n\n // Radiates outwards from the pill, biased upwards (the pill itself\n // paints above, being later in the DOM)\n &__input-stage &__aurora {\n inset: calc(-1 * var(--spacing-224)) calc(-1 * var(--spacing-96))\n calc(-1 * var(--spacing-160));\n\n // With the input docked at the bottom the glow becomes a wide\n // dome filling the lower part of the screen (Gemini-like)\n @include media-breakpoint-down('xs', $breakpoints) {\n --aurora-spread: 1.6;\n\n // Tall enough that the glow tail never meets the canvas\n // edge fade (it would draw a visible horizontal band)\n inset: calc(-1 * var(--spacing-384) - var(--spacing-128))\n calc(-1 * var(--spacing-96)) calc(-1 * var(--spacing-160));\n }\n }\n\n // Agent identity inside the input pill (fullscreen has no header)\n &__agent-chip {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-6);\n flex-shrink: 0;\n padding: var(--spacing-4) var(--spacing-10) var(--spacing-4)\n var(--spacing-4);\n border: 1px solid var(--color-surface-3);\n border-radius: var(--rounded-full);\n background-color: var(--color-surface);\n\n // Don't crowd the textarea on small screens\n @include media-breakpoint-down('md', $breakpoints) {\n display: none;\n }\n }\n\n &__agent-chip-avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n font-size: var(--text-10);\n border-radius: var(--rounded-full);\n }\n\n &__agent-chip-name {\n font-size: var(--text-12);\n font-weight: var(--font-semibold);\n color: var(--color-word-2);\n white-space: nowrap;\n }\n\n // Crossfade empty state ↔ messages: exit faster than enter so the\n // switch never feels like a snap\n &-fade-enter-active {\n transition: opacity 250ms var(--ease-out);\n }\n\n &-fade-leave-active {\n transition: opacity 150ms var(--ease-out);\n }\n\n &-fade-enter-from,\n &-fade-leave-to {\n opacity: 0;\n }\n\n @media (prefers-reduced-motion: reduce) {\n &-fade-enter-active,\n &-fade-leave-active {\n transition: none;\n }\n }\n\n &__drop-overlay {\n position: absolute;\n inset: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm)\n var(--spacing-sm);\n z-index: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-xs);\n background-color: color-mix(\n in srgb,\n var(--color-surface) 85%,\n transparent\n );\n border-radius: var(--rounded-xl);\n border: var(--spacing-2) dashed var(--color-surface-5);\n pointer-events: none;\n color: var(--color-word-3);\n\n &-icon {\n font-size: var(--spacing-32);\n }\n }\n }\n</style>\n"],"mappings":"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"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { a as e, n as t, o as n } from "./Document-CSdfKj_B.js";
|
|
2
2
|
import { t as r } from "./useChatbotStore-C3uKi2FS.js";
|
|
3
3
|
import { t as i } from "./useDialogConfirm-DaoekZdk.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { t as a } from "./useImageFallback-5vQIO0pC.js";
|
|
5
|
+
import { VvButton as o, VvIcon as s } from "@volverjs/ui-vue/components";
|
|
6
|
+
import { Fragment as c, createBlock as l, createElementBlock as u, createElementVNode as d, createVNode as f, defineComponent as p, openBlock as m, ref as h, renderList as g, toDisplayString as _, unref as v } from "vue";
|
|
6
7
|
import { useI18n as y } from "vue-i18n";
|
|
7
8
|
import { storeToRefs as b } from "pinia";
|
|
8
9
|
//#region ../../packages/composable/src/useFileRow.ts
|
|
9
10
|
function x(t) {
|
|
10
|
-
let { locale: r } = y({ useScope: "global" }), i =
|
|
11
|
+
let { locale: r } = y({ useScope: "global" }), { canShowImage: i, onImageError: o } = a(), s = (e) => t.preview(e.documentId);
|
|
11
12
|
return {
|
|
12
|
-
showThumb: (t) => e(t.mimeType) &&
|
|
13
|
-
thumbUrl:
|
|
13
|
+
showThumb: (t) => e(t.mimeType) && i(s(t)),
|
|
14
|
+
thumbUrl: s,
|
|
14
15
|
onThumbError: (e) => {
|
|
15
|
-
|
|
16
|
+
o(s(e));
|
|
16
17
|
},
|
|
17
18
|
openFile: (e, r = !1) => {
|
|
18
19
|
let i = !r && n(e.mimeType);
|
|
@@ -36,17 +37,17 @@ var S = { class: "flex flex-col flex-1 min-h-0 relative" }, C = {
|
|
|
36
37
|
}, T = { class: "w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center" }, E = { class: "text-14 text-word-3 leading-relaxed" }, D = {
|
|
37
38
|
key: 2,
|
|
38
39
|
class: "pk-chatbot-files"
|
|
39
|
-
}, O = { class: "pk-chatbot-files__icon" }, k = ["src", "onError"], A = ["title", "onClick"], j = { class: "pk-chatbot-files__name" }, M = { class: "pk-chatbot-files__date" }, N = { class: "pk-chatbot-files__actions" }, P = /* @__PURE__ */
|
|
40
|
+
}, O = { class: "pk-chatbot-files__icon" }, k = ["src", "onError"], A = ["title", "onClick"], j = { class: "pk-chatbot-files__name" }, M = { class: "pk-chatbot-files__date" }, N = { class: "pk-chatbot-files__actions" }, P = /* @__PURE__ */ p({
|
|
40
41
|
__name: "PkChatbotViewFiles",
|
|
41
42
|
props: { agentId: {} },
|
|
42
43
|
setup(e) {
|
|
43
|
-
let n = e, { t:
|
|
44
|
+
let n = e, { t: a } = y({ useScope: "global" }), { openDialogConfirm: p } = i(), P = r(n.agentId), { chatFiles: F, isLoadingChatFiles: I } = b(P), { showThumb: L, thumbUrl: R, onThumbError: z, openFile: B, formatDate: V } = x({
|
|
44
45
|
view: P.getChatFileUrl,
|
|
45
46
|
preview: P.getChatFilePreviewUrl
|
|
46
47
|
}), H = h(null), U = async (e) => {
|
|
47
|
-
if (await
|
|
48
|
-
title:
|
|
49
|
-
questionLabel:
|
|
48
|
+
if (await p({
|
|
49
|
+
title: a("userFiles.excludeTitle"),
|
|
50
|
+
questionLabel: a("userFiles.excludeConfirm")
|
|
50
51
|
})) {
|
|
51
52
|
H.value = e.documentId;
|
|
52
53
|
try {
|
|
@@ -57,18 +58,18 @@ var S = { class: "flex flex-col flex-1 min-h-0 relative" }, C = {
|
|
|
57
58
|
}
|
|
58
59
|
};
|
|
59
60
|
return (e, n) => {
|
|
60
|
-
let r =
|
|
61
|
-
return
|
|
61
|
+
let r = s, i = o;
|
|
62
|
+
return m(), u("div", S, [v(I) && v(F).length === 0 ? (m(), u("div", C, [(m(), u(c, null, g(4, (e) => d("div", {
|
|
62
63
|
key: e,
|
|
63
64
|
class: "vv-skeleton__item rounded-md h-56"
|
|
64
|
-
})), 64))])) : v(F).length === 0 ? (
|
|
65
|
+
})), 64))])) : v(F).length === 0 ? (m(), u("div", w, [d("div", T, [f(r, {
|
|
65
66
|
name: "ri:folder-open-line",
|
|
66
67
|
class: "w-28 h-28 text-word-4"
|
|
67
|
-
})]),
|
|
68
|
+
})]), d("span", E, _(v(a)("userFiles.chatFilesEmpty")), 1)])) : (m(), u("ul", D, [(m(!0), u(c, null, g(v(F), (e) => (m(), u("li", {
|
|
68
69
|
key: e.documentId,
|
|
69
70
|
class: "pk-chatbot-files__item"
|
|
70
71
|
}, [
|
|
71
|
-
|
|
72
|
+
d("span", O, [v(L)(e) ? (m(), u("img", {
|
|
72
73
|
key: 0,
|
|
73
74
|
src: v(R)(e),
|
|
74
75
|
loading: "lazy",
|
|
@@ -76,25 +77,25 @@ var S = { class: "flex flex-col flex-1 min-h-0 relative" }, C = {
|
|
|
76
77
|
alt: "",
|
|
77
78
|
class: "pk-chatbot-files__thumb",
|
|
78
79
|
onError: (t) => v(z)(e)
|
|
79
|
-
}, null, 40, k)) : (
|
|
80
|
+
}, null, 40, k)) : (m(), l(r, {
|
|
80
81
|
key: 1,
|
|
81
82
|
name: v(t)(e.mimeType)
|
|
82
83
|
}, null, 8, ["name"]))]),
|
|
83
|
-
|
|
84
|
+
d("button", {
|
|
84
85
|
type: "button",
|
|
85
86
|
class: "pk-chatbot-files__main",
|
|
86
|
-
title: v(
|
|
87
|
+
title: v(a)("userFiles.open"),
|
|
87
88
|
onClick: (t) => v(B)(e)
|
|
88
|
-
}, [
|
|
89
|
-
|
|
89
|
+
}, [d("span", j, _(e.name || v(a)("userFiles.untitled")), 1), d("span", M, _(v(V)(e.createdAt)), 1)], 8, A),
|
|
90
|
+
d("div", N, [f(i, {
|
|
90
91
|
modifiers: "action-quiet xs",
|
|
91
92
|
icon: "ri:download-line",
|
|
92
|
-
title: v(
|
|
93
|
+
title: v(a)("action.download"),
|
|
93
94
|
onClick: (t) => v(B)(e, !0)
|
|
94
|
-
}, null, 8, ["title", "onClick"]),
|
|
95
|
+
}, null, 8, ["title", "onClick"]), f(i, {
|
|
95
96
|
modifiers: "action-quiet-danger xs",
|
|
96
97
|
icon: "ri:delete-bin-line",
|
|
97
|
-
title: v(
|
|
98
|
+
title: v(a)("userFiles.exclude"),
|
|
98
99
|
loading: H.value === e.documentId,
|
|
99
100
|
disabled: H.value === e.documentId,
|
|
100
101
|
onClick: (t) => U(e)
|
|
@@ -111,4 +112,4 @@ var S = { class: "flex flex-col flex-1 min-h-0 relative" }, C = {
|
|
|
111
112
|
//#endregion
|
|
112
113
|
export { P as t };
|
|
113
114
|
|
|
114
|
-
//# sourceMappingURL=PkChatbotViewFiles-
|
|
115
|
+
//# sourceMappingURL=PkChatbotViewFiles-DwrKKSjU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PkChatbotViewFiles-DwrKKSjU.js","names":[],"sources":["../../../../packages/composable/src/useFileRow.ts","../../../../packages/components/src/chat/PkChatbotViewFiles.vue","../../../../packages/components/src/chat/PkChatbotViewFiles.vue"],"sourcesContent":["import { useI18n } from 'vue-i18n'\nimport { isImageMime, isInlineViewable } from 'models'\nimport { useImageFallback } from './useImageFallback'\n\n/** Minimal shape a file-list row needs for the shared display helpers. */\nexport interface FileRowLike {\n documentId: string\n mimeType: string | null\n}\n\n/**\n * Shared display logic for the user-files list rows, used by both the in-app\n * table (PjUserFilesTable) and the embedded widget (PkChatbotViewFiles). The two\n * surfaces differ only in how they build URLs (authenticated store vs widget API\n * client), so the URL builders are injected.\n *\n * - `view(id, inline)` → opens/downloads the original file.\n * - `preview(id)` → small resized blob for the row thumbnail.\n */\nexport function useFileRow(urls: {\n view: (documentId: string, inline: boolean) => string\n preview: (documentId: string) => string\n}) {\n const { locale } = useI18n({ useScope: 'global' })\n\n // Image rows show a thumbnail; if it fails to load (auth/redirect, or the\n // preview isn't generated yet) we fall back to the file-type icon.\n const { canShowImage, onImageError } = useImageFallback()\n const thumbUrl = (file: FileRowLike): string =>\n urls.preview(file.documentId)\n const showThumb = (file: FileRowLike): boolean =>\n isImageMime(file.mimeType) && canShowImage(thumbUrl(file))\n const onThumbError = (file: FileRowLike): void => {\n onImageError(thumbUrl(file))\n }\n\n // Name click previews images/PDF inline; an explicit download forces an\n // attachment (inline=false → download SAS).\n const openFile = (file: FileRowLike, download = false): void => {\n const inline = !download && isInlineViewable(file.mimeType)\n window.open(urls.view(file.documentId, inline), '_blank', 'noopener')\n }\n\n const formatDate = (iso: string): string =>\n new Date(iso).toLocaleDateString(locale.value, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n\n return { showThumb, thumbUrl, onThumbError, openFile, formatDate }\n}\n","<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { fileIconForMime } from 'models'\n import {\n useChatbotStore,\n useDialogConfirm,\n useFileRow,\n type ChatbotFile,\n } from 'composables'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n const { openDialogConfirm } = useDialogConfirm()\n\n const store = useChatbotStore(props.agentId)\n const { chatFiles, isLoadingChatFiles } = storeToRefs(store)\n const { showThumb, thumbUrl, onThumbError, openFile, formatDate } =\n useFileRow({\n view: store.getChatFileUrl,\n preview: store.getChatFilePreviewUrl,\n })\n\n const deletingId = ref<string | null>(null)\n\n const handleExclude = async (file: ChatbotFile) => {\n const proceed = await openDialogConfirm({\n title: $t('userFiles.excludeTitle'),\n questionLabel: $t('userFiles.excludeConfirm'),\n })\n if (!proceed) {\n return\n }\n deletingId.value = file.documentId\n try {\n await store.deleteChatFile(file.documentId)\n } catch {\n // surfaced by the host's error handling\n } finally {\n deletingId.value = null\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-56\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"chatFiles.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon\n name=\"ri:folder-open-line\"\n class=\"w-28 h-28 text-word-4\" />\n </div>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('userFiles.chatFilesEmpty') }}\n </span>\n </div>\n\n <!-- File list -->\n <ul v-else class=\"pk-chatbot-files\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"pk-chatbot-files__item\">\n <span class=\"pk-chatbot-files__icon\">\n <img\n v-if=\"showThumb(file)\"\n :src=\"thumbUrl(file)\"\n loading=\"lazy\"\n decoding=\"async\"\n alt=\"\"\n class=\"pk-chatbot-files__thumb\"\n @error=\"onThumbError(file)\" />\n <VvIcon v-else :name=\"fileIconForMime(file.mimeType)\" />\n </span>\n <button\n type=\"button\"\n class=\"pk-chatbot-files__main\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__name\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"pk-chatbot-files__date\">\n {{ formatDate(file.createdAt) }}\n </span>\n </button>\n <div class=\"pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet xs\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-danger xs\"\n icon=\"ri:delete-bin-line\"\n :title=\"$t('userFiles.exclude')\"\n :loading=\"deletingId === file.documentId\"\n :disabled=\"deletingId === file.documentId\"\n @click=\"handleExclude(file)\" />\n </div>\n </li>\n </ul>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-files {\n display: flex;\n flex-direction: column;\n list-style: none;\n margin: 0;\n padding: var(--spacing-8);\n overflow-y: auto;\n\n &__item {\n display: flex;\n align-items: center;\n gap: var(--spacing-10);\n padding: var(--spacing-8) var(--spacing-10);\n border-radius: var(--rounded-md);\n transition: var(--transition-colors);\n\n // Separator between files\n & + & {\n border-top: 1px solid var(--color-surface-3);\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\n &__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-36);\n height: var(--spacing-36);\n flex-shrink: 0;\n border-radius: var(--rounded-md);\n background-color: var(--color-surface-1);\n color: var(--color-word-3);\n overflow: hidden;\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n text-align: left;\n background: none;\n border: 0;\n padding: 0;\n cursor: pointer;\n }\n\n &__name {\n font-size: var(--text-14);\n color: var(--color-word-1);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__date {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2);\n flex-shrink: 0;\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { ref } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import { fileIconForMime } from 'models'\n import {\n useChatbotStore,\n useDialogConfirm,\n useFileRow,\n type ChatbotFile,\n } from 'composables'\n\n const props = defineProps<{ agentId: string }>()\n\n const { t: $t } = useI18n({ useScope: 'global' })\n const { openDialogConfirm } = useDialogConfirm()\n\n const store = useChatbotStore(props.agentId)\n const { chatFiles, isLoadingChatFiles } = storeToRefs(store)\n const { showThumb, thumbUrl, onThumbError, openFile, formatDate } =\n useFileRow({\n view: store.getChatFileUrl,\n preview: store.getChatFilePreviewUrl,\n })\n\n const deletingId = ref<string | null>(null)\n\n const handleExclude = async (file: ChatbotFile) => {\n const proceed = await openDialogConfirm({\n title: $t('userFiles.excludeTitle'),\n questionLabel: $t('userFiles.excludeConfirm'),\n })\n if (!proceed) {\n return\n }\n deletingId.value = file.documentId\n try {\n await store.deleteChatFile(file.documentId)\n } catch {\n // surfaced by the host's error handling\n } finally {\n deletingId.value = null\n }\n }\n</script>\n\n<template>\n <div class=\"flex flex-col flex-1 min-h-0 relative\">\n <!-- Loading skeleton -->\n <div\n v-if=\"isLoadingChatFiles && chatFiles.length === 0\"\n class=\"flex flex-col gap-8 p-16 vv-skeleton\">\n <div\n v-for=\"i in 4\"\n :key=\"i\"\n class=\"vv-skeleton__item rounded-md h-56\" />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"chatFiles.length === 0\"\n class=\"flex flex-col flex-1 items-center justify-center gap-12 p-24 text-center\">\n <div\n class=\"w-64 h-64 rounded-full bg-surface-1 border border-surface-3 flex items-center justify-center\">\n <VvIcon\n name=\"ri:folder-open-line\"\n class=\"w-28 h-28 text-word-4\" />\n </div>\n <span class=\"text-14 text-word-3 leading-relaxed\">\n {{ $t('userFiles.chatFilesEmpty') }}\n </span>\n </div>\n\n <!-- File list -->\n <ul v-else class=\"pk-chatbot-files\">\n <li\n v-for=\"file in chatFiles\"\n :key=\"file.documentId\"\n class=\"pk-chatbot-files__item\">\n <span class=\"pk-chatbot-files__icon\">\n <img\n v-if=\"showThumb(file)\"\n :src=\"thumbUrl(file)\"\n loading=\"lazy\"\n decoding=\"async\"\n alt=\"\"\n class=\"pk-chatbot-files__thumb\"\n @error=\"onThumbError(file)\" />\n <VvIcon v-else :name=\"fileIconForMime(file.mimeType)\" />\n </span>\n <button\n type=\"button\"\n class=\"pk-chatbot-files__main\"\n :title=\"$t('userFiles.open')\"\n @click=\"openFile(file)\">\n <span class=\"pk-chatbot-files__name\">\n {{ file.name || $t('userFiles.untitled') }}\n </span>\n <span class=\"pk-chatbot-files__date\">\n {{ formatDate(file.createdAt) }}\n </span>\n </button>\n <div class=\"pk-chatbot-files__actions\">\n <VvButton\n modifiers=\"action-quiet xs\"\n icon=\"ri:download-line\"\n :title=\"$t('action.download')\"\n @click=\"openFile(file, true)\" />\n <VvButton\n modifiers=\"action-quiet-danger xs\"\n icon=\"ri:delete-bin-line\"\n :title=\"$t('userFiles.exclude')\"\n :loading=\"deletingId === file.documentId\"\n :disabled=\"deletingId === file.documentId\"\n @click=\"handleExclude(file)\" />\n </div>\n </li>\n </ul>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot-files {\n display: flex;\n flex-direction: column;\n list-style: none;\n margin: 0;\n padding: var(--spacing-8);\n overflow-y: auto;\n\n &__item {\n display: flex;\n align-items: center;\n gap: var(--spacing-10);\n padding: var(--spacing-8) var(--spacing-10);\n border-radius: var(--rounded-md);\n transition: var(--transition-colors);\n\n // Separator between files\n & + & {\n border-top: 1px solid var(--color-surface-3);\n }\n\n &:hover {\n background-color: var(--color-surface-2);\n }\n }\n\n &__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--spacing-36);\n height: var(--spacing-36);\n flex-shrink: 0;\n border-radius: var(--rounded-md);\n background-color: var(--color-surface-1);\n color: var(--color-word-3);\n overflow: hidden;\n }\n\n &__thumb {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n &__main {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n text-align: left;\n background: none;\n border: 0;\n padding: 0;\n cursor: pointer;\n }\n\n &__name {\n font-size: var(--text-14);\n color: var(--color-word-1);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__date {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: var(--spacing-2);\n flex-shrink: 0;\n }\n }\n</style>\n"],"mappings":"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"}
|
package/dist-vue/_chunks/{PkChatbotViewProfile-DuLwYpGZ.js → PkChatbotViewProfile-B7yqkE-h.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as e } from "./useChatbotStore-C3uKi2FS.js";
|
|
2
|
-
import { t } from "./PkAvatar-
|
|
2
|
+
import { t } from "./PkAvatar-paEJByEA.js";
|
|
3
3
|
import { t as n } from "./chatbotProfileContext-CgnFhNPn.js";
|
|
4
4
|
import { VvAlert as r, VvButton as i, VvIcon as a } from "@volverjs/ui-vue/components";
|
|
5
5
|
import { computed as o, createBlock as s, createCommentVNode as c, createElementBlock as l, createElementVNode as u, createVNode as d, defineComponent as f, inject as p, openBlock as m, ref as h, renderSlot as g, toDisplayString as _, unref as v } from "vue";
|
|
@@ -83,4 +83,4 @@ var x = { class: "flex flex-col flex-1 min-h-0 overflow-y-auto" }, S = { class:
|
|
|
83
83
|
//#endregion
|
|
84
84
|
export { N as t };
|
|
85
85
|
|
|
86
|
-
//# sourceMappingURL=PkChatbotViewProfile-
|
|
86
|
+
//# sourceMappingURL=PkChatbotViewProfile-B7yqkE-h.js.map
|