open-chat-studio-widget 0.5.3 → 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 -24
  2. package/dist/cjs/{index-D8A4RBzq.js → index-CvB341El.js} +3 -3
  3. package/dist/cjs/{index-D8A4RBzq.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 +387 -139
  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 +329 -76
  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 -6
  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 +394 -138
  26. package/dist/components/open-chat-studio-widget.js.map +1 -1
  27. package/dist/esm/{index-C53whb-B.js → index-C2QZK0Ui.js} +3 -3
  28. package/dist/esm/{index-C53whb-B.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 +387 -139
  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-C53whb-B.js → p-C2QZK0Ui.js} +2 -2
  36. package/dist/open-chat-studio-widget/{p-C53whb-B.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 +41 -1
  40. package/dist/types/components.d.ts +31 -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-b9556259.entry.js +0 -4
  45. package/dist/open-chat-studio-widget/p-b9556259.entry.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2
2
  import { Host, h, Env } from "@stencil/core";
3
- import { XMarkIcon, GripDotsVerticalIcon, PlusWithCircleIcon, ArrowsPointingOutIcon, ArrowsPointingInIcon, PaperClipIcon, CheckDocumentIcon, XIcon, OcsWidgetAvatar } from "./icons";
3
+ import { XMarkIcon, GripDotsVerticalIcon, PlusWithCircleIcon, ArrowsPointingOutIcon, ArrowsPointingInIcon, PaperClipIcon, CheckDocumentIcon, XIcon, OcsWidgetAvatar, } from "./icons";
4
4
  import { renderMarkdownSync as renderMarkdownComplete } from "../../utils/markdown";
5
5
  import { varToPixels } from "../../utils/utils";
6
6
  import { TranslationManager, defaultTranslations } from "../../utils/translations";
@@ -11,11 +11,23 @@ export class OcsChat {
11
11
  /**
12
12
  * The base URL for the API.
13
13
  */
14
- this.apiBaseUrl = "https://www.openchatstudio.com";
14
+ this.apiBaseUrl = 'https://www.openchatstudio.com';
15
15
  /**
16
16
  * The shape of the chat button. 'round' makes it circular, 'square' keeps it rectangular.
17
17
  */
18
18
  this.buttonShape = 'square';
19
+ /**
20
+ * Whether to show the launcher button. Set to false to hide the button
21
+ * and open the chat window programmatically via the `visible` property.
22
+ */
23
+ this.showButton = true;
24
+ /**
25
+ * The operating mode of the widget.
26
+ * - 'standard': Default floating window with launcher button.
27
+ * - 'kiosk': Fills parent container, always visible, no header or launcher button.
28
+ * The parent element must establish a containing block (e.g. `position: relative`).
29
+ */
30
+ this.mode = 'standard';
19
31
  /**
20
32
  * Whether the chat widget is visible on load.
21
33
  */
@@ -41,12 +53,13 @@ export class OcsChat {
41
53
  * Allow the user to attach files to their messages.
42
54
  */
43
55
  this.allowAttachments = false;
44
- this.error = "";
56
+ this.error = '';
45
57
  this.messages = [];
46
58
  this.isLoading = false;
47
59
  this.isTyping = false;
48
- this.messageInput = "";
49
- this.currentPollTaskId = "";
60
+ this.typingProgressMessage = '';
61
+ this.messageInput = '';
62
+ this.currentPollTaskId = '';
50
63
  this.isDragging = false;
51
64
  this.dragOffset = { x: 0, y: 0 };
52
65
  this.windowPosition = { x: 0, y: 0 };
@@ -75,6 +88,7 @@ export class OcsChat {
75
88
  this.chatWindowWidth = 450;
76
89
  this.chatWindowFullscreenWidth = 1024;
77
90
  this.positionInitialized = false;
91
+ this.sessionEpoch = 0;
78
92
  this.handleMouseDown = (event) => {
79
93
  if (!this.isFullscreen && window.innerWidth < OcsChat.MOBILE_BREAKPOINT)
80
94
  return;
@@ -120,6 +134,8 @@ export class OcsChat {
120
134
  };
121
135
  this.handleWindowResize = () => {
122
136
  var _a, _b;
137
+ if (this.isKioskMode())
138
+ return;
123
139
  this.positionInitialized = false;
124
140
  this.initializePosition();
125
141
  // Revalidate button position after resize to keep it within viewport bounds
@@ -131,7 +147,7 @@ export class OcsChat {
131
147
  const minPadding = 10;
132
148
  this.buttonPosition = {
133
149
  x: Math.max(minPadding, Math.min(this.buttonPosition.x, windowWidth - buttonWidth - minPadding)),
134
- y: Math.max(minPadding, Math.min(this.buttonPosition.y, windowHeight - buttonHeight - minPadding))
150
+ y: Math.max(minPadding, Math.min(this.buttonPosition.y, windowHeight - buttonHeight - minPadding)),
135
151
  };
136
152
  this.updateHostPosition();
137
153
  }
@@ -149,7 +165,7 @@ export class OcsChat {
149
165
  const rect = this.host.getBoundingClientRect();
150
166
  this.buttonDragOffset = {
151
167
  x: pointer.clientX - rect.left,
152
- y: pointer.clientY - rect.top
168
+ y: pointer.clientY - rect.top,
153
169
  };
154
170
  this.addButtonEventListeners();
155
171
  };
@@ -166,7 +182,7 @@ export class OcsChat {
166
182
  const rect = this.host.getBoundingClientRect();
167
183
  this.buttonDragOffset = {
168
184
  x: pointer.clientX - rect.left,
169
- y: pointer.clientY - rect.top
185
+ y: pointer.clientY - rect.top,
170
186
  };
171
187
  this.addButtonEventListeners();
172
188
  };
@@ -213,6 +229,9 @@ export class OcsChat {
213
229
  this.error = 'Chatbot ID is required';
214
230
  return;
215
231
  }
232
+ if (this.isKioskMode()) {
233
+ this.visible = true;
234
+ }
216
235
  await this.initializeTranslations();
217
236
  // Always try to load existing session if localStorage is available
218
237
  if (this.persistentSession && this.isLocalStorageAvailable()) {
@@ -224,6 +243,7 @@ export class OcsChat {
224
243
  }
225
244
  this.parseWelcomeMessages();
226
245
  this.parseStarterQuestions();
246
+ this.loadInternalPageContext();
227
247
  }
228
248
  componentDidLoad() {
229
249
  const computedStyle = getComputedStyle(this.host);
@@ -234,11 +254,20 @@ export class OcsChat {
234
254
  this.chatWindowWidth = varToPixels(windowWidthVar, window.innerWidth, this.chatWindowWidth);
235
255
  this.chatWindowFullscreenWidth = varToPixels(fullscreenWidthVar, window.innerWidth, this.chatWindowFullscreenWidth);
236
256
  // Initialize button position from computed styles
237
- this.initializeButtonPosition();
238
- // Defer position initialization to avoid state changes during componentDidLoad
257
+ if (this.showButton && !this.isKioskMode()) {
258
+ this.initializeButtonPosition();
259
+ }
260
+ // Defer state changes to avoid triggering them during componentDidLoad
239
261
  setTimeout(() => {
262
+ // Restore visible state after dimensions are read so initializePosition
263
+ // uses the correct CSS-derived chatWindowWidth/chatWindowHeight.
264
+ if (!this.isKioskMode() && this.showButton && this.persistentSession && this.isLocalStorageAvailable()) {
265
+ this.restoreVisibleState();
266
+ }
240
267
  if (this.visible) {
241
- this.initializePosition();
268
+ if (!this.isKioskMode()) {
269
+ this.initializePosition();
270
+ }
242
271
  }
243
272
  // Resume polling for existing session (don't auto-start new sessions)
244
273
  if (this.visible && this.sessionId) {
@@ -271,7 +300,7 @@ export class OcsChat {
271
300
  created_at: new Date().toISOString(),
272
301
  role: 'system',
273
302
  content: `**Error:** ${errorText}\nPlease try again.`,
274
- attachments: []
303
+ attachments: [],
275
304
  };
276
305
  this.messages = [...this.messages, errorMessage];
277
306
  this.saveSessionToStorage();
@@ -316,6 +345,16 @@ export class OcsChat {
316
345
  }
317
346
  this.translationManager = new TranslationManager(this.language, customTranslationsObj);
318
347
  }
348
+ loadInternalPageContext() {
349
+ if (this.pageContext === undefined || this.pageContext === null) {
350
+ return;
351
+ }
352
+ if (typeof this.pageContext !== 'object' || Array.isArray(this.pageContext)) {
353
+ console.error('pageContext is expected to be a plain JavaScript object.');
354
+ return;
355
+ }
356
+ this.internalPageContext = this.pageContext;
357
+ }
319
358
  async loadTranslationsFromUrl(url) {
320
359
  try {
321
360
  const response = await fetch(url);
@@ -339,6 +378,7 @@ export class OcsChat {
339
378
  this.currentPollTaskId = '';
340
379
  }
341
380
  async startSession() {
381
+ const epoch = this.sessionEpoch;
342
382
  try {
343
383
  this.isLoading = true;
344
384
  const userId = this.getOrGenerateUserId();
@@ -346,19 +386,26 @@ export class OcsChat {
346
386
  chatbot_id: this.chatbotId,
347
387
  session_data: {
348
388
  source: 'widget',
349
- page_url: window.location.href
389
+ page_url: window.location.href,
350
390
  },
351
- participant_remote_id: userId
391
+ participant_remote_id: userId,
352
392
  };
353
393
  if (this.userName) {
354
394
  requestBody.participant_name = this.userName;
355
395
  }
396
+ if (this.versionNumber != null) {
397
+ requestBody.version_number = this.versionNumber;
398
+ }
356
399
  const data = await this.getChatService().startSession(requestBody);
400
+ if (epoch !== this.sessionEpoch)
401
+ return;
357
402
  this.sessionId = data.session_id;
358
403
  this.saveSessionToStorage();
359
404
  this.startMessagePolling();
360
405
  }
361
406
  catch (_error) {
407
+ if (epoch !== this.sessionEpoch)
408
+ return;
362
409
  this.handleError('Failed to start chat session');
363
410
  }
364
411
  finally {
@@ -387,6 +434,7 @@ export class OcsChat {
387
434
  async sendMessage(message) {
388
435
  if (!message.trim())
389
436
  return;
437
+ const epoch = this.sessionEpoch;
390
438
  // Start session if we don't have one yet
391
439
  if (!this.sessionId) {
392
440
  // Prevent concurrent session initialization
@@ -420,7 +468,7 @@ export class OcsChat {
420
468
  created_at: new Date(now.getTime() - (welcomeMessagesToAdd.length - index) * 1000).toISOString(),
421
469
  role: 'assistant',
422
470
  content: welcomeMsg,
423
- attachments: []
471
+ attachments: [],
424
472
  }));
425
473
  this.messages = [...this.messages, ...welcomeMessages];
426
474
  }
@@ -429,13 +477,15 @@ export class OcsChat {
429
477
  created_at: new Date().toISOString(),
430
478
  role: 'user',
431
479
  content: message.trim(),
432
- attachments: this.allowAttachments ? this.selectedFiles
433
- .filter(sf => !sf.error && sf.uploaded)
434
- .map(sf => ({
435
- name: sf.file.name,
436
- content_type: sf.file.type,
437
- size: sf.file.size,
438
- })) : []
480
+ attachments: this.allowAttachments
481
+ ? this.selectedFiles
482
+ .filter(sf => !sf.error && sf.uploaded)
483
+ .map(sf => ({
484
+ name: sf.file.name,
485
+ content_type: sf.file.type,
486
+ size: sf.file.size,
487
+ }))
488
+ : [],
439
489
  };
440
490
  this.messages = [...this.messages, userMessage];
441
491
  this.saveSessionToStorage();
@@ -448,13 +498,24 @@ export class OcsChat {
448
498
  if (this.allowAttachments && attachmentIds.length > 0) {
449
499
  requestBody.attachment_ids = attachmentIds;
450
500
  }
501
+ if (this.internalPageContext) {
502
+ requestBody.context = this.internalPageContext;
503
+ }
504
+ if (this.versionNumber != null) {
505
+ requestBody.version_number = this.versionNumber;
506
+ }
451
507
  const data = await this.getChatService().sendMessage(this.sessionId, requestBody);
508
+ if (epoch !== this.sessionEpoch)
509
+ return;
452
510
  if (data.status === 'error') {
453
511
  throw new Error(data.error || 'Failed to send message');
454
512
  }
513
+ this.internalPageContext = undefined;
455
514
  this.startTaskPolling(data.task_id);
456
515
  }
457
516
  catch (error) {
517
+ if (epoch !== this.sessionEpoch)
518
+ return;
458
519
  const errorText = error instanceof Error ? error.message : 'Failed to send message';
459
520
  this.handleError(errorText);
460
521
  }
@@ -477,10 +538,10 @@ export class OcsChat {
477
538
  const childRect = lastChild.getBoundingClientRect();
478
539
  const currentScrollTop = this.messageListRef.scrollTop;
479
540
  const childTopRelativeToParent = childRect.top - parentRect.top;
480
- const targetScroll = currentScrollTop + childTopRelativeToParent - (parentRect.height / 2);
541
+ const targetScroll = currentScrollTop + childTopRelativeToParent - parentRect.height / 2;
481
542
  this.messageListRef.scrollTo({
482
543
  top: targetScroll,
483
- behavior: 'smooth'
544
+ behavior: 'smooth',
484
545
  });
485
546
  }
486
547
  else {
@@ -525,10 +586,10 @@ export class OcsChat {
525
586
  const k = 1024;
526
587
  if (bytes < k * k) {
527
588
  // Less than 1MB, show in KB
528
- return Math.round(bytes / k * 100) / 100 + ' KB';
589
+ return Math.round((bytes / k) * 100) / 100 + ' KB';
529
590
  }
530
591
  else {
531
- return Math.round(bytes / (k * k) * 100) / 100 + ' MB';
592
+ return Math.round((bytes / (k * k)) * 100) / 100 + ' MB';
532
593
  }
533
594
  }
534
595
  formatTime(dateString) {
@@ -538,19 +599,38 @@ export class OcsChat {
538
599
  toggleWindowVisibility() {
539
600
  this.visible = !this.visible;
540
601
  }
602
+ /**
603
+ * Watch for changes to the `pageContext` prop and sync to internal variable.
604
+ *
605
+ * @param pageContext - The new value for the field.
606
+ */
607
+ pageContextHandler() {
608
+ this.loadInternalPageContext();
609
+ }
610
+ async chatbotConfigHandler() {
611
+ await this.clearSession();
612
+ }
541
613
  /**
542
614
  * Watch for changes to the `visible` attribute and update accordingly.
543
615
  *
544
616
  * @param visible - The new value for the field.
545
617
  */
546
618
  async visibilityHandler(visible) {
619
+ // Kiosk mode is always visible
620
+ if (this.isKioskMode() && !visible) {
621
+ this.visible = true;
622
+ return;
623
+ }
624
+ this.saveVisibleState(visible);
547
625
  if (this.isButtonDragging) {
548
626
  this.isButtonDragging = false;
549
627
  this.buttonWasDragged = false;
550
628
  this.removeButtonEventListeners();
551
629
  }
552
630
  if (visible) {
553
- this.initializePosition();
631
+ if (!this.isKioskMode()) {
632
+ this.initializePosition();
633
+ }
554
634
  // Resume polling for existing session (don't auto-start new sessions)
555
635
  if (this.sessionId) {
556
636
  this.scrollToBottom(true);
@@ -571,37 +651,43 @@ export class OcsChat {
571
651
  this.taskPollingHandle.cancel();
572
652
  }
573
653
  this.taskPollingHandle = this.getChatService().pollTask(this.sessionId, taskId, {
574
- onMessage: (message) => {
654
+ onMessage: message => {
575
655
  this.messages = [...this.messages, message];
576
656
  this.saveSessionToStorage();
577
657
  this.scrollToBottom();
578
658
  this.isTyping = false;
659
+ this.typingProgressMessage = '';
579
660
  this.currentPollTaskId = '';
580
661
  this.taskPollingHandle = undefined;
581
662
  this.startMessagePolling();
582
663
  this.focusInput();
583
664
  },
665
+ onProgress: message => {
666
+ this.typingProgressMessage = message;
667
+ },
584
668
  onTimeout: () => {
585
669
  const timeoutMessage = {
586
670
  created_at: new Date().toISOString(),
587
671
  role: 'system',
588
672
  content: 'The response is taking longer than expected. The system may be experiencing delays. Please try sending your message again.',
589
- attachments: []
673
+ attachments: [],
590
674
  };
591
675
  this.messages = [...this.messages, timeoutMessage];
592
676
  this.saveSessionToStorage();
593
677
  this.scrollToBottom();
594
678
  this.isTyping = false;
679
+ this.typingProgressMessage = '';
595
680
  this.currentPollTaskId = '';
596
681
  this.taskPollingHandle = undefined;
597
682
  this.startMessagePolling();
598
683
  this.focusInput();
599
684
  },
600
- onError: (error) => {
685
+ onError: error => {
686
+ this.typingProgressMessage = '';
601
687
  this.handleError(error.message);
602
688
  this.taskPollingHandle = undefined;
603
689
  this.startMessagePolling();
604
- }
690
+ },
605
691
  });
606
692
  }
607
693
  startMessagePolling() {
@@ -612,8 +698,8 @@ export class OcsChat {
612
698
  return;
613
699
  }
614
700
  this.messagePollingHandle = this.getChatService().startMessagePolling(this.sessionId, {
615
- getSince: () => { var _a; return this.messages.length > 0 ? (_a = this.messages.at(-1)) === null || _a === void 0 ? void 0 : _a.created_at : undefined; },
616
- onMessages: (messages) => {
701
+ getSince: () => { var _a; return (this.messages.length > 0 ? (_a = this.messages.at(-1)) === null || _a === void 0 ? void 0 : _a.created_at : undefined); },
702
+ onMessages: messages => {
617
703
  if (messages.length === 0)
618
704
  return;
619
705
  this.messages = [...this.messages, ...messages];
@@ -623,7 +709,7 @@ export class OcsChat {
623
709
  },
624
710
  onError: () => {
625
711
  // Silently ignore polling errors to match previous behaviour
626
- }
712
+ },
627
713
  });
628
714
  }
629
715
  stopMessagePolling() {
@@ -642,6 +728,9 @@ export class OcsChat {
642
728
  this.position = position;
643
729
  }
644
730
  getPositionClasses() {
731
+ if (this.isKioskMode()) {
732
+ return 'chat-window-kiosk';
733
+ }
645
734
  if (this.isFullscreen) {
646
735
  return 'chat-window-fullscreen';
647
736
  }
@@ -657,6 +746,9 @@ export class OcsChat {
657
746
  return { windowWidth, actualChatWidth, centeredX, maxOffset };
658
747
  }
659
748
  getPositionStyles() {
749
+ if (this.isKioskMode()) {
750
+ return {};
751
+ }
660
752
  if (this.isFullscreen) {
661
753
  const { centeredX } = this.getFullscreenBounds();
662
754
  const finalX = centeredX + this.fullscreenPosition.x;
@@ -688,19 +780,19 @@ export class OcsChat {
688
780
  case 'left':
689
781
  this.windowPosition = {
690
782
  x: OcsChat.WINDOW_MARGIN,
691
- y: windowHeight - this.chatWindowHeight - OcsChat.WINDOW_MARGIN
783
+ y: windowHeight - this.chatWindowHeight - OcsChat.WINDOW_MARGIN,
692
784
  };
693
785
  break;
694
786
  case 'right':
695
787
  this.windowPosition = {
696
788
  x: windowWidth - chatWidth - OcsChat.WINDOW_MARGIN,
697
- y: windowHeight - this.chatWindowHeight - OcsChat.WINDOW_MARGIN
789
+ y: windowHeight - this.chatWindowHeight - OcsChat.WINDOW_MARGIN,
698
790
  };
699
791
  break;
700
792
  case 'center':
701
793
  this.windowPosition = {
702
794
  x: (windowWidth - chatWidth) / 2,
703
- y: (windowHeight - this.chatWindowHeight) / 2
795
+ y: (windowHeight - this.chatWindowHeight) / 2,
704
796
  };
705
797
  break;
706
798
  }
@@ -723,14 +815,14 @@ export class OcsChat {
723
815
  // For fullscreen, track relative to current position
724
816
  this.dragOffset = {
725
817
  x: pointer.clientX,
726
- y: pointer.clientY
818
+ y: pointer.clientY,
727
819
  };
728
820
  }
729
821
  else {
730
822
  const rect = this.chatWindowRef.getBoundingClientRect();
731
823
  this.dragOffset = {
732
824
  x: pointer.clientX - rect.left,
733
- y: pointer.clientY - rect.top
825
+ y: pointer.clientY - rect.top,
734
826
  };
735
827
  }
736
828
  }
@@ -742,7 +834,7 @@ export class OcsChat {
742
834
  const { maxOffset } = this.getFullscreenBounds();
743
835
  const deltaX = pointer.clientX - this.dragOffset.x;
744
836
  this.fullscreenPosition = {
745
- x: Math.max(-maxOffset, Math.min(maxOffset, deltaX))
837
+ x: Math.max(-maxOffset, Math.min(maxOffset, deltaX)),
746
838
  };
747
839
  }
748
840
  else {
@@ -755,7 +847,7 @@ export class OcsChat {
755
847
  const chatHeight = this.chatWindowRef.offsetHeight;
756
848
  this.windowPosition = {
757
849
  x: Math.max(0, Math.min(newX, windowWidth - chatWidth)),
758
- y: Math.max(0, Math.min(newY, windowHeight - chatHeight))
850
+ y: Math.max(0, Math.min(newY, windowHeight - chatHeight)),
759
851
  };
760
852
  }
761
853
  }
@@ -802,7 +894,7 @@ export class OcsChat {
802
894
  const verticalValue = this.buttonVerticalSide === 'top' ? resolvedTop : resolvedBottom;
803
895
  this.buttonPosition = {
804
896
  x: horizontalValue,
805
- y: verticalValue
897
+ y: verticalValue,
806
898
  };
807
899
  // Apply the position to the host
808
900
  this.updateHostPosition();
@@ -845,12 +937,8 @@ export class OcsChat {
845
937
  const maxTop = windowHeight - buttonHeight - minPadding;
846
938
  const constrainedLeft = Math.max(minLeft, Math.min(candidateLeft, maxLeft));
847
939
  const constrainedTop = Math.max(minTop, Math.min(candidateTop, maxTop));
848
- const newHorizontalValue = this.buttonHorizontalSide === 'left'
849
- ? constrainedLeft
850
- : Math.max(minPadding, windowWidth - (constrainedLeft + buttonWidth));
851
- const newVerticalValue = this.buttonVerticalSide === 'top'
852
- ? constrainedTop
853
- : Math.max(minPadding, windowHeight - (constrainedTop + buttonHeight));
940
+ const newHorizontalValue = this.buttonHorizontalSide === 'left' ? constrainedLeft : Math.max(minPadding, windowWidth - (constrainedLeft + buttonWidth));
941
+ const newVerticalValue = this.buttonVerticalSide === 'top' ? constrainedTop : Math.max(minPadding, windowHeight - (constrainedTop + buttonHeight));
854
942
  if (newHorizontalValue !== this.buttonPosition.x || newVerticalValue !== this.buttonPosition.y) {
855
943
  this.buttonWasDragged = true;
856
944
  this.buttonPosition = { x: newHorizontalValue, y: newVerticalValue };
@@ -913,16 +1001,12 @@ export class OcsChat {
913
1001
  return fallback;
914
1002
  }
915
1003
  getWelcomeMessages() {
916
- const translated = this.translationManager.getArray("content.welcomeMessages");
917
- return translated && translated.length > 0
918
- ? translated
919
- : this.parsedWelcomeMessages;
1004
+ const translated = this.translationManager.getArray('content.welcomeMessages');
1005
+ return translated && translated.length > 0 ? translated : this.parsedWelcomeMessages;
920
1006
  }
921
1007
  getStarterQuestions() {
922
- const translated = this.translationManager.getArray("content.starterQuestions");
923
- return translated && translated.length > 0
924
- ? translated
925
- : this.parsedStarterQuestions;
1008
+ const translated = this.translationManager.getArray('content.starterQuestions');
1009
+ return translated && translated.length > 0 ? translated : this.parsedStarterQuestions;
926
1010
  }
927
1011
  getButtonClasses() {
928
1012
  const buttonText = this.translationManager.get('branding.buttonText', this.buttonText);
@@ -933,6 +1017,9 @@ export class OcsChat {
933
1017
  }
934
1018
  renderButton() {
935
1019
  var _a;
1020
+ if (!this.showButton || this.isKioskMode()) {
1021
+ return null;
1022
+ }
936
1023
  const buttonText = this.translationManager.get('branding.buttonText', this.buttonText);
937
1024
  const hasText = !!(buttonText && buttonText.trim());
938
1025
  const hasCustomIcon = this.iconUrl && this.iconUrl.trim();
@@ -942,21 +1029,24 @@ export class OcsChat {
942
1029
  const buttonAriaLabel = finalButtonText ? `${openLabel} - ${finalButtonText}` : openLabel;
943
1030
  // Only show drag cursor if button is draggable
944
1031
  const isDraggable = this.isButtonDraggable();
945
- const buttonStyle = isDraggable ? {
946
- cursor: this.isButtonDragging ? 'grabbing' : 'grab',
947
- } : {};
1032
+ const buttonStyle = isDraggable
1033
+ ? {
1034
+ cursor: this.isButtonDragging ? 'grabbing' : 'grab',
1035
+ }
1036
+ : {};
948
1037
  if (hasText) {
949
- return (h("button", { ref: (el) => this.buttonRef = el, class: buttonClasses, "aria-label": buttonAriaLabel, title: finalButtonText || openLabel, style: buttonStyle, onClick: () => this.handleButtonClick(), onMouseDown: (e) => this.handleButtonMouseDown(e), onTouchStart: (e) => this.handleButtonTouchStart(e), "aria-grabbed": this.isButtonDragging, "aria-describedby": isDraggable ? "chat-button-drag-hint" : undefined }, hasCustomIcon ? h("img", { src: this.iconUrl, alt: "" }) : h(OcsWidgetAvatar, null), h("span", null, finalButtonText), isDraggable && (h("span", { id: "chat-button-drag-hint", style: { display: 'none' } }, "Draggable. Use mouse or touch to reposition."))));
1038
+ return (h("button", { ref: el => (this.buttonRef = el), class: buttonClasses, "aria-label": buttonAriaLabel, title: finalButtonText || openLabel, style: buttonStyle, onClick: () => this.handleButtonClick(), onMouseDown: e => this.handleButtonMouseDown(e), onTouchStart: e => this.handleButtonTouchStart(e), "aria-grabbed": this.isButtonDragging, "aria-describedby": isDraggable ? 'chat-button-drag-hint' : undefined }, hasCustomIcon ? h("img", { src: this.iconUrl, alt: "" }) : h(OcsWidgetAvatar, null), h("span", null, finalButtonText), isDraggable && (h("span", { id: "chat-button-drag-hint", style: { display: 'none' } }, "Draggable. Use mouse or touch to reposition."))));
950
1039
  }
951
1040
  else {
952
- return (h("button", { ref: (el) => this.buttonRef = el, class: buttonClasses, "aria-label": openLabel, title: openLabel, style: buttonStyle, onClick: () => this.handleButtonClick(), onMouseDown: (e) => this.handleButtonMouseDown(e), onTouchStart: (e) => this.handleButtonTouchStart(e), "aria-grabbed": this.isButtonDragging, "aria-describedby": isDraggable ? "chat-button-drag-hint" : undefined }, hasCustomIcon ? h("img", { src: this.iconUrl, alt: "" }) : h(OcsWidgetAvatar, null), isDraggable && (h("span", { id: "chat-button-drag-hint", style: { display: 'none' } }, "Draggable. Use mouse or touch to reposition."))));
1041
+ return (h("button", { ref: el => (this.buttonRef = el), class: buttonClasses, "aria-label": openLabel, title: openLabel, style: buttonStyle, onClick: () => this.handleButtonClick(), onMouseDown: e => this.handleButtonMouseDown(e), onTouchStart: e => this.handleButtonTouchStart(e), "aria-grabbed": this.isButtonDragging, "aria-describedby": isDraggable ? 'chat-button-drag-hint' : undefined }, hasCustomIcon ? h("img", { src: this.iconUrl, alt: "" }) : h(OcsWidgetAvatar, null), isDraggable && (h("span", { id: "chat-button-drag-hint", style: { display: 'none' } }, "Draggable. Use mouse or touch to reposition."))));
953
1042
  }
954
1043
  }
955
1044
  getStorageKeys() {
956
1045
  return {
957
1046
  sessionId: `ocs-chat-session-${this.chatbotId}`,
958
1047
  messages: `ocs-chat-messages-${this.chatbotId}`,
959
- lastActivity: `ocs-chat-activity-${this.chatbotId}`
1048
+ lastActivity: `ocs-chat-activity-${this.chatbotId}`,
1049
+ visible: `ocs-chat-visible-${this.chatbotId}`,
960
1050
  };
961
1051
  }
962
1052
  saveSessionToStorage() {
@@ -1019,30 +1109,70 @@ export class OcsChat {
1019
1109
  return this.generatedUserId;
1020
1110
  }
1021
1111
  const storageKey = `ocs-user-id`;
1022
- const stored = localStorage.getItem(storageKey);
1112
+ let stored = null;
1113
+ try {
1114
+ stored = localStorage.getItem(storageKey);
1115
+ }
1116
+ catch (_a) {
1117
+ // localStorage blocked; fall through to in-memory id generation
1118
+ }
1023
1119
  if (stored) {
1024
1120
  this.generatedUserId = stored;
1025
1121
  return stored;
1026
1122
  }
1027
1123
  const array = new Uint8Array(9);
1028
1124
  window.crypto.getRandomValues(array);
1029
- const randomString = Array.from(array, byte => byte.toString(36)).join('').substr(0, 9);
1125
+ const randomString = Array.from(array, byte => byte.toString(36))
1126
+ .join('')
1127
+ .substr(0, 9);
1030
1128
  const newUserId = `ocs:${Date.now()}_${randomString}`;
1031
1129
  this.generatedUserId = newUserId;
1032
- localStorage.setItem(storageKey, newUserId);
1130
+ try {
1131
+ localStorage.setItem(storageKey, newUserId);
1132
+ }
1133
+ catch (_b) {
1134
+ // localStorage blocked; the generated id lives in component state for this page
1135
+ }
1033
1136
  return newUserId;
1034
1137
  }
1138
+ saveVisibleState(visible) {
1139
+ if (!this.persistentSession)
1140
+ return;
1141
+ try {
1142
+ const keys = this.getStorageKeys();
1143
+ localStorage.setItem(keys.visible, visible ? '1' : '0');
1144
+ }
1145
+ catch (_a) {
1146
+ // ignore
1147
+ }
1148
+ }
1149
+ restoreVisibleState() {
1150
+ try {
1151
+ const keys = this.getStorageKeys();
1152
+ const stored = localStorage.getItem(keys.visible);
1153
+ if (stored === '1') {
1154
+ this.visible = true;
1155
+ }
1156
+ }
1157
+ catch (_a) {
1158
+ // ignore
1159
+ }
1160
+ }
1035
1161
  clearSessionStorage() {
1036
1162
  const keys = this.getStorageKeys();
1037
1163
  try {
1038
1164
  localStorage.removeItem(keys.sessionId);
1039
1165
  localStorage.removeItem(keys.messages);
1040
1166
  localStorage.removeItem(keys.lastActivity);
1167
+ localStorage.removeItem(keys.visible);
1041
1168
  }
1042
1169
  catch (error) {
1043
1170
  console.warn('Failed to clear chat session from localStorage:', error);
1044
1171
  }
1045
1172
  }
1173
+ isKioskMode() {
1174
+ return this.mode === 'kiosk';
1175
+ }
1046
1176
  isLocalStorageAvailable() {
1047
1177
  try {
1048
1178
  localStorage.setItem(OcsChat.LOCALSTORAGE_TEST_KEY, 'test');
@@ -1068,6 +1198,7 @@ export class OcsChat {
1068
1198
  * will start when the user sends a message.
1069
1199
  */
1070
1200
  async clearSession() {
1201
+ this.sessionEpoch += 1;
1071
1202
  this.clearSessionStorage();
1072
1203
  this.sessionId = undefined;
1073
1204
  this.messages = [];
@@ -1088,18 +1219,12 @@ export class OcsChat {
1088
1219
  if (this.error && !this.sessionId) {
1089
1220
  return (h(Host, null, h("p", { class: "error-message" }, this.error)));
1090
1221
  }
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'
1092
- ? 'message-bubble-user'
1093
- : message.role === 'assistant'
1094
- ? 'message-bubble-assistant'
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) => {
1222
+ 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 && (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.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' ? '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 => {
1096
1223
  // Unclear why but after removing all attachments this is being set to `null`.
1097
1224
  if (el) {
1098
1225
  this.fileInputRef = el;
1099
1226
  }
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()
1101
- ? 'send-button-enabled'
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"))))))));
1227
+ }, 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"))))))));
1103
1228
  }
1104
1229
  static get is() { return "open-chat-studio-widget"; }
1105
1230
  static get encapsulation() { return "shadow"; }
@@ -1152,7 +1277,7 @@ export class OcsChat {
1152
1277
  "getter": false,
1153
1278
  "setter": false,
1154
1279
  "reflect": false,
1155
- "defaultValue": "\"https://www.openchatstudio.com\""
1280
+ "defaultValue": "'https://www.openchatstudio.com'"
1156
1281
  },
1157
1282
  "buttonText": {
1158
1283
  "type": "string",
@@ -1231,6 +1356,46 @@ export class OcsChat {
1231
1356
  "reflect": false,
1232
1357
  "defaultValue": "'square'"
1233
1358
  },
1359
+ "showButton": {
1360
+ "type": "boolean",
1361
+ "attribute": "show-button",
1362
+ "mutable": false,
1363
+ "complexType": {
1364
+ "original": "boolean",
1365
+ "resolved": "boolean",
1366
+ "references": {}
1367
+ },
1368
+ "required": false,
1369
+ "optional": false,
1370
+ "docs": {
1371
+ "tags": [],
1372
+ "text": "Whether to show the launcher button. Set to false to hide the button\nand open the chat window programmatically via the `visible` property."
1373
+ },
1374
+ "getter": false,
1375
+ "setter": false,
1376
+ "reflect": false,
1377
+ "defaultValue": "true"
1378
+ },
1379
+ "mode": {
1380
+ "type": "string",
1381
+ "attribute": "mode",
1382
+ "mutable": false,
1383
+ "complexType": {
1384
+ "original": "'standard' | 'kiosk'",
1385
+ "resolved": "\"kiosk\" | \"standard\"",
1386
+ "references": {}
1387
+ },
1388
+ "required": false,
1389
+ "optional": false,
1390
+ "docs": {
1391
+ "tags": [],
1392
+ "text": "The operating mode of the widget.\n- 'standard': Default floating window with launcher button.\n- 'kiosk': Fills parent container, always visible, no header or launcher button.\n The parent element must establish a containing block (e.g. `position: relative`)."
1393
+ },
1394
+ "getter": false,
1395
+ "setter": false,
1396
+ "reflect": false,
1397
+ "defaultValue": "'standard'"
1398
+ },
1234
1399
  "headerText": {
1235
1400
  "type": "string",
1236
1401
  "attribute": "header-text",
@@ -1521,6 +1686,51 @@ export class OcsChat {
1521
1686
  "getter": false,
1522
1687
  "setter": false,
1523
1688
  "reflect": false
1689
+ },
1690
+ "pageContext": {
1691
+ "type": "unknown",
1692
+ "attribute": "page-context",
1693
+ "mutable": true,
1694
+ "complexType": {
1695
+ "original": "Record<string, any>",
1696
+ "resolved": "{ [x: string]: any; }",
1697
+ "references": {
1698
+ "Record": {
1699
+ "location": "global",
1700
+ "id": "global::Record"
1701
+ }
1702
+ }
1703
+ },
1704
+ "required": false,
1705
+ "optional": true,
1706
+ "docs": {
1707
+ "tags": [],
1708
+ "text": "Optional context object to send with each message. This provides page-specific context to the bot."
1709
+ },
1710
+ "getter": false,
1711
+ "setter": false
1712
+ },
1713
+ "versionNumber": {
1714
+ "type": "number",
1715
+ "attribute": "version-number",
1716
+ "mutable": false,
1717
+ "complexType": {
1718
+ "original": "number",
1719
+ "resolved": "number",
1720
+ "references": {}
1721
+ },
1722
+ "required": false,
1723
+ "optional": true,
1724
+ "docs": {
1725
+ "tags": [{
1726
+ "name": "internal",
1727
+ "text": "Optional version number of the chatbot to use. Requires authentication.\nThis is for internal use only and is not intended for public-facing widgets."
1728
+ }],
1729
+ "text": ""
1730
+ },
1731
+ "getter": false,
1732
+ "setter": false,
1733
+ "reflect": false
1524
1734
  }
1525
1735
  };
1526
1736
  }
@@ -1531,6 +1741,7 @@ export class OcsChat {
1531
1741
  "sessionId": {},
1532
1742
  "isLoading": {},
1533
1743
  "isTyping": {},
1744
+ "typingProgressMessage": {},
1534
1745
  "messageInput": {},
1535
1746
  "currentPollTaskId": {},
1536
1747
  "isDragging": {},
@@ -1551,6 +1762,15 @@ export class OcsChat {
1551
1762
  static get elementRef() { return "host"; }
1552
1763
  static get watchers() {
1553
1764
  return [{
1765
+ "propName": "pageContext",
1766
+ "methodName": "pageContextHandler"
1767
+ }, {
1768
+ "propName": "chatbotId",
1769
+ "methodName": "chatbotConfigHandler"
1770
+ }, {
1771
+ "propName": "versionNumber",
1772
+ "methodName": "chatbotConfigHandler"
1773
+ }, {
1554
1774
  "propName": "visible",
1555
1775
  "methodName": "visibilityHandler"
1556
1776
  }];
@@ -1566,6 +1786,39 @@ OcsChat.WINDOW_MARGIN = 20;
1566
1786
  OcsChat.LOCALSTORAGE_TEST_KEY = '__ocs_test__';
1567
1787
  OcsChat.MAX_FILE_SIZE_MB = 50;
1568
1788
  OcsChat.MAX_TOTAL_SIZE_MB = 50;
1569
- OcsChat.SUPPORTED_FILE_EXTENSIONS = ['.txt', '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.csv', '.jpg', '.jpeg',
1570
- '.png', '.gif', '.bmp', '.webp', '.svg', '.mp4', '.mov', '.avi', '.mp3', '.wav'];
1789
+ OcsChat.SUPPORTED_FILE_EXTENSIONS = [
1790
+ '.txt',
1791
+ '.pdf',
1792
+ '.doc',
1793
+ '.docx',
1794
+ '.xls',
1795
+ '.xlsx',
1796
+ '.csv',
1797
+ '.jpg',
1798
+ '.jpeg',
1799
+ '.png',
1800
+ '.gif',
1801
+ '.bmp',
1802
+ '.webp',
1803
+ '.svg',
1804
+ '.mp4',
1805
+ '.mov',
1806
+ '.avi',
1807
+ '.mp3',
1808
+ '.wav',
1809
+ '.html',
1810
+ '.htm',
1811
+ '.css',
1812
+ '.js',
1813
+ '.xml',
1814
+ '.md',
1815
+ '.ics',
1816
+ '.vcf',
1817
+ '.rtf',
1818
+ '.tsv',
1819
+ '.yaml',
1820
+ '.yml',
1821
+ '.py',
1822
+ '.c',
1823
+ ];
1571
1824
  //# sourceMappingURL=ocs-chat.js.map