open-chat-studio-widget 0.9.0 → 0.10.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 (36) hide show
  1. package/dist/cjs/{index-DDod9Zyw.js → index-DMXmZhVD.js} +3 -3
  2. package/dist/cjs/index-DMXmZhVD.js.map +1 -0
  3. package/dist/cjs/loader.cjs.js +2 -2
  4. package/dist/cjs/open-chat-studio-widget.cjs.entry.js +1261 -322
  5. package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
  6. package/dist/cjs/open-chat-studio-widget.cjs.js +2 -2
  7. package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
  8. package/dist/collection/components/ocs-chat/ocs-chat.js +91 -6
  9. package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
  10. package/dist/collection/services/chat-session-service.js +12 -2
  11. package/dist/collection/services/chat-session-service.js.map +1 -1
  12. package/dist/collection/services/file-attachment-manager.js +2 -5
  13. package/dist/collection/services/file-attachment-manager.js.map +1 -1
  14. package/dist/components/open-chat-studio-widget.js +1261 -321
  15. package/dist/components/open-chat-studio-widget.js.map +1 -1
  16. package/dist/esm/{index-iUBQH9om.js → index-JDApwJx_.js} +3 -3
  17. package/dist/esm/index-JDApwJx_.js.map +1 -0
  18. package/dist/esm/loader.js +3 -3
  19. package/dist/esm/open-chat-studio-widget.entry.js +1261 -322
  20. package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
  21. package/dist/esm/open-chat-studio-widget.js +3 -3
  22. package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
  23. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
  24. package/dist/open-chat-studio-widget/{p-iUBQH9om.js → p-JDApwJx_.js} +2 -2
  25. package/dist/open-chat-studio-widget/p-JDApwJx_.js.map +1 -0
  26. package/dist/open-chat-studio-widget/p-c2d3b2d1.entry.js +4 -0
  27. package/dist/open-chat-studio-widget/p-c2d3b2d1.entry.js.map +1 -0
  28. package/dist/types/components/ocs-chat/ocs-chat.d.ts +15 -0
  29. package/dist/types/services/chat-session-service.d.ts +4 -0
  30. package/dist/types/services/file-attachment-manager.d.ts +2 -1
  31. package/package.json +2 -2
  32. package/dist/cjs/index-DDod9Zyw.js.map +0 -1
  33. package/dist/esm/index-iUBQH9om.js.map +0 -1
  34. package/dist/open-chat-studio-widget/p-9c925476.entry.js +0 -4
  35. package/dist/open-chat-studio-widget/p-9c925476.entry.js.map +0 -1
  36. package/dist/open-chat-studio-widget/p-iUBQH9om.js.map +0 -1
@@ -69,6 +69,7 @@ export class OcsChat {
69
69
  this.parsedStarterQuestions = [];
70
70
  this.isFullscreen = false;
71
71
  this.showNewChatConfirmation = false;
72
+ this.sessionEnded = false;
72
73
  this.selectedFiles = [];
73
74
  this.isUploadingFiles = false;
74
75
  this.buttonPosition = { x: 30, y: 30 };
@@ -294,6 +295,23 @@ export class OcsChat {
294
295
  this.removeButtonEventListeners();
295
296
  window.removeEventListener('resize', this.handleWindowResize);
296
297
  }
298
+ // ---------------------------------------------------------------------------
299
+ // Public event API
300
+ // ---------------------------------------------------------------------------
301
+ /**
302
+ * Dispatch a composed, bubbling CustomEvent on the host element so that
303
+ * embedders can listen with plain addEventListener outside the shadow root.
304
+ *
305
+ * All events are dispatched synchronously so that handlers can update
306
+ * reactive props (e.g. `pageContext`) before the calling code reads them.
307
+ */
308
+ dispatchWidgetEvent(name, detail = null) {
309
+ this.host.dispatchEvent(new CustomEvent(name, {
310
+ bubbles: true,
311
+ composed: true,
312
+ detail,
313
+ }));
314
+ }
297
315
  applySessionToken(token) {
298
316
  var _a;
299
317
  this.currentSessionToken = token;
@@ -345,6 +363,38 @@ export class OcsChat {
345
363
  this.clearSessionStorage();
346
364
  this.addErrorMessage(this.translationManager.get('status.sessionExpired', 'Your chat session expired. Starting a new chat — please resend your message.'));
347
365
  }
366
+ /**
367
+ * The server reported the session has ended (e.g. closed from another tab or
368
+ * by the bot). Polling has already stopped; disable the composer and tell the
369
+ * user. Unbound widgets can recover via the "new chat" button (clearSession).
370
+ */
371
+ handleSessionEnded() {
372
+ var _a;
373
+ if (this.sessionEnded) {
374
+ return;
375
+ }
376
+ this.sessionEnded = true;
377
+ this.dispatchWidgetEvent('ocs:session:ended', { sessionId: this.activeSessionId });
378
+ this.stopMessagePolling();
379
+ this.isTyping = false;
380
+ this.typingProgressMessage = '';
381
+ const content = (_a = this.translationManager.get('status.chatEnded')) !== null && _a !== void 0 ? _a : 'This chat has ended.';
382
+ // An unbound widget restores persisted messages and re-polls after a reload,
383
+ // so the notice may already be the last message.
384
+ const last = this.messages.at(-1);
385
+ if ((last === null || last === void 0 ? void 0 : last.role) === 'system' && last.content === content) {
386
+ return;
387
+ }
388
+ const notice = {
389
+ created_at: new Date().toISOString(),
390
+ role: 'system',
391
+ content,
392
+ attachments: [],
393
+ };
394
+ this.messages = [...this.messages, notice];
395
+ this.saveSessionToStorage();
396
+ this.scrollToBottom();
397
+ }
348
398
  handleError(errorText) {
349
399
  // show as system message
350
400
  this.addErrorMessage(errorText);
@@ -443,6 +493,7 @@ export class OcsChat {
443
493
  this.activeSessionId = data.session_id;
444
494
  this.applySessionToken((_a = data.session_token) !== null && _a !== void 0 ? _a : undefined);
445
495
  this.saveSessionToStorage();
496
+ this.dispatchWidgetEvent('ocs:session:started', { sessionId: this.activeSessionId });
446
497
  this.startMessagePolling();
447
498
  }
448
499
  catch (_error) {
@@ -493,7 +544,7 @@ export class OcsChat {
493
544
  sessionId: this.activeSessionId,
494
545
  participantId: this.getOrGenerateUserId(),
495
546
  participantName: this.userName,
496
- sessionToken: this.currentSessionToken,
547
+ headers: this.getChatService().getUploadHeaders(),
497
548
  });
498
549
  this.selectedFiles = uploadResult.selectedFiles;
499
550
  if (uploadResult.tokenRejected) {
@@ -506,7 +557,8 @@ export class OcsChat {
506
557
  }
507
558
  }
508
559
  async sendMessage(message) {
509
- if (!message.trim())
560
+ var _a;
561
+ if (!message.trim() || this.sessionEnded)
510
562
  return;
511
563
  const epoch = this.sessionEpoch;
512
564
  // Start session if we don't have one yet
@@ -568,6 +620,12 @@ export class OcsChat {
568
620
  this.selectedFiles = []; // Clear selected files after sending
569
621
  }
570
622
  this.scrollToBottom();
623
+ // Fire before-send first so handlers can update pageContext synchronously
624
+ // before we read internalPageContext into the request body.
625
+ this.dispatchWidgetEvent('ocs:message:before-send', {
626
+ message: message.trim(),
627
+ sessionId: (_a = this.activeSessionId) !== null && _a !== void 0 ? _a : '',
628
+ });
571
629
  const requestBody = { message: message.trim() };
572
630
  if (this.allowAttachments && attachmentIds.length > 0) {
573
631
  requestBody.attachment_ids = attachmentIds;
@@ -585,6 +643,10 @@ export class OcsChat {
585
643
  throw new Error(data.error || 'Failed to send message');
586
644
  }
587
645
  this.internalPageContext = undefined;
646
+ this.dispatchWidgetEvent('ocs:message:sent', {
647
+ message: message.trim(),
648
+ sessionId: this.activeSessionId,
649
+ });
588
650
  this.startTaskPolling(data.task_id);
589
651
  }
590
652
  catch (error) {
@@ -700,6 +762,7 @@ export class OcsChat {
700
762
  return;
701
763
  }
702
764
  this.saveVisibleState(visible);
765
+ this.dispatchWidgetEvent(visible ? 'ocs:open' : 'ocs:close');
703
766
  if (this.isButtonDragging) {
704
767
  this.isButtonDragging = false;
705
768
  this.buttonWasDragged = false;
@@ -736,8 +799,13 @@ export class OcsChat {
736
799
  }
737
800
  this.taskPollingHandle = this.getChatService().pollTask(this.activeSessionId, taskId, {
738
801
  onMessage: message => {
802
+ var _a;
739
803
  this.messages = [...this.messages, message];
740
804
  this.saveSessionToStorage();
805
+ this.dispatchWidgetEvent('ocs:message:received', {
806
+ message: Object.assign({}, message),
807
+ sessionId: (_a = this.activeSessionId) !== null && _a !== void 0 ? _a : '',
808
+ });
741
809
  this.scrollToBottom();
742
810
  this.isTyping = false;
743
811
  this.typingProgressMessage = '';
@@ -779,7 +847,7 @@ export class OcsChat {
779
847
  });
780
848
  }
781
849
  startMessagePolling() {
782
- if (!this.activeSessionId || this.currentPollTaskId || !this.visible) {
850
+ if (!this.activeSessionId || this.currentPollTaskId || !this.visible || this.sessionEnded) {
783
851
  return;
784
852
  }
785
853
  if (this.messagePollingHandle) {
@@ -788,13 +856,26 @@ export class OcsChat {
788
856
  this.messagePollingHandle = this.getChatService().startMessagePolling(this.activeSessionId, {
789
857
  getSince: () => { var _a; return (this.messages.length > 0 ? (_a = this.messages.at(-1)) === null || _a === void 0 ? void 0 : _a.created_at : undefined); },
790
858
  onMessages: messages => {
859
+ var _a;
791
860
  if (messages.length === 0)
792
861
  return;
793
862
  this.messages = [...this.messages, ...messages];
794
863
  this.saveSessionToStorage();
864
+ for (const message of messages) {
865
+ if (message.role !== 'user') {
866
+ this.dispatchWidgetEvent('ocs:message:received', {
867
+ message: Object.assign({}, message),
868
+ sessionId: (_a = this.activeSessionId) !== null && _a !== void 0 ? _a : '',
869
+ });
870
+ }
871
+ }
795
872
  this.scrollToBottom();
796
873
  this.focusInput();
797
874
  },
875
+ onSessionEnded: () => {
876
+ this.messagePollingHandle = undefined;
877
+ this.handleSessionEnded();
878
+ },
798
879
  onError: () => {
799
880
  // Silently ignore polling errors to match previous behaviour
800
881
  },
@@ -1233,7 +1314,9 @@ export class OcsChat {
1233
1314
  return newUserId;
1234
1315
  }
1235
1316
  saveVisibleState(visible) {
1236
- if (!this.persistentSession)
1317
+ // Kiosk visibility is forced, so persisting it would only leak into a
1318
+ // standard-mode widget for the same chatbot on another page.
1319
+ if (!this.persistentSession || this.isKioskMode())
1237
1320
  return;
1238
1321
  try {
1239
1322
  const keys = this.getStorageKeys();
@@ -1307,6 +1390,7 @@ export class OcsChat {
1307
1390
  this.applySessionToken(this.isSessionBound() ? this.sessionToken : undefined);
1308
1391
  this.messages = [];
1309
1392
  this.isTyping = false;
1393
+ this.sessionEnded = false;
1310
1394
  this.currentPollTaskId = '';
1311
1395
  if (this.allowAttachments) {
1312
1396
  this.selectedFiles = [];
@@ -1328,12 +1412,12 @@ export class OcsChat {
1328
1412
  if (this.error && !this.activeSessionId) {
1329
1413
  return (h(Host, null, h("p", { class: "error-message" }, this.error)));
1330
1414
  }
1331
- return (h(Host, null, this.renderButton(), this.visible && (h("div", { ref: el => (this.chatWindowRef = el), id: "ocs-chat-window", class: this.getPositionClasses(), style: this.getPositionStyles() }, !this.isKioskMode() && (h("div", { class: `chat-header ${this.isDragging ? 'chat-header-dragging' : 'chat-header-draggable'}`, onMouseDown: this.handleMouseDown, onTouchStart: this.handleTouchStart }, h("div", { class: "drag-indicator" }, h("div", { class: "drag-dots header-button" }, h(GripDotsVerticalIcon, null))), h("div", { class: "header-text" }, this.translationManager.get('branding.headerText', this.headerText)), h("div", { class: "header-buttons" }, this.messages.length > 0 && !this.isSessionBound() && (h("button", { class: "header-button", onClick: () => this.showConfirmationDialog(), title: this.translationManager.get('window.newChat'), "aria-label": this.translationManager.get('window.newChat') }, h(PlusWithCircleIcon, null))), this.allowFullScreen && (h("button", { class: "header-button fullscreen-button", onClick: () => this.toggleFullscreen(), title: this.isFullscreen ? this.translationManager.get('window.exitFullscreen') : this.translationManager.get('window.fullscreen'), "aria-label": this.isFullscreen ? this.translationManager.get('window.exitFullscreen') : this.translationManager.get('window.fullscreen') }, this.isFullscreen ? h(ArrowsPointingInIcon, null) : h(ArrowsPointingOutIcon, null))), h("button", { class: "header-button", onClick: () => (this.visible = false), "aria-label": this.translationManager.get('window.close') }, h(XMarkIcon, null))))), !this.isKioskMode() && this.showNewChatConfirmation && (h("div", { class: "confirmation-overlay" }, h("div", { class: "confirmation-dialog" }, h("div", { class: "confirmation-content" }, h("h3", { class: "confirmation-title" }, this.translationManager.get('modal.newChatTitle')), h("p", { class: "confirmation-message" }, this.translationManager.get('modal.newChatBody', this.newChatConfirmationMessage)), h("div", { class: "confirmation-buttons" }, h("button", { class: "confirmation-button confirmation-button-cancel", onClick: () => this.hideConfirmationDialog() }, this.translationManager.get('modal.cancel')), h("button", { class: "confirmation-button confirmation-button-confirm", onClick: () => this.confirmNewChat() }, this.translationManager.get('modal.confirm'))))))), h("div", { class: "chat-content" }, this.isLoading && !this.activeSessionId && (h("div", { class: "loading-container" }, h("div", { class: "loading-spinner" }), h("span", { class: "loading-text" }, this.translationManager.get('status.starting')))), h("div", { ref: el => (this.messageListRef = el), class: "messages-container" }, this.messages.length === 0 && this.getWelcomeMessages().length > 0 && (h("div", { class: "welcome-messages" }, this.getWelcomeMessages().map((message, index) => (h("div", { key: `welcome-${index}`, class: "message-row message-row-assistant" }, h("div", { class: "message-bubble message-bubble-assistant" }, h("div", { class: "chat-markdown", innerHTML: renderMarkdownComplete(message) }))))))), this.messages.map((message, index) => (h("div", { key: index, class: `message-row ${message.role === 'user' ? 'message-row-user' : 'message-row-assistant'}` }, h("div", { class: `message-bubble ${message.role === 'user' ? 'message-bubble-user' : message.role === 'assistant' ? 'message-bubble-assistant' : 'message-bubble-system'}` }, h("div", { class: "chat-markdown", innerHTML: renderMarkdownComplete(message.content) }), message.attachments && message.attachments.length > 0 && (h("div", { class: "message-attachments" }, message.attachments.map((attachment, attachmentIndex) => (h("div", { key: attachmentIndex, class: "flex items-center gap-[0.5em]" }, h("span", { class: "message-attachment-icon" }, h(PaperClipIcon, null)), h("span", { class: "message-attachment-name" }, attachment.name)))))), h("div", { class: "message-timestamp" }, this.formatTime(message.created_at)))))), this.isTyping && (h("div", null, h("div", { class: "typing-indicator" }, h("div", { class: "typing-progress" })), h("div", { class: "typing-text" }, h("span", null, this.typingProgressMessage || this.translationManager.get('status.typing', this.typingIndicatorText)), h("span", { class: "typing-dots loading" }))))), this.messages.length === 0 && this.getStarterQuestions().length > 0 && (h("div", { class: "starter-questions" }, this.getStarterQuestions().map((question, index) => (h("div", { key: `starter-${index}`, class: "starter-question-row" }, h("button", { class: "starter-question", onClick: () => this.handleStarterQuestionClick(question) }, question)))))), this.allowAttachments && this.selectedFiles.length > 0 && (h("div", { class: "selected-files-container" }, h("div", { class: "space-y-[0.25em]" }, this.selectedFiles.map((selectedFile, index) => (h("div", { key: index, class: "selected-file-item" }, h("div", { class: "flex items-center gap-[0.5em]" }, h("span", { class: "selected-file-icon" }, h(PaperClipIcon, null)), h("span", null, selectedFile.file.name), h("span", { class: "selected-file-size" }, "(", this.formatFileSize(selectedFile.file.size), ")"), selectedFile.error && h("span", { class: "selected-file-error" }, selectedFile.error), selectedFile.uploaded && (h("span", { class: "selected-file-success-icon" }, h(CheckDocumentIcon, null)))), h("button", { onClick: () => this.removeSelectedFile(index), class: "selected-file-remove-button", "aria-label": this.translationManager.get('attach.remove') }, h(XIcon, null)))))))), h("div", { class: "input-area" }, h("div", { class: "input-container" }, h("textarea", { ref: el => (this.textareaRef = el), class: "message-textarea", rows: 1, placeholder: this.translationManager.get('composer.placeholder'), value: this.messageInput, onInput: e => this.handleInputChange(e), onKeyPress: e => this.handleKeyPress(e), disabled: this.isTyping || this.isUploadingFiles || this.isLoading }), this.allowAttachments && (h("input", { ref: el => {
1415
+ return (h(Host, null, this.renderButton(), this.visible && (h("div", { ref: el => (this.chatWindowRef = el), id: "ocs-chat-window", class: this.getPositionClasses(), style: this.getPositionStyles() }, !this.isKioskMode() && (h("div", { class: `chat-header ${this.isDragging ? 'chat-header-dragging' : 'chat-header-draggable'}`, onMouseDown: this.handleMouseDown, onTouchStart: this.handleTouchStart }, h("div", { class: "drag-indicator" }, h("div", { class: "drag-dots header-button" }, h(GripDotsVerticalIcon, null))), h("div", { class: "header-text" }, this.translationManager.get('branding.headerText', this.headerText)), h("div", { class: "header-buttons" }, this.messages.length > 0 && !this.isSessionBound() && (h("button", { class: "header-button", onClick: () => this.showConfirmationDialog(), title: this.translationManager.get('window.newChat'), "aria-label": this.translationManager.get('window.newChat') }, h(PlusWithCircleIcon, null))), this.allowFullScreen && (h("button", { class: "header-button fullscreen-button", onClick: () => this.toggleFullscreen(), title: this.isFullscreen ? this.translationManager.get('window.exitFullscreen') : this.translationManager.get('window.fullscreen'), "aria-label": this.isFullscreen ? this.translationManager.get('window.exitFullscreen') : this.translationManager.get('window.fullscreen') }, this.isFullscreen ? h(ArrowsPointingInIcon, null) : h(ArrowsPointingOutIcon, null))), h("button", { class: "header-button", onClick: () => (this.visible = false), "aria-label": this.translationManager.get('window.close') }, h(XMarkIcon, null))))), !this.isKioskMode() && this.showNewChatConfirmation && (h("div", { class: "confirmation-overlay" }, h("div", { class: "confirmation-dialog" }, h("div", { class: "confirmation-content" }, h("h3", { class: "confirmation-title" }, this.translationManager.get('modal.newChatTitle')), h("p", { class: "confirmation-message" }, this.translationManager.get('modal.newChatBody', this.newChatConfirmationMessage)), h("div", { class: "confirmation-buttons" }, h("button", { class: "confirmation-button confirmation-button-cancel", onClick: () => this.hideConfirmationDialog() }, this.translationManager.get('modal.cancel')), h("button", { class: "confirmation-button confirmation-button-confirm", onClick: () => this.confirmNewChat() }, this.translationManager.get('modal.confirm'))))))), h("div", { class: "chat-content" }, this.isLoading && !this.activeSessionId && (h("div", { class: "loading-container" }, h("div", { class: "loading-spinner" }), h("span", { class: "loading-text" }, this.translationManager.get('status.starting')))), h("div", { ref: el => (this.messageListRef = el), class: "messages-container" }, this.messages.length === 0 && this.getWelcomeMessages().length > 0 && (h("div", { class: "welcome-messages" }, this.getWelcomeMessages().map((message, index) => (h("div", { key: `welcome-${index}`, class: "message-row message-row-assistant" }, h("div", { class: "message-bubble message-bubble-assistant" }, h("div", { class: "chat-markdown", innerHTML: renderMarkdownComplete(message) }))))))), this.messages.map((message, index) => (h("div", { key: index, class: `message-row ${message.role === 'user' ? 'message-row-user' : 'message-row-assistant'}` }, h("div", { class: `message-bubble ${message.role === 'user' ? 'message-bubble-user' : message.role === 'assistant' ? 'message-bubble-assistant' : 'message-bubble-system'}` }, h("div", { class: "chat-markdown", innerHTML: renderMarkdownComplete(message.content) }), message.attachments && message.attachments.length > 0 && (h("div", { class: "message-attachments" }, message.attachments.map((attachment, attachmentIndex) => (h("div", { key: attachmentIndex, class: "flex items-center gap-[0.5em]" }, h("span", { class: "message-attachment-icon" }, h(PaperClipIcon, null)), h("span", { class: "message-attachment-name" }, attachment.name)))))), h("div", { class: "message-timestamp" }, this.formatTime(message.created_at)))))), this.isTyping && (h("div", null, h("div", { class: "typing-indicator" }, h("div", { class: "typing-progress" })), h("div", { class: "typing-text" }, h("span", null, this.typingProgressMessage || this.translationManager.get('status.typing', this.typingIndicatorText)), h("span", { class: "typing-dots loading" }))))), this.messages.length === 0 && this.getStarterQuestions().length > 0 && (h("div", { class: "starter-questions" }, this.getStarterQuestions().map((question, index) => (h("div", { key: `starter-${index}`, class: "starter-question-row" }, h("button", { class: "starter-question", onClick: () => this.handleStarterQuestionClick(question) }, question)))))), this.allowAttachments && this.selectedFiles.length > 0 && (h("div", { class: "selected-files-container" }, h("div", { class: "space-y-[0.25em]" }, this.selectedFiles.map((selectedFile, index) => (h("div", { key: index, class: "selected-file-item" }, h("div", { class: "flex items-center gap-[0.5em]" }, h("span", { class: "selected-file-icon" }, h(PaperClipIcon, null)), h("span", null, selectedFile.file.name), h("span", { class: "selected-file-size" }, "(", this.formatFileSize(selectedFile.file.size), ")"), selectedFile.error && h("span", { class: "selected-file-error" }, selectedFile.error), selectedFile.uploaded && (h("span", { class: "selected-file-success-icon" }, h(CheckDocumentIcon, null)))), h("button", { onClick: () => this.removeSelectedFile(index), class: "selected-file-remove-button", "aria-label": this.translationManager.get('attach.remove') }, h(XIcon, null)))))))), h("div", { class: "input-area" }, h("div", { class: "input-container" }, h("textarea", { ref: el => (this.textareaRef = el), class: "message-textarea", rows: 1, placeholder: this.sessionEnded ? this.translationManager.get('status.chatEnded') : this.translationManager.get('composer.placeholder'), value: this.messageInput, onInput: e => this.handleInputChange(e), onKeyPress: e => this.handleKeyPress(e), disabled: this.isTyping || this.isUploadingFiles || this.isLoading || this.sessionEnded }), this.allowAttachments && (h("input", { ref: el => {
1332
1416
  // Unclear why but after removing all attachments this is being set to `null`.
1333
1417
  if (el) {
1334
1418
  this.fileInputRef = el;
1335
1419
  }
1336
- }, id: "ocs-file-input", type: "file", multiple: true, accept: OcsChat.SUPPORTED_FILE_EXTENSIONS.join(',') + ',text/*', onChange: e => this.handleFileSelect(e), class: "hidden" })), this.allowAttachments && (h("button", { class: "file-attachment-button", onClick: () => { var _a; return (_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: this.isTyping || this.isUploadingFiles || this.isLoading, title: this.translationManager.get('attach.add'), "aria-label": this.translationManager.get('attach.add') }, h(PaperClipIcon, null))), h("button", { class: `send-button ${!this.isTyping && !this.isLoading && !!this.messageInput.trim() ? 'send-button-enabled' : 'send-button-disabled'}`, onClick: () => this.sendMessage(this.messageInput), disabled: this.isTyping || this.isUploadingFiles || this.isLoading || !this.messageInput.trim() }, this.isUploadingFiles ? `${this.translationManager.get('status.uploading')}...` : this.translationManager.get('composer.send')))), h("div", { class: "flex items-center justify-center text-[0.8em] font-light w-full text-slate-500 py-[2px]" }, h("p", null, this.translationManager.get('branding.poweredBy'), ' ', h("a", { class: "underline", href: "https://www.dimagi.com", target: "_blank", rel: "noopener noreferrer" }, "Dimagi"))))))));
1420
+ }, id: "ocs-file-input", type: "file", multiple: true, accept: OcsChat.SUPPORTED_FILE_EXTENSIONS.join(',') + ',text/*', onChange: e => this.handleFileSelect(e), class: "hidden" })), this.allowAttachments && (h("button", { class: "file-attachment-button", onClick: () => { var _a; return (_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: this.isTyping || this.isUploadingFiles || this.isLoading || this.sessionEnded, title: this.translationManager.get('attach.add'), "aria-label": this.translationManager.get('attach.add') }, h(PaperClipIcon, null))), h("button", { class: `send-button ${!this.isTyping && !this.isLoading && !this.sessionEnded && !!this.messageInput.trim() ? 'send-button-enabled' : 'send-button-disabled'}`, onClick: () => this.sendMessage(this.messageInput), disabled: this.isTyping || this.isUploadingFiles || this.isLoading || this.sessionEnded || !this.messageInput.trim() }, this.isUploadingFiles ? `${this.translationManager.get('status.uploading')}...` : this.translationManager.get('composer.send')))), h("div", { class: "flex items-center justify-center text-[0.8em] font-light w-full text-slate-500 py-[2px]" }, h("p", null, this.translationManager.get('branding.poweredBy'), ' ', h("a", { class: "underline", href: "https://www.dimagi.com", target: "_blank", rel: "noopener noreferrer" }, "Dimagi"))))))));
1337
1421
  }
1338
1422
  static get is() { return "open-chat-studio-widget"; }
1339
1423
  static get encapsulation() { return "shadow"; }
@@ -1900,6 +1984,7 @@ export class OcsChat {
1900
1984
  "generatedUserId": {},
1901
1985
  "isFullscreen": {},
1902
1986
  "showNewChatConfirmation": {},
1987
+ "sessionEnded": {},
1903
1988
  "selectedFiles": {},
1904
1989
  "isUploadingFiles": {},
1905
1990
  "isButtonDragging": {},