customer-chat-sdk 1.0.35 → 1.0.36

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.
@@ -227,13 +227,18 @@ export declare class ScreenshotManager {
227
227
  */
228
228
  private waitForImagesToLoad;
229
229
  /**
230
- * 等待 CSS 和字体加载完成(优化:减少等待时间,使用 requestAnimationFrame)
230
+ * 等待 CSS 和字体加载完成
231
+ * html2canvas 需要确保所有样式表都加载完成才能正确截图
231
232
  */
232
233
  private waitForStylesAndFonts;
233
234
  /**
234
235
  * 等待字体加载完成
235
236
  */
236
237
  private waitForFonts;
238
+ /**
239
+ * 等待所有样式表加载完成(html2canvas 专用)
240
+ */
241
+ private waitForAllStylesLoaded;
237
242
  /**
238
243
  * 计算压缩后的尺寸
239
244
  */
@@ -276,6 +281,7 @@ export declare class ScreenshotManager {
276
281
  private getCachedImage;
277
282
  /**
278
283
  * 设置缓存的图片(带时间戳)
284
+ * 添加内存大小限制,防止缓存无限增长
279
285
  */
280
286
  private setCachedImage;
281
287
  /**
@@ -284,6 +290,7 @@ export declare class ScreenshotManager {
284
290
  private cleanExpiredCache;
285
291
  /**
286
292
  * 定期清理过期缓存(可选,在截图时也会自动清理)
293
+ * 增加清理频率,防止内存积累
287
294
  */
288
295
  private startCacheCleanup;
289
296
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"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"}
@@ -6900,28 +6900,62 @@ class ScreenshotManager {
6900
6900
  }
6901
6901
  }
6902
6902
  let dataUrl;
6903
- // 等待一小段时间,确保 DOM 更新完成(减少等待时间)
6904
- await new Promise(resolve => setTimeout(resolve, 50));
6905
6903
  // 根据选择的引擎进行截图
6906
- if (selectedEngine === 'snapdom') {
6907
- dataUrl = await this.takeScreenshotWithSnapdom(this.targetElement);
6908
- }
6909
- else if (selectedEngine === 'html2canvas') {
6904
+ if (selectedEngine === 'html2canvas') {
6905
+ // html2canvas 需要更长的等待时间确保样式加载
6906
+ // 额外等待样式和字体加载完成
6907
+ await Promise.all([
6908
+ this.waitForStylesAndFonts(),
6909
+ this.waitForFonts()
6910
+ ]);
6911
+ // 再等待一段时间,确保样式完全应用
6912
+ await new Promise(resolve => setTimeout(resolve, 200));
6910
6913
  dataUrl = await this.takeScreenshotWithHtml2Canvas(this.targetElement);
6911
6914
  }
6915
+ else if (selectedEngine === 'snapdom') {
6916
+ // 等待一小段时间,确保 DOM 更新完成
6917
+ await new Promise(resolve => setTimeout(resolve, 50));
6918
+ dataUrl = await this.takeScreenshotWithSnapdom(this.targetElement);
6919
+ }
6912
6920
  else {
6913
6921
  // 默认使用 modern-screenshot
6922
+ // 等待一小段时间,确保 DOM 更新完成
6923
+ await new Promise(resolve => setTimeout(resolve, 50));
6914
6924
  dataUrl = await this.takeScreenshotWithModernScreenshot(this.targetElement);
6915
6925
  }
6916
6926
  const timestamp = Date.now();
6917
6927
  // 更新状态
6918
6928
  this.screenshotCount++;
6919
6929
  this.lastScreenshotTime = timestamp;
6920
- // 管理历史记录
6930
+ // 管理历史记录(限制内存占用)
6931
+ // base64 字符串很大,需要严格控制历史记录数量
6921
6932
  if (this.screenshotHistory.length >= this.options.maxHistory) {
6922
- this.screenshotHistory.shift();
6933
+ // 删除最旧的截图,释放内存
6934
+ const removed = this.screenshotHistory.shift();
6935
+ // 强制 GC(如果可能)
6936
+ if (removed && removed.length > 1000000) { // 大于1MB的字符串
6937
+ if (!this.options.silentMode) {
6938
+ console.log(`📸 清理旧截图,释放内存: ${Math.round(removed.length * 0.75 / 1024)} KB`);
6939
+ }
6940
+ }
6923
6941
  }
6924
6942
  this.screenshotHistory.push(dataUrl);
6943
+ // 如果历史记录总大小超过限制,清理最旧的
6944
+ let totalSize = this.screenshotHistory.reduce((sum, item) => sum + item.length, 0);
6945
+ const maxTotalSize = 50 * 1024 * 1024; // 最大50MB
6946
+ while (this.screenshotHistory.length > 0 && totalSize > maxTotalSize) {
6947
+ const removed = this.screenshotHistory.shift();
6948
+ if (removed) {
6949
+ const removedSize = removed.length;
6950
+ totalSize -= removedSize;
6951
+ if (!this.options.silentMode) {
6952
+ console.warn(`📸 ⚠️ 历史记录总大小超过限制,清理最旧截图: ${Math.round(removedSize * 0.75 / 1024)} KB`);
6953
+ }
6954
+ }
6955
+ else {
6956
+ break;
6957
+ }
6958
+ }
6925
6959
  // 打印基本信息
6926
6960
  const base64Data = dataUrl.split(',')[1];
6927
6961
  if (!this.options.silentMode) {
@@ -7093,6 +7127,19 @@ class ScreenshotManager {
7093
7127
  console.log('📸 使用 html2canvas 引擎截图...');
7094
7128
  }
7095
7129
  try {
7130
+ // html2canvas 需要确保样式完全加载,额外等待
7131
+ // 等待所有样式表加载完成
7132
+ await this.waitForAllStylesLoaded();
7133
+ // 等待字体加载完成
7134
+ await this.waitForFonts();
7135
+ // 等待 DOM 完全渲染
7136
+ await new Promise(resolve => {
7137
+ requestAnimationFrame(() => {
7138
+ requestAnimationFrame(() => {
7139
+ setTimeout(() => resolve(), 100);
7140
+ });
7141
+ });
7142
+ });
7096
7143
  // 检查元素是否存在和可见
7097
7144
  const rect = element.getBoundingClientRect();
7098
7145
  if (rect.width === 0 || rect.height === 0) {
@@ -7124,6 +7171,26 @@ class ScreenshotManager {
7124
7171
  logging: !this.options.silentMode,
7125
7172
  width: finalWidth,
7126
7173
  height: finalHeight,
7174
+ // 关键配置:确保样式正确渲染
7175
+ foreignObjectRendering: false, // 禁用 foreignObject,使用传统渲染方式(更稳定)
7176
+ onclone: (_clonedDoc, _element) => {
7177
+ // 在克隆的文档中,确保所有样式都正确应用
7178
+ // html2canvas 会自动处理样式,这里不需要手动复制
7179
+ // 如果需要,可以在这里添加额外的样式处理逻辑
7180
+ // 复制所有样式表到克隆的文档
7181
+ const originalStyleSheets = Array.from(document.styleSheets);
7182
+ originalStyleSheets.forEach((originalSheet) => {
7183
+ try {
7184
+ // 检查样式表是否可访问
7185
+ if (originalSheet.cssRules) {
7186
+ // 样式表可访问,html2canvas 会自动处理
7187
+ }
7188
+ }
7189
+ catch (e) {
7190
+ // 跨域样式表无法访问,html2canvas 会尝试其他方式
7191
+ }
7192
+ });
7193
+ },
7127
7194
  // 性能优化
7128
7195
  removeContainer: true, // 截图后移除临时容器
7129
7196
  imageTimeout: this.options.imageLoadTimeout || 5000,
@@ -7530,37 +7597,49 @@ class ScreenshotManager {
7530
7597
  throw new Error('无法获取 canvas context');
7531
7598
  }
7532
7599
  const img = new Image();
7533
- await new Promise((resolve, reject) => {
7534
- img.onload = () => {
7535
- canvas.width = img.width;
7536
- canvas.height = img.height;
7537
- ctx.drawImage(img, 0, 0);
7538
- resolve();
7539
- };
7540
- img.onerror = reject;
7541
- img.src = dataUrl;
7542
- });
7543
- let mimeType = 'image/jpeg';
7544
- // 使用与 createContext 相同的质量设置
7545
- let conversionQuality = finalQuality;
7546
- if (this.options.outputFormat === 'webp' && !isMobile) {
7547
- try {
7548
- const testCanvas = document.createElement('canvas');
7549
- testCanvas.width = 1;
7550
- testCanvas.height = 1;
7551
- const testDataUrl = testCanvas.toDataURL('image/webp');
7552
- if (testDataUrl.indexOf('webp') !== -1) {
7553
- mimeType = 'image/webp';
7600
+ let convertedDataUrl;
7601
+ try {
7602
+ await new Promise((resolve, reject) => {
7603
+ img.onload = () => {
7604
+ canvas.width = img.width;
7605
+ canvas.height = img.height;
7606
+ ctx.drawImage(img, 0, 0);
7607
+ resolve();
7608
+ };
7609
+ img.onerror = reject;
7610
+ img.src = dataUrl;
7611
+ });
7612
+ let mimeType = 'image/jpeg';
7613
+ // 使用与 createContext 相同的质量设置
7614
+ let conversionQuality = finalQuality;
7615
+ if (this.options.outputFormat === 'webp' && !isMobile) {
7616
+ try {
7617
+ const testCanvas = document.createElement('canvas');
7618
+ testCanvas.width = 1;
7619
+ testCanvas.height = 1;
7620
+ const testDataUrl = testCanvas.toDataURL('image/webp');
7621
+ if (testDataUrl.indexOf('webp') !== -1) {
7622
+ mimeType = 'image/webp';
7623
+ }
7624
+ }
7625
+ catch {
7626
+ mimeType = 'image/jpeg';
7554
7627
  }
7555
7628
  }
7556
- catch {
7557
- mimeType = 'image/jpeg';
7558
- }
7629
+ // 使用优化后的质量进行格式转换
7630
+ convertedDataUrl = mimeType === 'image/png'
7631
+ ? canvas.toDataURL(mimeType)
7632
+ : canvas.toDataURL(mimeType, conversionQuality);
7633
+ }
7634
+ finally {
7635
+ // 清理资源,释放内存
7636
+ img.src = ''; // 清除图片引用
7637
+ img.onload = null;
7638
+ img.onerror = null;
7639
+ canvas.width = 0; // 清空 canvas
7640
+ canvas.height = 0;
7641
+ ctx.clearRect(0, 0, 0, 0); // 清除绘制内容
7559
7642
  }
7560
- // 使用优化后的质量进行格式转换
7561
- const convertedDataUrl = mimeType === 'image/png'
7562
- ? canvas.toDataURL(mimeType)
7563
- : canvas.toDataURL(mimeType, conversionQuality);
7564
7643
  return convertedDataUrl;
7565
7644
  }
7566
7645
  return dataUrl;
@@ -7581,15 +7660,34 @@ class ScreenshotManager {
7581
7660
  throw error;
7582
7661
  }
7583
7662
  finally {
7584
- // 每次截图后清理 context,下次重新创建(确保元素状态最新)
7663
+ // 每次截图后立即清理 context,释放 Worker 和内存
7664
+ // 这是防止内存泄漏的关键步骤
7585
7665
  if (this.screenshotContext) {
7586
7666
  try {
7587
7667
  destroyContext(this.screenshotContext);
7668
+ if (!this.options.silentMode) {
7669
+ console.log('📸 ✅ modern-screenshot context 已清理');
7670
+ }
7588
7671
  }
7589
7672
  catch (e) {
7590
- // 忽略清理错误
7673
+ if (!this.options.silentMode) {
7674
+ console.warn('📸 ⚠️ 清理 context 失败:', e);
7675
+ }
7676
+ }
7677
+ finally {
7678
+ // 确保 context 引用被清除
7679
+ this.screenshotContext = null;
7680
+ }
7681
+ }
7682
+ // 强制触发垃圾回收(如果可能)
7683
+ // 注意:这需要浏览器支持,不是所有浏览器都有效
7684
+ if (typeof window !== 'undefined' && window.gc && typeof window.gc === 'function') {
7685
+ try {
7686
+ window.gc();
7687
+ }
7688
+ catch {
7689
+ // 忽略 GC 错误
7591
7690
  }
7592
- this.screenshotContext = null;
7593
7691
  }
7594
7692
  }
7595
7693
  }
@@ -8164,15 +8262,42 @@ class ScreenshotManager {
8164
8262
  }
8165
8263
  }
8166
8264
  /**
8167
- * 等待 CSS 和字体加载完成(优化:减少等待时间,使用 requestAnimationFrame)
8265
+ * 等待 CSS 和字体加载完成
8266
+ * html2canvas 需要确保所有样式表都加载完成才能正确截图
8168
8267
  */
8169
8268
  async waitForStylesAndFonts() {
8170
- // 使用 requestAnimationFrame 优化渲染时机
8171
- return new Promise((resolve) => {
8172
- requestAnimationFrame(() => {
8173
- setTimeout(() => {
8269
+ // 等待所有样式表加载完成
8270
+ const styleSheets = Array.from(document.styleSheets);
8271
+ const styleSheetPromises = styleSheets.map((sheet) => {
8272
+ return new Promise((resolve) => {
8273
+ try {
8274
+ // 检查样式表是否已加载
8275
+ // 尝试访问 cssRules,如果成功说明样式表已加载
8276
+ const rules = sheet.cssRules;
8277
+ if (rules) {
8278
+ resolve();
8279
+ }
8280
+ else {
8281
+ // 如果样式表还在加载,等待一下
8282
+ setTimeout(() => resolve(), 100);
8283
+ }
8284
+ }
8285
+ catch (e) {
8286
+ // 跨域样式表可能无法访问,忽略错误
8174
8287
  resolve();
8175
- }, 30); // 减少到30ms
8288
+ }
8289
+ });
8290
+ });
8291
+ await Promise.all(styleSheetPromises);
8292
+ // 使用 requestAnimationFrame 确保 DOM 已渲染
8293
+ await new Promise((resolve) => {
8294
+ requestAnimationFrame(() => {
8295
+ requestAnimationFrame(() => {
8296
+ // 额外等待,确保样式应用完成
8297
+ setTimeout(() => {
8298
+ resolve();
8299
+ }, 100); // 增加到100ms,确保样式加载
8300
+ });
8176
8301
  });
8177
8302
  });
8178
8303
  }
@@ -8191,6 +8316,52 @@ class ScreenshotManager {
8191
8316
  // 忽略错误
8192
8317
  }
8193
8318
  }
8319
+ /**
8320
+ * 等待所有样式表加载完成(html2canvas 专用)
8321
+ */
8322
+ async waitForAllStylesLoaded() {
8323
+ const styleSheets = Array.from(document.styleSheets);
8324
+ const promises = [];
8325
+ styleSheets.forEach((sheet) => {
8326
+ promises.push(new Promise((resolve) => {
8327
+ try {
8328
+ // 尝试访问样式表规则,如果成功说明样式表已加载
8329
+ const rules = sheet.cssRules;
8330
+ if (rules) {
8331
+ resolve();
8332
+ }
8333
+ else {
8334
+ // 等待样式表加载
8335
+ const checkInterval = setInterval(() => {
8336
+ try {
8337
+ if (sheet.cssRules) {
8338
+ clearInterval(checkInterval);
8339
+ resolve();
8340
+ }
8341
+ }
8342
+ catch {
8343
+ // 跨域样式表无法访问,直接 resolve
8344
+ clearInterval(checkInterval);
8345
+ resolve();
8346
+ }
8347
+ }, 50);
8348
+ // 超时保护(5秒)
8349
+ setTimeout(() => {
8350
+ clearInterval(checkInterval);
8351
+ resolve();
8352
+ }, 5000);
8353
+ }
8354
+ }
8355
+ catch (e) {
8356
+ // 跨域样式表无法访问,直接 resolve
8357
+ resolve();
8358
+ }
8359
+ }));
8360
+ });
8361
+ await Promise.all(promises);
8362
+ // 额外等待,确保样式应用
8363
+ await new Promise(resolve => setTimeout(resolve, 150));
8364
+ }
8194
8365
  /**
8195
8366
  * 计算压缩后的尺寸
8196
8367
  */
@@ -8407,6 +8578,11 @@ class ScreenshotManager {
8407
8578
  }
8408
8579
  // 清理图片代理缓存
8409
8580
  this.imageProxyCache.clear();
8581
+ // 清理截图历史记录(释放大量内存)
8582
+ this.screenshotHistory.length = 0;
8583
+ // 清理图片下载队列
8584
+ this.imageDownloadQueue.clear();
8585
+ this.activeDownloads.clear();
8410
8586
  }
8411
8587
  /**
8412
8588
  * 获取缓存的图片(检查是否过期)
@@ -8428,8 +8604,34 @@ class ScreenshotManager {
8428
8604
  }
8429
8605
  /**
8430
8606
  * 设置缓存的图片(带时间戳)
8607
+ * 添加内存大小限制,防止缓存无限增长
8431
8608
  */
8432
8609
  setCachedImage(url, dataUrl) {
8610
+ // 估算当前缓存总大小(MB)
8611
+ let totalSizeMB = 0;
8612
+ this.imageProxyCache.forEach((cached) => {
8613
+ totalSizeMB += cached.dataUrl.length * 0.75 / (1024 * 1024); // base64 转字节再转MB
8614
+ });
8615
+ // 添加新项的大小
8616
+ const newItemSizeMB = dataUrl.length * 0.75 / (1024 * 1024);
8617
+ const maxCacheSizeMB = 100; // 最大100MB内存缓存
8618
+ // 如果超过限制,清理最旧的缓存
8619
+ if (totalSizeMB + newItemSizeMB > maxCacheSizeMB) {
8620
+ const sortedEntries = Array.from(this.imageProxyCache.entries())
8621
+ .sort((a, b) => a[1].timestamp - b[1].timestamp); // 按时间排序
8622
+ let currentSizeMB = totalSizeMB;
8623
+ for (const [key, value] of sortedEntries) {
8624
+ if (currentSizeMB + newItemSizeMB <= maxCacheSizeMB) {
8625
+ break;
8626
+ }
8627
+ const itemSizeMB = value.dataUrl.length * 0.75 / (1024 * 1024);
8628
+ this.imageProxyCache.delete(key);
8629
+ currentSizeMB -= itemSizeMB;
8630
+ if (!this.options.silentMode) {
8631
+ console.log(`📸 清理内存缓存(超过限制): ${key.substring(0, 50)}...`);
8632
+ }
8633
+ }
8634
+ }
8433
8635
  this.imageProxyCache.set(url, {
8434
8636
  dataUrl,
8435
8637
  timestamp: Date.now()
@@ -8456,9 +8658,10 @@ class ScreenshotManager {
8456
8658
  }
8457
8659
  /**
8458
8660
  * 定期清理过期缓存(可选,在截图时也会自动清理)
8661
+ * 增加清理频率,防止内存积累
8459
8662
  */
8460
8663
  startCacheCleanup() {
8461
- // 每5分钟清理一次过期缓存
8664
+ // 每2分钟清理一次过期缓存(从5分钟改为2分钟,更频繁)
8462
8665
  setInterval(() => {
8463
8666
  this.cleanExpiredCache();
8464
8667
  // 如果启用 IndexedDB,也清理 IndexedDB 缓存
@@ -8469,9 +8672,12 @@ class ScreenshotManager {
8469
8672
  }
8470
8673
  if (!this.options.silentMode) {
8471
8674
  const memoryCacheSize = this.imageProxyCache.size;
8472
- console.log(`📸 清理过期缓存,内存缓存数量: ${memoryCacheSize}`);
8675
+ const memoryCacheSizeMB = Array.from(this.imageProxyCache.values())
8676
+ .reduce((sum, cached) => sum + cached.dataUrl.length * 0.75 / (1024 * 1024), 0);
8677
+ const historySizeMB = this.screenshotHistory.reduce((sum, item) => sum + item.length * 0.75 / (1024 * 1024), 0);
8678
+ console.log(`📸 清理过期缓存,内存缓存: ${memoryCacheSize} 项,${memoryCacheSizeMB.toFixed(2)} MB,历史记录: ${historySizeMB.toFixed(2)} MB`);
8473
8679
  }
8474
- }, 300000); // 5分钟
8680
+ }, 120000); // 2分钟(从5分钟改为2分钟,更频繁清理)
8475
8681
  }
8476
8682
  /**
8477
8683
  * 获取状态