embeddedaichatux 1.3.2 → 1.5.1

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 (2) hide show
  1. package/EmbeddedChat.js +149 -49
  2. package/package.json +1 -1
package/EmbeddedChat.js CHANGED
@@ -1,5 +1,4 @@
1
- // embeddedChat.js
2
- class EmbeddedChat {
1
+ class EmbeddedChat {
3
2
  constructor(containerDiv, chatId, options) {
4
3
  if (!containerDiv) {
5
4
  containerDiv = this.createChatContainer();
@@ -11,88 +10,187 @@ class EmbeddedChat {
11
10
  this.locale = options.locale || 'en';
12
11
  this.previewParam = this.isPreview ? "?isPreview=true" : "";
13
12
  this.serverUrl = this.options.serverUrl || 'https://embedgpt.chat/';
14
- this.positionStyle = this.containerDiv.dataset.position === 'in-place' ? 'width:100%; height:600px' : 'position: fixed; right: 0; bottom: 0; width: 300px; height: 600px; z-index: 100000';
13
+ this.height = options.height || 600; // Default height
14
+ this.width = options.width || 320; // Default width
15
+ this.enableAnimation = options.enableAnimation !== false; // Default to true if not provided
16
+ this.minimizedHeight = `${this.height * 0.3}px`; // 30% of the full height
17
+ this.mouseInsideChat = false;
18
+ this.hasRefreshed = false; // Flag to prevent endless loop
19
+
20
+ // Determine transition styles
21
+ const transitionStyle = this.enableAnimation ? 'transition: height 0.3s ease, opacity 0.3s ease;' : '';
22
+
23
+ this.positionStyle = this.containerDiv.dataset.position === 'in-place' ?
24
+ `width:100%; height:${this.height}px; ${transitionStyle}` :
25
+ `position: fixed; right: 0; bottom: 0; width: ${this.width}px; height: ${this.height}px; z-index: 100000; ${transitionStyle}`;
15
26
  this.mode = options.mode || 'Chat'; // default to 'chat' if mode isn't provided
27
+ this.minimizeOnScroll = false; // Default to false
16
28
  if (this.mode === 'ContactForm') {
17
29
  // Adjust position style for contact form if mode is 'ContactForm'
18
- this.positionStyle = 'width:100%; height:600px';
19
- this.containerDiv.style.height = '600px';
30
+ this.positionStyle = `width:100%; height:${this.height}px; ${transitionStyle}`;
31
+ this.containerDiv.style.height = `${this.height}px`;
20
32
  }
33
+
34
+ this.conversationId = this.getStoredConversationId();
35
+
21
36
  this.sessionInfo = options.sessionInfo || null;
22
37
  this.init();
23
38
  }
24
39
 
40
+ isSafari() {
41
+ var ua = navigator.userAgent.toLowerCase();
42
+ return ua.indexOf('safari') != -1 && ua.indexOf('chrome') == -1;
43
+ }
44
+
45
+ getStoredConversationId() {
46
+ try {
47
+ return localStorage.getItem(`conversationId_${this.chatId}`);
48
+ } catch (error) {
49
+ console.error('Error accessing localStorage:', error);
50
+ return null;
51
+ }
52
+ }
53
+
54
+ setStoredConversationId(conversationId) {
55
+ try {
56
+ localStorage.setItem(`conversationId_${this.chatId}`, conversationId);
57
+ } catch (error) {
58
+ console.error('Error accessing localStorage:', error);
59
+ }
60
+ }
61
+
25
62
  init() {
63
+ this.updateIframes();
64
+ this.addEventListeners();
65
+ }
66
+
67
+ updateIframes() {
26
68
  const cleanedServerUrl = this.serverUrl.endsWith('/') ? this.serverUrl.slice(0, -1) : this.serverUrl;
27
69
  const baseIframeUrl = `${cleanedServerUrl}/ChatUX/${this.chatId}`;
28
- const minimizedPositionStyle = 'position: fixed; right: 0; bottom: 0; width: 300px; height: 70px; z-index: 100000';
70
+ const minimizedPositionStyle = `position: fixed; right: 0; bottom: 0; width: ${this.width}px; height: ${this.minimizedHeight}; z-index: 100000; ${this.enableAnimation ? 'transition: height 0.3s ease, opacity 0.3s ease;' : ''}`;
29
71
 
30
72
  const iframeHtml = `
31
- <iframe id="embedded-chat" style="${this.positionStyle}; display: none; border:none; overflow:hidden;" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="${this.buildIframeUrl(baseIframeUrl, { isPreview: this.isPreview, mode: this.mode, locale: this.locale })}"></iframe>
32
- <iframe id="embedded-chat-minimized" style="${minimizedPositionStyle}; display: none; border:none; overflow:hidden;" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="${this.buildIframeUrl(baseIframeUrl, { isPreview: this.isPreview, isMinimized: true, locale: this.locale })}"></iframe>
33
- `;
73
+ <iframe id="embedded-chat" style="${this.positionStyle}; display: none; border:none; overflow:hidden;" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-top-navigation-by-user-activation" src="${this.buildIframeUrl(baseIframeUrl, { isPreview: this.isPreview, mode: this.mode, locale: this.locale, conversationId: this.conversationId })}"></iframe>
74
+ <iframe id="embedded-chat-minimized" style="${minimizedPositionStyle}; display: none; border:none; overflow:hidden;" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="${this.buildIframeUrl(baseIframeUrl, { isPreview: this.isPreview, isMinimized: true, locale: this.locale, conversationId: this.conversationId })}"></iframe>
75
+ `;
34
76
 
35
77
  this.containerDiv.innerHTML = iframeHtml;
36
78
  this.iframe = this.containerDiv.querySelector("#embedded-chat");
37
79
  this.minimizedIframe = this.containerDiv.querySelector("#embedded-chat-minimized");
80
+ }
81
+
82
+ addEventListeners() {
83
+ // Add event listeners to track mouse position
84
+ this.iframe.addEventListener("mouseenter", () => { this.mouseInsideChat = true; });
85
+ this.iframe.addEventListener("mouseleave", () => { this.mouseInsideChat = false; });
86
+ this.minimizedIframe.addEventListener("mouseenter", () => { this.mouseInsideChat = true; });
87
+ this.minimizedIframe.addEventListener("mouseleave", () => { this.mouseInsideChat = false; });
38
88
 
39
89
  window.addEventListener("message", (e) => {
40
- // Check if the data is an object and has a message property
41
- if (typeof e.data === "object" && e.data.message && (!e.data.chatId || e.data.chatId === this.chatId)) {
90
+ this.handleMessage(e);
91
+ });
92
+
93
+ window.addEventListener("scroll", () => {
94
+ if (this.minimizeOnScroll && !this.mouseInsideChat) {
95
+ this.minimizeOnScrollAction();
96
+ }
97
+ });
98
+ }
99
+
100
+ handleMessage(e) {
101
+ if (typeof e.data === "object" && (!e.data.chatId || e.data.chatId === this.chatId)) {
102
+ if (e.data.type === "setMinimizeOnScroll") {
103
+ this.minimizeOnScroll = e.data.value === "true";
104
+ }
105
+ if (e.data.message) {
42
106
  if (e.data.message === "minimize") {
43
107
  if (this.mode !== 'ContactForm') {
44
- // No minimize support for ContactForm
45
- this.iframe.style.display = "none";
46
- this.minimizedIframe.style.display = "block";
108
+ this.animateMinimize();
47
109
  }
48
110
  } else if (e.data.message === "show" || e.data.message === "maximize") {
49
- this.iframe.style.display = "block";
50
- this.minimizedIframe.style.display = "none";
51
- // Check if the data has a scale property
52
- if (e.data.scale) {
53
- if (this.mode !== 'ContactForm') {
54
- // No scaling support for ContactForm
55
-
56
- // Resize the iframe according to the scale factor
57
- this.iframe.style.transform = `scale(${e.data.scale})`;
58
- this.iframe.style.transformOrigin = "bottom right";
59
- }
60
- }
61
- // Send sessionInfo if available
62
- if (this.sessionInfo !== null) {
63
- this.setSessionInfo(this.sessionInfo);
64
- }
111
+ this.showMaximized();
65
112
  }
66
- }
67
- // For backward compatibility, handle the case when the data is a string
68
- else if (typeof e.data === "string") {
69
- if (e.data === "minimize") {
70
- this.iframe.style.display = "none";
71
- this.minimizedIframe.style.display = "block";
72
- } else if (e.data === "show" || e.data === "maximize") {
73
- this.iframe.style.display = "block";
74
- this.minimizedIframe.style.display = "none";
113
+ if (e.data.scale) {
114
+ this.applyScale(e.data.scale);
115
+ }
116
+ if (this.sessionInfo !== null) {
117
+ this.setSessionInfo(this.sessionInfo);
118
+ }
119
+ if (e.data.message === "show" && e.data.conversationId) {
120
+ this.conversationId = e.data.conversationId;
121
+ this.setStoredConversationId(this.conversationId);
75
122
  }
76
123
  }
77
- });
124
+ } else if (typeof e.data === "string") {
125
+ if (e.data === "minimize") {
126
+ this.animateMinimize();
127
+ } else if (e.data === "show" || e.data === "maximize") {
128
+ this.showMaximized();
129
+ }
130
+ }
131
+ }
132
+
133
+ animateMinimize() {
134
+ if (this.mode !== 'ContactForm') {
135
+ this.iframe.style.height = this.minimizedHeight;
136
+ this.iframe.style.opacity = '0'; // Full transparency at the end
137
+ setTimeout(() => {
138
+ this.iframe.style.display = "none";
139
+ this.iframe.style.opacity = '1'; // Reset opacity to 1 for next time
140
+ this.minimizedIframe.style.display = "block";
141
+ this.minimizedIframe.style.height = this.minimizedHeight;
142
+ this.minimizedIframe.style.opacity = '1'; // Reset opacity to 1
143
+ }, this.enableAnimation ? 300 : 0); // Match the transition duration
144
+ }
145
+ }
146
+
147
+ showMaximized() {
148
+ this.minimizedIframe.style.display = "none";
149
+
150
+ this.minimizedIframe.style.height = `${this.height}px`;
151
+ this.minimizedIframe.style.opacity = ''; // Reset opacity to unset
152
+
153
+ this.iframe.style.display = "block";
154
+
155
+ if (this.isSafari() && !this.hasRefreshed) {
156
+ // Force refresh by resetting the iframe's src
157
+ const currentSrc = this.iframe.src;
158
+ this.iframe.src = ''; // Temporarily clear the src
159
+ this.iframe.src = currentSrc; // Reset to the original src to trigger a reload
160
+ this.hasRefreshed = true; // Prevent endless loop
161
+ }
162
+
163
+ this.iframe.style.height = `${this.height}px`;
164
+ this.iframe.style.opacity = '1';
165
+ }
166
+
167
+ applyScale(scale) {
168
+ if (this.mode !== 'ContactForm') {
169
+ this.iframe.style.transform = `scale(${scale})`;
170
+ this.iframe.style.transformOrigin = "bottom right";
171
+ this.minimizedIframe.style.transform = `scale(${scale})`;
172
+ this.minimizedIframe.style.transformOrigin = "bottom right";
173
+ }
174
+ }
175
+
176
+ minimizeOnScrollAction() {
177
+ if (this.mode !== 'ContactForm' && this.iframe.style.display !== "none") {
178
+ this.animateMinimize();
179
+ }
78
180
  }
79
181
 
80
182
  setSessionInfo(sessionInfo) {
81
183
  console.log('setSessionInfo called with sessionInfo:', sessionInfo);
82
184
 
83
- // Store the new sessionInfo
84
185
  this.sessionInfo = sessionInfo;
85
186
 
86
- // Obtain the iframe element
87
187
  const iframe = this.containerDiv.querySelector("#embedded-chat");
88
188
 
89
- // Function to post the message with a structured object
90
189
  const postMessage = () => {
91
190
  console.log('Posting message to iframe:', { type: 'setSessionInfo', sessionInfo: sessionInfo });
92
191
  iframe.contentWindow.postMessage({ type: 'setSessionInfo', sessionInfo: sessionInfo }, "*");
93
192
  };
94
193
 
95
- // If the iframe is visible, post the message
96
194
  if (iframe.style.display !== "none") {
97
195
  postMessage();
98
196
  }
@@ -107,9 +205,8 @@ class EmbeddedChat {
107
205
  }
108
206
 
109
207
  buildIframeUrl(baseIframeUrl, params = {}) {
110
- const urlParams = new URLSearchParams(params);
208
+ const urlParams = new URLSearchParams();
111
209
 
112
- // Add parameters as needed, based on values in the params object
113
210
  if (params.isPreview) {
114
211
  urlParams.set('isPreview', 'true');
115
212
  }
@@ -122,9 +219,12 @@ class EmbeddedChat {
122
219
  urlParams.set('locale', params.locale);
123
220
  }
124
221
 
125
- // Handle other parameters directly from the params object
222
+ if (params.conversationId) {
223
+ urlParams.set('conversationId', params.conversationId);
224
+ }
225
+
126
226
  for (const [key, value] of Object.entries(params)) {
127
- if (key !== 'mode' && key !== 'isPreview') { // Avoid duplicates
227
+ if (key !== 'mode' && key !== 'isPreview' && key !== 'conversationId' && value !== null && value !== undefined) {
128
228
  urlParams.set(key, value);
129
229
  }
130
230
  }
@@ -138,6 +238,6 @@ export function initEmbeddedChat(containerDiv, chatId, options) {
138
238
  }
139
239
 
140
240
  export function initContactForm(containerDiv, chatId, options) {
141
- const clonedOptions = { ...options, mode: 'ContactForm' }; // Clone with spread syntax
241
+ const clonedOptions = { ...options, mode: 'ContactForm' };
142
242
  return new EmbeddedChat(containerDiv, chatId, clonedOptions);
143
243
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "embeddedaichatux",
3
- "version": "1.3.2",
3
+ "version": "1.5.1",
4
4
  "description": "A lightweight and customizable embedded AI chat UI component that seamlessly integrates into web applications, offering minimized and expanded views, with iframe-based content rendering.",
5
5
  "main": "EmbeddedChat.js",
6
6
  "scripts": {