customer-chat-sdk 1.0.33 → 1.0.34

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.
@@ -28,6 +28,8 @@ export interface ScreenshotOptions {
28
28
  fetchPriority?: 'high' | 'low' | 'auto';
29
29
  maxCacheSize?: number;
30
30
  maxCacheAge?: number;
31
+ maxImageSize?: number;
32
+ skipLargeImages?: boolean;
31
33
  }
32
34
  /**
33
35
  * 上传配置接口
@@ -69,6 +71,9 @@ export declare class ScreenshotManager {
69
71
  private intersectionObserver;
70
72
  private visibleElementsCache;
71
73
  private preconnected;
74
+ private imageDownloadQueue;
75
+ private activeDownloads;
76
+ private maxConcurrentImageDownloads;
72
77
  private globalErrorHandler;
73
78
  private globalRejectionHandler;
74
79
  constructor(targetElement: HTMLElement | null, options?: ScreenshotOptions);
@@ -176,6 +181,10 @@ export declare class ScreenshotManager {
176
181
  * 通过代理服务器获取图片并转换为 data URL
177
182
  */
178
183
  private proxyImage;
184
+ /**
185
+ * 不使用代理时下载图片(带内存保护)
186
+ */
187
+ private downloadImageWithoutProxy;
179
188
  /**
180
189
  * 将 blob 转换为 data URL
181
190
  */
@@ -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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"}
@@ -6507,13 +6507,17 @@ class ScreenshotManager {
6507
6507
  this.visibleElementsCache = new Set();
6508
6508
  // 预连接状态
6509
6509
  this.preconnected = false;
6510
+ // 图片下载队列和并发控制(防止频繁截图时重复下载)
6511
+ this.imageDownloadQueue = new Map(); // URL -> Promise,避免重复下载
6512
+ this.activeDownloads = new Set(); // 正在下载的 URL
6513
+ this.maxConcurrentImageDownloads = 5; // 最大并发下载数(降低,避免内存问题)
6510
6514
  // 全局错误处理器
6511
6515
  this.globalErrorHandler = null;
6512
6516
  this.globalRejectionHandler = null;
6513
6517
  this.targetElement = targetElement;
6514
6518
  this.options = {
6515
6519
  interval: options.interval ?? 5000,
6516
- quality: options.quality ?? 0.4,
6520
+ quality: options.quality ?? 0.3, // 降低默认质量:0.4 -> 0.3,减少 base64 大小
6517
6521
  scale: options.scale ?? 1,
6518
6522
  maxHistory: options.maxHistory ?? 10,
6519
6523
  compress: options.compress ?? false,
@@ -6537,7 +6541,9 @@ class ScreenshotManager {
6537
6541
  useIntersectionObserver: options.useIntersectionObserver ?? true, // 默认使用 Intersection Observer
6538
6542
  fetchPriority: options.fetchPriority ?? 'high', // 默认高优先级
6539
6543
  maxCacheSize: options.maxCacheSize ?? 50, // 默认最大50MB
6540
- maxCacheAge: options.maxCacheAge ?? 86400000 // 默认24小时(86400000ms)
6544
+ maxCacheAge: options.maxCacheAge ?? 86400000, // 默认24小时(86400000ms)
6545
+ maxImageSize: options.maxImageSize ?? 5, // 不使用代理时,单个图片最大尺寸(MB),默认5MB
6546
+ skipLargeImages: options.skipLargeImages ?? true // 不使用代理时,是否跳过过大的图片,默认true(跳过)
6541
6547
  };
6542
6548
  this.setupMessageListener();
6543
6549
  this.setupVisibilityChangeListener();
@@ -7094,13 +7100,19 @@ class ScreenshotManager {
7094
7100
  }
7095
7101
  const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
7096
7102
  const isLowEndDevice = navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 4;
7097
- const mobileQuality = isMobile || isLowEndDevice ? Math.max(this.options.quality * 0.7, 0.3) : this.options.quality;
7098
- // 不限制宽度和高度,让 modern-screenshot 自动处理完整内容
7099
- // 只在需要压缩时才应用尺寸限制
7103
+ // 进一步降低质量以减少 base64 大小
7104
+ // 桌面设备:使用配置的质量(默认 0.3)
7105
+ // 移动设备/低端设备:进一步降低到 0.2(最低)
7106
+ const finalQuality = isMobile || isLowEndDevice
7107
+ ? Math.max(this.options.quality * 0.65, 0.2) // 移动设备:质量 * 0.65,最低 0.2
7108
+ : this.options.quality; // 桌面设备:使用配置的质量(默认 0.3)
7109
+ // 计算压缩后的尺寸(对所有元素都应用,包括 document.body)
7110
+ // 这样可以避免生成过大的截图,减少 base64 大小
7100
7111
  const { width, height } = this.calculateCompressedSize(elementWidth, elementHeight, this.options.maxWidth, this.options.maxHeight);
7101
- // 如果计算后的尺寸小于元素实际尺寸,说明需要压缩,否则使用元素实际尺寸
7102
- const finalWidth = width < elementWidth ? width : undefined;
7103
- const finalHeight = height < elementHeight ? height : undefined;
7112
+ // 对于所有元素都应用尺寸限制(包括 body),避免截图过大
7113
+ // 如果计算后的尺寸小于元素实际尺寸,使用压缩尺寸;否则使用元素实际尺寸(但不超过最大值)
7114
+ const finalWidth = width < elementWidth ? width : Math.min(elementWidth, this.options.maxWidth);
7115
+ const finalHeight = height < elementHeight ? height : Math.min(elementHeight, this.options.maxHeight);
7104
7116
  // 处理跨域图片的函数
7105
7117
  const handleCrossOriginImage = async (url) => {
7106
7118
  // 如果是 data URL 或 blob URL,直接返回
@@ -7199,9 +7211,64 @@ class ScreenshotManager {
7199
7211
  return url;
7200
7212
  }
7201
7213
  }
7202
- // 如果没有配置代理,尝试使用 CORS
7214
+ // 如果没有配置代理,需要添加内存保护机制
7215
+ // 不使用代理时,modern-screenshot 会直接下载图片,可能导致内存问题
7216
+ // 由于已配置 CORS,可以直接下载并检查大小
7203
7217
  if (this.options.enableCORS) {
7204
- return url;
7218
+ // 对于不使用代理的情况,添加内存保护和缓存机制:
7219
+ // 1. 先检查内存缓存(避免重复下载)
7220
+ // 2. 检查 IndexedDB 缓存
7221
+ // 3. 使用下载队列避免并发重复下载
7222
+ // 4. 下载时检查大小,如果过大则使用占位符
7223
+ // 先检查内存缓存(优先使用缓存,避免重复下载)
7224
+ const cachedDataUrl = this.getCachedImage(url);
7225
+ if (cachedDataUrl) {
7226
+ if (!this.options.silentMode) {
7227
+ console.log(`📸 ✅ 使用内存缓存图片(无代理模式): ${url.substring(0, 50)}...`);
7228
+ }
7229
+ return cachedDataUrl;
7230
+ }
7231
+ // 检查 IndexedDB 缓存(如果启用)
7232
+ if (this.options.useIndexedDB) {
7233
+ const indexedDBCache = await this.getIndexedDBCache(url);
7234
+ if (indexedDBCache) {
7235
+ // 同步到内存缓存
7236
+ this.setCachedImage(url, indexedDBCache);
7237
+ if (!this.options.silentMode) {
7238
+ console.log(`📸 ✅ 使用 IndexedDB 缓存图片(无代理模式): ${url.substring(0, 50)}...`);
7239
+ }
7240
+ return indexedDBCache;
7241
+ }
7242
+ }
7243
+ // 检查是否正在下载(避免重复下载)
7244
+ if (this.imageDownloadQueue.has(url)) {
7245
+ // 如果已经在下载队列中,等待现有下载完成
7246
+ if (!this.options.silentMode) {
7247
+ console.log(`📸 ⏳ 等待图片下载完成: ${url.substring(0, 50)}...`);
7248
+ }
7249
+ return await this.imageDownloadQueue.get(url);
7250
+ }
7251
+ // 检查并发下载数限制
7252
+ if (this.activeDownloads.size >= this.maxConcurrentImageDownloads) {
7253
+ // 并发数已满,返回原 URL,让 modern-screenshot 自己处理(可能会失败,但不阻塞)
7254
+ if (!this.options.silentMode) {
7255
+ console.warn(`📸 ⚠️ 并发下载数已满(${this.activeDownloads.size}/${this.maxConcurrentImageDownloads}),跳过: ${url.substring(0, 50)}...`);
7256
+ }
7257
+ return url;
7258
+ }
7259
+ // 创建下载 Promise 并加入队列
7260
+ const downloadPromise = this.downloadImageWithoutProxy(url);
7261
+ this.imageDownloadQueue.set(url, downloadPromise);
7262
+ this.activeDownloads.add(url);
7263
+ try {
7264
+ const result = await downloadPromise;
7265
+ return result;
7266
+ }
7267
+ finally {
7268
+ // 下载完成后清理
7269
+ this.imageDownloadQueue.delete(url);
7270
+ this.activeDownloads.delete(url);
7271
+ }
7205
7272
  }
7206
7273
  // 默认返回原 URL
7207
7274
  return url;
@@ -7222,11 +7289,14 @@ class ScreenshotManager {
7222
7289
  }
7223
7290
  this.screenshotContext = null;
7224
7291
  }
7292
+ // Worker 数量配置:移动设备/低端设备使用 1 个 Worker,桌面设备使用 2 个
7293
+ // workerNumber > 0 会启用 Worker 模式,截图处理在后台线程执行,不会阻塞主线程 UI
7225
7294
  const workerNumber = isMobile || isLowEndDevice ? 1 : 2;
7226
7295
  // 构建 createContext 配置
7296
+ // 参考: https://github.com/qq15725/modern-screenshot/blob/main/src/options.ts
7227
7297
  const contextOptions = {
7228
- workerNumber,
7229
- quality: mobileQuality,
7298
+ workerNumber, // Worker 数量,> 0 启用 Worker 模式
7299
+ quality: finalQuality, // 图片质量(0-1),已优化为更低的值以减少 base64 大小
7230
7300
  fetchFn: handleCrossOriginImage, // 使用代理服务器处理跨域图片
7231
7301
  fetch: {
7232
7302
  requestInit: {
@@ -7234,35 +7304,45 @@ class ScreenshotManager {
7234
7304
  },
7235
7305
  bypassingCache: true,
7236
7306
  },
7307
+ // 设置最大 canvas 尺寸,防止生成过大的 canvas(避免内存问题)
7308
+ // 参考: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size
7309
+ // 大多数浏览器限制为 16,777,216 像素(4096x4096),这里设置为更保守的值
7310
+ maximumCanvasSize: 16777216, // 16M 像素(约 4096x4096)
7237
7311
  };
7238
- // 对于 document.body,不设置 width/height,让 modern-screenshot 自动处理完整页面
7239
- // 只有在需要压缩且不是 body 元素时才指定尺寸
7240
- if (element !== document.body && element !== document.documentElement) {
7241
- if (finalWidth && finalHeight && (finalWidth < elementWidth || finalHeight < elementHeight)) {
7242
- contextOptions.width = finalWidth;
7243
- contextOptions.height = finalHeight;
7244
- if (!this.options.silentMode) {
7245
- console.log(`📸 使用压缩尺寸: ${finalWidth}x${finalHeight}`);
7312
+ // 对所有元素都设置尺寸限制(包括 document.body),避免截图过大
7313
+ // 这样可以减少 base64 大小,提高性能
7314
+ if (finalWidth && finalHeight) {
7315
+ contextOptions.width = finalWidth;
7316
+ contextOptions.height = finalHeight;
7317
+ if (!this.options.silentMode) {
7318
+ if (element === document.body || element === document.documentElement) {
7319
+ console.log(`📸 截取完整页面(document.body),使用压缩尺寸: ${finalWidth}x${finalHeight}`);
7246
7320
  }
7247
- }
7248
- else {
7249
- if (!this.options.silentMode) {
7250
- console.log(`📸 使用元素实际尺寸: ${elementWidth}x${elementHeight}`);
7321
+ else {
7322
+ console.log(`📸 使用压缩尺寸: ${finalWidth}x${finalHeight}`);
7251
7323
  }
7252
7324
  }
7253
7325
  }
7254
7326
  else {
7255
- // 对于 body,不设置尺寸限制,让 modern-screenshot 自动截取完整页面
7256
7327
  if (!this.options.silentMode) {
7257
- console.log(`📸 截取完整页面(document.body),不设置尺寸限制(让 modern-screenshot 自动处理)`);
7328
+ console.log(`📸 使用元素实际尺寸: ${elementWidth}x${elementHeight}`);
7258
7329
  }
7259
7330
  }
7260
- // 如果指定了缩放比例,添加缩放配置
7331
+ // 缩放配置:移动设备使用更低的缩放比例,进一步减少图片大小
7332
+ // scale < 1 会降低图片分辨率,减少 base64 大小
7261
7333
  if (this.options.scale !== 1) {
7262
- contextOptions.scale = isMobile ? 0.8 : this.options.scale;
7334
+ contextOptions.scale = isMobile ? 0.7 : this.options.scale; // 移动设备:0.8 -> 0.7
7263
7335
  }
7264
- // modern-screenshot 会自动处理 worker URL,不需要手动设置
7336
+ else if (isMobile) {
7337
+ // 如果未指定 scale,移动设备默认使用 0.7
7338
+ contextOptions.scale = 0.7;
7339
+ }
7340
+ // modern-screenshot 会自动处理 worker URL,不需要手动设置 workerUrl
7341
+ // 当 workerNumber > 0 时,截图处理会在 Worker 线程中执行,不会阻塞主线程 UI
7265
7342
  // 创建 Worker 上下文(每次截图都创建新的,确保元素状态最新)
7343
+ if (!this.options.silentMode) {
7344
+ console.log(`📸 Worker 模式: ${workerNumber} 个 Worker,质量: ${finalQuality.toFixed(2)},缩放: ${contextOptions.scale || 1}`);
7345
+ }
7266
7346
  this.screenshotContext = await createContext$1(element, contextOptions);
7267
7347
  try {
7268
7348
  // 使用 Worker 上下文进行截图
@@ -7291,7 +7371,8 @@ class ScreenshotManager {
7291
7371
  img.src = dataUrl;
7292
7372
  });
7293
7373
  let mimeType = 'image/jpeg';
7294
- let finalQuality = mobileQuality;
7374
+ // 使用与 createContext 相同的质量设置
7375
+ let conversionQuality = finalQuality;
7295
7376
  if (this.options.outputFormat === 'webp' && !isMobile) {
7296
7377
  try {
7297
7378
  const testCanvas = document.createElement('canvas');
@@ -7306,9 +7387,10 @@ class ScreenshotManager {
7306
7387
  mimeType = 'image/jpeg';
7307
7388
  }
7308
7389
  }
7390
+ // 使用优化后的质量进行格式转换
7309
7391
  const convertedDataUrl = mimeType === 'image/png'
7310
7392
  ? canvas.toDataURL(mimeType)
7311
- : canvas.toDataURL(mimeType, finalQuality);
7393
+ : canvas.toDataURL(mimeType, conversionQuality);
7312
7394
  return convertedDataUrl;
7313
7395
  }
7314
7396
  return dataUrl;
@@ -7784,6 +7866,87 @@ class ScreenshotManager {
7784
7866
  }
7785
7867
  return dataUrl;
7786
7868
  }
7869
+ /**
7870
+ * 不使用代理时下载图片(带内存保护)
7871
+ */
7872
+ async downloadImageWithoutProxy(url) {
7873
+ try {
7874
+ // 直接下载图片并检查大小
7875
+ const controller = new AbortController();
7876
+ const timeoutId = setTimeout(() => controller.abort(), this.options.imageLoadTimeout || 5000);
7877
+ const response = await fetch(url, {
7878
+ method: 'GET',
7879
+ mode: 'cors',
7880
+ credentials: 'omit',
7881
+ cache: 'no-cache',
7882
+ signal: controller.signal
7883
+ }).catch(() => null).finally(() => {
7884
+ clearTimeout(timeoutId);
7885
+ });
7886
+ if (response && response.ok) {
7887
+ // 检查 Content-Length(如果可用)
7888
+ const contentLength = response.headers.get('content-length');
7889
+ if (contentLength) {
7890
+ const sizeMB = parseInt(contentLength, 10) / (1024 * 1024);
7891
+ const maxSizeMB = this.options.maxImageSize || 5;
7892
+ if (sizeMB > maxSizeMB) {
7893
+ if (this.options.skipLargeImages) {
7894
+ // 跳过过大的图片,返回占位符
7895
+ if (!this.options.silentMode) {
7896
+ console.warn(`📸 ⚠️ 跳过过大图片(${sizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
7897
+ }
7898
+ // 返回一个 1x1 的透明占位符,避免截图失败
7899
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
7900
+ }
7901
+ else {
7902
+ // 不跳过,但添加警告
7903
+ if (!this.options.silentMode) {
7904
+ console.warn(`📸 ⚠️ 图片较大(${sizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
7905
+ }
7906
+ }
7907
+ }
7908
+ }
7909
+ // 如果大小检查通过或无法获取大小,下载图片并转换为 data URL
7910
+ // 但为了进一步保护内存,在下载 blob 后也检查实际大小
7911
+ const blob = await response.blob();
7912
+ const blobSizeMB = blob.size / (1024 * 1024);
7913
+ const maxSizeMB = this.options.maxImageSize || 5;
7914
+ if (blobSizeMB > maxSizeMB) {
7915
+ if (this.options.skipLargeImages) {
7916
+ // 跳过过大的图片,返回占位符
7917
+ if (!this.options.silentMode) {
7918
+ console.warn(`📸 ⚠️ 跳过过大图片(实际大小 ${blobSizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
7919
+ }
7920
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
7921
+ }
7922
+ else {
7923
+ // 不跳过,但添加警告
7924
+ if (!this.options.silentMode) {
7925
+ console.warn(`📸 ⚠️ 图片较大(实际大小 ${blobSizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
7926
+ }
7927
+ }
7928
+ }
7929
+ // 转换为 data URL
7930
+ const dataUrl = await this.blobToDataUrl(blob);
7931
+ // 缓存结果(带时间戳,10分钟有效)
7932
+ this.setCachedImage(url, dataUrl);
7933
+ // 如果启用 IndexedDB,也保存到 IndexedDB
7934
+ if (this.options.useIndexedDB) {
7935
+ await this.setIndexedDBCache(url, dataUrl);
7936
+ }
7937
+ return dataUrl;
7938
+ }
7939
+ // 如果下载失败,返回原 URL,让 modern-screenshot 自己处理
7940
+ return url;
7941
+ }
7942
+ catch (error) {
7943
+ // 下载失败,返回原 URL,让 modern-screenshot 自己处理
7944
+ if (!this.options.silentMode) {
7945
+ console.warn(`📸 ⚠️ 下载图片失败: ${url.substring(0, 100)}...`, error);
7946
+ }
7947
+ return url;
7948
+ }
7949
+ }
7787
7950
  /**
7788
7951
  * 将 blob 转换为 data URL
7789
7952
  */