open-chat-studio-widget 0.6.0 → 0.7.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 (45) hide show
  1. package/README.md +27 -25
  2. package/dist/cjs/{index-CcvroTR_.js → index-CvB341El.js} +3 -3
  3. package/dist/cjs/{index-CcvroTR_.js.map → index-CvB341El.js.map} +1 -1
  4. package/dist/cjs/loader.cjs.js +2 -2
  5. package/dist/cjs/open-chat-studio-widget.cjs.entry.js +364 -142
  6. package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
  7. package/dist/cjs/open-chat-studio-widget.cjs.js +2 -2
  8. package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
  9. package/dist/collection/components/ocs-chat/icons.js +2 -2
  10. package/dist/collection/components/ocs-chat/icons.js.map +1 -1
  11. package/dist/collection/components/ocs-chat/ocs-chat.css +29 -34
  12. package/dist/collection/components/ocs-chat/ocs-chat.js +281 -78
  13. package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
  14. package/dist/collection/services/chat-session-service.js +4 -0
  15. package/dist/collection/services/chat-session-service.js.map +1 -1
  16. package/dist/collection/services/file-attachment-manager.js +4 -7
  17. package/dist/collection/services/file-attachment-manager.js.map +1 -1
  18. package/dist/collection/utils/cookies.js.map +1 -1
  19. package/dist/collection/utils/markdown.js +43 -17
  20. package/dist/collection/utils/markdown.js.map +1 -1
  21. package/dist/collection/utils/translations.js +1 -3
  22. package/dist/collection/utils/translations.js.map +1 -1
  23. package/dist/collection/utils/utils.js +2 -2
  24. package/dist/collection/utils/utils.js.map +1 -1
  25. package/dist/components/open-chat-studio-widget.js +369 -141
  26. package/dist/components/open-chat-studio-widget.js.map +1 -1
  27. package/dist/esm/{index-BKVXO_5E.js → index-C2QZK0Ui.js} +3 -3
  28. package/dist/esm/{index-BKVXO_5E.js.map → index-C2QZK0Ui.js.map} +1 -1
  29. package/dist/esm/loader.js +3 -3
  30. package/dist/esm/open-chat-studio-widget.entry.js +364 -142
  31. package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
  32. package/dist/esm/open-chat-studio-widget.js +3 -3
  33. package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
  34. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
  35. package/dist/open-chat-studio-widget/{p-BKVXO_5E.js → p-C2QZK0Ui.js} +2 -2
  36. package/dist/open-chat-studio-widget/{p-BKVXO_5E.js.map → p-C2QZK0Ui.js.map} +1 -1
  37. package/dist/open-chat-studio-widget/p-e87d4e31.entry.js +4 -0
  38. package/dist/open-chat-studio-widget/p-e87d4e31.entry.js.map +1 -0
  39. package/dist/types/components/ocs-chat/ocs-chat.d.ts +29 -1
  40. package/dist/types/components.d.ts +23 -2
  41. package/dist/types/services/chat-session-service.d.ts +1 -0
  42. package/dist/types/utils/markdown.d.ts +8 -0
  43. package/package.json +7 -2
  44. package/dist/open-chat-studio-widget/p-a0d04423.entry.js +0 -4
  45. package/dist/open-chat-studio-widget/p-a0d04423.entry.js.map +0 -1
@@ -1 +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}\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 headers: this.getCommonHeaders(),\n });\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 headers: this.getCommonHeaders(),\n });\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 = this.getCommonHeaders();\n headers['Content-Type'] = 'application/json'\n\n const csrfToken = this.csrfTokenProvider(this.apiBaseUrl);\n if (csrfToken) {\n headers['X-CSRFToken'] = csrfToken;\n }\n\n return headers;\n }\n\n private getCommonHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'x-ocs-widget-version': this.widgetVersion,\n };\n\n if (this.embedKey) {\n headers['X-Embed-Key'] = this.embedKey;\n }\n\n return headers;\n }\n}\n"]}
1
+ {"version":3,"file":"chat-session-service.js","sourceRoot":"","sources":["../../src/services/chat-session-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAyEhD,MAAM,OAAO,kBAAkB;IAU7B,YAAY,OAAkC;;QAC5C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,iBAAiB,GAAG,MAAA,OAAO,CAAC,iBAAiB,mCAAI,YAAY,CAAC;QACnE,IAAI,CAAC,qBAAqB,GAAG,MAAA,OAAO,CAAC,qBAAqB,mCAAI,IAAI,CAAC;QACnE,IAAI,CAAC,sBAAsB,GAAG,MAAA,OAAO,CAAC,sBAAsB,mCAAI,GAAG,CAAC;QACpE,IAAI,CAAC,wBAAwB,GAAG,MAAA,OAAO,CAAC,wBAAwB,mCAAI,KAAK,CAAC;IAC5E,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,WAAoC;QACrD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,kBAAkB,EAAE;YACjE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE;YAC9B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;SAClC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAuC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,OAAgC;QACnE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,aAAa,SAAS,WAAW,EAAE;YAChF,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE;YAC9B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAC9B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAsC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAiB,EAAE,MAAc;QAClD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,aAAa,SAAS,IAAI,MAAM,QAAQ,EAAE;YACvF,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;SACjC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAmC,CAAC;IAC1D,CAAC;IAED,QAAQ,CAAC,SAAiB,EAAE,MAAc,EAAE,SAA+B;QACzE,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,SAAoD,CAAC;QAEzD,MAAM,gBAAgB,GAAG,GAAG,EAAE;YAC5B,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC1B,KAAK,IAAI,EAAE,CAAC;YACd,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACjC,CAAC,CAAC;QAEF,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;;YACtB,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO;YACT,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;gBAExD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;oBACf,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC9B,CAAC;gBAED,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBAC/C,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAClC,OAAO;gBACT,CAAC;gBAED,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,KAAI,MAAA,IAAI,CAAC,OAAO,0CAAE,OAAO,CAAA,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;oBAClF,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC7C,CAAC;gBAED,QAAQ,IAAI,CAAC,CAAC;gBACd,IAAI,QAAQ,IAAI,IAAI,CAAC,sBAAsB,EAAE,CAAC;oBAC5C,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;wBACxB,SAAS,CAAC,SAAS,EAAE,CAAC;oBACxB,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,gBAAgB,EAAE,CAAC;YACrB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;oBACtB,SAAS,CAAC,OAAO,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;gBAC1F,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,KAAK,IAAI,EAAE,CAAC;QAEZ,OAAO;YACL,MAAM,EAAE,GAAG,EAAE;gBACX,SAAS,GAAG,IAAI,CAAC;gBACjB,IAAI,SAAS,EAAE,CAAC;oBACd,YAAY,CAAC,SAAS,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB,EAAE,KAAc;QACnD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,aAAa,SAAS,QAAQ,CAAC,CAAC;QACtE,IAAI,KAAK,EAAE,CAAC;YACV,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAC3C,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE;SACjC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAA+B,CAAC;IACtD,CAAC;IAED,mBAAmB,CAAC,SAAiB,EAAE,SAAkC;QACvE,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;YACtB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;gBACnC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;gBACxD,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC7B,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACtC,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;oBACtB,SAAS,CAAC,OAAO,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;gBAC3F,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,sCAAsC;QACtC,KAAK,IAAI,EAAE,CAAC;QAEZ,IAAI,CAAC,mBAAmB,GAAG,WAAW,CAAC,GAAG,EAAE;YAC1C,KAAK,IAAI,EAAE,CAAC;QACd,CAAC,EAAE,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAElC,OAAO;YACL,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE;SACtC,CAAC;IACJ,CAAC;IAED,kBAAkB;QAChB,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,aAAa,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YACxC,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;QACvC,CAAC;IACH,CAAC;IAEO,cAAc;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxC,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAE7C,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1D,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC;QACrC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,gBAAgB;QACtB,MAAM,OAAO,GAA2B;YACtC,sBAAsB,EAAE,IAAI,CAAC,aAAa;SAC3C,CAAC;QAEF,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QACzC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF","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}\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 onProgress?: (message: string) => 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 headers: this.getCommonHeaders(),\n });\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 if (data.status === 'processing' && data.message?.content && callbacks.onProgress) {\n callbacks.onProgress(data.message.content);\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 headers: this.getCommonHeaders(),\n });\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(sessionId: string, callbacks: MessagePollingCallbacks): 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 = this.getCommonHeaders();\n headers['Content-Type'] = 'application/json';\n\n const csrfToken = this.csrfTokenProvider(this.apiBaseUrl);\n if (csrfToken) {\n headers['X-CSRFToken'] = csrfToken;\n }\n\n return headers;\n }\n\n private getCommonHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'x-ocs-widget-version': this.widgetVersion,\n };\n\n if (this.embedKey) {\n headers['X-Embed-Key'] = this.embedKey;\n }\n\n return headers;\n }\n}\n"]}
@@ -10,8 +10,8 @@ export class FileAttachmentManager {
10
10
  let totalSize = existingFiles.reduce((sum, f) => sum + f.file.size, 0);
11
11
  for (const file of fileArray) {
12
12
  const extension = this.getFileExtension(file.name);
13
- const contentType = file.type.split("/")[0];
14
- if (contentType != "text" && !this.supportedExtensions.includes(extension)) {
13
+ const contentType = file.type.split('/')[0];
14
+ if (contentType != 'text' && !this.supportedExtensions.includes(extension)) {
15
15
  newSelected.push({ file, error: `File type ${extension} not supported` });
16
16
  continue;
17
17
  }
@@ -46,9 +46,7 @@ export class FileAttachmentManager {
46
46
  return { selectedFiles: existingFiles, uploadedIds: [] };
47
47
  }
48
48
  const uploadCandidates = existingFiles.filter(file => !file.error && !file.uploaded);
49
- const uploadedIds = existingFiles
50
- .filter(file => file.uploaded)
51
- .map(file => file.uploaded.id);
49
+ const uploadedIds = existingFiles.filter(file => file.uploaded).map(file => file.uploaded.id);
52
50
  if (uploadCandidates.length === 0) {
53
51
  return { selectedFiles: existingFiles, uploadedIds };
54
52
  }
@@ -67,8 +65,7 @@ export class FileAttachmentManager {
67
65
  });
68
66
  if (!response.ok) {
69
67
  const errorData = await this.safeJson(response);
70
- const errorMessage = (errorData && typeof errorData === 'object' && 'error' in errorData && errorData.error) ||
71
- 'Failed to upload files';
68
+ const errorMessage = (errorData && typeof errorData === 'object' && 'error' in errorData && errorData.error) || 'Failed to upload files';
72
69
  return {
73
70
  selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),
74
71
  uploadedIds,
@@ -1 +1 @@
1
- {"version":3,"file":"file-attachment-manager.js","sourceRoot":"","sources":["../../src/services/file-attachment-manager.ts"],"names":[],"mappings":"AAgCA,MAAM,OAAO,qBAAqB;IAKhC,YAAY,MAAkC;QAC5C,IAAI,CAAC,mBAAmB,GAAG,MAAM,CAAC,mBAAmB,CAAC;QACtD,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;QAC1C,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;IAC9C,CAAC;IAED,QAAQ,CAAC,aAA6B,EAAE,KAAwB;QAC9D,MAAM,WAAW,GAAmB,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,YAAY,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACpF,IAAI,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAEvE,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5C,IAAI,WAAW,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3E,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,SAAS,gBAAgB,EAAE,CAAC,CAAC;gBAC1E,SAAS;YACX,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;gBACpC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,IAAI,CAAC,aAAa,UAAU,EAAE,CAAC,CAAC;gBAChF,SAAS;YACX,CAAC;YAED,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC;YACvB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YAC9C,IAAI,WAAW,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;gBACtC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,sBAAsB,IAAI,CAAC,cAAc,UAAU,EAAE,CAAC,CAAC;gBACvF,SAAS;YACX,CAAC;YAED,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7B,CAAC;QAED,OAAO,CAAC,GAAG,aAAa,EAAE,GAAG,WAAW,CAAC,CAAC;IAC5C,CAAC;IAED,UAAU,CAAC,aAA6B,EAAE,KAAa;QACrD,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;IACrD,CAAC;IAED,yBAAyB,CAAC,aAA6B,EAAE,YAAoB;QAC3E,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClC,uCAAY,IAAI,KAAE,KAAK,EAAE,YAAY,IAAG;YAC1C,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,aAA6B,EAC7B,OAAsB;QAEtB,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;QAC3D,CAAC;QAED,MAAM,gBAAgB,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrF,MAAM,WAAW,GAAa,aAAa;aACxC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;aAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAS,CAAC,EAAE,CAAC,CAAC;QAElC,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC;QACvD,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;YACpC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QACD,QAAQ,CAAC,MAAM,CAAC,uBAAuB,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;QAChE,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,UAAU,aAAa,OAAO,CAAC,SAAS,UAAU,EAAE;gBAC1F,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,QAAQ;aACf,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBAChD,MAAM,YAAY,GAChB,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,OAAO,IAAI,SAAS,IAAK,SAAgC,CAAC,KAAK,CAAC;oBAC/G,wBAAwB,CAAC;gBAC3B,OAAO;oBACL,aAAa,EAAE,IAAI,CAAC,yBAAyB,CAAC,aAAa,EAAE,YAAY,CAAC;oBAC1E,WAAW;oBACX,YAAY;iBACb,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAE,IAA4B,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7F,MAAM,YAAY,GAAG,kCAAkC,CAAC;gBACxD,OAAO;oBACL,aAAa,EAAE,IAAI,CAAC,yBAAyB,CAAC,aAAa,EAAE,YAAY,CAAC;oBAC1E,WAAW;oBACX,YAAY;iBACb,CAAC;YACJ,CAAC;YAED,MAAM,aAAa,GAAI,IAAkC,CAAC,KAAK,CAAC;YAChE,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,MAAM,eAAe,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBAC/C,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAClC,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;oBACtC,KAAK,IAAI,CAAC,CAAC;oBACX,uCAAY,IAAI,KAAE,QAAQ,IAAG;gBAC/B,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC,CAAC,CAAC;YACH,WAAW,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YAExD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;YACvF,OAAO;gBACL,aAAa,EAAE,IAAI,CAAC,yBAAyB,CAAC,aAAa,EAAE,YAAY,CAAC;gBAC1E,WAAW;gBACX,YAAY;aACb,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,KAAa;QAC7B,OAAO,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC/B,CAAC;IAEO,gBAAgB,CAAC,QAAgB;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,QAAkB;QACvC,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;CACF","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 const contentType = file.type.split(\"/\")[0];\n if (contentType != \"text\" && !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"]}
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 const contentType = file.type.split('/')[0];\n if (contentType != 'text' && !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(existingFiles: SelectedFile[], context: UploadContext): 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.filter(file => file.uploaded).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 = (errorData && typeof errorData === 'object' && 'error' in errorData && (errorData as { error?: string }).error) || '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"]}
@@ -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,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"]}
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/utils/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,WAAW,CAAC;AAEhC;;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,CAAC;AAClC,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 * 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"]}
@@ -18,7 +18,7 @@ export function postProcessMarkdownHTML(html) {
18
18
  tempDiv.innerHTML = html;
19
19
  // Add target="_blank" and rel="noopener noreferrer" to external links
20
20
  const links = tempDiv.querySelectorAll('a[href]');
21
- links.forEach((link) => {
21
+ links.forEach(link => {
22
22
  const href = link.getAttribute('href');
23
23
  if (href && (href.startsWith('http://') || href.startsWith('https://'))) {
24
24
  link.setAttribute('target', '_blank');
@@ -32,28 +32,54 @@ export function postProcessMarkdownHTML(html) {
32
32
  return html;
33
33
  }
34
34
  }
35
+ export const SANITIZE_CONFIG = {
36
+ ALLOWED_TAGS: [
37
+ 'p',
38
+ 'br',
39
+ 'strong',
40
+ 'b',
41
+ 'em',
42
+ 'i',
43
+ 'u',
44
+ 'code',
45
+ 'pre',
46
+ 'ul',
47
+ 'ol',
48
+ 'li',
49
+ 'h1',
50
+ 'h2',
51
+ 'h3',
52
+ 'h4',
53
+ 'h5',
54
+ 'h6',
55
+ 'blockquote',
56
+ 'a',
57
+ 'img',
58
+ 'hr',
59
+ 'table',
60
+ 'thead',
61
+ 'tbody',
62
+ 'tr',
63
+ 'td',
64
+ 'th',
65
+ 'del',
66
+ 'ins',
67
+ 'sub',
68
+ 'sup',
69
+ ],
70
+ ALLOWED_ATTR: ['href', 'target', 'rel', 'class', 'src', 'alt', 'title', 'width', 'height', 'align', 'colspan', 'rowspan'],
71
+ ALLOWED_URI_REGEXP: /^(?:(?:https?):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i,
72
+ ADD_ATTR: ['target'],
73
+ FORBID_TAGS: ['script', 'style', 'form', 'input', 'button', 'iframe', 'object', 'embed', 'svg', 'math'],
74
+ FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],
75
+ };
35
76
  export function renderMarkdownSync(content) {
36
77
  if (!content || typeof content !== 'string') {
37
78
  return '';
38
79
  }
39
80
  try {
40
81
  const html = marked.parse(content);
41
- const sanitized = DOMPurify.sanitize(html, {
42
- ALLOWED_TAGS: [
43
- 'p', 'br', 'strong', 'b', 'em', 'i', 'u', 'code', 'pre',
44
- 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
45
- 'blockquote', 'a', 'img', 'hr', 'table', 'thead', 'tbody',
46
- 'tr', 'td', 'th', 'del', 'ins', 'sub', 'sup'
47
- ],
48
- ALLOWED_ATTR: [
49
- 'href', 'target', 'rel', 'class', 'src', 'alt', 'title',
50
- 'width', 'height', 'align', 'colspan', 'rowspan'
51
- ],
52
- ALLOWED_URI_REGEXP: /^(?:(?:https?):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i,
53
- ADD_ATTR: ['target'],
54
- FORBID_TAGS: ['script', 'style', 'form', 'input', 'button'],
55
- FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],
56
- });
82
+ const sanitized = DOMPurify.sanitize(html, SANITIZE_CONFIG);
57
83
  return postProcessMarkdownHTML(sanitized);
58
84
  }
59
85
  catch (error) {
@@ -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":"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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\nexport const SANITIZE_CONFIG = {\n ALLOWED_TAGS: [\n 'p',\n 'br',\n 'strong',\n 'b',\n 'em',\n 'i',\n 'u',\n 'code',\n 'pre',\n 'ul',\n 'ol',\n 'li',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'blockquote',\n 'a',\n 'img',\n 'hr',\n 'table',\n 'thead',\n 'tbody',\n 'tr',\n 'td',\n 'th',\n 'del',\n 'ins',\n 'sub',\n 'sup',\n ],\n ALLOWED_ATTR: ['href', 'target', 'rel', 'class', 'src', 'alt', 'title', 'width', 'height', 'align', 'colspan', 'rowspan'],\n ALLOWED_URI_REGEXP: /^(?:(?:https?):|[^a-z]|[a-z+.-]+(?:[^a-z+.\\-:]|$))/i,\n ADD_ATTR: ['target'],\n FORBID_TAGS: ['script', 'style', 'form', 'input', 'button', 'iframe', 'object', 'embed', 'svg', 'math'],\n FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onmouseover'],\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, SANITIZE_CONFIG);\n\n return postProcessMarkdownHTML(sanitized);\n } catch (error) {\n console.error('Error rendering markdown sync:', error);\n return DOMPurify.sanitize(content);\n }\n}\n"]}
@@ -64,9 +64,7 @@ export class TranslationManager {
64
64
  console.error('Failed to load translations:', error);
65
65
  baseTranslations = defaultTranslations;
66
66
  }
67
- this.translations = customTranslations
68
- ? mergeTranslations(baseTranslations, customTranslations)
69
- : baseTranslations;
67
+ this.translations = customTranslations ? mergeTranslations(baseTranslations, customTranslations) : baseTranslations;
70
68
  }
71
69
  get(key, override) {
72
70
  var _a;
@@ -1 +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/it.json';\nimport pt from '../assets/translations/pt.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
+ {"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/it.json';\nimport pt from '../assets/translations/pt.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\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(baseTranslations: TranslationStrings, customTranslations: Partial<TranslationStrings>): 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 ? mergeTranslations(baseTranslations, customTranslations) : 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"]}
@@ -6,13 +6,13 @@
6
6
  */
7
7
  export const varToPixels = (value, maxValue, defaultValue) => {
8
8
  value = value.trim();
9
- if (value.includes("%")) {
9
+ if (value.includes('%')) {
10
10
  const percent = percentToFloat(value);
11
11
  if (!isNaN(percent)) {
12
12
  return maxValue * percent;
13
13
  }
14
14
  }
15
- else if (value.includes("px")) {
15
+ else if (value.includes('px')) {
16
16
  const pixels = parseFloat(value);
17
17
  if (!isNaN(pixels)) {
18
18
  return pixels;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,QAAgB,EAAE,YAAoB,EAAE,EAAE;IACnF,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IACpB,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACpB,OAAO,QAAQ,GAAG,OAAO,CAAC;QAC5B,CAAC;IACH,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,YAAY,CAAC;AACxB,CAAC,CAAA;AAED,MAAM,cAAc,GAAG,CAAC,gBAAwB,EAAE,EAAE;IAClD,MAAM,YAAY,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,YAAY,GAAG,GAAG,CAAC;AAC5B,CAAC,CAAC","sourcesContent":["/**\n * Convert a CSS percentage (60%) or pixel (10px) value to pixels.\n * @param value The CSS string value\n * @param maxValue The max value to use when converting from a percentage\n * @param defaultValue The default value if the CSS value is neither a percentage nor a pixel value.\n */\nexport const varToPixels = (value: string, maxValue: number, defaultValue: number) => {\n value = value.trim()\n if (value.includes(\"%\")) {\n const percent = percentToFloat(value);\n if (!isNaN(percent)) {\n return maxValue * percent;\n }\n } else if (value.includes(\"px\")) {\n const pixels = parseFloat(value);\n if (!isNaN(pixels)) {\n return pixels;\n }\n }\n return defaultValue;\n}\n\nconst percentToFloat = (percentageString: string) => {\n const numericValue = parseFloat(percentageString);\n if (isNaN(numericValue)) {\n return NaN;\n }\n return numericValue / 100;\n};\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,QAAgB,EAAE,YAAoB,EAAE,EAAE;IACnF,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACrB,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACpB,OAAO,QAAQ,GAAG,OAAO,CAAC;QAC5B,CAAC;IACH,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,gBAAwB,EAAE,EAAE;IAClD,MAAM,YAAY,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,YAAY,GAAG,GAAG,CAAC;AAC5B,CAAC,CAAC","sourcesContent":["/**\n * Convert a CSS percentage (60%) or pixel (10px) value to pixels.\n * @param value The CSS string value\n * @param maxValue The max value to use when converting from a percentage\n * @param defaultValue The default value if the CSS value is neither a percentage nor a pixel value.\n */\nexport const varToPixels = (value: string, maxValue: number, defaultValue: number) => {\n value = value.trim();\n if (value.includes('%')) {\n const percent = percentToFloat(value);\n if (!isNaN(percent)) {\n return maxValue * percent;\n }\n } else if (value.includes('px')) {\n const pixels = parseFloat(value);\n if (!isNaN(pixels)) {\n return pixels;\n }\n }\n return defaultValue;\n};\n\nconst percentToFloat = (percentageString: string) => {\n const numericValue = parseFloat(percentageString);\n if (isNaN(numericValue)) {\n return NaN;\n }\n return numericValue / 100;\n};\n"]}