embeddedaichatux 1.6.0 → 1.7.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.
package/EmbeddedChat.js CHANGED
@@ -23,8 +23,8 @@
23
23
 
24
24
  this.positionStyle = this.containerDiv.dataset.position === 'in-place' ?
25
25
  `width:100%; height:${this.height}px; ${transitionStyle}` :
26
- `position: fixed; right: 0; bottom: 0; width: ${this.width}px; height: ${this.height}px; z-index: 100000; max-height:80%; ${transitionStyle}`;
27
- this.mode = options.mode || 'Chat'; // default to 'chat' if mode isn't provided
26
+ `position: fixed; right: 1em; bottom: 0; width: ${this.width}px; height: ${this.height}px; z-index: 100000; max-width:80%; ${transitionStyle}`;
27
+ this.mode = options.mode || 'Chat'; // default to 'chat'
28
28
  this.minimizeOnScroll = false; // Default to false
29
29
  if (this.mode === 'ContactForm') {
30
30
  // Adjust position style for contact form if mode is 'ContactForm'
@@ -33,7 +33,6 @@
33
33
  }
34
34
 
35
35
  this.conversationId = this.getStoredConversationId();
36
-
37
36
  this.sessionInfo = options.sessionInfo || null;
38
37
  this.init();
39
38
  }
@@ -65,7 +64,6 @@
65
64
  const previousId = this.conversationId || 'null';
66
65
 
67
66
  if (newConversationId !== previousId) {
68
- // Separate block for changes to allow breakpoint placement
69
67
  this.conversationId = newConversationId;
70
68
  this.setStoredConversationId(newConversationId);
71
69
  }
@@ -76,7 +74,7 @@
76
74
  this.addEventListeners();
77
75
  }
78
76
 
79
- updateIframes() {
77
+ async updateIframes() {
80
78
  const cleanedServerUrl = this.serverUrl.endsWith('/') ? this.serverUrl.slice(0, -1) : this.serverUrl;
81
79
  const baseIframeUrl = `${cleanedServerUrl}/ChatUX/${this.chatId}`;
82
80
  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;' : ''}`;
@@ -89,10 +87,11 @@
89
87
  this.containerDiv.innerHTML = iframeHtml;
90
88
  this.iframe = this.containerDiv.querySelector("#embedded-chat");
91
89
  this.minimizedIframe = this.containerDiv.querySelector("#embedded-chat-minimized");
90
+
91
+ await this.waitForIframeLoad(this.iframe);
92
92
  }
93
93
 
94
94
  addEventListeners() {
95
- // Add event listeners to track mouse position
96
95
  this.iframe.addEventListener("mouseenter", () => { this.mouseInsideChat = true; });
97
96
  this.iframe.addEventListener("mouseleave", () => { this.mouseInsideChat = false; });
98
97
  this.minimizedIframe.addEventListener("mouseenter", () => { this.mouseInsideChat = true; });
@@ -111,30 +110,47 @@
111
110
 
112
111
  handleMessage(e) {
113
112
  if (typeof e.data === "object" && (!e.data.chatId || e.data.chatId === this.chatId)) {
114
- if (e.data.type === "setMinimizeOnScroll") {
115
- this.minimizeOnScroll = e.data.value === "true";
113
+ const updates = {};
114
+
115
+ if (e.data.type === "setMinimizeOnScroll" || e.data.minimizeOnScroll !== undefined) {
116
+ const minimizeValue = e.data.type === "setMinimizeOnScroll" ? e.data.value : e.data.minimizeOnScroll;
117
+ this.minimizeOnScroll = minimizeValue === true || minimizeValue === "true";
118
+ }
119
+
120
+ // Collect updates for locale, width, and scale
121
+ if (e.data.locale) {
122
+ updates.locale = e.data.locale;
116
123
  }
124
+ if (e.data.width) {
125
+ updates.width = e.data.width;
126
+ }
127
+ if (e.data.scale) {
128
+ updates.scale = e.data.scale;
129
+ }
130
+
131
+ // Apply iframe updates (locale, width, scale) in one go
132
+ if (Object.keys(updates).length > 0) {
133
+ this.applyIframeUpdates(updates);
134
+ }
135
+
136
+ // Handle maximize/minimize
117
137
  if (e.data.message) {
118
138
  if (e.data.message === "minimize") {
119
139
  if (this.mode !== 'ContactForm') {
120
140
  this.animateMinimize();
121
141
  }
122
142
  } else if (e.data.message === "show" || e.data.message === "maximize") {
123
- this.showMaximized();
124
- }
125
- if (e.data.scale) {
126
- this.applyScale(e.data.scale);
127
- }
128
- if (this.sessionInfo !== null) {
129
- this.setSessionInfo(this.sessionInfo);
143
+ const animate = e.data.animate === true;
144
+ this.showMaximized(animate);
130
145
  }
131
146
  }
132
147
 
133
- // Check and update conversationId after processing other conditions
148
+ // Handle conversationId update
134
149
  if (e.data.conversationId) {
135
150
  this.setConversationId(e.data.conversationId);
136
151
  }
137
152
  } else if (typeof e.data === "string") {
153
+ // Preserve original handling for string messages
138
154
  if (e.data === "minimize") {
139
155
  this.animateMinimize();
140
156
  } else if (e.data === "show" || e.data === "maximize") {
@@ -146,37 +162,107 @@
146
162
  animateMinimize() {
147
163
  if (this.mode !== 'ContactForm') {
148
164
  this.iframe.style.height = this.minimizedHeight;
149
- this.iframe.style.opacity = '0'; // Full transparency at the end
165
+ this.iframe.style.opacity = '0';
150
166
  setTimeout(() => {
151
167
  this.iframe.style.display = "none";
152
- this.iframe.style.opacity = '1'; // Reset opacity to 1 for next time
168
+ this.iframe.style.opacity = '1';
153
169
  this.minimizedIframe.style.display = "block";
154
170
  this.minimizedIframe.style.height = this.minimizedHeight;
155
- this.minimizedIframe.style.opacity = '1'; // Reset opacity to 1
156
- }, this.enableAnimation ? 300 : 0); // Match the transition duration
171
+ this.minimizedIframe.style.opacity = '1';
172
+ }, this.enableAnimation ? 300 : 0);
157
173
  }
158
174
  }
159
175
 
160
- showMaximized() {
161
- this.minimizedIframe.style.display = "none";
176
+ animateMaximize() {
177
+ if (this.mode !== 'ContactForm') {
178
+ // Start the animation for minimizing iframe
179
+ this.minimizedIframe.style.height = `${this.height}px`;
180
+ this.minimizedIframe.style.opacity = '0';
181
+
182
+ setTimeout(() => {
183
+ // Hide the minimized iframe
184
+ this.minimizedIframe.style.display = "none";
185
+ this.minimizedIframe.style.opacity = '1'; // Reset opacity
186
+
187
+ // Show and animate the main iframe
188
+ this.iframe.style.display = "block";
189
+ this.iframe.style.height = this.minimizedHeight; // Start with minimized height
190
+ this.iframe.style.opacity = '0'; // Start with zero opacity
191
+
192
+ // Trigger the maximize animation
193
+ setTimeout(() => {
194
+ this.iframe.style.height = `${this.height}px`; // Animate to full height
195
+ this.iframe.style.opacity = '1'; // Animate opacity to full
196
+ }, 10); // Small delay to allow display changes to take effect
197
+ }, this.enableAnimation ? 300 : 0); // Match the minimize animation duration
198
+ }
199
+ }
200
+
201
+ applyIframeUpdates({ locale, width, scale } = {}) {
202
+ // Update locale if provided and different (but do not update the iframe URL)
203
+ if (locale && typeof locale === 'string' && locale !== this.locale) {
204
+ this.locale = locale;
205
+ console.log(`Locale stored locally: ${this.locale}`);
206
+ }
207
+
208
+ // Update width if provided and different
209
+ const parsedWidth = parseInt(width, 10);
210
+ if (!isNaN(parsedWidth) && parsedWidth > 0 && parsedWidth !== this.width) {
211
+ this.width = Math.max(parsedWidth, 320);
212
+ console.log(`Width updated to: ${this.width}px`);
213
+
214
+ // Apply width to container
215
+ this.containerDiv.style.width = `${this.width}px`;
162
216
 
163
- this.minimizedIframe.style.height = `${this.height}px`;
164
- this.minimizedIframe.style.opacity = ''; // Reset opacity to unset
217
+ // Apply width to the main iframe
218
+ if (this.iframe) {
219
+ this.iframe.style.width = `${this.width}px`;
220
+ }
165
221
 
166
- this.iframe.style.display = "block";
222
+ // Apply width to the minimized iframe
223
+ if (this.minimizedIframe) {
224
+ this.minimizedIframe.style.width = `${this.width}px`;
225
+ }
226
+ }
167
227
 
168
- if (this.isSafari() && !this.hasRefreshed) {
169
- // Force refresh by resetting the iframe's src
170
- const currentSrc = this.iframe.src;
171
- this.iframe.src = ''; // Temporarily clear the src
172
- this.iframe.src = currentSrc; // Reset to the original src to trigger a reload
173
- this.hasRefreshed = true; // Prevent endless loop
228
+ // Apply scale if provided
229
+ if (typeof scale === 'number' && scale > 0) {
230
+ if (this.iframe) {
231
+ this.iframe.style.transform = `scale(${scale})`;
232
+ this.iframe.style.transformOrigin = "bottom right";
233
+ }
234
+ if (this.minimizedIframe) {
235
+ this.minimizedIframe.style.transform = `scale(${scale})`;
236
+ this.minimizedIframe.style.transformOrigin = "bottom right";
237
+ }
238
+ console.log(`Scale applied: ${scale}`);
174
239
  }
240
+ }
175
241
 
176
- this.iframe.style.height = `${this.height}px`;
177
- this.iframe.style.opacity = '1';
178
242
 
179
- this.iframe.contentWindow.postMessage({ message: "show" }, "*");
243
+ showMaximized(animate = false) {
244
+ if (animate) {
245
+ this.animateMaximize();
246
+ }
247
+ else {
248
+ this.minimizedIframe.style.display = "none";
249
+ this.minimizedIframe.style.height = `${this.height}px`;
250
+ this.minimizedIframe.style.opacity = ''; // Reset opacity to unset
251
+
252
+ this.iframe.style.display = "block";
253
+
254
+ if (this.isSafari() && !this.hasRefreshed) {
255
+ const currentSrc = this.iframe.src;
256
+ this.iframe.src = ''; // Temporarily clear the src
257
+ this.iframe.src = currentSrc; // Reset to the original src to trigger a reload
258
+ this.hasRefreshed = true; // Prevent endless loop
259
+ }
260
+
261
+ this.iframe.style.height = `${this.height}px`;
262
+ this.iframe.style.opacity = '1';
263
+
264
+ this.iframe.contentWindow.postMessage({ message: "show" }, "*");
265
+ }
180
266
  }
181
267
 
182
268
  applyScale(scale) {
@@ -245,6 +331,27 @@
245
331
 
246
332
  return `${baseIframeUrl}?${urlParams.toString()}`;
247
333
  }
334
+
335
+ waitForIframeLoad(iframe, timeout = 5000) {
336
+ return new Promise((resolve, reject) => {
337
+ // Check if the iframe is already loaded
338
+ if (iframe.contentDocument || iframe.contentWindow) {
339
+ resolve();
340
+ return;
341
+ }
342
+
343
+ // Set up a timeout to avoid waiting indefinitely
344
+ const timeoutId = setTimeout(() => {
345
+ reject(new Error('Iframe did not load within the timeout period.'));
346
+ }, timeout);
347
+
348
+ // Add the load event listener
349
+ iframe.addEventListener('load', () => {
350
+ clearTimeout(timeoutId); // Clear the timeout
351
+ resolve();
352
+ }, { once: true }); // Ensure the listener is triggered only once
353
+ });
354
+ }
248
355
  }
249
356
 
250
357
  export function initEmbeddedChat(containerDiv, chatId, options) {
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "embeddedaichatux",
3
- "version": "1.6.0",
3
+ "version": "1.7.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": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"
8
8
  },
9
9
  "author": "EmbedGPT.chat, LLC",
10
- "license": "MIT"
10
+ "license": "MIT",
11
+ "files": [
12
+ "EmbeddedChat.js"
13
+ ]
11
14
  }
package/AIChat.npm.csproj DELETED
@@ -1,29 +0,0 @@
1
- <Project Sdk="Microsoft.NET.Sdk.Web">
2
-
3
- <PropertyGroup>
4
- <TargetFramework>net7.0</TargetFramework>
5
- <Nullable>enable</Nullable>
6
- <ImplicitUsings>enable</ImplicitUsings>
7
- </PropertyGroup>
8
-
9
- <Target Name="CopyCommonWebFiles" BeforeTargets="Build">
10
- <ItemGroup>
11
- <_CommonWebJsFiles Include="..\AIChat.Web\wwwroot\js\EmbeddedChat.js" />
12
- </ItemGroup>
13
-
14
- <Copy SourceFiles="@(_CommonWebJsFiles)" DestinationFolder="$(MSBuildProjectDirectory)" SkipUnchangedFiles="true" />
15
- </Target>
16
-
17
- <ItemGroup>
18
- <None Remove="EmbeddedChat.js" />
19
- </ItemGroup>
20
-
21
- <ItemGroup>
22
- <Content Include="EmbeddedChat.js">
23
- <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
24
- <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
25
- </Content>
26
- </ItemGroup>
27
-
28
-
29
- </Project>
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
- <PropertyGroup>
4
- <ActiveDebugProfile>https</ActiveDebugProfile>
5
- </PropertyGroup>
6
- </Project>
package/AIChat.npm.sln DELETED
@@ -1,25 +0,0 @@
1
- 
2
- Microsoft Visual Studio Solution File, Format Version 12.00
3
- # Visual Studio Version 17
4
- VisualStudioVersion = 17.7.34024.191
5
- MinimumVisualStudioVersion = 10.0.40219.1
6
- Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AIChat.npm", "AIChat.npm.csproj", "{37DB7D9F-39C4-428D-A205-7E78FAB8BB25}"
7
- EndProject
8
- Global
9
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
10
- Debug|Any CPU = Debug|Any CPU
11
- Release|Any CPU = Release|Any CPU
12
- EndGlobalSection
13
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
14
- {37DB7D9F-39C4-428D-A205-7E78FAB8BB25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15
- {37DB7D9F-39C4-428D-A205-7E78FAB8BB25}.Debug|Any CPU.Build.0 = Debug|Any CPU
16
- {37DB7D9F-39C4-428D-A205-7E78FAB8BB25}.Release|Any CPU.ActiveCfg = Release|Any CPU
17
- {37DB7D9F-39C4-428D-A205-7E78FAB8BB25}.Release|Any CPU.Build.0 = Release|Any CPU
18
- EndGlobalSection
19
- GlobalSection(SolutionProperties) = preSolution
20
- HideSolutionNode = FALSE
21
- EndGlobalSection
22
- GlobalSection(ExtensibilityGlobals) = postSolution
23
- SolutionGuid = {33D9F6CE-532F-4C22-AAAD-2D8B3BB56596}
24
- EndGlobalSection
25
- EndGlobal
@@ -1,37 +0,0 @@
1
- {
2
- "iisSettings": {
3
- "windowsAuthentication": false,
4
- "anonymousAuthentication": true,
5
- "iisExpress": {
6
- "applicationUrl": "http://localhost:27434",
7
- "sslPort": 44389
8
- }
9
- },
10
- "profiles": {
11
- "http": {
12
- "commandName": "Project",
13
- "dotnetRunMessages": true,
14
- "launchBrowser": true,
15
- "applicationUrl": "http://localhost:5103",
16
- "environmentVariables": {
17
- "ASPNETCORE_ENVIRONMENT": "Development"
18
- }
19
- },
20
- "https": {
21
- "commandName": "Project",
22
- "dotnetRunMessages": true,
23
- "launchBrowser": true,
24
- "applicationUrl": "https://localhost:7299;http://localhost:5103",
25
- "environmentVariables": {
26
- "ASPNETCORE_ENVIRONMENT": "Development"
27
- }
28
- },
29
- "IIS Express": {
30
- "commandName": "IISExpress",
31
- "launchBrowser": true,
32
- "environmentVariables": {
33
- "ASPNETCORE_ENVIRONMENT": "Development"
34
- }
35
- }
36
- }
37
- }