open-chat-studio-widget 0.4.7 → 0.5.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.
Files changed (78) hide show
  1. package/README.md +23 -20
  2. package/dist/cjs/app-globals-V2Kpy_OQ.js +8 -0
  3. package/dist/cjs/app-globals-V2Kpy_OQ.js.map +1 -0
  4. package/dist/cjs/{index-c9203be6.js → index-CC3Krx2K.js} +331 -238
  5. package/dist/cjs/index-CC3Krx2K.js.map +1 -0
  6. package/dist/cjs/index.cjs.js +1 -0
  7. package/dist/cjs/index.cjs.js.map +1 -1
  8. package/dist/cjs/loader.cjs.js +4 -5
  9. package/dist/cjs/loader.cjs.js.map +1 -1
  10. package/dist/cjs/open-chat-studio-widget.cjs.entry.js +5124 -4272
  11. package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
  12. package/dist/cjs/open-chat-studio-widget.cjs.js +8 -7
  13. package/dist/cjs/open-chat-studio-widget.cjs.js.map +1 -1
  14. package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -0
  15. package/dist/collection/collection-manifest.json +1 -1
  16. package/dist/collection/components/ocs-chat/{heroicons.js → icons.js} +23 -1
  17. package/dist/collection/components/ocs-chat/icons.js.map +1 -0
  18. package/dist/collection/components/ocs-chat/ocs-chat.css +596 -1947
  19. package/dist/collection/components/ocs-chat/ocs-chat.js +521 -293
  20. package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
  21. package/dist/collection/services/chat-session-service.js +145 -0
  22. package/dist/collection/services/chat-session-service.js.map +1 -0
  23. package/dist/collection/services/file-attachment-manager.js +125 -0
  24. package/dist/collection/services/file-attachment-manager.js.map +1 -0
  25. package/dist/collection/utils/cookies.js +5 -12
  26. package/dist/collection/utils/cookies.js.map +1 -1
  27. package/dist/collection/utils/markdown.js +1 -1
  28. package/dist/collection/utils/markdown.js.map +1 -1
  29. package/dist/collection/utils/translations.js +99 -0
  30. package/dist/collection/utils/translations.js.map +1 -0
  31. package/dist/components/index.js +2 -1
  32. package/dist/components/open-chat-studio-widget.js +5125 -4266
  33. package/dist/components/open-chat-studio-widget.js.map +1 -1
  34. package/dist/esm/app-globals-DQuL1Twl.js +6 -0
  35. package/dist/esm/app-globals-DQuL1Twl.js.map +1 -0
  36. package/dist/esm/{index-0349ca51.js → index-BF7CYZiN.js} +329 -217
  37. package/dist/esm/index-BF7CYZiN.js.map +1 -0
  38. package/dist/esm/index.js +1 -0
  39. package/dist/esm/index.js.map +1 -1
  40. package/dist/esm/loader.js +5 -4
  41. package/dist/esm/loader.js.map +1 -1
  42. package/dist/esm/open-chat-studio-widget.entry.js +5125 -4271
  43. package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
  44. package/dist/esm/open-chat-studio-widget.js +7 -5
  45. package/dist/esm/open-chat-studio-widget.js.map +1 -1
  46. package/dist/open-chat-studio-widget/index.esm.js.map +1 -1
  47. package/dist/open-chat-studio-widget/loader.esm.js.map +1 -0
  48. package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -0
  49. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
  50. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js.map +1 -1
  51. package/dist/open-chat-studio-widget/p-400b1f47.entry.js +4 -0
  52. package/dist/open-chat-studio-widget/p-400b1f47.entry.js.map +1 -0
  53. package/dist/open-chat-studio-widget/p-BF7CYZiN.js +3 -0
  54. package/dist/open-chat-studio-widget/p-BF7CYZiN.js.map +1 -0
  55. package/dist/open-chat-studio-widget/p-DQuL1Twl.js +2 -0
  56. package/dist/open-chat-studio-widget/p-DQuL1Twl.js.map +1 -0
  57. package/dist/types/components/ocs-chat/{heroicons.d.ts → icons.d.ts} +19 -0
  58. package/dist/types/components/ocs-chat/ocs-chat.d.ts +57 -36
  59. package/dist/types/components.d.ts +36 -2
  60. package/dist/types/services/chat-session-service.d.ts +78 -0
  61. package/dist/types/services/file-attachment-manager.d.ts +40 -0
  62. package/dist/types/stencil-public-runtime.d.ts +35 -6
  63. package/dist/types/utils/translations.d.ts +23 -0
  64. package/package.json +9 -4
  65. package/dist/cjs/app-globals-3a1e7e63.js +0 -7
  66. package/dist/cjs/app-globals-3a1e7e63.js.map +0 -1
  67. package/dist/cjs/index-c9203be6.js.map +0 -1
  68. package/dist/collection/components/ocs-chat/heroicons.js.map +0 -1
  69. package/dist/esm/app-globals-0f993ce5.js +0 -5
  70. package/dist/esm/app-globals-0f993ce5.js.map +0 -1
  71. package/dist/esm/index-0349ca51.js.map +0 -1
  72. package/dist/open-chat-studio-widget/p-3dc66a9a.js +0 -3
  73. package/dist/open-chat-studio-widget/p-3dc66a9a.js.map +0 -1
  74. package/dist/open-chat-studio-widget/p-6b9a332c.entry.js +0 -4
  75. package/dist/open-chat-studio-widget/p-6b9a332c.entry.js.map +0 -1
  76. package/dist/open-chat-studio-widget/p-e1255160.js +0 -2
  77. package/dist/open-chat-studio-widget/p-e1255160.js.map +0 -1
  78. package/loader/package.json +0 -11
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-session-service.js","sourceRoot":"","sources":["../../src/services/chat-session-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getCSRFToken } from '../utils/cookies';\n\nexport type ChatRole = 'system' | 'user' | 'assistant';\n\nexport interface ChatAttachment {\n name: string;\n content_type: string;\n size: number;\n}\n\nexport interface ChatMessage {\n created_at: string;\n role: ChatRole;\n content: string;\n metadata?: unknown;\n attachments?: ChatAttachment[];\n}\n\nexport interface ChatStartSessionResponse {\n session_id: string;\n chatbot: unknown;\n participant: unknown;\n seed_message_task_id?: string;\n}\n\nexport interface ChatSendMessageResponse {\n task_id: string;\n status: 'processing' | 'completed' | 'error';\n error?: string;\n}\n\nexport interface ChatTaskPollResponse {\n message?: ChatMessage;\n status: 'processing' | 'complete';\n error?: string;\n}\n\nexport interface ChatPollResponse {\n messages: ChatMessage[];\n has_more: boolean;\n session_status: 'active' | 'ended';\n}\n\nexport interface ChatSessionServiceOptions {\n apiBaseUrl: string;\n embedKey?: string;\n widgetVersion: string;\n csrfTokenProvider?: (apiBaseUrl: string) => string | undefined;\n taskPollingIntervalMs?: number;\n taskPollingMaxAttempts?: number;\n messagePollingIntervalMs?: number;\n}\n\nexport interface TaskPollingCallbacks {\n onMessage: (message: ChatMessage) => void;\n onTimeout?: () => void;\n onError?: (error: Error) => void;\n}\n\nexport interface TaskPollingHandle {\n cancel: () => void;\n}\n\nexport interface MessagePollingCallbacks {\n getSince: () => string | undefined;\n onMessages: (messages: ChatMessage[]) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface MessagePollingHandle {\n stop: () => void;\n}\n\nexport class ChatSessionService {\n private readonly apiBaseUrl: string;\n private readonly embedKey?: string;\n private readonly widgetVersion: string;\n private readonly csrfTokenProvider: (apiBaseUrl: string) => string | undefined;\n private readonly taskPollingIntervalMs: number;\n private readonly taskPollingMaxAttempts: number;\n private readonly messagePollingIntervalMs: number;\n private messagePollingTimer?: ReturnType<typeof setInterval>;\n\n constructor(options: ChatSessionServiceOptions) {\n this.apiBaseUrl = options.apiBaseUrl;\n this.embedKey = options.embedKey;\n this.widgetVersion = options.widgetVersion;\n this.csrfTokenProvider = options.csrfTokenProvider ?? getCSRFToken;\n this.taskPollingIntervalMs = options.taskPollingIntervalMs ?? 1000;\n this.taskPollingMaxAttempts = options.taskPollingMaxAttempts ?? 120;\n this.messagePollingIntervalMs = options.messagePollingIntervalMs ?? 30000;\n }\n\n async startSession(requestBody: Record<string, unknown>): Promise<ChatStartSessionResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/start/`, {\n method: 'POST',\n headers: this.getJsonHeaders(),\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to start session: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatStartSessionResponse>;\n }\n\n async sendMessage(sessionId: string, payload: Record<string, unknown>): Promise<ChatSendMessageResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/message/`, {\n method: 'POST',\n headers: this.getJsonHeaders(),\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send message: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatSendMessageResponse>;\n }\n\n async pollTaskOnce(sessionId: string, taskId: string): Promise<ChatTaskPollResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/${taskId}/poll/`);\n\n if (!response.ok) {\n throw new Error(`Failed to poll task: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatTaskPollResponse>;\n }\n\n pollTask(sessionId: string, taskId: string, callbacks: TaskPollingCallbacks): TaskPollingHandle {\n let attempts = 0;\n let cancelled = false;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const scheduleNextPoll = () => {\n timeoutId = setTimeout(() => {\n void poll();\n }, this.taskPollingIntervalMs);\n };\n\n const poll = async () => {\n if (cancelled) {\n return;\n }\n\n try {\n const data = await this.pollTaskOnce(sessionId, taskId);\n\n if (data.error) {\n throw new Error(data.error);\n }\n\n if (data.status === 'complete' && data.message) {\n callbacks.onMessage(data.message);\n return;\n }\n\n attempts += 1;\n if (attempts >= this.taskPollingMaxAttempts) {\n if (callbacks.onTimeout) {\n callbacks.onTimeout();\n }\n return;\n }\n\n scheduleNextPoll();\n } catch (error) {\n if (callbacks.onError) {\n callbacks.onError(error instanceof Error ? error : new Error('Failed to get response'));\n }\n }\n };\n\n void poll();\n\n return {\n cancel: () => {\n cancelled = true;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n },\n };\n }\n\n async fetchMessages(sessionId: string, since?: string): Promise<ChatPollResponse> {\n const url = new URL(`${this.apiBaseUrl}/api/chat/${sessionId}/poll/`);\n if (since) {\n url.searchParams.set('since', since);\n }\n\n const response = await fetch(url.toString());\n if (!response.ok) {\n throw new Error(`Failed to poll messages: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatPollResponse>;\n }\n\n startMessagePolling(\n sessionId: string,\n callbacks: MessagePollingCallbacks,\n ): MessagePollingHandle {\n const poll = async () => {\n try {\n const since = callbacks.getSince();\n const data = await this.fetchMessages(sessionId, since);\n if (data.messages.length > 0) {\n callbacks.onMessages(data.messages);\n }\n } catch (error) {\n if (callbacks.onError) {\n callbacks.onError(error instanceof Error ? error : new Error('Failed to poll messages'));\n }\n }\n };\n\n // perform an initial poll immediately\n void poll();\n\n this.messagePollingTimer = setInterval(() => {\n void poll();\n }, this.messagePollingIntervalMs);\n\n return {\n stop: () => this.stopMessagePolling(),\n };\n }\n\n stopMessagePolling(): void {\n if (this.messagePollingTimer) {\n clearInterval(this.messagePollingTimer);\n this.messagePollingTimer = undefined;\n }\n }\n\n private getJsonHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'x-ocs-widget-version': this.widgetVersion,\n };\n\n const csrfToken = this.csrfTokenProvider(this.apiBaseUrl);\n if (csrfToken) {\n headers['X-CSRFToken'] = csrfToken;\n }\n\n if (this.embedKey) {\n headers['X-Embed-Key'] = this.embedKey;\n }\n\n return headers;\n }\n}\n"]}
@@ -0,0 +1,125 @@
1
+ export class FileAttachmentManager {
2
+ constructor(config) {
3
+ this.supportedExtensions = config.supportedExtensions;
4
+ this.maxFileSizeMb = config.maxFileSizeMb;
5
+ this.maxTotalSizeMb = config.maxTotalSizeMb;
6
+ }
7
+ addFiles(existingFiles, files) {
8
+ const newSelected = [];
9
+ const fileArray = Array.from(files instanceof FileList ? Array.from(files) : files);
10
+ let totalSize = existingFiles.reduce((sum, f) => sum + f.file.size, 0);
11
+ for (const file of fileArray) {
12
+ const extension = this.getFileExtension(file.name);
13
+ if (!this.supportedExtensions.includes(extension)) {
14
+ newSelected.push({ file, error: `File type ${extension} not supported` });
15
+ continue;
16
+ }
17
+ const fileSizeMb = this.bytesToMb(file.size);
18
+ if (fileSizeMb > this.maxFileSizeMb) {
19
+ newSelected.push({ file, error: `File exceeds ${this.maxFileSizeMb}MB limit` });
20
+ continue;
21
+ }
22
+ totalSize += file.size;
23
+ const totalSizeMb = this.bytesToMb(totalSize);
24
+ if (totalSizeMb > this.maxTotalSizeMb) {
25
+ newSelected.push({ file, error: `Total size exceeds ${this.maxTotalSizeMb}MB limit` });
26
+ continue;
27
+ }
28
+ newSelected.push({ file });
29
+ }
30
+ return [...existingFiles, ...newSelected];
31
+ }
32
+ removeFile(existingFiles, index) {
33
+ return existingFiles.filter((_, i) => i !== index);
34
+ }
35
+ markPendingFilesWithError(existingFiles, errorMessage) {
36
+ return existingFiles.map(file => {
37
+ if (!file.error && !file.uploaded) {
38
+ return Object.assign(Object.assign({}, file), { error: errorMessage });
39
+ }
40
+ return file;
41
+ });
42
+ }
43
+ async uploadPendingFiles(existingFiles, context) {
44
+ if (existingFiles.length === 0) {
45
+ return { selectedFiles: existingFiles, uploadedIds: [] };
46
+ }
47
+ const uploadCandidates = existingFiles.filter(file => !file.error && !file.uploaded);
48
+ const uploadedIds = existingFiles
49
+ .filter(file => file.uploaded)
50
+ .map(file => file.uploaded.id);
51
+ if (uploadCandidates.length === 0) {
52
+ return { selectedFiles: existingFiles, uploadedIds };
53
+ }
54
+ const formData = new FormData();
55
+ for (const file of uploadCandidates) {
56
+ formData.append('files', file.file);
57
+ }
58
+ formData.append('participant_remote_id', context.participantId);
59
+ if (context.participantName) {
60
+ formData.append('participant_name', context.participantName);
61
+ }
62
+ try {
63
+ const response = await fetch(`${context.apiBaseUrl}/api/chat/${context.sessionId}/upload/`, {
64
+ method: 'POST',
65
+ body: formData,
66
+ });
67
+ if (!response.ok) {
68
+ const errorData = await this.safeJson(response);
69
+ const errorMessage = (errorData && typeof errorData === 'object' && 'error' in errorData && errorData.error) ||
70
+ 'Failed to upload files';
71
+ return {
72
+ selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),
73
+ uploadedIds,
74
+ errorMessage,
75
+ };
76
+ }
77
+ const data = await this.safeJson(response);
78
+ if (!data || typeof data !== 'object' || !Array.isArray(data.files)) {
79
+ const errorMessage = 'Unexpected upload response shape';
80
+ return {
81
+ selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),
82
+ uploadedIds,
83
+ errorMessage,
84
+ };
85
+ }
86
+ const uploadedFiles = data.files;
87
+ let index = 0;
88
+ const updatedSelected = existingFiles.map(file => {
89
+ if (!file.error && !file.uploaded) {
90
+ const uploaded = uploadedFiles[index];
91
+ index += 1;
92
+ return Object.assign(Object.assign({}, file), { uploaded });
93
+ }
94
+ return file;
95
+ });
96
+ uploadedIds.push(...uploadedFiles.map(file => file.id));
97
+ return { selectedFiles: updatedSelected, uploadedIds };
98
+ }
99
+ catch (error) {
100
+ const errorMessage = error instanceof Error ? error.message : 'Failed to upload files';
101
+ return {
102
+ selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),
103
+ uploadedIds,
104
+ errorMessage,
105
+ };
106
+ }
107
+ }
108
+ bytesToMb(bytes) {
109
+ return bytes / (1024 * 1024);
110
+ }
111
+ getFileExtension(filename) {
112
+ const parts = filename.split('.');
113
+ const ext = parts.pop();
114
+ return ext ? `.${ext.toLowerCase()}` : '';
115
+ }
116
+ async safeJson(response) {
117
+ try {
118
+ return await response.json();
119
+ }
120
+ catch (_a) {
121
+ return undefined;
122
+ }
123
+ }
124
+ }
125
+ //# sourceMappingURL=file-attachment-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-attachment-manager.js","sourceRoot":"","sources":["../../src/services/file-attachment-manager.ts"],"names":[],"mappings":"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","sourcesContent":["export interface UploadedFile {\n id: number;\n name: string;\n size: number;\n content_type: string;\n}\n\nexport interface SelectedFile {\n file: File;\n uploaded?: UploadedFile;\n error?: string;\n}\n\nexport interface AttachmentValidationConfig {\n supportedExtensions: string[];\n maxFileSizeMb: number;\n maxTotalSizeMb: number;\n}\n\nexport interface UploadContext {\n apiBaseUrl: string;\n sessionId: string;\n participantId: string;\n participantName?: string;\n}\n\nexport interface UploadResult {\n selectedFiles: SelectedFile[];\n uploadedIds: number[];\n errorMessage?: string;\n}\n\nexport class FileAttachmentManager {\n private readonly supportedExtensions: string[];\n private readonly maxFileSizeMb: number;\n private readonly maxTotalSizeMb: number;\n\n constructor(config: AttachmentValidationConfig) {\n this.supportedExtensions = config.supportedExtensions;\n this.maxFileSizeMb = config.maxFileSizeMb;\n this.maxTotalSizeMb = config.maxTotalSizeMb;\n }\n\n addFiles(existingFiles: SelectedFile[], files: FileList | File[]): SelectedFile[] {\n const newSelected: SelectedFile[] = [];\n const fileArray = Array.from(files instanceof FileList ? Array.from(files) : files);\n let totalSize = existingFiles.reduce((sum, f) => sum + f.file.size, 0);\n\n for (const file of fileArray) {\n const extension = this.getFileExtension(file.name);\n if (!this.supportedExtensions.includes(extension)) {\n newSelected.push({ file, error: `File type ${extension} not supported` });\n continue;\n }\n\n const fileSizeMb = this.bytesToMb(file.size);\n if (fileSizeMb > this.maxFileSizeMb) {\n newSelected.push({ file, error: `File exceeds ${this.maxFileSizeMb}MB limit` });\n continue;\n }\n\n totalSize += file.size;\n const totalSizeMb = this.bytesToMb(totalSize);\n if (totalSizeMb > this.maxTotalSizeMb) {\n newSelected.push({ file, error: `Total size exceeds ${this.maxTotalSizeMb}MB limit` });\n continue;\n }\n\n newSelected.push({ file });\n }\n\n return [...existingFiles, ...newSelected];\n }\n\n removeFile(existingFiles: SelectedFile[], index: number): SelectedFile[] {\n return existingFiles.filter((_, i) => i !== index);\n }\n\n markPendingFilesWithError(existingFiles: SelectedFile[], errorMessage: string): SelectedFile[] {\n return existingFiles.map(file => {\n if (!file.error && !file.uploaded) {\n return { ...file, error: errorMessage };\n }\n return file;\n });\n }\n\n async uploadPendingFiles(\n existingFiles: SelectedFile[],\n context: UploadContext\n ): Promise<UploadResult> {\n if (existingFiles.length === 0) {\n return { selectedFiles: existingFiles, uploadedIds: [] };\n }\n\n const uploadCandidates = existingFiles.filter(file => !file.error && !file.uploaded);\n const uploadedIds: number[] = existingFiles\n .filter(file => file.uploaded)\n .map(file => file.uploaded!.id);\n\n if (uploadCandidates.length === 0) {\n return { selectedFiles: existingFiles, uploadedIds };\n }\n\n const formData = new FormData();\n for (const file of uploadCandidates) {\n formData.append('files', file.file);\n }\n formData.append('participant_remote_id', context.participantId);\n if (context.participantName) {\n formData.append('participant_name', context.participantName);\n }\n\n try {\n const response = await fetch(`${context.apiBaseUrl}/api/chat/${context.sessionId}/upload/`, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n const errorData = await this.safeJson(response);\n const errorMessage =\n (errorData && typeof errorData === 'object' && 'error' in errorData && (errorData as { error?: string }).error) ||\n 'Failed to upload files';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n\n const data = await this.safeJson(response);\n if (!data || typeof data !== 'object' || !Array.isArray((data as { files?: unknown }).files)) {\n const errorMessage = 'Unexpected upload response shape';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n\n const uploadedFiles = (data as { files: UploadedFile[] }).files;\n let index = 0;\n const updatedSelected = existingFiles.map(file => {\n if (!file.error && !file.uploaded) {\n const uploaded = uploadedFiles[index];\n index += 1;\n return { ...file, uploaded };\n }\n return file;\n });\n uploadedIds.push(...uploadedFiles.map(file => file.id));\n\n return { selectedFiles: updatedSelected, uploadedIds };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Failed to upload files';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n }\n\n private bytesToMb(bytes: number): number {\n return bytes / (1024 * 1024);\n }\n\n private getFileExtension(filename: string): string {\n const parts = filename.split('.');\n const ext = parts.pop();\n return ext ? `.${ext.toLowerCase()}` : '';\n }\n\n private async safeJson(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return undefined;\n }\n }\n}\n"]}
@@ -9,20 +9,13 @@ export function getCSRFToken(apiBaseUrl) {
9
9
  return Cookies.get('csrftoken');
10
10
  }
11
11
  function currentDomainMatchesApiBaseUrl(apiBaseUrl) {
12
- const currentDomain = window.location.hostname;
13
- const apiDomain = getDomainFromUrl(apiBaseUrl);
14
- if (!apiDomain) {
15
- return false;
16
- }
17
- return currentDomain === apiDomain;
18
- }
19
- function getDomainFromUrl(url) {
12
+ let apiBase;
20
13
  try {
21
- const urlObj = new URL(url);
22
- return urlObj.hostname;
14
+ apiBase = new URL(apiBaseUrl);
23
15
  }
24
- catch (error) {
25
- return null;
16
+ catch (_a) {
17
+ return false;
26
18
  }
19
+ return window.location.origin === apiBase.origin;
27
20
  }
28
21
  //# sourceMappingURL=cookies.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/utils/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,WAAW,CAAC;AAGhC;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,IAAI,CAAC,8BAA8B,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;AACjC,CAAC;AAED,SAAS,8BAA8B,CAAC,UAAkB;IACxD,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC/C,MAAM,SAAS,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAE/C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,aAAa,KAAK,SAAS,CAAC;AACrC,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,OAAO,MAAM,CAAC,QAAQ,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["import Cookies from \"js-cookie\";\n\n\n/**\n * Get CSRF token from cookies if the current domain matches the API base URL\n */\nexport function getCSRFToken(apiBaseUrl: string): string | undefined {\n if (!currentDomainMatchesApiBaseUrl(apiBaseUrl)) {\n return undefined;\n }\n\n return Cookies.get('csrftoken')\n}\n\nfunction currentDomainMatchesApiBaseUrl(apiBaseUrl: string): boolean {\n const currentDomain = window.location.hostname;\n const apiDomain = getDomainFromUrl(apiBaseUrl);\n\n if (!apiDomain) {\n return false;\n }\n\n return currentDomain === apiDomain;\n}\n\nfunction getDomainFromUrl(url: string): string | null {\n try {\n const urlObj = new URL(url);\n return urlObj.hostname;\n } catch (error) {\n return null;\n }\n}\n"]}
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/utils/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,WAAW,CAAC;AAGhC;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,IAAI,CAAC,8BAA8B,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;AACjC,CAAC;AAED,SAAS,8BAA8B,CAAC,UAAkB;IACxD,IAAI,OAAY,CAAC;IACjB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAChC,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC;AACnD,CAAC","sourcesContent":["import Cookies from \"js-cookie\";\n\n\n/**\n * Get CSRF token from cookies if the current domain matches the API base URL\n */\nexport function getCSRFToken(apiBaseUrl: string): string | undefined {\n if (!currentDomainMatchesApiBaseUrl(apiBaseUrl)) {\n return undefined;\n }\n\n return Cookies.get('csrftoken')\n}\n\nfunction currentDomainMatchesApiBaseUrl(apiBaseUrl: string): boolean {\n let apiBase: URL;\n try {\n apiBase = new URL(apiBaseUrl);\n } catch {\n return false;\n }\n\n return window.location.origin === apiBase.origin;\n}\n"]}
@@ -49,7 +49,7 @@ export function renderMarkdownSync(content) {
49
49
  'href', 'target', 'rel', 'class', 'src', 'alt', 'title',
50
50
  'width', 'height', 'align', 'colspan', 'rowspan'
51
51
  ],
52
- ALLOWED_URI_REGEXP: /^(?:(?:https?):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
52
+ ALLOWED_URI_REGEXP: /^(?:(?:https?):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i,
53
53
  ADD_ATTR: ['target'],
54
54
  FORBID_TAGS: ['script', 'style', 'form', 'input', 'button'],
55
55
  FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/utils/markdown.ts"],"names":[],"mappings":"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","sourcesContent":["import { marked } from 'marked';\nimport DOMPurify from 'dompurify';\n\nmarked.setOptions({\n breaks: true,\n gfm: true,\n smartypants: true,\n});\n\n/**\n * Post-processes rendered HTML to add additional attributes\n * This is called after DOMPurify to ensure external links open in new tabs\n */\nexport function postProcessMarkdownHTML(html: string): string {\n if (typeof window === 'undefined') {\n return html;\n }\n\n try {\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = html;\n\n // Add target=\"_blank\" and rel=\"noopener noreferrer\" to external links\n const links = tempDiv.querySelectorAll('a[href]');\n links.forEach((link) => {\n const href = link.getAttribute('href');\n if (href && (href.startsWith('http://') || href.startsWith('https://'))) {\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n }\n });\n\n return tempDiv.innerHTML;\n } catch (error) {\n console.error('Error post-processing markdown HTML:', error);\n return html;\n }\n}\n\n\nexport function renderMarkdownSync(content: string): string {\n if (!content || typeof content !== 'string') {\n return '';\n }\n\n try {\n const html = marked.parse(content);\n const sanitized = DOMPurify.sanitize(html, {\n ALLOWED_TAGS: [\n 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 'code', 'pre',\n 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'blockquote', 'a', 'img', 'hr', 'table', 'thead', 'tbody',\n 'tr', 'td', 'th', 'del', 'ins', 'sub', 'sup'\n ],\n ALLOWED_ATTR: [\n 'href', 'target', 'rel', 'class', 'src', 'alt', 'title',\n 'width', 'height', 'align', 'colspan', 'rowspan'\n ],\n ALLOWED_URI_REGEXP: /^(?:(?:https?):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i,\n ADD_ATTR: ['target'],\n FORBID_TAGS: ['script', 'style', 'form', 'input', 'button'],\n FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],\n });\n\n return postProcessMarkdownHTML(sanitized);\n } catch (error) {\n console.error('Error rendering markdown sync:', error);\n return DOMPurify.sanitize(content);\n }\n}\n"]}
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/utils/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,MAAM,CAAC,UAAU,CAAC;IAChB,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IACT,WAAW,EAAE,IAAI;CAClB,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;QAEzB,sEAAsE;QACtE,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAClD,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YACrB,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;gBACxE,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACtC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;YAClD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,SAAS,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAGD,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE;YACzC,YAAY,EAAE;gBACZ,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK;gBACvD,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;gBACpD,YAAY,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO;gBACzD,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;aAC7C;YACD,YAAY,EAAE;gBACZ,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO;gBACvD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;aACjD;YACD,kBAAkB,EAAE,qDAAqD;YACzE,QAAQ,EAAE,CAAC,QAAQ,CAAC;YACpB,WAAW,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC;YAC3D,WAAW,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,CAAC;SAC7D,CAAC,CAAC;QAEH,OAAO,uBAAuB,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;QACvD,OAAO,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;AACH,CAAC","sourcesContent":["import { marked } from 'marked';\nimport DOMPurify from 'dompurify';\n\nmarked.setOptions({\n breaks: true,\n gfm: true,\n smartypants: true,\n});\n\n/**\n * Post-processes rendered HTML to add additional attributes\n * This is called after DOMPurify to ensure external links open in new tabs\n */\nexport function postProcessMarkdownHTML(html: string): string {\n if (typeof window === 'undefined') {\n return html;\n }\n\n try {\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = html;\n\n // Add target=\"_blank\" and rel=\"noopener noreferrer\" to external links\n const links = tempDiv.querySelectorAll('a[href]');\n links.forEach((link) => {\n const href = link.getAttribute('href');\n if (href && (href.startsWith('http://') || href.startsWith('https://'))) {\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n }\n });\n\n return tempDiv.innerHTML;\n } catch (error) {\n console.error('Error post-processing markdown HTML:', error);\n return html;\n }\n}\n\n\nexport function renderMarkdownSync(content: string): string {\n if (!content || typeof content !== 'string') {\n return '';\n }\n\n try {\n const html = marked.parse(content);\n const sanitized = DOMPurify.sanitize(html, {\n ALLOWED_TAGS: [\n 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 'code', 'pre',\n 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'blockquote', 'a', 'img', 'hr', 'table', 'thead', 'tbody',\n 'tr', 'td', 'th', 'del', 'ins', 'sub', 'sup'\n ],\n ALLOWED_ATTR: [\n 'href', 'target', 'rel', 'class', 'src', 'alt', 'title',\n 'width', 'height', 'align', 'colspan', 'rowspan'\n ],\n ALLOWED_URI_REGEXP: /^(?:(?:https?):|[^a-z]|[a-z+.-]+(?:[^a-z+.\\-:]|$))/i,\n ADD_ATTR: ['target'],\n FORBID_TAGS: ['script', 'style', 'form', 'input', 'button'],\n FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],\n });\n\n return postProcessMarkdownHTML(sanitized);\n } catch (error) {\n console.error('Error rendering markdown sync:', error);\n return DOMPurify.sanitize(content);\n }\n}\n"]}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Translation utilities for the chat widget
3
+ */
4
+ import ar from "../assets/translations/ar.json";
5
+ import en from "../assets/translations/en.json";
6
+ import es from "../assets/translations/es.json";
7
+ import fr from "../assets/translations/fr.json";
8
+ import hi from "../assets/translations/hi.json";
9
+ import it from "../assets/translations/ita.json";
10
+ import pt from "../assets/translations/por.json";
11
+ import sw from "../assets/translations/sw.json";
12
+ import uk from "../assets/translations/uk.json";
13
+ // Default (English) translations
14
+ export const defaultTranslations = en;
15
+ // Available translations map
16
+ const translationFiles = {
17
+ ar: ar,
18
+ en: en,
19
+ es: es,
20
+ fr: fr,
21
+ hi: hi,
22
+ it: it,
23
+ pt: pt,
24
+ sw: sw,
25
+ uk: uk,
26
+ };
27
+ export function getBrowserLanguage() {
28
+ if (typeof navigator !== 'undefined') {
29
+ const lang = navigator.language || navigator.userLanguage;
30
+ if (lang) {
31
+ return lang.split('-')[0].toLowerCase();
32
+ }
33
+ }
34
+ return 'en';
35
+ }
36
+ export function resolveLanguage(langProp) {
37
+ if (langProp) {
38
+ return langProp.toLowerCase();
39
+ }
40
+ return getBrowserLanguage();
41
+ }
42
+ export async function loadTranslations(language) {
43
+ return translationFiles[language] || defaultTranslations;
44
+ }
45
+ /**
46
+ * Overrides matching keys
47
+ */
48
+ export function mergeTranslations(baseTranslations, customTranslations) {
49
+ return Object.assign(Object.assign({}, baseTranslations), customTranslations);
50
+ }
51
+ export class TranslationManager {
52
+ constructor(language, customTranslations) {
53
+ this.translations = defaultTranslations;
54
+ this.language = 'en';
55
+ this.language = resolveLanguage(language);
56
+ this.loadTranslations(customTranslations);
57
+ }
58
+ async loadTranslations(customTranslations) {
59
+ let baseTranslations;
60
+ try {
61
+ baseTranslations = await loadTranslations(this.language);
62
+ }
63
+ catch (error) {
64
+ console.error('Failed to load translations:', error);
65
+ baseTranslations = defaultTranslations;
66
+ }
67
+ this.translations = customTranslations
68
+ ? mergeTranslations(baseTranslations, customTranslations)
69
+ : baseTranslations;
70
+ }
71
+ get(key, override) {
72
+ var _a;
73
+ if (override !== undefined && override !== null) {
74
+ return override;
75
+ }
76
+ const value = (_a = this.translations[key]) !== null && _a !== void 0 ? _a : defaultTranslations[key];
77
+ if (Array.isArray(value)) {
78
+ return value.length > 0 ? value[0] : undefined;
79
+ }
80
+ return value !== null && value !== void 0 ? value : undefined;
81
+ }
82
+ getAll() {
83
+ return this.translations;
84
+ }
85
+ getArray(key) {
86
+ const value = this.translations[key] || defaultTranslations[key];
87
+ if (Array.isArray(value)) {
88
+ return value;
89
+ }
90
+ if (typeof value === 'string') {
91
+ return [value];
92
+ }
93
+ return [];
94
+ }
95
+ getLanguage() {
96
+ return this.language;
97
+ }
98
+ }
99
+ //# sourceMappingURL=translations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations.js","sourceRoot":"","sources":["../../src/utils/translations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Translation utilities for the chat widget\n */\n\nimport ar from '../assets/translations/ar.json';\nimport en from '../assets/translations/en.json';\nimport es from '../assets/translations/es.json';\nimport fr from '../assets/translations/fr.json';\nimport hi from '../assets/translations/hi.json';\nimport it from '../assets/translations/ita.json';\nimport pt from '../assets/translations/por.json';\nimport sw from '../assets/translations/sw.json';\nimport uk from '../assets/translations/uk.json';\n\nexport type TranslationStrings = typeof en;\n\n// Default (English) translations\nexport const defaultTranslations: TranslationStrings = en as TranslationStrings;\n\n// Available translations map\nconst translationFiles: Record<string, TranslationStrings> = {\n ar: ar as TranslationStrings,\n en: en as TranslationStrings,\n es: es as TranslationStrings,\n fr: fr as TranslationStrings,\n hi: hi as TranslationStrings,\n it: it as TranslationStrings,\n pt: pt as TranslationStrings,\n sw: sw as TranslationStrings,\n uk: uk as TranslationStrings,\n};\n\n\nexport function getBrowserLanguage(): string {\n if (typeof navigator !== 'undefined') {\n const lang = navigator.language || (navigator as any).userLanguage;\n if (lang) {\n return lang.split('-')[0].toLowerCase();\n }\n }\n return 'en';\n}\n\nexport function resolveLanguage(langProp?: string): string {\n if (langProp) {\n return langProp.toLowerCase();\n }\n return getBrowserLanguage();\n}\n\nexport async function loadTranslations(language: string): Promise<TranslationStrings> {\n return translationFiles[language] || defaultTranslations;\n}\n\n/**\n * Overrides matching keys\n */\nexport function mergeTranslations(\n baseTranslations: TranslationStrings,\n customTranslations: Partial<TranslationStrings>\n): TranslationStrings {\n return { ...baseTranslations, ...customTranslations };\n}\n\nexport class TranslationManager {\n private translations: TranslationStrings = defaultTranslations;\n private language: string = 'en';\n\n constructor(language?: string, customTranslations?: Partial<TranslationStrings>) {\n this.language = resolveLanguage(language);\n this.loadTranslations(customTranslations);\n }\n\n private async loadTranslations(customTranslations?: Partial<TranslationStrings>) {\n let baseTranslations: TranslationStrings;\n try {\n baseTranslations = await loadTranslations(this.language);\n } catch (error) {\n console.error('Failed to load translations:', error);\n baseTranslations = defaultTranslations;\n }\n\n this.translations = customTranslations\n ? mergeTranslations(baseTranslations, customTranslations)\n : baseTranslations;\n }\n\n get(key: keyof TranslationStrings, override?: string | null): string | undefined {\n if (override !== undefined && override !== null) {\n return override;\n }\n\n const value = this.translations[key] ?? defaultTranslations[key];\n if (Array.isArray(value)) {\n return value.length > 0 ? value[0] : undefined;\n }\n return value ?? undefined;\n }\n\n getAll(): TranslationStrings {\n return this.translations;\n }\n\n getArray(key: keyof TranslationStrings): string[] {\n const value = this.translations[key] || defaultTranslations[key];\n if (Array.isArray(value)) {\n return value;\n }\n if (typeof value === 'string') {\n return [value];\n }\n return [];\n }\n\n getLanguage(): string {\n return this.language;\n }\n}\n"]}
@@ -1,3 +1,4 @@
1
- export { getAssetPath, setAssetPath, setNonce, setPlatformOptions } from '@stencil/core/internal/client';
1
+ export { getAssetPath, render, setAssetPath, setNonce, setPlatformOptions } from '@stencil/core/internal/client';
2
+ //# sourceMappingURL=index.js.map
2
3
 
3
4
  //# sourceMappingURL=index.js.map