@8wave/ai-elements 0.101.0 → 0.102.0
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/{Agent-DMCeO6o-.js → Agent-Cq-9OZHI.js} +77 -65
- package/dist/_chunks/Agent-Cq-9OZHI.js.map +1 -0
- package/dist/_chunks/{Document-C1Fp99qu.js → Document-s7Ct8xXa.js} +2 -2
- package/dist/_chunks/{Document-C1Fp99qu.js.map → Document-s7Ct8xXa.js.map} +1 -1
- package/dist/_chunks/{Media-c-RubI3_.js → Media-CMWDdtLL.js} +2 -2
- package/dist/_chunks/Media-CMWDdtLL.js.map +1 -0
- package/dist/_chunks/{PkToolRequestGeolocation-OZYwTIls.js → PkToolRequestGeolocation-hAaQJW-t.js} +2 -2
- package/dist/_chunks/{PkToolRequestGeolocation-OZYwTIls.js.map → PkToolRequestGeolocation-hAaQJW-t.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestOAuthConnection-HmWtu2b3.js → PkToolRequestOAuthConnection-Bf_zoT2r.js} +2 -2
- package/dist/_chunks/{PkToolRequestOAuthConnection-HmWtu2b3.js.map → PkToolRequestOAuthConnection-Bf_zoT2r.js.map} +1 -1
- package/dist/_chunks/{PkToolShowForm-BzopoW7z.js → PkToolShowForm-CdUUAjPc.js} +2 -2
- package/dist/_chunks/{PkToolShowForm-BzopoW7z.js.map → PkToolShowForm-CdUUAjPc.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSources-Blw5XOA7.js → PkToolShowSources-U7etupUP.js} +2 -2
- package/dist/_chunks/{PkToolShowSources-Blw5XOA7.js.map → PkToolShowSources-U7etupUP.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSuggestedReply-nsXsvLUW.js → PkToolShowSuggestedReply-BvHgjMQ4.js} +2 -2
- package/dist/_chunks/{PkToolShowSuggestedReply-nsXsvLUW.js.map → PkToolShowSuggestedReply-BvHgjMQ4.js.map} +1 -1
- package/dist/ai-elements.es.js +6006 -5923
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotMessages.js +1 -1
- package/dist-vue/PkChatbotViewChat.js +1 -1
- package/dist-vue/PkChatbotViewConversations.js +1 -1
- package/dist-vue/PkChatbotViewFiles.js +1 -1
- package/dist-vue/PkChatbotViewProfile.js +1 -1
- package/dist-vue/_chunks/{Agent-WEwgRwnY.js → Agent-Cy-XE9N1.js} +77 -66
- package/dist-vue/_chunks/Agent-Cy-XE9N1.js.map +1 -0
- package/dist-vue/_chunks/{Document-Vx3oKWp3.js → Document-B6xVkCfQ.js} +8 -8
- package/dist-vue/_chunks/{Document-Vx3oKWp3.js.map → Document-B6xVkCfQ.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbot-1Y7fGR-C.js → PkChatbot-CHe6t-LR.js} +6 -6
- package/dist-vue/_chunks/{PkChatbot-1Y7fGR-C.js.map → PkChatbot-CHe6t-LR.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-C6X92Igt.js → PkChatbotMessages-CWPSx2ce.js} +8 -8
- package/dist-vue/_chunks/{PkChatbotMessages-C6X92Igt.js.map → PkChatbotMessages-CWPSx2ce.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-CBvcSg0J.js → PkChatbotViewChat-DeeGCFJf.js} +2674 -2651
- package/dist-vue/_chunks/PkChatbotViewChat-DeeGCFJf.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewConversations-BT5ojZEm.js → PkChatbotViewConversations-DXwqumyn.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewConversations-BT5ojZEm.js.map → PkChatbotViewConversations-DXwqumyn.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewFiles-DHzFAQZP.js → PkChatbotViewFiles-B4u87oH6.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotViewFiles-DHzFAQZP.js.map → PkChatbotViewFiles-B4u87oH6.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewProfile-DgN0Pydw.js → PkChatbotViewProfile-C_A0AP8x.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-DgN0Pydw.js.map → PkChatbotViewProfile-C_A0AP8x.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolRequestGeolocation-B1K2fN7U.js → PkToolRequestGeolocation-CaHo_418.js} +2 -2
- package/dist-vue/_chunks/{PkToolRequestGeolocation-B1K2fN7U.js.map → PkToolRequestGeolocation-CaHo_418.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolRequestOAuthConnection-D-qoxuPc.js → PkToolRequestOAuthConnection-URjYDFhm.js} +2 -2
- package/dist-vue/_chunks/{PkToolRequestOAuthConnection-D-qoxuPc.js.map → PkToolRequestOAuthConnection-URjYDFhm.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowDiagram-BvOy4aeQ.js → PkToolShowDiagram-CuQyY0uv.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowDiagram-BvOy4aeQ.js.map → PkToolShowDiagram-CuQyY0uv.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowForm-BzzeoKss.js → PkToolShowForm-vXgu3bZO.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowForm-BzzeoKss.js.map → PkToolShowForm-vXgu3bZO.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSources-DdfHFaPA.js → PkToolShowSources-Die_j1bn.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowSources-DdfHFaPA.js.map → PkToolShowSources-Die_j1bn.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSuggestedReply-DSd1dnZM.js → PkToolShowSuggestedReply-CryAdy6t.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowSuggestedReply-DSd1dnZM.js.map → PkToolShowSuggestedReply-CryAdy6t.js.map} +1 -1
- package/dist-vue/_chunks/{createChatbotApiClient-xbpuy6e1.js → createChatbotApiClient-CoTSUkuj.js} +107 -99
- package/dist-vue/_chunks/createChatbotApiClient-CoTSUkuj.js.map +1 -0
- package/dist-vue/_chunks/{useChatbotStore-DWLLr7sg.js → useChatbotStore-BcLvyaIV.js} +1043 -1014
- package/dist-vue/_chunks/{useChatbotStore-DWLLr7sg.js.map → useChatbotStore-BcLvyaIV.js.map} +1 -1
- package/dist-vue/api.js +1 -1
- package/dist-vue/composables.js +2 -2
- package/dist-vue/index.js +63 -63
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/locales.js +2 -0
- package/dist-vue/packages/components/src/chat/PkAgentSettingsPanel.d.ts +2 -2
- package/dist-vue/packages/components/src/chat/PkReasoningSettings.d.ts +2 -2
- package/dist-vue/packages/components/src/chat/PkVoiceSession.d.ts +12 -0
- package/dist-vue/packages/components/src/chat/useVoiceSession.d.ts +7 -0
- package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +40 -10
- package/dist-vue/packages/models/src/schema/Agent.d.ts +39 -7
- package/dist-vue/packages/models/src/schema/AgentChatStatistics.d.ts +42 -0
- package/dist-vue/packages/models/src/schema/Chat.d.ts +46 -1
- package/dist-vue/packages/models/src/schema/ReasoningChat.d.ts +2 -2
- package/dist-vue/packages/models/src/schema/SubAgent.d.ts +2 -2
- package/dist-vue/packages/models/src/schema/constants.d.ts +7 -3
- package/package.json +1 -1
- package/dist/_chunks/Agent-DMCeO6o-.js.map +0 -1
- package/dist/_chunks/Media-c-RubI3_.js.map +0 -1
- package/dist-vue/_chunks/Agent-WEwgRwnY.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotViewChat-CBvcSg0J.js.map +0 -1
- package/dist-vue/_chunks/createChatbotApiClient-xbpuy6e1.js.map +0 -1
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { S as e, _ as t, a as n, b as r, g as i, h as a, n as o, o as s, v as c, x as l, y as u } from "./schemas-COkEQ0wM.js";
|
|
2
2
|
import { i as d, n as f } from "./Media-yi949VrV.js";
|
|
3
|
-
import {
|
|
3
|
+
import { A as p, F as m, N as h, P as g, j as _ } from "./Agent-Cy-XE9N1.js";
|
|
4
4
|
//#region ../../packages/models/src/schema/Header.ts
|
|
5
5
|
var v = i({
|
|
6
6
|
key: c().optional(),
|
|
7
7
|
value: c().optional()
|
|
8
|
-
}), y =
|
|
8
|
+
}), y = g(n(v), n(i({
|
|
9
9
|
key: c(),
|
|
10
10
|
value: c()
|
|
11
11
|
})), (e) => e?.filter((e) => e.key?.trim() && e.value?.trim())), b = /* @__PURE__ */ function(e) {
|
|
12
12
|
return e.Pending = "pending", e.Processing = "processing", e.Success = "success", e.Failed = "failed", e;
|
|
13
13
|
}({}), x = i({
|
|
14
14
|
extractionStrategy: o(_).optional(),
|
|
15
|
-
browserEngine: o(
|
|
15
|
+
browserEngine: o(p).optional().default(p.FETCH),
|
|
16
16
|
headers: y.nullish(),
|
|
17
17
|
aiDescriptionModel: c().optional(),
|
|
18
18
|
aiDescriptionPrompt: c().max(5e3).optional(),
|
|
@@ -35,7 +35,7 @@ var v = i({
|
|
|
35
35
|
producer: c().optional(),
|
|
36
36
|
creationDate: c().optional(),
|
|
37
37
|
modificationDate: c().optional(),
|
|
38
|
-
pageCount:
|
|
38
|
+
pageCount: g(u([a(), n(r())]), a(), (e) => Array.isArray(e) ? e.length : e).optional(),
|
|
39
39
|
encrypted: s().optional(),
|
|
40
40
|
characterCount: a().optional(),
|
|
41
41
|
lineCount: a().optional(),
|
|
@@ -117,7 +117,7 @@ i({
|
|
|
117
117
|
}), i({
|
|
118
118
|
type: o(C).default(C.File),
|
|
119
119
|
name: c().min(1).max(255).optional(),
|
|
120
|
-
language: o(
|
|
120
|
+
language: o(h).optional(),
|
|
121
121
|
organizationId: c(),
|
|
122
122
|
agentIds: n(c()).min(1, "At least one agent is required"),
|
|
123
123
|
folderId: e().nullable().optional(),
|
|
@@ -126,7 +126,7 @@ i({
|
|
|
126
126
|
content: c().optional(),
|
|
127
127
|
metadata: t(c(), r()).optional(),
|
|
128
128
|
extractionStrategy: o(_).optional(),
|
|
129
|
-
browserEngine: o(
|
|
129
|
+
browserEngine: o(p).optional(),
|
|
130
130
|
headers: y.nullish().default([]),
|
|
131
131
|
aiDescriptionModel: c().optional(),
|
|
132
132
|
aiDescriptionPrompt: c().max(5e3).optional(),
|
|
@@ -160,7 +160,7 @@ i({
|
|
|
160
160
|
organizationId: c(),
|
|
161
161
|
content: c().optional(),
|
|
162
162
|
name: c().min(1).max(255).optional(),
|
|
163
|
-
language: o(
|
|
163
|
+
language: o(h).optional(),
|
|
164
164
|
priority: a().int().min(1).max(5).optional(),
|
|
165
165
|
agentIds: n(c()).min(1).optional(),
|
|
166
166
|
metadata: t(c(), r()).optional()
|
|
@@ -246,4 +246,4 @@ var w = (e) => {
|
|
|
246
246
|
//#endregion
|
|
247
247
|
export { E as a, w as i, k as n, D as o, T as r, C as t };
|
|
248
248
|
|
|
249
|
-
//# sourceMappingURL=Document-
|
|
249
|
+
//# sourceMappingURL=Document-B6xVkCfQ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Document-Vx3oKWp3.js","names":[],"sources":["../../../../packages/models/src/schema/Header.ts","../../../../packages/models/src/schema/Document.ts"],"sourcesContent":["import * as z from 'zod'\nimport { decodeOnly } from '../utils'\n\nexport const HeaderSchema = z.object({\n key: z.string().optional(),\n value: z.string().optional(),\n})\n\nexport const HeadesSchema = decodeOnly(\n z.array(HeaderSchema),\n z.array(z.object({ key: z.string(), value: z.string() })),\n (headers) =>\n headers?.filter(\n (header) => header.key?.trim() && header.value?.trim(),\n ) as Array<{ key: string; value: string }>,\n)\n\nexport type Header = z.infer<typeof HeaderSchema>\nexport type Headers = z.infer<typeof HeadesSchema>\n","import * as z from 'zod'\nimport { zodQs, decodeOnly } from '../utils'\nimport {\n SupportedLanguage,\n ExtractionStrategy,\n BrowserEngine,\n} from './constants'\nimport { MediaSchema } from './Media'\nimport { HeadesSchema } from './Header'\n\n/**\n * Processing status for document indexing and embedding generation\n */\nexport enum IndexStatus {\n Pending = 'pending',\n Processing = 'processing',\n Success = 'success',\n Failed = 'failed',\n}\n\n// ===== Document Source Types =====\n\n/**\n * Conversion options for document processing\n * Used when creating or reprocessing documents\n */\nexport const ConversionOptionsSchema = z.object({\n extractionStrategy: z.enum(ExtractionStrategy).optional(),\n browserEngine: z\n .enum(BrowserEngine)\n .optional()\n .default(BrowserEngine.FETCH),\n headers: HeadesSchema.nullish(),\n aiDescriptionModel: z.string().optional(),\n aiDescriptionPrompt: z.string().max(5000).optional(),\n language: z.string().optional(),\n forceOcr: z.boolean().default(false),\n pages: z\n .string()\n .max(100)\n .regex(/^\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$/)\n .optional(),\n})\n\nexport type ConversionOptions = z.infer<typeof ConversionOptionsSchema>\n\n/**\n * Metadata structure for document.metadata column\n * Consolidates all metadata from converters and source info\n */\nexport const DocumentMetadataSchema = z\n .object({\n // === Source Info (URL documents only) ===\n source: z\n .object({\n url: z.string(),\n lastScrapedAt: z.string(),\n })\n .optional(),\n\n // === Media Info (File documents only) ===\n media: MediaSchema.optional(),\n\n // === Common Fields (all document types) ===\n title: z.string().optional(),\n author: z.string().optional(),\n description: z.string().optional(),\n summary: z.string().optional(),\n keywords: z.array(z.string()).optional(),\n\n // === PDF Specific ===\n subject: z.string().optional(),\n creator: z.string().optional(), // Original application\n producer: z.string().optional(), // PDF generator software\n creationDate: z.string().optional(),\n modificationDate: z.string().optional(),\n pageCount: decodeOnly(\n z.union([z.number(), z.array(z.unknown())]),\n z.number(),\n (val) => (Array.isArray(val) ? val.length : val),\n ).optional(),\n encrypted: z.boolean().optional(),\n\n // === DOCX Specific ===\n characterCount: z.number().optional(),\n lineCount: z.number().optional(),\n\n // === URL Specific ===\n siteName: z.string().optional(),\n canonicalUrl: z.url().optional(),\n imageUrl: z.url().optional(),\n favicon: z.url().optional(),\n type: z.string().optional(), // og:type (article, website, etc.)\n locale: z.string().optional(),\n tags: z.array(z.string()).optional(),\n publishedTime: z.string().optional(),\n modifiedTime: z.string().optional(),\n\n // === Metrics (all document types) ===\n wordCount: z.number().optional(),\n paragraphCount: z.number().optional(),\n readingTime: z.number().optional(), // in minutes\n\n // === OCR (PDF documents) ===\n ocrUsed: z.boolean().optional(),\n ocrProvider: z.string().optional(),\n ocrModel: z.string().optional(),\n ocrPages: z.string().optional(),\n ocrCost: z.number().optional(),\n ocrError: z.string().optional(),\n\n // === RAG / Duplicates / Sync Info ===\n chunksCount: z.number().optional(),\n redundantChunksCount: z.number().optional(),\n hasRedundantChunks: z.boolean().optional(),\n qdrantSynced: z.boolean().optional(),\n\n // === Conversion Options (for reprocessing) ===\n conversionOptions: ConversionOptionsSchema.optional(),\n })\n .loose() // Allow additional custom fields\n\nexport type DocumentMetadata = z.infer<typeof DocumentMetadataSchema>\n\n/**\n * Document type enum\n *\n * - 'file': Document created from uploaded file (PDF, DOCX)\n * - 'url': Document created from web scraping\n * - 'revised-answer': Document created from revised AI answer\n * - 'plain-text': Document created from plain text input\n */\nexport enum DocumentType {\n File = 'file',\n Url = 'url',\n RevisedAnswer = 'revised-answer',\n PlainText = 'plain-text',\n}\n\n// ===== Schemas =====\n\n/**\n * Full document schema from database\n * Extended with computed field chunksCount\n */\nconst BaseDocumentSchema = z.object({\n id: z.string(),\n externalId: z.string().nullable(),\n name: z.string(),\n content: z.string(),\n type: z.string(),\n language: z.string(),\n metadata: z.record(z.string(), z.unknown()),\n indexStatus: z.string(),\n processedAt: z.coerce.date().nullable(),\n createdAt: z.coerce.date(),\n createdBy: z.string(),\n updatedAt: z.coerce.date(),\n updatedBy: z.string(),\n deleted: z.boolean(),\n deletedAt: z.coerce.date().nullable(),\n deletedBy: z.string().nullable(),\n organizationId: z.string(),\n priority: z.number().int().min(1).max(5).default(3),\n})\n\nexport const DocumentSchema = BaseDocumentSchema.extend({\n type: z.enum(DocumentType),\n indexStatus: z.enum(IndexStatus),\n metadata: DocumentMetadataSchema,\n // Add computed field for chunks count (populated by service layer)\n chunksCount: z.number().optional(),\n // Computed fields for redundancy tracking\n redundantChunksCount: z.number().optional(),\n hasRedundantChunks: z.boolean().optional(),\n // Qdrant sync status\n qdrantSynced: z.boolean().optional(),\n // Agent IDs associated with this document (populated in single-item reads)\n agentIds: z.array(z.string()).optional(),\n // Folder ID for this document within an agent context (populated in agent-scoped reads)\n folderId: z.string().nullable().optional(),\n // Full folder path string e.g. \"Parent / Child\" (populated in list reads when agentId is set)\n folderPath: z.string().nullable().optional(),\n})\n\n/**\n * Query parameters for listing documents with pagination, sorting, and filters\n */\nexport const DocumentQuerystringSchema = z.object({\n ...zodQs.pagination(),\n ...zodQs.sort(['id', 'updatedAt', 'createdAt', 'name']),\n ...zodQs.filters(['type', 'language', 'agentId', 'indexStatus']),\n ...zodQs.fullText(),\n ...zodQs.ids(),\n 'filter[similarity]': z.string().optional(),\n // UUID = documents in that folder; 'null' = documents at root (no folder); absent = all documents\n 'filter[folderId]': z.string().optional(),\n 'filter[hasRedundantChunks]': z.string().optional(),\n})\n\n/**\n * Document creation schema for POST /documents endpoint\n *\n * Supports three types:\n * - 'file': Requires media object with uploaded file info\n * - 'url': Requires url string for web scraping\n * - 'revised-answer': Requires content (created from revisedAnswer service)\n */\nexport const DocumentCreateDtoSchema = z\n .object({\n type: z.enum(DocumentType).default(DocumentType.File),\n name: z.string().min(1).max(255).optional(),\n language: z.enum(SupportedLanguage).optional(),\n organizationId: z.string(),\n agentIds: z.array(z.string()).min(1, 'At least one agent is required'),\n folderId: z.uuid().nullable().optional(),\n media: MediaSchema.optional(),\n url: z.url().optional(),\n content: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n extractionStrategy: z.enum(ExtractionStrategy).optional(),\n browserEngine: z.enum(BrowserEngine).optional(),\n headers: HeadesSchema.nullish().default([]),\n aiDescriptionModel: z.string().optional(),\n aiDescriptionPrompt: z.string().max(5000).optional(),\n forceOcr: z.boolean().default(false),\n pages: z\n .string()\n .max(100)\n .regex(/^\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$/)\n .optional(),\n priority: z.number().int().min(1).max(5).default(3).optional(),\n })\n .superRefine((data, ctx) => {\n if (data.type === DocumentType.File && !data.media) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['media'],\n })\n return\n }\n if (data.type === DocumentType.Url && !data.url) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['url'],\n })\n }\n if (\n data.type === DocumentType.PlainText &&\n !(data.name || data.language)\n ) {\n if (!data.name) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['name'],\n })\n }\n if (!data.language) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['language'],\n })\n }\n }\n })\n\n/**\n * Document update schema for PUT /documents/:id endpoint\n *\n * All fields are optional, but at least one must be provided\n */\nexport const DocumentUpdateDtoSchema = z.object({\n id: z.string(),\n organizationId: z.string(),\n content: z.string().optional(),\n name: z.string().min(1).max(255).optional(),\n language: z.enum(SupportedLanguage).optional(),\n priority: z.number().int().min(1).max(5).optional(),\n agentIds: z.array(z.string()).min(1).optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n})\n\n// ===== Type Exports =====\n\nexport type Document = z.infer<typeof DocumentSchema>\nexport type DocumentQuerystring = z.infer<typeof DocumentQuerystringSchema>\nexport type DocumentCreateDto = z.infer<typeof DocumentCreateDtoSchema>\nexport type DocumentUpdateDto = z.infer<typeof DocumentUpdateDtoSchema>\n\n// ===== Helper Functions =====\n\nexport const getStatusColor = (status: IndexStatus) => {\n switch (status) {\n case IndexStatus.Success:\n return 'success'\n case IndexStatus.Failed:\n return 'danger'\n case IndexStatus.Processing:\n return 'warning'\n default:\n return 'info'\n }\n}\n\nexport const getStatusIcon = (status: IndexStatus) => {\n switch (status) {\n case IndexStatus.Success:\n return 'ri:check-line'\n case IndexStatus.Failed:\n return 'ri:error-warning-line'\n case IndexStatus.Processing:\n return 'ri:hourglass-line'\n default:\n return 'ri:time-line'\n }\n}\n\nexport const getTypeIcon = (type: DocumentType) => {\n switch (type) {\n case DocumentType.File:\n return 'ri:file-line'\n case DocumentType.Url:\n return 'ri:link'\n case DocumentType.RevisedAnswer:\n return 'ri:feedback-line'\n default:\n return 'ri:input-method-line'\n }\n}\n\nexport const getFileIcon = (extension: string) => {\n const ext = (\n extension.startsWith('.') ? extension.slice(1) : extension\n ).toLowerCase()\n switch (ext) {\n case 'pdf':\n return 'ri:file-pdf-2-line'\n case 'doc':\n case 'docx':\n return 'ri:file-word-line'\n case 'xls':\n case 'xlsx':\n return 'ri:file-excel-line'\n case 'ppt':\n case 'pptx':\n return 'ri:file-ppt-line'\n case 'txt':\n return 'ri:file-text-line'\n case 'mp3':\n case 'wav':\n case 'ogg':\n return 'ri:file-music-line'\n case 'mp4':\n case 'avi':\n case 'mkv':\n return 'ri:file-video-line'\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'gif':\n case 'bmp':\n case 'svg':\n return 'ri:file-image-line'\n case 'zip':\n case 'rar':\n case '7z':\n return 'ri:file-zip-line'\n default:\n return 'ri:file-line'\n }\n}\n\n/** Whether a MIME type is an image. */\nexport const isImageMime = (mimeType?: string | null): boolean =>\n (mimeType ?? '').startsWith('image/')\n\n/** Whether a file MIME type can be previewed inline in the browser (image or\n * PDF) instead of being forced as a download. Shared by every file-list surface\n * so the preview/download decision stays consistent. */\nexport const isInlineViewable = (mimeType?: string | null): boolean =>\n isImageMime(mimeType) || mimeType === 'application/pdf'\n\n/** File-type identity for list rows and previews: icon, accent color\n * (CSS value driving glyph and tile tint) and a short human kind label. */\nexport interface FileKind {\n icon: string\n accent: string\n label: string\n}\n\n/** File-type identity derived from a MIME type. Accents match the chat\n * attachment cards (PkChatbotFilePreview) so files read the same everywhere. */\nexport const fileKindForMime = (mimeType?: string | null): FileKind => {\n const mime = mimeType ?? ''\n if (isImageMime(mime)) {\n return { icon: 'ri:image-line', accent: '#ec4899', label: 'Image' }\n }\n if (mime === 'application/pdf') {\n return { icon: 'ri:file-pdf-2-line', accent: '#e2483d', label: 'PDF' }\n }\n if (mime === 'application/msword' || mime.includes('wordprocessingml')) {\n return { icon: 'ri:file-word-line', accent: '#2b7de9', label: 'Word' }\n }\n if (mime === 'application/vnd.ms-excel' || mime.includes('spreadsheetml')) {\n return {\n icon: 'ri:file-excel-line',\n accent: '#1f9d57',\n label: 'Spreadsheet',\n }\n }\n if (\n mime === 'application/vnd.ms-powerpoint' ||\n mime.includes('presentationml')\n ) {\n return {\n icon: 'ri:file-ppt-line',\n accent: '#d98324',\n label: 'Presentation',\n }\n }\n if (mime.startsWith('audio/')) {\n return { icon: 'ri:file-music-line', accent: '#8b5cf6', label: 'Audio' }\n }\n if (mime.startsWith('video/')) {\n return { icon: 'ri:file-video-line', accent: '#6366f1', label: 'Video' }\n }\n if (\n mime === 'application/zip' ||\n mime === 'application/x-rar-compressed' ||\n mime === 'application/x-7z-compressed'\n ) {\n return { icon: 'ri:file-zip-line', accent: '#c08a1e', label: 'Archive' }\n }\n if (mime.startsWith('text/')) {\n return { icon: 'ri:file-text-line', accent: '#64748b', label: 'Text' }\n }\n return { icon: 'ri:file-line', accent: '#64748b', label: 'File' }\n}\n\n/** Icon for a file-list row, derived from its MIME type. */\nexport const fileIconForMime = (mimeType?: string | null): string =>\n fileKindForMime(mimeType).icon\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Document-B6xVkCfQ.js","names":[],"sources":["../../../../packages/models/src/schema/Header.ts","../../../../packages/models/src/schema/Document.ts"],"sourcesContent":["import * as z from 'zod'\nimport { decodeOnly } from '../utils'\n\nexport const HeaderSchema = z.object({\n key: z.string().optional(),\n value: z.string().optional(),\n})\n\nexport const HeadesSchema = decodeOnly(\n z.array(HeaderSchema),\n z.array(z.object({ key: z.string(), value: z.string() })),\n (headers) =>\n headers?.filter(\n (header) => header.key?.trim() && header.value?.trim(),\n ) as Array<{ key: string; value: string }>,\n)\n\nexport type Header = z.infer<typeof HeaderSchema>\nexport type Headers = z.infer<typeof HeadesSchema>\n","import * as z from 'zod'\nimport { zodQs, decodeOnly } from '../utils'\nimport {\n SupportedLanguage,\n ExtractionStrategy,\n BrowserEngine,\n} from './constants'\nimport { MediaSchema } from './Media'\nimport { HeadesSchema } from './Header'\n\n/**\n * Processing status for document indexing and embedding generation\n */\nexport enum IndexStatus {\n Pending = 'pending',\n Processing = 'processing',\n Success = 'success',\n Failed = 'failed',\n}\n\n// ===== Document Source Types =====\n\n/**\n * Conversion options for document processing\n * Used when creating or reprocessing documents\n */\nexport const ConversionOptionsSchema = z.object({\n extractionStrategy: z.enum(ExtractionStrategy).optional(),\n browserEngine: z\n .enum(BrowserEngine)\n .optional()\n .default(BrowserEngine.FETCH),\n headers: HeadesSchema.nullish(),\n aiDescriptionModel: z.string().optional(),\n aiDescriptionPrompt: z.string().max(5000).optional(),\n language: z.string().optional(),\n forceOcr: z.boolean().default(false),\n pages: z\n .string()\n .max(100)\n .regex(/^\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$/)\n .optional(),\n})\n\nexport type ConversionOptions = z.infer<typeof ConversionOptionsSchema>\n\n/**\n * Metadata structure for document.metadata column\n * Consolidates all metadata from converters and source info\n */\nexport const DocumentMetadataSchema = z\n .object({\n // === Source Info (URL documents only) ===\n source: z\n .object({\n url: z.string(),\n lastScrapedAt: z.string(),\n })\n .optional(),\n\n // === Media Info (File documents only) ===\n media: MediaSchema.optional(),\n\n // === Common Fields (all document types) ===\n title: z.string().optional(),\n author: z.string().optional(),\n description: z.string().optional(),\n summary: z.string().optional(),\n keywords: z.array(z.string()).optional(),\n\n // === PDF Specific ===\n subject: z.string().optional(),\n creator: z.string().optional(), // Original application\n producer: z.string().optional(), // PDF generator software\n creationDate: z.string().optional(),\n modificationDate: z.string().optional(),\n pageCount: decodeOnly(\n z.union([z.number(), z.array(z.unknown())]),\n z.number(),\n (val) => (Array.isArray(val) ? val.length : val),\n ).optional(),\n encrypted: z.boolean().optional(),\n\n // === DOCX Specific ===\n characterCount: z.number().optional(),\n lineCount: z.number().optional(),\n\n // === URL Specific ===\n siteName: z.string().optional(),\n canonicalUrl: z.url().optional(),\n imageUrl: z.url().optional(),\n favicon: z.url().optional(),\n type: z.string().optional(), // og:type (article, website, etc.)\n locale: z.string().optional(),\n tags: z.array(z.string()).optional(),\n publishedTime: z.string().optional(),\n modifiedTime: z.string().optional(),\n\n // === Metrics (all document types) ===\n wordCount: z.number().optional(),\n paragraphCount: z.number().optional(),\n readingTime: z.number().optional(), // in minutes\n\n // === OCR (PDF documents) ===\n ocrUsed: z.boolean().optional(),\n ocrProvider: z.string().optional(),\n ocrModel: z.string().optional(),\n ocrPages: z.string().optional(),\n ocrCost: z.number().optional(),\n ocrError: z.string().optional(),\n\n // === RAG / Duplicates / Sync Info ===\n chunksCount: z.number().optional(),\n redundantChunksCount: z.number().optional(),\n hasRedundantChunks: z.boolean().optional(),\n qdrantSynced: z.boolean().optional(),\n\n // === Conversion Options (for reprocessing) ===\n conversionOptions: ConversionOptionsSchema.optional(),\n })\n .loose() // Allow additional custom fields\n\nexport type DocumentMetadata = z.infer<typeof DocumentMetadataSchema>\n\n/**\n * Document type enum\n *\n * - 'file': Document created from uploaded file (PDF, DOCX)\n * - 'url': Document created from web scraping\n * - 'revised-answer': Document created from revised AI answer\n * - 'plain-text': Document created from plain text input\n */\nexport enum DocumentType {\n File = 'file',\n Url = 'url',\n RevisedAnswer = 'revised-answer',\n PlainText = 'plain-text',\n}\n\n// ===== Schemas =====\n\n/**\n * Full document schema from database\n * Extended with computed field chunksCount\n */\nconst BaseDocumentSchema = z.object({\n id: z.string(),\n externalId: z.string().nullable(),\n name: z.string(),\n content: z.string(),\n type: z.string(),\n language: z.string(),\n metadata: z.record(z.string(), z.unknown()),\n indexStatus: z.string(),\n processedAt: z.coerce.date().nullable(),\n createdAt: z.coerce.date(),\n createdBy: z.string(),\n updatedAt: z.coerce.date(),\n updatedBy: z.string(),\n deleted: z.boolean(),\n deletedAt: z.coerce.date().nullable(),\n deletedBy: z.string().nullable(),\n organizationId: z.string(),\n priority: z.number().int().min(1).max(5).default(3),\n})\n\nexport const DocumentSchema = BaseDocumentSchema.extend({\n type: z.enum(DocumentType),\n indexStatus: z.enum(IndexStatus),\n metadata: DocumentMetadataSchema,\n // Add computed field for chunks count (populated by service layer)\n chunksCount: z.number().optional(),\n // Computed fields for redundancy tracking\n redundantChunksCount: z.number().optional(),\n hasRedundantChunks: z.boolean().optional(),\n // Qdrant sync status\n qdrantSynced: z.boolean().optional(),\n // Agent IDs associated with this document (populated in single-item reads)\n agentIds: z.array(z.string()).optional(),\n // Folder ID for this document within an agent context (populated in agent-scoped reads)\n folderId: z.string().nullable().optional(),\n // Full folder path string e.g. \"Parent / Child\" (populated in list reads when agentId is set)\n folderPath: z.string().nullable().optional(),\n})\n\n/**\n * Query parameters for listing documents with pagination, sorting, and filters\n */\nexport const DocumentQuerystringSchema = z.object({\n ...zodQs.pagination(),\n ...zodQs.sort(['id', 'updatedAt', 'createdAt', 'name']),\n ...zodQs.filters(['type', 'language', 'agentId', 'indexStatus']),\n ...zodQs.fullText(),\n ...zodQs.ids(),\n 'filter[similarity]': z.string().optional(),\n // UUID = documents in that folder; 'null' = documents at root (no folder); absent = all documents\n 'filter[folderId]': z.string().optional(),\n 'filter[hasRedundantChunks]': z.string().optional(),\n})\n\n/**\n * Document creation schema for POST /documents endpoint\n *\n * Supports three types:\n * - 'file': Requires media object with uploaded file info\n * - 'url': Requires url string for web scraping\n * - 'revised-answer': Requires content (created from revisedAnswer service)\n */\nexport const DocumentCreateDtoSchema = z\n .object({\n type: z.enum(DocumentType).default(DocumentType.File),\n name: z.string().min(1).max(255).optional(),\n language: z.enum(SupportedLanguage).optional(),\n organizationId: z.string(),\n agentIds: z.array(z.string()).min(1, 'At least one agent is required'),\n folderId: z.uuid().nullable().optional(),\n media: MediaSchema.optional(),\n url: z.url().optional(),\n content: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n extractionStrategy: z.enum(ExtractionStrategy).optional(),\n browserEngine: z.enum(BrowserEngine).optional(),\n headers: HeadesSchema.nullish().default([]),\n aiDescriptionModel: z.string().optional(),\n aiDescriptionPrompt: z.string().max(5000).optional(),\n forceOcr: z.boolean().default(false),\n pages: z\n .string()\n .max(100)\n .regex(/^\\d+(\\s*-\\s*\\d+)?(\\s*,\\s*\\d+(\\s*-\\s*\\d+)?)*$/)\n .optional(),\n priority: z.number().int().min(1).max(5).default(3).optional(),\n })\n .superRefine((data, ctx) => {\n if (data.type === DocumentType.File && !data.media) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['media'],\n })\n return\n }\n if (data.type === DocumentType.Url && !data.url) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['url'],\n })\n }\n if (\n data.type === DocumentType.PlainText &&\n !(data.name || data.language)\n ) {\n if (!data.name) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['name'],\n })\n }\n if (!data.language) {\n ctx.addIssue({\n code: 'custom',\n params: {\n i18n: 'errors.required',\n },\n path: ['language'],\n })\n }\n }\n })\n\n/**\n * Document update schema for PUT /documents/:id endpoint\n *\n * All fields are optional, but at least one must be provided\n */\nexport const DocumentUpdateDtoSchema = z.object({\n id: z.string(),\n organizationId: z.string(),\n content: z.string().optional(),\n name: z.string().min(1).max(255).optional(),\n language: z.enum(SupportedLanguage).optional(),\n priority: z.number().int().min(1).max(5).optional(),\n agentIds: z.array(z.string()).min(1).optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n})\n\n// ===== Type Exports =====\n\nexport type Document = z.infer<typeof DocumentSchema>\nexport type DocumentQuerystring = z.infer<typeof DocumentQuerystringSchema>\nexport type DocumentCreateDto = z.infer<typeof DocumentCreateDtoSchema>\nexport type DocumentUpdateDto = z.infer<typeof DocumentUpdateDtoSchema>\n\n// ===== Helper Functions =====\n\nexport const getStatusColor = (status: IndexStatus) => {\n switch (status) {\n case IndexStatus.Success:\n return 'success'\n case IndexStatus.Failed:\n return 'danger'\n case IndexStatus.Processing:\n return 'warning'\n default:\n return 'info'\n }\n}\n\nexport const getStatusIcon = (status: IndexStatus) => {\n switch (status) {\n case IndexStatus.Success:\n return 'ri:check-line'\n case IndexStatus.Failed:\n return 'ri:error-warning-line'\n case IndexStatus.Processing:\n return 'ri:hourglass-line'\n default:\n return 'ri:time-line'\n }\n}\n\nexport const getTypeIcon = (type: DocumentType) => {\n switch (type) {\n case DocumentType.File:\n return 'ri:file-line'\n case DocumentType.Url:\n return 'ri:link'\n case DocumentType.RevisedAnswer:\n return 'ri:feedback-line'\n default:\n return 'ri:input-method-line'\n }\n}\n\nexport const getFileIcon = (extension: string) => {\n const ext = (\n extension.startsWith('.') ? extension.slice(1) : extension\n ).toLowerCase()\n switch (ext) {\n case 'pdf':\n return 'ri:file-pdf-2-line'\n case 'doc':\n case 'docx':\n return 'ri:file-word-line'\n case 'xls':\n case 'xlsx':\n return 'ri:file-excel-line'\n case 'ppt':\n case 'pptx':\n return 'ri:file-ppt-line'\n case 'txt':\n return 'ri:file-text-line'\n case 'mp3':\n case 'wav':\n case 'ogg':\n return 'ri:file-music-line'\n case 'mp4':\n case 'avi':\n case 'mkv':\n return 'ri:file-video-line'\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'gif':\n case 'bmp':\n case 'svg':\n return 'ri:file-image-line'\n case 'zip':\n case 'rar':\n case '7z':\n return 'ri:file-zip-line'\n default:\n return 'ri:file-line'\n }\n}\n\n/** Whether a MIME type is an image. */\nexport const isImageMime = (mimeType?: string | null): boolean =>\n (mimeType ?? '').startsWith('image/')\n\n/** Whether a file MIME type can be previewed inline in the browser (image or\n * PDF) instead of being forced as a download. Shared by every file-list surface\n * so the preview/download decision stays consistent. */\nexport const isInlineViewable = (mimeType?: string | null): boolean =>\n isImageMime(mimeType) || mimeType === 'application/pdf'\n\n/** File-type identity for list rows and previews: icon, accent color\n * (CSS value driving glyph and tile tint) and a short human kind label. */\nexport interface FileKind {\n icon: string\n accent: string\n label: string\n}\n\n/** File-type identity derived from a MIME type. Accents match the chat\n * attachment cards (PkChatbotFilePreview) so files read the same everywhere. */\nexport const fileKindForMime = (mimeType?: string | null): FileKind => {\n const mime = mimeType ?? ''\n if (isImageMime(mime)) {\n return { icon: 'ri:image-line', accent: '#ec4899', label: 'Image' }\n }\n if (mime === 'application/pdf') {\n return { icon: 'ri:file-pdf-2-line', accent: '#e2483d', label: 'PDF' }\n }\n if (mime === 'application/msword' || mime.includes('wordprocessingml')) {\n return { icon: 'ri:file-word-line', accent: '#2b7de9', label: 'Word' }\n }\n if (mime === 'application/vnd.ms-excel' || mime.includes('spreadsheetml')) {\n return {\n icon: 'ri:file-excel-line',\n accent: '#1f9d57',\n label: 'Spreadsheet',\n }\n }\n if (\n mime === 'application/vnd.ms-powerpoint' ||\n mime.includes('presentationml')\n ) {\n return {\n icon: 'ri:file-ppt-line',\n accent: '#d98324',\n label: 'Presentation',\n }\n }\n if (mime.startsWith('audio/')) {\n return { icon: 'ri:file-music-line', accent: '#8b5cf6', label: 'Audio' }\n }\n if (mime.startsWith('video/')) {\n return { icon: 'ri:file-video-line', accent: '#6366f1', label: 'Video' }\n }\n if (\n mime === 'application/zip' ||\n mime === 'application/x-rar-compressed' ||\n mime === 'application/x-7z-compressed'\n ) {\n return { icon: 'ri:file-zip-line', accent: '#c08a1e', label: 'Archive' }\n }\n if (mime.startsWith('text/')) {\n return { icon: 'ri:file-text-line', accent: '#64748b', label: 'Text' }\n }\n return { icon: 'ri:file-line', accent: '#64748b', label: 'File' }\n}\n\n/** Icon for a file-list row, derived from its MIME type. */\nexport const fileIconForMime = (mimeType?: string | null): string =>\n fileKindForMime(mimeType).icon\n"],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { n as e, t } from "./PkStreamingMarkdown-BBni7OTn.js";
|
|
2
|
-
import { n } from "./useChatbotStore-
|
|
3
|
-
import { t as ee } from "./PkChatbotViewConversations-
|
|
4
|
-
import { t as te } from "./PkChatbotViewChat-
|
|
5
|
-
import { t as ne } from "./PkChatbotViewFiles-
|
|
2
|
+
import { r as n } from "./useChatbotStore-BcLvyaIV.js";
|
|
3
|
+
import { t as ee } from "./PkChatbotViewConversations-DXwqumyn.js";
|
|
4
|
+
import { t as te } from "./PkChatbotViewChat-DeeGCFJf.js";
|
|
5
|
+
import { t as ne } from "./PkChatbotViewFiles-B4u87oH6.js";
|
|
6
6
|
import { t as r } from "./useLocalizedString-PfaKjIQY.js";
|
|
7
7
|
import { t as re } from "./PkAvatar-paEJByEA.js";
|
|
8
8
|
import { g as i } from "./utils-CgxndZld.js";
|
|
9
9
|
import { t as a } from "./chatbotProfileContext-CgnFhNPn.js";
|
|
10
|
-
import { t as ie } from "./PkChatbotViewProfile-
|
|
10
|
+
import { t as ie } from "./PkChatbotViewProfile-C_A0AP8x.js";
|
|
11
11
|
import { VvAlert as ae, VvButton as oe, VvButtonGroup as se, VvDropdown as ce, VvDropdownAction as le, VvIcon as ue } from "@volverjs/ui-vue/components";
|
|
12
12
|
import { Fragment as o, computed as s, createBlock as c, createCommentVNode as l, createElementBlock as u, createElementVNode as d, createSlots as f, createTextVNode as p, createVNode as m, defineComponent as h, guardReactiveProps as g, inject as _, mergeModels as v, mergeProps as y, normalizeClass as de, normalizeProps as b, normalizeStyle as x, openBlock as S, renderList as C, renderSlot as w, toDisplayString as T, toRef as fe, unref as E, useModel as pe, useSlots as me, watch as D, watchEffect as he, withCtx as O } from "vue";
|
|
13
13
|
import { useI18n as ge } from "vue-i18n";
|
|
@@ -261,4 +261,4 @@ var ve = { class: "pk-chatbot__header-start" }, ye = ["title"], be = { key: 1 },
|
|
|
261
261
|
//#endregion
|
|
262
262
|
export { k as t };
|
|
263
263
|
|
|
264
|
-
//# sourceMappingURL=PkChatbot-
|
|
264
|
+
//# sourceMappingURL=PkChatbot-CHe6t-LR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkChatbot-1Y7fGR-C.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbot.vue","../../../../packages/components/src/chat/PkChatbot.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, inject, toRef, watch, watchEffect, useSlots } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type {\n AgentInterface,\n AgentModel,\n AgentFileUpload,\n AgentGatewayOptions,\n AgentVoiceCapability,\n ChatMessageActions,\n RevisedAnswer,\n UIChatMessage,\n AgentTools,\n } from 'models'\n import {\n useChatbotStore,\n useLocalizedString,\n provideShortUrlResolver,\n } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotViewChat from './PkChatbotViewChat.vue'\n import PkChatbotViewConversations from './PkChatbotViewConversations.vue'\n import PkChatbotViewFiles from './PkChatbotViewFiles.vue'\n import PkChatbotViewProfile from './PkChatbotViewProfile.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n import { resolveContrastColor } from './utils'\n\n // Captured in script setup to avoid TS7022 circular inference in template\n const slots = useSlots()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = defineProps<{\n baseUrl: string\n basePath?: string\n agentId: string\n agentType?: 'reasoning' | 'chat'\n userId?: string\n name?: string\n model?: string\n agentModel?: AgentModel\n agentTools?: AgentTools\n agentInterface?: AgentInterface\n actions?: ChatMessageActions[]\n revisedAnswers?: RevisedAnswer[]\n headers?: Record<string, string>\n systemTheme?: 'light' | 'dark'\n agentFileUpload?: AgentFileUpload\n /** Voice configuration from the agent settings: enables the talk button. */\n voice?: AgentVoiceCapability\n fallbackModels?: string[]\n gatewayOptions?: Partial<AgentGatewayOptions>\n externalContext?: Record<string, unknown>\n /** Layout modifier: `widget` (default) keeps the contained layout with header, `fullscreen` removes header/background for full-page usage (the page provides the chrome), `panel` keeps the header but slims it down for a docked side panel (the host provides background and border) */\n modifier?: 'widget' | 'fullscreen' | 'panel'\n /** Display name of the current user, used for the `{name}` placeholder in the fullscreen greeting */\n userName?: string\n /** Show the \"Files in this chat\" menu for hosts that don't provide the\n * profile context but whose user is authenticated (e.g. the backoffice\n * playground). The widget host relies on `canManageProfile` instead. */\n userFilesEnabled?: boolean\n }>()\n const emit = defineEmits<{\n 'message-update': [message: UIChatMessage]\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n error: [error: { type: string; title: string; status: number }]\n }>()\n\n const chatId = defineModel<string | undefined>('chatId', {\n type: String,\n default: undefined,\n })\n\n const store = useChatbotStore(props.agentId)\n\n // Resolve backend short-urls (`/api/r/<hash>`) back to their real target\n // for display in tool components. Batched + cached across the chat tree.\n provideShortUrlResolver((hashes) =>\n store.apiClient.resolveShortUrls(hashes),\n )\n\n const { messages, chat, localChatId, activeView, chatError, isDark } =\n storeToRefs(store)\n\n // Ref to store.agentId for watchers (avoids duplicate-key with agentId prop)\n const storeAgentId = toRef(store, 'agentId')\n\n const { startNewChat, stopGeneration, regenerate, navigate, sendMessage } =\n store\n\n // Optional profile-management context provided by the host widget.\n // Present only for authenticated, non-anonymous sessions.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canManageProfile = computed(\n () => profile?.canManageProfile.value ?? false,\n )\n // Anonymous users get a \"Sign in\" entry instead of the profile one,\n // provided the host filled the `view-auth` slot and the agent has at\n // least one auth method enabled.\n const canSignIn = computed(\n () =>\n !canManageProfile.value &&\n (profile?.canSignIn?.value ?? false) &&\n slots['view-auth'] !== undefined,\n )\n\n // Once the sign-in completes (the session becomes non-anonymous) leave\n // the auth view and return to the conversation.\n watch(canManageProfile, (canManage) => {\n if (canManage && activeView.value === 'auth') {\n navigate('chat')\n }\n })\n\n // Sync props → store reactively\n watchEffect(() => {\n store.baseUrl = props.baseUrl\n store.basePath = props.basePath\n store.name = props.name\n store.agentType = props.agentType\n store.userId = props.userId\n store.agentInterface = props.agentInterface\n store.agentModel = props.agentModel\n store.agentTools = props.agentTools\n store.headers = props.headers\n store.revisedAnswers = props.revisedAnswers\n store.model = props.model\n store.actionsInput = props.actions\n store.agentFileUpload = props.agentFileUpload\n store.agentVoice = props.voice\n store.fallbackModels = props.fallbackModels\n store.gatewayOptions = props.gatewayOptions\n store.externalContext = props.externalContext\n store.systemTheme = props.systemTheme\n store.hideWelcomeMessage = props.modifier === 'fullscreen'\n })\n\n // Sync chatId v-model ↔ store (bidirectional)\n // Parent → store: if the parent provides an external chatId, push it into\n // the store so the Chat instance picks it up.\n watch(\n () => chatId.value,\n (id) => {\n if (id && id !== store.localChatId) {\n store.localChatId = id\n }\n },\n { immediate: true },\n )\n // Store → parent: keep the v-model in sync whenever the store changes\n // the chatId internally (e.g. startNewChat).\n watch(localChatId, (id) => {\n chatId.value = id\n })\n\n // Emit structured errors from chat to the parent component\n watch(chatError, (error) => {\n if (!error) {\n return\n }\n try {\n const parsedError = JSON.parse(error.message) as {\n type: string\n title: string\n status: number\n }\n emit('error', parsedError)\n } catch {\n // ignore non-JSON errors\n }\n })\n\n // ui\n const isFullscreen = computed(() => props.modifier === 'fullscreen')\n const isPanel = computed(() => props.modifier === 'panel')\n const isReasoningAgent = computed(() => props.agentType === 'reasoning')\n const useColorsForAgentHeader = computed(\n () => props.agentInterface?.useColorsForAgentHeader,\n )\n // Panel mode promotes the everyday actions (new chat, history) from the\n // dropdown to direct header buttons: the dropdown then only remains for\n // the secondary entries (files, profile, sign-in) and disappears when\n // none of them applies.\n const showHeaderMenu = computed(\n () =>\n !isPanel.value ||\n canManageProfile.value ||\n canSignIn.value ||\n (!!props.userFilesEnabled && !!localChatId.value),\n )\n const themeClass = computed(() => [\n 'theme',\n `theme--${isDark.value ? 'dark' : 'light'}`,\n ])\n const mainColor = computed(() => props.agentInterface?.mainColor)\n const contrastColor = computed(() =>\n resolveContrastColor(props.agentInterface?.textColor, mainColor.value),\n )\n const footerMessage = useLocalizedString(\n () => props.agentInterface?.footerMessage,\n )\n const title = computed(() => {\n if (activeView.value === 'conversations') {\n return $t('label.conversations')\n }\n if (activeView.value === 'profile') {\n return $t('label.profile')\n }\n if (activeView.value === 'auth') {\n return $t('label.authentication')\n }\n if (activeView.value === 'files') {\n return $t('userFiles.chatFilesTitle')\n }\n return props.name\n })\n\n // state\n const isReady = computed(() => chat.value.status === 'ready')\n const activeMessage = computed(() => {\n return messages.value?.[messages.value.length - 1]\n })\n\n // expose\n defineExpose({\n startNewChat,\n regenerate,\n stopGeneration,\n sendMessage,\n })\n\n // load chat, conversations and feedbacks on agent or chat change\n watch(\n [storeAgentId, localChatId],\n async ([newAgentId, newChatId]) => {\n await store.initialize(newAgentId, newChatId)\n },\n {\n immediate: true,\n },\n )\n // load conversations when navigating to conversations view\n watch(activeView, (newView) => {\n if (newView === 'conversations') {\n store.loadConversations(storeAgentId.value)\n } else if (newView === 'files') {\n store.loadChatFiles()\n }\n })\n // emit message updates for up-to-date context in parent components\n watch(\n activeMessage,\n (message) => {\n if (message) {\n emit('message-update', message)\n }\n },\n {\n deep: true,\n immediate: true,\n },\n )\n</script>\n\n<template>\n <div\n class=\"pk-chatbot\"\n :class=\"[\n themeClass,\n {\n 'pk-chatbot--fullscreen': isFullscreen,\n 'pk-chatbot--panel': isPanel,\n },\n ]\">\n <!-- #region title -->\n <div\n v-if=\"!isFullscreen\"\n class=\"pk-chatbot__header\"\n :style=\"\n useColorsForAgentHeader\n ? {\n backgroundColor: mainColor,\n }\n : undefined\n \">\n <div class=\"pk-chatbot__header-start\">\n <template v-if=\"activeView !== 'chat'\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:arrow-left-s-line',\n title: $t('action.goBack'),\n }\"\n @click=\"navigate('chat')\" />\n <VvIcon\n :name=\"\n activeView === 'conversations'\n ? 'ri:history-line'\n : activeView === 'files'\n ? 'ri:attachment-2'\n : 'ri:user-line'\n \"\n class=\"pk-chatbot__nav-icon\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \" />\n </template>\n <!-- Panel mode: no avatar, the docked panel identifies the\n agent with the title alone -->\n <PkAvatar\n v-else-if=\"!isPanel\"\n modifiers=\"surface ring shrink-0\"\n class=\"pk-chatbot__avatar\"\n :name\n :img-src=\"agentInterface?.logo\" />\n <strong\n class=\"pk-chatbot__title\"\n :title=\"title\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \">\n {{ title }}\n </strong>\n </div>\n <VvButtonGroup class=\"pk-chatbot__header-end\" modifiers=\"compact\">\n <slot\n name=\"header-actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n <template v-if=\"isPanel\">\n <VvButton\n :modifiers=\"\n useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet'\n \"\n icon=\"ri:edit-box-line\"\n :disabled=\"!isReady\"\n :title=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n <VvButton\n v-if=\"!isReasoningAgent\"\n :modifiers=\"\n useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet'\n \"\n icon=\"ri:history-line\"\n :disabled=\"!isReady\"\n :title=\"$t('action.viewRecentChats')\"\n @click=\"navigate('conversations')\" />\n </template>\n <VvDropdown\n v-if=\"showHeaderMenu\"\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:more-2-fill',\n disabled: !isReady,\n title: $t('action.moreActions'),\n }\" />\n <template #items>\n <VvDropdownAction\n v-if=\"!isPanel\"\n @click=\"startNewChat()\">\n <VvIcon name=\"ri:edit-box-line\" />\n {{ $t('action.startNewChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"!isReasoningAgent && !isPanel\"\n @click=\"navigate('conversations')\">\n <VvIcon name=\"ri:history-line\" />\n {{ $t('action.viewRecentChats') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"\n (canManageProfile || userFilesEnabled) &&\n localChatId\n \"\n @click=\"navigate('files')\">\n <VvIcon name=\"ri:attachment-2\" />\n {{ $t('userFiles.chatFilesTitle') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"canManageProfile\"\n @click=\"navigate('profile')\">\n <VvIcon name=\"ri:user-line\" />\n {{ $t('label.profile') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-else-if=\"canSignIn\"\n @click=\"navigate('auth')\">\n <VvIcon name=\"ri:account-circle-line\" />\n {{ $t('action.signIn') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n <slot\n name=\"actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n </VvButtonGroup>\n </div>\n <!-- #endregion -->\n\n <div v-if=\"isReasoningAgent\">\n <div class=\"pk-chatbot__warning-body\">\n <VvAlert modifiers=\"callout\">\n <div class=\"pk-chatbot__warning-header\">\n <VvIcon\n name=\"ri:alert-line\"\n class=\"pk-chatbot__warning-icon\" />\n <strong class=\"pk-chatbot__warning-title\">\n {{ $t('message.warning') }}\n </strong>\n </div>\n <p class=\"pk-chatbot__warning-text\">\n {{ $t('message.reasoningAgent') }}\n </p>\n </VvAlert>\n </div>\n </div>\n\n <!-- #region views -->\n <PkChatbotViewChat\n v-if=\"activeView === 'chat'\"\n :agent-id\n :modifier\n :user-name\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\">\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewChat>\n <PkChatbotViewConversations\n v-else-if=\"activeView === 'conversations'\"\n :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewConversations>\n <PkChatbotViewFiles v-else-if=\"activeView === 'files'\" :agent-id />\n <div\n v-else-if=\"activeView === 'auth'\"\n class=\"pk-chatbot__view-auth flex-1 min-h-0 overflow-y-auto\">\n <!-- Sign-in form provided by the host widget (owns the auth client) -->\n <slot name=\"view-auth\" />\n </div>\n <PkChatbotViewProfile v-else :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewProfile>\n <!-- #endregion -->\n\n <!-- #region footer -->\n <div v-if=\"footerMessage\" class=\"pk-chatbot__footer\">\n <PkStreamingMarkdown :markdown=\"footerMessage\" class=\"wysiwyg\" />\n </div>\n <!-- #endregion -->\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot {\n display: flex;\n flex-direction: column;\n position: relative;\n height: 100%;\n background-color: var(--color-surface);\n\n &__header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-14);\n background-color: var(--color-surface-1);\n padding: var(--spacing-14);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__header-start {\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n min-width: 0;\n }\n\n &__nav-icon {\n font-size: var(--text-18);\n }\n\n &__avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n }\n\n &__title {\n font-size: var(--text-16);\n font-weight: var(--font-semibold);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__header-end {\n flex-shrink: 0;\n }\n\n &__warning-body {\n padding: var(--spacing-md);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__warning-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n margin-bottom: var(--spacing-4);\n }\n\n &__warning-icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n\n &__warning-title {\n font-weight: 600;\n font-size: var(--text-14);\n }\n\n &__warning-text {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__footer {\n padding: var(--spacing-md);\n border-top: 1px solid var(--color-surface-4);\n text-align: center;\n font-size: var(--text-12);\n color: var(--color-word-2);\n }\n\n // Modifier: fullscreen — layout full-page (Gemini-style): nessun\n // header interno, sfondo trasparente, input pill centrato.\n // Il chrome (sidebar, navigazione) è gestito dalla pagina ospite.\n &--fullscreen {\n background-color: transparent;\n\n .pk-chatbot__footer {\n border-top: none;\n padding: 0 var(--spacing-md) var(--spacing-sm) var(--spacing-md);\n }\n\n // Reading column aligned with the input (Gemini-style). The\n // constraint lives on the inner wrapper so the scroll container\n // spans the page and the scrollbar sits at its edge.\n .pk-chatbot-messages {\n // Symmetric gutters: a visible scrollbar would otherwise\n // shrink the content box on one side only, shifting the\n // column off the input axis\n scrollbar-gutter: stable both-edges;\n // Dissolve the content at both edges (works on any page\n // background, dots included — a surface-colored fade band\n // would cut through the pattern instead)\n mask-image: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--spacing-40),\n #000 calc(100% - var(--spacing-40)),\n transparent 100%\n );\n // Resting clearance so the first/last lines sit inside the\n // fully opaque zone\n padding-block: var(--spacing-48);\n }\n\n .pk-chatbot-messages__wrapper {\n width: 100%;\n max-width: 768px;\n margin-inline: auto;\n }\n\n .pk-chatbot-file-preview {\n background-color: var(--color-surface);\n }\n\n .pk-chatbot-input__notice {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-3);\n border-top: 1px solid var(--color-surface-2);\n padding: var(--spacing-10) var(--spacing-16);\n\n &::after {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-2);\n bottom: calc(-1 * var(--spacing-20));\n height: var(--spacing-20);\n left: -1px;\n right: -1px;\n }\n }\n\n .pk-chatbot-input {\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n\n &__form {\n border-radius: var(--rounded-xxxl); // pill shape\n padding: var(--spacing-12) var(--spacing-16);\n background-color: var(--color-surface-1);\n border-color: var(--color-surface-2);\n box-shadow: none;\n transition:\n border-color var(--duration-200) var(--ease-in-out),\n box-shadow var(--duration-200) var(--ease-in-out);\n\n &:has(:focus-within) {\n border-color: var(--color-surface-4);\n box-shadow: var(--shadow-sm);\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n }\n\n &__textarea {\n background-color: transparent;\n }\n }\n\n // Touch target ≥44px: estende l'hit area dei pulsanti\n // (icona 20px + padding 4px ≈ 28px) senza ingrandire l'icona.\n // Vale per l'input e per la barra della chiamata, che montano\n // lo stesso pulsante.\n .pk-chat-button {\n position: relative;\n\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-8));\n }\n }\n\n // A live call takes the place of the input, so it takes its shape,\n // its column and its height: full-bleed here would break the\n // reading axis, and a different height would make the dock jump\n // the moment the call starts.\n .pk-voice-session {\n // 60px. There is no 60 in the spacing scale and a raw `60px`\n // would stop following the root font size, which the label\n // inside does follow.\n --pk-chat-dock-height: calc(\n var(--spacing-56) + var(--spacing-4)\n );\n --pk-voice-orb-size: var(--spacing-40);\n --pk-voice-orb-bars-height: var(--spacing-20);\n --pk-voice-session-surface: var(--color-surface-1);\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n padding-inline: var(--spacing-16);\n border-color: var(--color-surface-2);\n border-radius: var(--rounded-xxxl); // pill shape\n box-shadow: none;\n\n &__timer {\n font-size: var(--text-14);\n }\n }\n }\n\n // Modifier: panel — compact chrome for a docked side panel: the host\n // container owns background and border, the header slims down and\n // drops its own surface so the chat blends into the panel.\n &--panel {\n background-color: transparent;\n\n .pk-chatbot__header {\n background-color: transparent;\n\n // Fixed height so the panel header lines up with the host's\n // own chrome. Custom properties inherit through the shadow\n // root: an embedding host can override it to match its\n // header (the default matches the platform header, 50px).\n height: var(--pk-chatbot-panel-header-height, 50px);\n align-items: center;\n padding-block: 0;\n padding-inline: var(--spacing-10);\n border-bottom: 1px solid var(--color-surface-3);\n }\n\n .pk-chatbot__title {\n font-size: var(--text-14);\n }\n\n .pk-chatbot__footer {\n border-top: none;\n padding: var(--spacing-6) var(--spacing-md);\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, inject, toRef, watch, watchEffect, useSlots } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type {\n AgentInterface,\n AgentModel,\n AgentFileUpload,\n AgentGatewayOptions,\n AgentVoiceCapability,\n ChatMessageActions,\n RevisedAnswer,\n UIChatMessage,\n AgentTools,\n } from 'models'\n import {\n useChatbotStore,\n useLocalizedString,\n provideShortUrlResolver,\n } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotViewChat from './PkChatbotViewChat.vue'\n import PkChatbotViewConversations from './PkChatbotViewConversations.vue'\n import PkChatbotViewFiles from './PkChatbotViewFiles.vue'\n import PkChatbotViewProfile from './PkChatbotViewProfile.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n import { resolveContrastColor } from './utils'\n\n // Captured in script setup to avoid TS7022 circular inference in template\n const slots = useSlots()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = defineProps<{\n baseUrl: string\n basePath?: string\n agentId: string\n agentType?: 'reasoning' | 'chat'\n userId?: string\n name?: string\n model?: string\n agentModel?: AgentModel\n agentTools?: AgentTools\n agentInterface?: AgentInterface\n actions?: ChatMessageActions[]\n revisedAnswers?: RevisedAnswer[]\n headers?: Record<string, string>\n systemTheme?: 'light' | 'dark'\n agentFileUpload?: AgentFileUpload\n /** Voice configuration from the agent settings: enables the talk button. */\n voice?: AgentVoiceCapability\n fallbackModels?: string[]\n gatewayOptions?: Partial<AgentGatewayOptions>\n externalContext?: Record<string, unknown>\n /** Layout modifier: `widget` (default) keeps the contained layout with header, `fullscreen` removes header/background for full-page usage (the page provides the chrome), `panel` keeps the header but slims it down for a docked side panel (the host provides background and border) */\n modifier?: 'widget' | 'fullscreen' | 'panel'\n /** Display name of the current user, used for the `{name}` placeholder in the fullscreen greeting */\n userName?: string\n /** Show the \"Files in this chat\" menu for hosts that don't provide the\n * profile context but whose user is authenticated (e.g. the backoffice\n * playground). The widget host relies on `canManageProfile` instead. */\n userFilesEnabled?: boolean\n }>()\n const emit = defineEmits<{\n 'message-update': [message: UIChatMessage]\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n error: [error: { type: string; title: string; status: number }]\n }>()\n\n const chatId = defineModel<string | undefined>('chatId', {\n type: String,\n default: undefined,\n })\n\n const store = useChatbotStore(props.agentId)\n\n // Resolve backend short-urls (`/api/r/<hash>`) back to their real target\n // for display in tool components. Batched + cached across the chat tree.\n provideShortUrlResolver((hashes) =>\n store.apiClient.resolveShortUrls(hashes),\n )\n\n const { messages, chat, localChatId, activeView, chatError, isDark } =\n storeToRefs(store)\n\n // Ref to store.agentId for watchers (avoids duplicate-key with agentId prop)\n const storeAgentId = toRef(store, 'agentId')\n\n const { startNewChat, stopGeneration, regenerate, navigate, sendMessage } =\n store\n\n // Optional profile-management context provided by the host widget.\n // Present only for authenticated, non-anonymous sessions.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canManageProfile = computed(\n () => profile?.canManageProfile.value ?? false,\n )\n // Anonymous users get a \"Sign in\" entry instead of the profile one,\n // provided the host filled the `view-auth` slot and the agent has at\n // least one auth method enabled.\n const canSignIn = computed(\n () =>\n !canManageProfile.value &&\n (profile?.canSignIn?.value ?? false) &&\n slots['view-auth'] !== undefined,\n )\n\n // Once the sign-in completes (the session becomes non-anonymous) leave\n // the auth view and return to the conversation.\n watch(canManageProfile, (canManage) => {\n if (canManage && activeView.value === 'auth') {\n navigate('chat')\n }\n })\n\n // Sync props → store reactively\n watchEffect(() => {\n store.baseUrl = props.baseUrl\n store.basePath = props.basePath\n store.name = props.name\n store.agentType = props.agentType\n store.userId = props.userId\n store.agentInterface = props.agentInterface\n store.agentModel = props.agentModel\n store.agentTools = props.agentTools\n store.headers = props.headers\n store.revisedAnswers = props.revisedAnswers\n store.model = props.model\n store.actionsInput = props.actions\n store.agentFileUpload = props.agentFileUpload\n store.agentVoice = props.voice\n store.fallbackModels = props.fallbackModels\n store.gatewayOptions = props.gatewayOptions\n store.externalContext = props.externalContext\n store.systemTheme = props.systemTheme\n store.hideWelcomeMessage = props.modifier === 'fullscreen'\n })\n\n // Sync chatId v-model ↔ store (bidirectional)\n // Parent → store: if the parent provides an external chatId, push it into\n // the store so the Chat instance picks it up.\n watch(\n () => chatId.value,\n (id) => {\n if (id && id !== store.localChatId) {\n store.localChatId = id\n }\n },\n { immediate: true },\n )\n // Store → parent: keep the v-model in sync whenever the store changes\n // the chatId internally (e.g. startNewChat).\n watch(localChatId, (id) => {\n chatId.value = id\n })\n\n // Emit structured errors from chat to the parent component\n watch(chatError, (error) => {\n if (!error) {\n return\n }\n try {\n const parsedError = JSON.parse(error.message) as {\n type: string\n title: string\n status: number\n }\n emit('error', parsedError)\n } catch {\n // ignore non-JSON errors\n }\n })\n\n // ui\n const isFullscreen = computed(() => props.modifier === 'fullscreen')\n const isPanel = computed(() => props.modifier === 'panel')\n const isReasoningAgent = computed(() => props.agentType === 'reasoning')\n const useColorsForAgentHeader = computed(\n () => props.agentInterface?.useColorsForAgentHeader,\n )\n // Panel mode promotes the everyday actions (new chat, history) from the\n // dropdown to direct header buttons: the dropdown then only remains for\n // the secondary entries (files, profile, sign-in) and disappears when\n // none of them applies.\n const showHeaderMenu = computed(\n () =>\n !isPanel.value ||\n canManageProfile.value ||\n canSignIn.value ||\n (!!props.userFilesEnabled && !!localChatId.value),\n )\n const themeClass = computed(() => [\n 'theme',\n `theme--${isDark.value ? 'dark' : 'light'}`,\n ])\n const mainColor = computed(() => props.agentInterface?.mainColor)\n const contrastColor = computed(() =>\n resolveContrastColor(props.agentInterface?.textColor, mainColor.value),\n )\n const footerMessage = useLocalizedString(\n () => props.agentInterface?.footerMessage,\n )\n const title = computed(() => {\n if (activeView.value === 'conversations') {\n return $t('label.conversations')\n }\n if (activeView.value === 'profile') {\n return $t('label.profile')\n }\n if (activeView.value === 'auth') {\n return $t('label.authentication')\n }\n if (activeView.value === 'files') {\n return $t('userFiles.chatFilesTitle')\n }\n return props.name\n })\n\n // state\n const isReady = computed(() => chat.value.status === 'ready')\n const activeMessage = computed(() => {\n return messages.value?.[messages.value.length - 1]\n })\n\n // expose\n defineExpose({\n startNewChat,\n regenerate,\n stopGeneration,\n sendMessage,\n })\n\n // load chat, conversations and feedbacks on agent or chat change\n watch(\n [storeAgentId, localChatId],\n async ([newAgentId, newChatId]) => {\n await store.initialize(newAgentId, newChatId)\n },\n {\n immediate: true,\n },\n )\n // load conversations when navigating to conversations view\n watch(activeView, (newView) => {\n if (newView === 'conversations') {\n store.loadConversations(storeAgentId.value)\n } else if (newView === 'files') {\n store.loadChatFiles()\n }\n })\n // emit message updates for up-to-date context in parent components\n watch(\n activeMessage,\n (message) => {\n if (message) {\n emit('message-update', message)\n }\n },\n {\n deep: true,\n immediate: true,\n },\n )\n</script>\n\n<template>\n <div\n class=\"pk-chatbot\"\n :class=\"[\n themeClass,\n {\n 'pk-chatbot--fullscreen': isFullscreen,\n 'pk-chatbot--panel': isPanel,\n },\n ]\">\n <!-- #region title -->\n <div\n v-if=\"!isFullscreen\"\n class=\"pk-chatbot__header\"\n :style=\"\n useColorsForAgentHeader\n ? {\n backgroundColor: mainColor,\n }\n : undefined\n \">\n <div class=\"pk-chatbot__header-start\">\n <template v-if=\"activeView !== 'chat'\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:arrow-left-s-line',\n title: $t('action.goBack'),\n }\"\n @click=\"navigate('chat')\" />\n <VvIcon\n :name=\"\n activeView === 'conversations'\n ? 'ri:history-line'\n : activeView === 'files'\n ? 'ri:attachment-2'\n : 'ri:user-line'\n \"\n class=\"pk-chatbot__nav-icon\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \" />\n </template>\n <!-- Panel mode: no avatar, the docked panel identifies the\n agent with the title alone -->\n <PkAvatar\n v-else-if=\"!isPanel\"\n modifiers=\"surface ring shrink-0\"\n class=\"pk-chatbot__avatar\"\n :name\n :img-src=\"agentInterface?.logo\" />\n <strong\n class=\"pk-chatbot__title\"\n :title=\"title\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \">\n {{ title }}\n </strong>\n </div>\n <VvButtonGroup class=\"pk-chatbot__header-end\" modifiers=\"compact\">\n <slot\n name=\"header-actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n <template v-if=\"isPanel\">\n <VvButton\n :modifiers=\"\n useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet'\n \"\n icon=\"ri:edit-box-line\"\n :disabled=\"!isReady\"\n :title=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n <VvButton\n v-if=\"!isReasoningAgent\"\n :modifiers=\"\n useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet'\n \"\n icon=\"ri:history-line\"\n :disabled=\"!isReady\"\n :title=\"$t('action.viewRecentChats')\"\n @click=\"navigate('conversations')\" />\n </template>\n <VvDropdown\n v-if=\"showHeaderMenu\"\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:more-2-fill',\n disabled: !isReady,\n title: $t('action.moreActions'),\n }\" />\n <template #items>\n <VvDropdownAction\n v-if=\"!isPanel\"\n @click=\"startNewChat()\">\n <VvIcon name=\"ri:edit-box-line\" />\n {{ $t('action.startNewChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"!isReasoningAgent && !isPanel\"\n @click=\"navigate('conversations')\">\n <VvIcon name=\"ri:history-line\" />\n {{ $t('action.viewRecentChats') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"\n (canManageProfile || userFilesEnabled) &&\n localChatId\n \"\n @click=\"navigate('files')\">\n <VvIcon name=\"ri:attachment-2\" />\n {{ $t('userFiles.chatFilesTitle') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"canManageProfile\"\n @click=\"navigate('profile')\">\n <VvIcon name=\"ri:user-line\" />\n {{ $t('label.profile') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-else-if=\"canSignIn\"\n @click=\"navigate('auth')\">\n <VvIcon name=\"ri:account-circle-line\" />\n {{ $t('action.signIn') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n <slot\n name=\"actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n </VvButtonGroup>\n </div>\n <!-- #endregion -->\n\n <div v-if=\"isReasoningAgent\">\n <div class=\"pk-chatbot__warning-body\">\n <VvAlert modifiers=\"callout\">\n <div class=\"pk-chatbot__warning-header\">\n <VvIcon\n name=\"ri:alert-line\"\n class=\"pk-chatbot__warning-icon\" />\n <strong class=\"pk-chatbot__warning-title\">\n {{ $t('message.warning') }}\n </strong>\n </div>\n <p class=\"pk-chatbot__warning-text\">\n {{ $t('message.reasoningAgent') }}\n </p>\n </VvAlert>\n </div>\n </div>\n\n <!-- #region views -->\n <PkChatbotViewChat\n v-if=\"activeView === 'chat'\"\n :agent-id\n :modifier\n :user-name\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\">\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewChat>\n <PkChatbotViewConversations\n v-else-if=\"activeView === 'conversations'\"\n :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewConversations>\n <PkChatbotViewFiles v-else-if=\"activeView === 'files'\" :agent-id />\n <div\n v-else-if=\"activeView === 'auth'\"\n class=\"pk-chatbot__view-auth flex-1 min-h-0 overflow-y-auto\">\n <!-- Sign-in form provided by the host widget (owns the auth client) -->\n <slot name=\"view-auth\" />\n </div>\n <PkChatbotViewProfile v-else :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewProfile>\n <!-- #endregion -->\n\n <!-- #region footer -->\n <div v-if=\"footerMessage\" class=\"pk-chatbot__footer\">\n <PkStreamingMarkdown :markdown=\"footerMessage\" class=\"wysiwyg\" />\n </div>\n <!-- #endregion -->\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot {\n display: flex;\n flex-direction: column;\n position: relative;\n height: 100%;\n background-color: var(--color-surface);\n\n &__header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-14);\n background-color: var(--color-surface-1);\n padding: var(--spacing-14);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__header-start {\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n min-width: 0;\n }\n\n &__nav-icon {\n font-size: var(--text-18);\n }\n\n &__avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n }\n\n &__title {\n font-size: var(--text-16);\n font-weight: var(--font-semibold);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__header-end {\n flex-shrink: 0;\n }\n\n &__warning-body {\n padding: var(--spacing-md);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__warning-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n margin-bottom: var(--spacing-4);\n }\n\n &__warning-icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n\n &__warning-title {\n font-weight: 600;\n font-size: var(--text-14);\n }\n\n &__warning-text {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__footer {\n padding: var(--spacing-md);\n border-top: 1px solid var(--color-surface-4);\n text-align: center;\n font-size: var(--text-12);\n color: var(--color-word-2);\n }\n\n // Modifier: fullscreen — layout full-page (Gemini-style): nessun\n // header interno, sfondo trasparente, input pill centrato.\n // Il chrome (sidebar, navigazione) è gestito dalla pagina ospite.\n &--fullscreen {\n background-color: transparent;\n\n .pk-chatbot__footer {\n border-top: none;\n padding: 0 var(--spacing-md) var(--spacing-sm) var(--spacing-md);\n }\n\n // Reading column aligned with the input (Gemini-style). The\n // constraint lives on the inner wrapper so the scroll container\n // spans the page and the scrollbar sits at its edge.\n .pk-chatbot-messages {\n // Symmetric gutters: a visible scrollbar would otherwise\n // shrink the content box on one side only, shifting the\n // column off the input axis\n scrollbar-gutter: stable both-edges;\n // Dissolve the content at both edges (works on any page\n // background, dots included — a surface-colored fade band\n // would cut through the pattern instead)\n mask-image: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--spacing-40),\n #000 calc(100% - var(--spacing-40)),\n transparent 100%\n );\n // Resting clearance so the first/last lines sit inside the\n // fully opaque zone\n padding-block: var(--spacing-48);\n }\n\n .pk-chatbot-messages__wrapper {\n width: 100%;\n max-width: 768px;\n margin-inline: auto;\n }\n\n .pk-chatbot-file-preview {\n background-color: var(--color-surface);\n }\n\n .pk-chatbot-input__notice {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-3);\n border-top: 1px solid var(--color-surface-2);\n padding: var(--spacing-10) var(--spacing-16);\n\n &::after {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-2);\n bottom: calc(-1 * var(--spacing-20));\n height: var(--spacing-20);\n left: -1px;\n right: -1px;\n }\n }\n\n .pk-chatbot-input {\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n\n &__form {\n border-radius: var(--rounded-xxxl); // pill shape\n padding: var(--spacing-12) var(--spacing-16);\n background-color: var(--color-surface-1);\n border-color: var(--color-surface-2);\n box-shadow: none;\n transition:\n border-color var(--duration-200) var(--ease-in-out),\n box-shadow var(--duration-200) var(--ease-in-out);\n\n &:has(:focus-within) {\n border-color: var(--color-surface-4);\n box-shadow: var(--shadow-sm);\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n }\n\n &__textarea {\n background-color: transparent;\n }\n }\n\n // Touch target ≥44px: estende l'hit area dei pulsanti\n // (icona 20px + padding 4px ≈ 28px) senza ingrandire l'icona.\n // Vale per l'input e per la barra della chiamata, che montano\n // lo stesso pulsante.\n .pk-chat-button {\n position: relative;\n\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-8));\n }\n }\n\n // A live call takes the place of the input, so it takes its shape,\n // its column and its height: full-bleed here would break the\n // reading axis, and a different height would make the dock jump\n // the moment the call starts.\n .pk-voice-session {\n // 60px. There is no 60 in the spacing scale and a raw `60px`\n // would stop following the root font size, which the label\n // inside does follow.\n --pk-chat-dock-height: calc(\n var(--spacing-56) + var(--spacing-4)\n );\n --pk-voice-orb-size: var(--spacing-40);\n --pk-voice-orb-bars-height: var(--spacing-20);\n --pk-voice-session-surface: var(--color-surface-1);\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n padding-inline: var(--spacing-16);\n border-color: var(--color-surface-2);\n border-radius: var(--rounded-xxxl); // pill shape\n box-shadow: none;\n\n &__timer {\n font-size: var(--text-14);\n }\n }\n }\n\n // Modifier: panel — compact chrome for a docked side panel: the host\n // container owns background and border, the header slims down and\n // drops its own surface so the chat blends into the panel.\n &--panel {\n background-color: transparent;\n\n .pk-chatbot__header {\n background-color: transparent;\n\n // Fixed height so the panel header lines up with the host's\n // own chrome. Custom properties inherit through the shadow\n // root: an embedding host can override it to match its\n // header (the default matches the platform header, 50px).\n height: var(--pk-chatbot-panel-header-height, 50px);\n align-items: center;\n padding-block: 0;\n padding-inline: var(--spacing-10);\n border-bottom: 1px solid var(--color-surface-3);\n }\n\n .pk-chatbot__title {\n font-size: var(--text-14);\n }\n\n .pk-chatbot__footer {\n border-top: none;\n padding: var(--spacing-6) var(--spacing-md);\n }\n }\n }\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PkChatbot-CHe6t-LR.js","names":[],"sources":["../../../../packages/components/src/chat/PkChatbot.vue","../../../../packages/components/src/chat/PkChatbot.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, inject, toRef, watch, watchEffect, useSlots } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type {\n AgentInterface,\n AgentModel,\n AgentFileUpload,\n AgentGatewayOptions,\n AgentVoiceCapability,\n ChatMessageActions,\n RevisedAnswer,\n UIChatMessage,\n AgentTools,\n } from 'models'\n import {\n useChatbotStore,\n useLocalizedString,\n provideShortUrlResolver,\n } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotViewChat from './PkChatbotViewChat.vue'\n import PkChatbotViewConversations from './PkChatbotViewConversations.vue'\n import PkChatbotViewFiles from './PkChatbotViewFiles.vue'\n import PkChatbotViewProfile from './PkChatbotViewProfile.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n import { resolveContrastColor } from './utils'\n\n // Captured in script setup to avoid TS7022 circular inference in template\n const slots = useSlots()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = defineProps<{\n baseUrl: string\n basePath?: string\n agentId: string\n agentType?: 'reasoning' | 'chat'\n userId?: string\n name?: string\n model?: string\n agentModel?: AgentModel\n agentTools?: AgentTools\n agentInterface?: AgentInterface\n actions?: ChatMessageActions[]\n revisedAnswers?: RevisedAnswer[]\n headers?: Record<string, string>\n systemTheme?: 'light' | 'dark'\n agentFileUpload?: AgentFileUpload\n /** Voice configuration from the agent settings: enables the talk button. */\n voice?: AgentVoiceCapability\n fallbackModels?: string[]\n gatewayOptions?: Partial<AgentGatewayOptions>\n externalContext?: Record<string, unknown>\n /** Layout modifier: `widget` (default) keeps the contained layout with header, `fullscreen` removes header/background for full-page usage (the page provides the chrome), `panel` keeps the header but slims it down for a docked side panel (the host provides background and border) */\n modifier?: 'widget' | 'fullscreen' | 'panel'\n /** Display name of the current user, used for the `{name}` placeholder in the fullscreen greeting */\n userName?: string\n /** Show the \"Files in this chat\" menu for hosts that don't provide the\n * profile context but whose user is authenticated (e.g. the backoffice\n * playground). The widget host relies on `canManageProfile` instead. */\n userFilesEnabled?: boolean\n }>()\n const emit = defineEmits<{\n 'message-update': [message: UIChatMessage]\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n error: [error: { type: string; title: string; status: number }]\n }>()\n\n const chatId = defineModel<string | undefined>('chatId', {\n type: String,\n default: undefined,\n })\n\n const store = useChatbotStore(props.agentId)\n\n // Resolve backend short-urls (`/api/r/<hash>`) back to their real target\n // for display in tool components. Batched + cached across the chat tree.\n provideShortUrlResolver((hashes) =>\n store.apiClient.resolveShortUrls(hashes),\n )\n\n const { messages, chat, localChatId, activeView, chatError, isDark } =\n storeToRefs(store)\n\n // Ref to store.agentId for watchers (avoids duplicate-key with agentId prop)\n const storeAgentId = toRef(store, 'agentId')\n\n const { startNewChat, stopGeneration, regenerate, navigate, sendMessage } =\n store\n\n // Optional profile-management context provided by the host widget.\n // Present only for authenticated, non-anonymous sessions.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canManageProfile = computed(\n () => profile?.canManageProfile.value ?? false,\n )\n // Anonymous users get a \"Sign in\" entry instead of the profile one,\n // provided the host filled the `view-auth` slot and the agent has at\n // least one auth method enabled.\n const canSignIn = computed(\n () =>\n !canManageProfile.value &&\n (profile?.canSignIn?.value ?? false) &&\n slots['view-auth'] !== undefined,\n )\n\n // Once the sign-in completes (the session becomes non-anonymous) leave\n // the auth view and return to the conversation.\n watch(canManageProfile, (canManage) => {\n if (canManage && activeView.value === 'auth') {\n navigate('chat')\n }\n })\n\n // Sync props → store reactively\n watchEffect(() => {\n store.baseUrl = props.baseUrl\n store.basePath = props.basePath\n store.name = props.name\n store.agentType = props.agentType\n store.userId = props.userId\n store.agentInterface = props.agentInterface\n store.agentModel = props.agentModel\n store.agentTools = props.agentTools\n store.headers = props.headers\n store.revisedAnswers = props.revisedAnswers\n store.model = props.model\n store.actionsInput = props.actions\n store.agentFileUpload = props.agentFileUpload\n store.agentVoice = props.voice\n store.fallbackModels = props.fallbackModels\n store.gatewayOptions = props.gatewayOptions\n store.externalContext = props.externalContext\n store.systemTheme = props.systemTheme\n store.hideWelcomeMessage = props.modifier === 'fullscreen'\n })\n\n // Sync chatId v-model ↔ store (bidirectional)\n // Parent → store: if the parent provides an external chatId, push it into\n // the store so the Chat instance picks it up.\n watch(\n () => chatId.value,\n (id) => {\n if (id && id !== store.localChatId) {\n store.localChatId = id\n }\n },\n { immediate: true },\n )\n // Store → parent: keep the v-model in sync whenever the store changes\n // the chatId internally (e.g. startNewChat).\n watch(localChatId, (id) => {\n chatId.value = id\n })\n\n // Emit structured errors from chat to the parent component\n watch(chatError, (error) => {\n if (!error) {\n return\n }\n try {\n const parsedError = JSON.parse(error.message) as {\n type: string\n title: string\n status: number\n }\n emit('error', parsedError)\n } catch {\n // ignore non-JSON errors\n }\n })\n\n // ui\n const isFullscreen = computed(() => props.modifier === 'fullscreen')\n const isPanel = computed(() => props.modifier === 'panel')\n const isReasoningAgent = computed(() => props.agentType === 'reasoning')\n const useColorsForAgentHeader = computed(\n () => props.agentInterface?.useColorsForAgentHeader,\n )\n // Panel mode promotes the everyday actions (new chat, history) from the\n // dropdown to direct header buttons: the dropdown then only remains for\n // the secondary entries (files, profile, sign-in) and disappears when\n // none of them applies.\n const showHeaderMenu = computed(\n () =>\n !isPanel.value ||\n canManageProfile.value ||\n canSignIn.value ||\n (!!props.userFilesEnabled && !!localChatId.value),\n )\n const themeClass = computed(() => [\n 'theme',\n `theme--${isDark.value ? 'dark' : 'light'}`,\n ])\n const mainColor = computed(() => props.agentInterface?.mainColor)\n const contrastColor = computed(() =>\n resolveContrastColor(props.agentInterface?.textColor, mainColor.value),\n )\n const footerMessage = useLocalizedString(\n () => props.agentInterface?.footerMessage,\n )\n const title = computed(() => {\n if (activeView.value === 'conversations') {\n return $t('label.conversations')\n }\n if (activeView.value === 'profile') {\n return $t('label.profile')\n }\n if (activeView.value === 'auth') {\n return $t('label.authentication')\n }\n if (activeView.value === 'files') {\n return $t('userFiles.chatFilesTitle')\n }\n return props.name\n })\n\n // state\n const isReady = computed(() => chat.value.status === 'ready')\n const activeMessage = computed(() => {\n return messages.value?.[messages.value.length - 1]\n })\n\n // expose\n defineExpose({\n startNewChat,\n regenerate,\n stopGeneration,\n sendMessage,\n })\n\n // load chat, conversations and feedbacks on agent or chat change\n watch(\n [storeAgentId, localChatId],\n async ([newAgentId, newChatId]) => {\n await store.initialize(newAgentId, newChatId)\n },\n {\n immediate: true,\n },\n )\n // load conversations when navigating to conversations view\n watch(activeView, (newView) => {\n if (newView === 'conversations') {\n store.loadConversations(storeAgentId.value)\n } else if (newView === 'files') {\n store.loadChatFiles()\n }\n })\n // emit message updates for up-to-date context in parent components\n watch(\n activeMessage,\n (message) => {\n if (message) {\n emit('message-update', message)\n }\n },\n {\n deep: true,\n immediate: true,\n },\n )\n</script>\n\n<template>\n <div\n class=\"pk-chatbot\"\n :class=\"[\n themeClass,\n {\n 'pk-chatbot--fullscreen': isFullscreen,\n 'pk-chatbot--panel': isPanel,\n },\n ]\">\n <!-- #region title -->\n <div\n v-if=\"!isFullscreen\"\n class=\"pk-chatbot__header\"\n :style=\"\n useColorsForAgentHeader\n ? {\n backgroundColor: mainColor,\n }\n : undefined\n \">\n <div class=\"pk-chatbot__header-start\">\n <template v-if=\"activeView !== 'chat'\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:arrow-left-s-line',\n title: $t('action.goBack'),\n }\"\n @click=\"navigate('chat')\" />\n <VvIcon\n :name=\"\n activeView === 'conversations'\n ? 'ri:history-line'\n : activeView === 'files'\n ? 'ri:attachment-2'\n : 'ri:user-line'\n \"\n class=\"pk-chatbot__nav-icon\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \" />\n </template>\n <!-- Panel mode: no avatar, the docked panel identifies the\n agent with the title alone -->\n <PkAvatar\n v-else-if=\"!isPanel\"\n modifiers=\"surface ring shrink-0\"\n class=\"pk-chatbot__avatar\"\n :name\n :img-src=\"agentInterface?.logo\" />\n <strong\n class=\"pk-chatbot__title\"\n :title=\"title\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \">\n {{ title }}\n </strong>\n </div>\n <VvButtonGroup class=\"pk-chatbot__header-end\" modifiers=\"compact\">\n <slot\n name=\"header-actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n <template v-if=\"isPanel\">\n <VvButton\n :modifiers=\"\n useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet'\n \"\n icon=\"ri:edit-box-line\"\n :disabled=\"!isReady\"\n :title=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n <VvButton\n v-if=\"!isReasoningAgent\"\n :modifiers=\"\n useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet'\n \"\n icon=\"ri:history-line\"\n :disabled=\"!isReady\"\n :title=\"$t('action.viewRecentChats')\"\n @click=\"navigate('conversations')\" />\n </template>\n <VvDropdown\n v-if=\"showHeaderMenu\"\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:more-2-fill',\n disabled: !isReady,\n title: $t('action.moreActions'),\n }\" />\n <template #items>\n <VvDropdownAction\n v-if=\"!isPanel\"\n @click=\"startNewChat()\">\n <VvIcon name=\"ri:edit-box-line\" />\n {{ $t('action.startNewChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"!isReasoningAgent && !isPanel\"\n @click=\"navigate('conversations')\">\n <VvIcon name=\"ri:history-line\" />\n {{ $t('action.viewRecentChats') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"\n (canManageProfile || userFilesEnabled) &&\n localChatId\n \"\n @click=\"navigate('files')\">\n <VvIcon name=\"ri:attachment-2\" />\n {{ $t('userFiles.chatFilesTitle') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"canManageProfile\"\n @click=\"navigate('profile')\">\n <VvIcon name=\"ri:user-line\" />\n {{ $t('label.profile') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-else-if=\"canSignIn\"\n @click=\"navigate('auth')\">\n <VvIcon name=\"ri:account-circle-line\" />\n {{ $t('action.signIn') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n <slot\n name=\"actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n </VvButtonGroup>\n </div>\n <!-- #endregion -->\n\n <div v-if=\"isReasoningAgent\">\n <div class=\"pk-chatbot__warning-body\">\n <VvAlert modifiers=\"callout\">\n <div class=\"pk-chatbot__warning-header\">\n <VvIcon\n name=\"ri:alert-line\"\n class=\"pk-chatbot__warning-icon\" />\n <strong class=\"pk-chatbot__warning-title\">\n {{ $t('message.warning') }}\n </strong>\n </div>\n <p class=\"pk-chatbot__warning-text\">\n {{ $t('message.reasoningAgent') }}\n </p>\n </VvAlert>\n </div>\n </div>\n\n <!-- #region views -->\n <PkChatbotViewChat\n v-if=\"activeView === 'chat'\"\n :agent-id\n :modifier\n :user-name\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\">\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewChat>\n <PkChatbotViewConversations\n v-else-if=\"activeView === 'conversations'\"\n :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewConversations>\n <PkChatbotViewFiles v-else-if=\"activeView === 'files'\" :agent-id />\n <div\n v-else-if=\"activeView === 'auth'\"\n class=\"pk-chatbot__view-auth flex-1 min-h-0 overflow-y-auto\">\n <!-- Sign-in form provided by the host widget (owns the auth client) -->\n <slot name=\"view-auth\" />\n </div>\n <PkChatbotViewProfile v-else :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewProfile>\n <!-- #endregion -->\n\n <!-- #region footer -->\n <div v-if=\"footerMessage\" class=\"pk-chatbot__footer\">\n <PkStreamingMarkdown :markdown=\"footerMessage\" class=\"wysiwyg\" />\n </div>\n <!-- #endregion -->\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot {\n display: flex;\n flex-direction: column;\n position: relative;\n height: 100%;\n background-color: var(--color-surface);\n\n &__header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-14);\n background-color: var(--color-surface-1);\n padding: var(--spacing-14);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__header-start {\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n min-width: 0;\n }\n\n &__nav-icon {\n font-size: var(--text-18);\n }\n\n &__avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n }\n\n &__title {\n font-size: var(--text-16);\n font-weight: var(--font-semibold);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__header-end {\n flex-shrink: 0;\n }\n\n &__warning-body {\n padding: var(--spacing-md);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__warning-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n margin-bottom: var(--spacing-4);\n }\n\n &__warning-icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n\n &__warning-title {\n font-weight: 600;\n font-size: var(--text-14);\n }\n\n &__warning-text {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__footer {\n padding: var(--spacing-md);\n border-top: 1px solid var(--color-surface-4);\n text-align: center;\n font-size: var(--text-12);\n color: var(--color-word-2);\n }\n\n // Modifier: fullscreen — layout full-page (Gemini-style): nessun\n // header interno, sfondo trasparente, input pill centrato.\n // Il chrome (sidebar, navigazione) è gestito dalla pagina ospite.\n &--fullscreen {\n background-color: transparent;\n\n .pk-chatbot__footer {\n border-top: none;\n padding: 0 var(--spacing-md) var(--spacing-sm) var(--spacing-md);\n }\n\n // Reading column aligned with the input (Gemini-style). The\n // constraint lives on the inner wrapper so the scroll container\n // spans the page and the scrollbar sits at its edge.\n .pk-chatbot-messages {\n // Symmetric gutters: a visible scrollbar would otherwise\n // shrink the content box on one side only, shifting the\n // column off the input axis\n scrollbar-gutter: stable both-edges;\n // Dissolve the content at both edges (works on any page\n // background, dots included — a surface-colored fade band\n // would cut through the pattern instead)\n mask-image: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--spacing-40),\n #000 calc(100% - var(--spacing-40)),\n transparent 100%\n );\n // Resting clearance so the first/last lines sit inside the\n // fully opaque zone\n padding-block: var(--spacing-48);\n }\n\n .pk-chatbot-messages__wrapper {\n width: 100%;\n max-width: 768px;\n margin-inline: auto;\n }\n\n .pk-chatbot-file-preview {\n background-color: var(--color-surface);\n }\n\n .pk-chatbot-input__notice {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-3);\n border-top: 1px solid var(--color-surface-2);\n padding: var(--spacing-10) var(--spacing-16);\n\n &::after {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-2);\n bottom: calc(-1 * var(--spacing-20));\n height: var(--spacing-20);\n left: -1px;\n right: -1px;\n }\n }\n\n .pk-chatbot-input {\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n\n &__form {\n border-radius: var(--rounded-xxxl); // pill shape\n padding: var(--spacing-12) var(--spacing-16);\n background-color: var(--color-surface-1);\n border-color: var(--color-surface-2);\n box-shadow: none;\n transition:\n border-color var(--duration-200) var(--ease-in-out),\n box-shadow var(--duration-200) var(--ease-in-out);\n\n &:has(:focus-within) {\n border-color: var(--color-surface-4);\n box-shadow: var(--shadow-sm);\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n }\n\n &__textarea {\n background-color: transparent;\n }\n }\n\n // Touch target ≥44px: estende l'hit area dei pulsanti\n // (icona 20px + padding 4px ≈ 28px) senza ingrandire l'icona.\n // Vale per l'input e per la barra della chiamata, che montano\n // lo stesso pulsante.\n .pk-chat-button {\n position: relative;\n\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-8));\n }\n }\n\n // A live call takes the place of the input, so it takes its shape,\n // its column and its height: full-bleed here would break the\n // reading axis, and a different height would make the dock jump\n // the moment the call starts.\n .pk-voice-session {\n // 60px. There is no 60 in the spacing scale and a raw `60px`\n // would stop following the root font size, which the label\n // inside does follow.\n --pk-chat-dock-height: calc(\n var(--spacing-56) + var(--spacing-4)\n );\n --pk-voice-orb-size: var(--spacing-40);\n --pk-voice-orb-bars-height: var(--spacing-20);\n --pk-voice-session-surface: var(--color-surface-1);\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n padding-inline: var(--spacing-16);\n border-color: var(--color-surface-2);\n border-radius: var(--rounded-xxxl); // pill shape\n box-shadow: none;\n\n &__timer {\n font-size: var(--text-14);\n }\n }\n }\n\n // Modifier: panel — compact chrome for a docked side panel: the host\n // container owns background and border, the header slims down and\n // drops its own surface so the chat blends into the panel.\n &--panel {\n background-color: transparent;\n\n .pk-chatbot__header {\n background-color: transparent;\n\n // Fixed height so the panel header lines up with the host's\n // own chrome. Custom properties inherit through the shadow\n // root: an embedding host can override it to match its\n // header (the default matches the platform header, 50px).\n height: var(--pk-chatbot-panel-header-height, 50px);\n align-items: center;\n padding-block: 0;\n padding-inline: var(--spacing-10);\n border-bottom: 1px solid var(--color-surface-3);\n }\n\n .pk-chatbot__title {\n font-size: var(--text-14);\n }\n\n .pk-chatbot__footer {\n border-top: none;\n padding: var(--spacing-6) var(--spacing-md);\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, inject, toRef, watch, watchEffect, useSlots } from 'vue'\n import { storeToRefs } from 'pinia'\n import { useI18n } from 'vue-i18n'\n import type {\n AgentInterface,\n AgentModel,\n AgentFileUpload,\n AgentGatewayOptions,\n AgentVoiceCapability,\n ChatMessageActions,\n RevisedAnswer,\n UIChatMessage,\n AgentTools,\n } from 'models'\n import {\n useChatbotStore,\n useLocalizedString,\n provideShortUrlResolver,\n } from 'composables'\n import PkAvatar from './PkAvatar.vue'\n import PkChatbotViewChat from './PkChatbotViewChat.vue'\n import PkChatbotViewConversations from './PkChatbotViewConversations.vue'\n import PkChatbotViewFiles from './PkChatbotViewFiles.vue'\n import PkChatbotViewProfile from './PkChatbotViewProfile.vue'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import { ChatbotProfileContextKey } from './chatbotProfileContext'\n import { resolveContrastColor } from './utils'\n\n // Captured in script setup to avoid TS7022 circular inference in template\n const slots = useSlots()\n\n const { t: $t } = useI18n({\n useScope: 'global',\n })\n\n const props = defineProps<{\n baseUrl: string\n basePath?: string\n agentId: string\n agentType?: 'reasoning' | 'chat'\n userId?: string\n name?: string\n model?: string\n agentModel?: AgentModel\n agentTools?: AgentTools\n agentInterface?: AgentInterface\n actions?: ChatMessageActions[]\n revisedAnswers?: RevisedAnswer[]\n headers?: Record<string, string>\n systemTheme?: 'light' | 'dark'\n agentFileUpload?: AgentFileUpload\n /** Voice configuration from the agent settings: enables the talk button. */\n voice?: AgentVoiceCapability\n fallbackModels?: string[]\n gatewayOptions?: Partial<AgentGatewayOptions>\n externalContext?: Record<string, unknown>\n /** Layout modifier: `widget` (default) keeps the contained layout with header, `fullscreen` removes header/background for full-page usage (the page provides the chrome), `panel` keeps the header but slims it down for a docked side panel (the host provides background and border) */\n modifier?: 'widget' | 'fullscreen' | 'panel'\n /** Display name of the current user, used for the `{name}` placeholder in the fullscreen greeting */\n userName?: string\n /** Show the \"Files in this chat\" menu for hosts that don't provide the\n * profile context but whose user is authenticated (e.g. the backoffice\n * playground). The widget host relies on `canManageProfile` instead. */\n userFilesEnabled?: boolean\n }>()\n const emit = defineEmits<{\n 'message-update': [message: UIChatMessage]\n 'show-info': [message: UIChatMessage]\n revise: [message: UIChatMessage]\n error: [error: { type: string; title: string; status: number }]\n }>()\n\n const chatId = defineModel<string | undefined>('chatId', {\n type: String,\n default: undefined,\n })\n\n const store = useChatbotStore(props.agentId)\n\n // Resolve backend short-urls (`/api/r/<hash>`) back to their real target\n // for display in tool components. Batched + cached across the chat tree.\n provideShortUrlResolver((hashes) =>\n store.apiClient.resolveShortUrls(hashes),\n )\n\n const { messages, chat, localChatId, activeView, chatError, isDark } =\n storeToRefs(store)\n\n // Ref to store.agentId for watchers (avoids duplicate-key with agentId prop)\n const storeAgentId = toRef(store, 'agentId')\n\n const { startNewChat, stopGeneration, regenerate, navigate, sendMessage } =\n store\n\n // Optional profile-management context provided by the host widget.\n // Present only for authenticated, non-anonymous sessions.\n const profile = inject(ChatbotProfileContextKey, undefined)\n const canManageProfile = computed(\n () => profile?.canManageProfile.value ?? false,\n )\n // Anonymous users get a \"Sign in\" entry instead of the profile one,\n // provided the host filled the `view-auth` slot and the agent has at\n // least one auth method enabled.\n const canSignIn = computed(\n () =>\n !canManageProfile.value &&\n (profile?.canSignIn?.value ?? false) &&\n slots['view-auth'] !== undefined,\n )\n\n // Once the sign-in completes (the session becomes non-anonymous) leave\n // the auth view and return to the conversation.\n watch(canManageProfile, (canManage) => {\n if (canManage && activeView.value === 'auth') {\n navigate('chat')\n }\n })\n\n // Sync props → store reactively\n watchEffect(() => {\n store.baseUrl = props.baseUrl\n store.basePath = props.basePath\n store.name = props.name\n store.agentType = props.agentType\n store.userId = props.userId\n store.agentInterface = props.agentInterface\n store.agentModel = props.agentModel\n store.agentTools = props.agentTools\n store.headers = props.headers\n store.revisedAnswers = props.revisedAnswers\n store.model = props.model\n store.actionsInput = props.actions\n store.agentFileUpload = props.agentFileUpload\n store.agentVoice = props.voice\n store.fallbackModels = props.fallbackModels\n store.gatewayOptions = props.gatewayOptions\n store.externalContext = props.externalContext\n store.systemTheme = props.systemTheme\n store.hideWelcomeMessage = props.modifier === 'fullscreen'\n })\n\n // Sync chatId v-model ↔ store (bidirectional)\n // Parent → store: if the parent provides an external chatId, push it into\n // the store so the Chat instance picks it up.\n watch(\n () => chatId.value,\n (id) => {\n if (id && id !== store.localChatId) {\n store.localChatId = id\n }\n },\n { immediate: true },\n )\n // Store → parent: keep the v-model in sync whenever the store changes\n // the chatId internally (e.g. startNewChat).\n watch(localChatId, (id) => {\n chatId.value = id\n })\n\n // Emit structured errors from chat to the parent component\n watch(chatError, (error) => {\n if (!error) {\n return\n }\n try {\n const parsedError = JSON.parse(error.message) as {\n type: string\n title: string\n status: number\n }\n emit('error', parsedError)\n } catch {\n // ignore non-JSON errors\n }\n })\n\n // ui\n const isFullscreen = computed(() => props.modifier === 'fullscreen')\n const isPanel = computed(() => props.modifier === 'panel')\n const isReasoningAgent = computed(() => props.agentType === 'reasoning')\n const useColorsForAgentHeader = computed(\n () => props.agentInterface?.useColorsForAgentHeader,\n )\n // Panel mode promotes the everyday actions (new chat, history) from the\n // dropdown to direct header buttons: the dropdown then only remains for\n // the secondary entries (files, profile, sign-in) and disappears when\n // none of them applies.\n const showHeaderMenu = computed(\n () =>\n !isPanel.value ||\n canManageProfile.value ||\n canSignIn.value ||\n (!!props.userFilesEnabled && !!localChatId.value),\n )\n const themeClass = computed(() => [\n 'theme',\n `theme--${isDark.value ? 'dark' : 'light'}`,\n ])\n const mainColor = computed(() => props.agentInterface?.mainColor)\n const contrastColor = computed(() =>\n resolveContrastColor(props.agentInterface?.textColor, mainColor.value),\n )\n const footerMessage = useLocalizedString(\n () => props.agentInterface?.footerMessage,\n )\n const title = computed(() => {\n if (activeView.value === 'conversations') {\n return $t('label.conversations')\n }\n if (activeView.value === 'profile') {\n return $t('label.profile')\n }\n if (activeView.value === 'auth') {\n return $t('label.authentication')\n }\n if (activeView.value === 'files') {\n return $t('userFiles.chatFilesTitle')\n }\n return props.name\n })\n\n // state\n const isReady = computed(() => chat.value.status === 'ready')\n const activeMessage = computed(() => {\n return messages.value?.[messages.value.length - 1]\n })\n\n // expose\n defineExpose({\n startNewChat,\n regenerate,\n stopGeneration,\n sendMessage,\n })\n\n // load chat, conversations and feedbacks on agent or chat change\n watch(\n [storeAgentId, localChatId],\n async ([newAgentId, newChatId]) => {\n await store.initialize(newAgentId, newChatId)\n },\n {\n immediate: true,\n },\n )\n // load conversations when navigating to conversations view\n watch(activeView, (newView) => {\n if (newView === 'conversations') {\n store.loadConversations(storeAgentId.value)\n } else if (newView === 'files') {\n store.loadChatFiles()\n }\n })\n // emit message updates for up-to-date context in parent components\n watch(\n activeMessage,\n (message) => {\n if (message) {\n emit('message-update', message)\n }\n },\n {\n deep: true,\n immediate: true,\n },\n )\n</script>\n\n<template>\n <div\n class=\"pk-chatbot\"\n :class=\"[\n themeClass,\n {\n 'pk-chatbot--fullscreen': isFullscreen,\n 'pk-chatbot--panel': isPanel,\n },\n ]\">\n <!-- #region title -->\n <div\n v-if=\"!isFullscreen\"\n class=\"pk-chatbot__header\"\n :style=\"\n useColorsForAgentHeader\n ? {\n backgroundColor: mainColor,\n }\n : undefined\n \">\n <div class=\"pk-chatbot__header-start\">\n <template v-if=\"activeView !== 'chat'\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:arrow-left-s-line',\n title: $t('action.goBack'),\n }\"\n @click=\"navigate('chat')\" />\n <VvIcon\n :name=\"\n activeView === 'conversations'\n ? 'ri:history-line'\n : activeView === 'files'\n ? 'ri:attachment-2'\n : 'ri:user-line'\n \"\n class=\"pk-chatbot__nav-icon\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \" />\n </template>\n <!-- Panel mode: no avatar, the docked panel identifies the\n agent with the title alone -->\n <PkAvatar\n v-else-if=\"!isPanel\"\n modifiers=\"surface ring shrink-0\"\n class=\"pk-chatbot__avatar\"\n :name\n :img-src=\"agentInterface?.logo\" />\n <strong\n class=\"pk-chatbot__title\"\n :title=\"title\"\n :style=\"\n useColorsForAgentHeader\n ? {\n color: contrastColor,\n }\n : undefined\n \">\n {{ title }}\n </strong>\n </div>\n <VvButtonGroup class=\"pk-chatbot__header-end\" modifiers=\"compact\">\n <slot\n name=\"header-actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n <template v-if=\"isPanel\">\n <VvButton\n :modifiers=\"\n useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet'\n \"\n icon=\"ri:edit-box-line\"\n :disabled=\"!isReady\"\n :title=\"$t('action.startNewChat')\"\n @click=\"startNewChat()\" />\n <VvButton\n v-if=\"!isReasoningAgent\"\n :modifiers=\"\n useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet'\n \"\n icon=\"ri:history-line\"\n :disabled=\"!isReady\"\n :title=\"$t('action.viewRecentChats')\"\n @click=\"navigate('conversations')\" />\n </template>\n <VvDropdown\n v-if=\"showHeaderMenu\"\n v-bind=\"{\n placement: 'bottom-end',\n modifiers: 'menu',\n flip: true,\n offset: 3,\n strategy: 'fixed',\n }\">\n <VvButton\n v-bind=\"{\n modifiers: useColorsForAgentHeader\n ? `action-quiet-${contrastColor}`\n : 'action-quiet',\n icon: 'ri:more-2-fill',\n disabled: !isReady,\n title: $t('action.moreActions'),\n }\" />\n <template #items>\n <VvDropdownAction\n v-if=\"!isPanel\"\n @click=\"startNewChat()\">\n <VvIcon name=\"ri:edit-box-line\" />\n {{ $t('action.startNewChat') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"!isReasoningAgent && !isPanel\"\n @click=\"navigate('conversations')\">\n <VvIcon name=\"ri:history-line\" />\n {{ $t('action.viewRecentChats') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"\n (canManageProfile || userFilesEnabled) &&\n localChatId\n \"\n @click=\"navigate('files')\">\n <VvIcon name=\"ri:attachment-2\" />\n {{ $t('userFiles.chatFilesTitle') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-if=\"canManageProfile\"\n @click=\"navigate('profile')\">\n <VvIcon name=\"ri:user-line\" />\n {{ $t('label.profile') }}\n </VvDropdownAction>\n <VvDropdownAction\n v-else-if=\"canSignIn\"\n @click=\"navigate('auth')\">\n <VvIcon name=\"ri:account-circle-line\" />\n {{ $t('action.signIn') }}\n </VvDropdownAction>\n </template>\n </VvDropdown>\n <slot\n name=\"actions\"\n v-bind=\"{\n mainColor,\n useColorsForAgentHeader,\n contrastColor,\n startNewChat,\n stopGeneration,\n }\" />\n </VvButtonGroup>\n </div>\n <!-- #endregion -->\n\n <div v-if=\"isReasoningAgent\">\n <div class=\"pk-chatbot__warning-body\">\n <VvAlert modifiers=\"callout\">\n <div class=\"pk-chatbot__warning-header\">\n <VvIcon\n name=\"ri:alert-line\"\n class=\"pk-chatbot__warning-icon\" />\n <strong class=\"pk-chatbot__warning-title\">\n {{ $t('message.warning') }}\n </strong>\n </div>\n <p class=\"pk-chatbot__warning-text\">\n {{ $t('message.reasoningAgent') }}\n </p>\n </VvAlert>\n </div>\n </div>\n\n <!-- #region views -->\n <PkChatbotViewChat\n v-if=\"activeView === 'chat'\"\n :agent-id\n :modifier\n :user-name\n @show-info=\"emit('show-info', $event)\"\n @revise=\"emit('revise', $event)\">\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewChat>\n <PkChatbotViewConversations\n v-else-if=\"activeView === 'conversations'\"\n :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewConversations>\n <PkChatbotViewFiles v-else-if=\"activeView === 'files'\" :agent-id />\n <div\n v-else-if=\"activeView === 'auth'\"\n class=\"pk-chatbot__view-auth flex-1 min-h-0 overflow-y-auto\">\n <!-- Sign-in form provided by the host widget (owns the auth client) -->\n <slot name=\"view-auth\" />\n </div>\n <PkChatbotViewProfile v-else :agent-id>\n <template\n v-for=\"slotName in Object.keys(slots)\"\n #[slotName]=\"slotData\"\n ><slot :name=\"slotName\" v-bind=\"slotData\"\n /></template>\n </PkChatbotViewProfile>\n <!-- #endregion -->\n\n <!-- #region footer -->\n <div v-if=\"footerMessage\" class=\"pk-chatbot__footer\">\n <PkStreamingMarkdown :markdown=\"footerMessage\" class=\"wysiwyg\" />\n </div>\n <!-- #endregion -->\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-chatbot {\n display: flex;\n flex-direction: column;\n position: relative;\n height: 100%;\n background-color: var(--color-surface);\n\n &__header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n justify-content: space-between;\n gap: var(--spacing-14);\n background-color: var(--color-surface-1);\n padding: var(--spacing-14);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__header-start {\n display: flex;\n align-items: center;\n gap: var(--spacing-sm);\n min-width: 0;\n }\n\n &__nav-icon {\n font-size: var(--text-18);\n }\n\n &__avatar {\n width: var(--spacing-24);\n height: var(--spacing-24);\n }\n\n &__title {\n font-size: var(--text-16);\n font-weight: var(--font-semibold);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__header-end {\n flex-shrink: 0;\n }\n\n &__warning-body {\n padding: var(--spacing-md);\n border-bottom: 1px solid var(--color-surface-4);\n }\n\n &__warning-header {\n display: flex;\n align-items: center;\n gap: var(--spacing-4);\n margin-bottom: var(--spacing-4);\n }\n\n &__warning-icon {\n width: var(--spacing-16);\n height: var(--spacing-16);\n }\n\n &__warning-title {\n font-weight: 600;\n font-size: var(--text-14);\n }\n\n &__warning-text {\n font-size: var(--text-12);\n color: var(--color-word-3);\n }\n\n &__footer {\n padding: var(--spacing-md);\n border-top: 1px solid var(--color-surface-4);\n text-align: center;\n font-size: var(--text-12);\n color: var(--color-word-2);\n }\n\n // Modifier: fullscreen — layout full-page (Gemini-style): nessun\n // header interno, sfondo trasparente, input pill centrato.\n // Il chrome (sidebar, navigazione) è gestito dalla pagina ospite.\n &--fullscreen {\n background-color: transparent;\n\n .pk-chatbot__footer {\n border-top: none;\n padding: 0 var(--spacing-md) var(--spacing-sm) var(--spacing-md);\n }\n\n // Reading column aligned with the input (Gemini-style). The\n // constraint lives on the inner wrapper so the scroll container\n // spans the page and the scrollbar sits at its edge.\n .pk-chatbot-messages {\n // Symmetric gutters: a visible scrollbar would otherwise\n // shrink the content box on one side only, shifting the\n // column off the input axis\n scrollbar-gutter: stable both-edges;\n // Dissolve the content at both edges (works on any page\n // background, dots included — a surface-colored fade band\n // would cut through the pattern instead)\n mask-image: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--spacing-40),\n #000 calc(100% - var(--spacing-40)),\n transparent 100%\n );\n // Resting clearance so the first/last lines sit inside the\n // fully opaque zone\n padding-block: var(--spacing-48);\n }\n\n .pk-chatbot-messages__wrapper {\n width: 100%;\n max-width: 768px;\n margin-inline: auto;\n }\n\n .pk-chatbot-file-preview {\n background-color: var(--color-surface);\n }\n\n .pk-chatbot-input__notice {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-3);\n border-top: 1px solid var(--color-surface-2);\n padding: var(--spacing-10) var(--spacing-16);\n\n &::after {\n background-color: var(--color-surface);\n border-inline: 1px solid var(--color-surface-2);\n bottom: calc(-1 * var(--spacing-20));\n height: var(--spacing-20);\n left: -1px;\n right: -1px;\n }\n }\n\n .pk-chatbot-input {\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n\n &__form {\n border-radius: var(--rounded-xxxl); // pill shape\n padding: var(--spacing-12) var(--spacing-16);\n background-color: var(--color-surface-1);\n border-color: var(--color-surface-2);\n box-shadow: none;\n transition:\n border-color var(--duration-200) var(--ease-in-out),\n box-shadow var(--duration-200) var(--ease-in-out);\n\n &:has(:focus-within) {\n border-color: var(--color-surface-4);\n box-shadow: var(--shadow-sm);\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n }\n\n &__textarea {\n background-color: transparent;\n }\n }\n\n // Touch target ≥44px: estende l'hit area dei pulsanti\n // (icona 20px + padding 4px ≈ 28px) senza ingrandire l'icona.\n // Vale per l'input e per la barra della chiamata, che montano\n // lo stesso pulsante.\n .pk-chat-button {\n position: relative;\n\n &::after {\n content: '';\n position: absolute;\n inset: calc(-1 * var(--spacing-8));\n }\n }\n\n // A live call takes the place of the input, so it takes its shape,\n // its column and its height: full-bleed here would break the\n // reading axis, and a different height would make the dock jump\n // the moment the call starts.\n .pk-voice-session {\n // 60px. There is no 60 in the spacing scale and a raw `60px`\n // would stop following the root font size, which the label\n // inside does follow.\n --pk-chat-dock-height: calc(\n var(--spacing-56) + var(--spacing-4)\n );\n --pk-voice-orb-size: var(--spacing-40);\n --pk-voice-orb-bars-height: var(--spacing-20);\n --pk-voice-session-surface: var(--color-surface-1);\n width: calc(100% - var(--spacing-sm) * 2);\n max-width: 768px;\n margin-inline: auto;\n padding-inline: var(--spacing-16);\n border-color: var(--color-surface-2);\n border-radius: var(--rounded-xxxl); // pill shape\n box-shadow: none;\n\n &__timer {\n font-size: var(--text-14);\n }\n }\n }\n\n // Modifier: panel — compact chrome for a docked side panel: the host\n // container owns background and border, the header slims down and\n // drops its own surface so the chat blends into the panel.\n &--panel {\n background-color: transparent;\n\n .pk-chatbot__header {\n background-color: transparent;\n\n // Fixed height so the panel header lines up with the host's\n // own chrome. Custom properties inherit through the shadow\n // root: an embedding host can override it to match its\n // header (the default matches the platform header, 50px).\n height: var(--pk-chatbot-panel-header-height, 50px);\n align-items: center;\n padding-block: 0;\n padding-inline: var(--spacing-10);\n border-bottom: 1px solid var(--color-surface-3);\n }\n\n .pk-chatbot__title {\n font-size: var(--text-14);\n }\n\n .pk-chatbot__footer {\n border-top: none;\n padding: var(--spacing-6) var(--spacing-md);\n }\n }\n }\n</style>\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { O as e } from "./Agent-Cy-XE9N1.js";
|
|
2
2
|
import { t } from "./PkChatbotError-B2iob9cQ.js";
|
|
3
3
|
import { t as n } from "./PkStreamingMarkdown-BBni7OTn.js";
|
|
4
4
|
import "./src-BbpxM17m.js";
|
|
@@ -15,22 +15,22 @@ import { Fragment as T, Transition as E, computed as D, createBlock as O, create
|
|
|
15
15
|
import { useI18n as me } from "vue-i18n";
|
|
16
16
|
//#region ../../packages/utils/src/text.ts
|
|
17
17
|
var G = (e) => e.normalize("NFD").replace(/[\u0300-\u036f]/g, ""), he = (e) => e ? e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[-_]+/g, " ") : "", ge = (e) => e ? G(e).replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").toLowerCase() : "", _e = (e) => e ? G(e).replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").toLowerCase() : "", ve = (e) => e ? e.charAt(0).toUpperCase() + e.slice(1) : "", ye = {
|
|
18
|
-
requestOAuthConnection: N(() => import("./PkToolRequestOAuthConnection-
|
|
18
|
+
requestOAuthConnection: N(() => import("./PkToolRequestOAuthConnection-URjYDFhm.js")),
|
|
19
19
|
showArtifact: N(() => import("./PkToolShowArtifact-BGGsbY-0.js").then((e) => e.n)),
|
|
20
20
|
showCalendarEvent: N(() => import("./PkToolShowCalendarEvent-CK5KnNxq.js").then((e) => e.n)),
|
|
21
21
|
showComparison: N(() => import("./PkToolShowComparison-D0iMf7JW.js").then((e) => e.n)),
|
|
22
22
|
showContactForm: N(() => import("./PkToolShowContactForm-CMK4zR2J.js").then((e) => e.n)),
|
|
23
|
-
showDiagram: N(() => import("./PkToolShowDiagram-
|
|
23
|
+
showDiagram: N(() => import("./PkToolShowDiagram-CuQyY0uv.js")),
|
|
24
24
|
showEmail: N(() => import("./PkToolShowEmail-tUYlAzar.js").then((e) => e.n)),
|
|
25
25
|
showImageGallery: N(() => import("./PkToolShowImageGallery-6MmMV2y_.js").then((e) => e.n)),
|
|
26
26
|
showLocation: N(() => import("./PkToolShowLocation-BBdeY0Ae.js").then((e) => e.n)),
|
|
27
27
|
showMessage: N(() => import("./PkToolShowMessage-BezAdT5s.js").then((e) => e.n)),
|
|
28
|
-
showForm: N(() => import("./PkToolShowForm-
|
|
29
|
-
showMultipleChoice: N(() => import("./PkToolShowForm-
|
|
28
|
+
showForm: N(() => import("./PkToolShowForm-vXgu3bZO.js").then((e) => e.n)),
|
|
29
|
+
showMultipleChoice: N(() => import("./PkToolShowForm-vXgu3bZO.js").then((e) => e.n)),
|
|
30
30
|
showProductList: N(() => import("./PkToolShowProductList-B4QeNa1a.js").then((e) => e.n)),
|
|
31
31
|
showQrCode: N(() => import("./PkToolShowQrCode-DEAvfLFu.js").then((e) => e.n)),
|
|
32
|
-
showSources: N(() => import("./PkToolShowSources-
|
|
33
|
-
showSuggestedReply: N(() => import("./PkToolShowSuggestedReply-
|
|
32
|
+
showSources: N(() => import("./PkToolShowSources-Die_j1bn.js").then((e) => e.n)),
|
|
33
|
+
showSuggestedReply: N(() => import("./PkToolShowSuggestedReply-CryAdy6t.js").then((e) => e.n)),
|
|
34
34
|
showVideo: N(() => import("./PkToolShowVideo-DCHy_8US.js")),
|
|
35
35
|
showWeather: N(() => import("./PkToolShowWeather-CKx1MJOB.js")),
|
|
36
36
|
showWebPages: N(() => import("./PkToolShowWebPages-Bu9cxKIk.js").then((e) => e.n))
|
|
@@ -767,4 +767,4 @@ var Ee = ["aria-busy"], De = {
|
|
|
767
767
|
//#endregion
|
|
768
768
|
export { _e as a, ge as i, ve as n, he as r, $e as t };
|
|
769
769
|
|
|
770
|
-
//# sourceMappingURL=PkChatbotMessages-
|
|
770
|
+
//# sourceMappingURL=PkChatbotMessages-CWPSx2ce.js.map
|