open-chat-studio-widget 0.5.0 → 0.5.2

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 (30) hide show
  1. package/dist/cjs/{index-CC3Krx2K.js → index-Ctja7z-R.js} +3 -3
  2. package/dist/cjs/{index-CC3Krx2K.js.map → index-Ctja7z-R.js.map} +1 -1
  3. package/dist/cjs/loader.cjs.js +1 -1
  4. package/dist/cjs/open-chat-studio-widget.cjs.entry.js +45 -38
  5. package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
  6. package/dist/cjs/open-chat-studio-widget.cjs.js +1 -1
  7. package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
  8. package/dist/collection/components/ocs-chat/ocs-chat.js +44 -37
  9. package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
  10. package/dist/collection/services/chat-session-service.js.map +1 -1
  11. package/dist/collection/utils/translations.js +2 -2
  12. package/dist/collection/utils/translations.js.map +1 -1
  13. package/dist/components/open-chat-studio-widget.js +44 -37
  14. package/dist/components/open-chat-studio-widget.js.map +1 -1
  15. package/dist/esm/{index-BF7CYZiN.js → index-BbCwiO7g.js} +3 -3
  16. package/dist/esm/{index-BF7CYZiN.js.map → index-BbCwiO7g.js.map} +1 -1
  17. package/dist/esm/loader.js +2 -2
  18. package/dist/esm/open-chat-studio-widget.entry.js +45 -38
  19. package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
  20. package/dist/esm/open-chat-studio-widget.js +2 -2
  21. package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
  22. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
  23. package/dist/open-chat-studio-widget/{p-400b1f47.entry.js → p-96920183.entry.js} +4 -4
  24. package/dist/open-chat-studio-widget/p-96920183.entry.js.map +1 -0
  25. package/dist/open-chat-studio-widget/{p-BF7CYZiN.js → p-BbCwiO7g.js} +2 -2
  26. package/dist/open-chat-studio-widget/{p-BF7CYZiN.js.map → p-BbCwiO7g.js.map} +1 -1
  27. package/dist/types/components/ocs-chat/ocs-chat.d.ts +5 -1
  28. package/dist/types/services/chat-session-service.d.ts +0 -1
  29. package/package.json +2 -2
  30. package/dist/open-chat-studio-widget/p-400b1f47.entry.js.map +0 -1
@@ -235,17 +235,16 @@ export class OcsChat {
235
235
  this.chatWindowFullscreenWidth = varToPixels(fullscreenWidthVar, window.innerWidth, this.chatWindowFullscreenWidth);
236
236
  // Initialize button position from computed styles
237
237
  this.initializeButtonPosition();
238
- if (this.visible) {
239
- this.initializePosition();
240
- }
241
- // Only auto-start session if we don't have an existing one
242
- if (this.visible && !this.sessionId) {
243
- this.startSession();
244
- }
245
- else if (this.visible && this.sessionId) {
246
- // Resume polling for existing session
247
- this.startMessagePolling();
248
- }
238
+ // Defer position initialization to avoid state changes during componentDidLoad
239
+ setTimeout(() => {
240
+ if (this.visible) {
241
+ this.initializePosition();
242
+ }
243
+ // Resume polling for existing session (don't auto-start new sessions)
244
+ if (this.visible && this.sessionId) {
245
+ this.startMessagePolling();
246
+ }
247
+ }, 0);
249
248
  window.addEventListener('resize', this.handleWindowResize);
250
249
  }
251
250
  disconnectedCallback() {
@@ -357,13 +356,7 @@ export class OcsChat {
357
356
  const data = await this.getChatService().startSession(requestBody);
358
357
  this.sessionId = data.session_id;
359
358
  this.saveSessionToStorage();
360
- // Handle seed message if present
361
- if (data.seed_message_task_id) {
362
- this.startTaskPolling(data.seed_message_task_id);
363
- }
364
- else {
365
- this.startMessagePolling();
366
- }
359
+ this.startMessagePolling();
367
360
  }
368
361
  catch (_error) {
369
362
  this.handleError('Failed to start chat session');
@@ -392,8 +385,20 @@ export class OcsChat {
392
385
  }
393
386
  }
394
387
  async sendMessage(message) {
395
- if (!this.sessionId || !message.trim())
388
+ if (!message.trim())
396
389
  return;
390
+ // Start session if we don't have one yet
391
+ if (!this.sessionId) {
392
+ // Prevent concurrent session initialization
393
+ if (this.isLoading) {
394
+ return;
395
+ }
396
+ await this.startSession();
397
+ // Check if session started successfully
398
+ if (!this.sessionId) {
399
+ return; // startSession already handled the error
400
+ }
401
+ }
397
402
  try {
398
403
  let attachmentIds = [];
399
404
  if (this.allowAttachments && this.selectedFiles.length > 0) {
@@ -408,10 +413,11 @@ export class OcsChat {
408
413
  }
409
414
  // If this is the first user message and there are welcome messages,
410
415
  // add them to chat history as assistant messages
411
- if (this.messages.length === 0 && this.parsedWelcomeMessages.length > 0) {
416
+ const welcomeMessagesToAdd = this.getWelcomeMessages();
417
+ if (this.messages.length === 0 && welcomeMessagesToAdd.length > 0) {
412
418
  const now = new Date();
413
- const welcomeMessages = this.parsedWelcomeMessages.map((welcomeMsg, index) => ({
414
- created_at: new Date(now.getTime() - (this.parsedWelcomeMessages.length - index) * 1000).toISOString(),
419
+ const welcomeMessages = welcomeMessagesToAdd.map((welcomeMsg, index) => ({
420
+ created_at: new Date(now.getTime() - (welcomeMessagesToAdd.length - index) * 1000).toISOString(),
415
421
  role: 'assistant',
416
422
  content: welcomeMsg,
417
423
  attachments: []
@@ -545,16 +551,14 @@ export class OcsChat {
545
551
  }
546
552
  if (visible) {
547
553
  this.initializePosition();
548
- }
549
- if (visible && !this.sessionId) {
550
- await this.startSession();
551
- }
552
- else if (!visible) {
553
- this.stopMessagePolling();
554
+ // Resume polling for existing session (don't auto-start new sessions)
555
+ if (this.sessionId) {
556
+ this.scrollToBottom(true);
557
+ this.startMessagePolling();
558
+ }
554
559
  }
555
560
  else {
556
- this.scrollToBottom(true);
557
- this.startMessagePolling();
561
+ this.stopMessagePolling();
558
562
  }
559
563
  }
560
564
  startTaskPolling(taskId) {
@@ -1057,9 +1061,13 @@ export class OcsChat {
1057
1061
  }
1058
1062
  async confirmNewChat() {
1059
1063
  this.hideConfirmationDialog();
1060
- await this.actuallyStartNewChat();
1064
+ await this.clearSession();
1061
1065
  }
1062
- async actuallyStartNewChat() {
1066
+ /**
1067
+ * This clears out all data related to the previous session. A new session
1068
+ * will start when the user sends a message.
1069
+ */
1070
+ async clearSession() {
1063
1071
  this.clearSessionStorage();
1064
1072
  this.sessionId = undefined;
1065
1073
  this.messages = [];
@@ -1069,7 +1077,6 @@ export class OcsChat {
1069
1077
  this.selectedFiles = [];
1070
1078
  }
1071
1079
  this.cleanup();
1072
- await this.startSession();
1073
1080
  }
1074
1081
  toggleFullscreen() {
1075
1082
  this.isFullscreen = !this.isFullscreen;
@@ -1081,18 +1088,18 @@ export class OcsChat {
1081
1088
  if (this.error && !this.sessionId) {
1082
1089
  return (h(Host, null, h("p", { class: "error-message" }, this.error)));
1083
1090
  }
1084
- 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() }, 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 && (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.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.sessionId && (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.parsedWelcomeMessages.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'
1091
+ 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() }, 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 && (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.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.sessionId && (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'
1085
1092
  ? 'message-bubble-user'
1086
1093
  : message.role === 'assistant'
1087
1094
  ? 'message-bubble-assistant'
1088
- : '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.translationManager.get('status.typing', this.typingIndicatorText)), h("span", { class: "typing-dots loading" })))))), this.messages.length === 0 && this.parsedStarterQuestions.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)))))))), this.sessionId && (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.allowAttachments && (h("input", { ref: (el) => {
1095
+ : '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.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) => {
1089
1096
  // Unclear why but after removing all attachments this is being set to `null`.
1090
1097
  if (el) {
1091
1098
  this.fileInputRef = el;
1092
1099
  }
1093
- }, id: "ocs-file-input", type: "file", multiple: true, accept: OcsChat.SUPPORTED_FILE_EXTENSIONS.join(','), 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, title: this.translationManager.get('attach.add'), "aria-label": this.translationManager.get('attach.add') }, h(PaperClipIcon, null))), h("button", { class: `send-button ${!this.isTyping && !!this.messageInput.trim()
1100
+ }, id: "ocs-file-input", type: "file", multiple: true, accept: OcsChat.SUPPORTED_FILE_EXTENSIONS.join(','), 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()
1094
1101
  ? 'send-button-enabled'
1095
- : 'send-button-disabled'}`, onClick: () => this.sendMessage(this.messageInput), disabled: this.isTyping || this.isUploadingFiles || !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" }, "Dimagi"))))))));
1102
+ : '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" }, "Dimagi"))))))));
1096
1103
  }
1097
1104
  static get is() { return "open-chat-studio-widget"; }
1098
1105
  static get encapsulation() { return "shadow"; }