customer-chat-sdk 1.0.67 → 1.0.70

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.
@@ -14338,11 +14338,12 @@ class ScreenshotManager {
14338
14338
  this.dynamicInterval = null;
14339
14339
  // 过期定时器
14340
14340
  this.expirationTimer = null;
14341
+ // 当前任务完成标志(用于串行定时任务)
14342
+ this.isCurrentTaskCompleted = true;
14343
+ // 保存 scheduleNext 函数引用(用于在压缩完成后触发下一次任务)
14344
+ this.scheduleNextFn = null;
14341
14345
  // 图片代理缓存(带过期时间)
14342
14346
  this.imageProxyCache = new Map();
14343
- // IndexedDB 缓存(持久化)
14344
- this.indexedDBCache = null;
14345
- this.indexedDBReady = false;
14346
14347
  // Intersection Observer(用于高效检测可见性)
14347
14348
  this.intersectionObserver = null;
14348
14349
  this.visibleElementsCache = new Set();
@@ -14377,13 +14378,10 @@ class ScreenshotManager {
14377
14378
  maxConcurrentDownloads: options.maxConcurrentDownloads ?? 10, // 增加并发数
14378
14379
  onlyVisibleImages: options.onlyVisibleImages ?? true, // 默认只处理可视区域
14379
14380
  imageCacheTTL: options.imageCacheTTL ?? 600000, // 默认10分钟(600000ms)
14380
- useIndexedDB: options.useIndexedDB ?? true, // 默认启用 IndexedDB 持久化缓存
14381
14381
  usePreconnect: options.usePreconnect ?? true, // 默认预连接代理服务器
14382
14382
  imageLoadTimeout: options.imageLoadTimeout ?? 5000, // 默认5秒超时
14383
14383
  useIntersectionObserver: options.useIntersectionObserver ?? true, // 默认使用 Intersection Observer
14384
14384
  fetchPriority: options.fetchPriority ?? 'high', // 默认高优先级
14385
- maxCacheSize: options.maxCacheSize ?? 50, // 默认最大50MB
14386
- maxCacheAge: options.maxCacheAge ?? 86400000, // 默认24小时(86400000ms)
14387
14385
  maxImageSize: options.maxImageSize ?? 5, // 不使用代理时,单个图片最大尺寸(MB),默认5MB
14388
14386
  skipLargeImages: options.skipLargeImages ?? true, // 不使用代理时,是否跳过过大的图片,默认true(跳过)
14389
14387
  workerNumber: options.workerNumber ?? undefined // modern-screenshot Worker 数量,默认自动计算(undefined 表示自动)
@@ -14406,15 +14404,6 @@ class ScreenshotManager {
14406
14404
  if (this.options.useIntersectionObserver && this.options.onlyVisibleImages) {
14407
14405
  this.initIntersectionObserver();
14408
14406
  }
14409
- // 初始化 IndexedDB(如果启用)
14410
- if (this.options.useIndexedDB) {
14411
- this.initIndexedDB().catch(() => {
14412
- // IndexedDB 初始化失败,回退到内存缓存
14413
- if (!this.options.silentMode) {
14414
- console.warn('📸 IndexedDB 初始化失败,使用内存缓存');
14415
- }
14416
- });
14417
- }
14418
14407
  }
14419
14408
  /**
14420
14409
  * 设置目标元素
@@ -14680,9 +14669,28 @@ class ScreenshotManager {
14680
14669
  this.worker = this.createWorker();
14681
14670
  }
14682
14671
  // 设置定时器(使用递归 setTimeout,确保等待前一个完成)
14683
- // 这样可以避免 setInterval 不等待异步完成的问题
14672
+ // 串行执行:必须等待上次任务完成后才进行下次任务
14684
14673
  const scheduleNext = async () => {
14674
+ // 如果上次任务还没完成,等待完成
14675
+ if (!this.isCurrentTaskCompleted) {
14676
+ if (!this.options.silentMode) {
14677
+ console.log('📸 [定时] 等待上次任务完成...');
14678
+ }
14679
+ // 每100ms检查一次任务是否完成
14680
+ const checkInterval = setInterval(() => {
14681
+ if (this.isCurrentTaskCompleted && this.isRunning) {
14682
+ clearInterval(checkInterval);
14683
+ scheduleNext();
14684
+ }
14685
+ else if (!this.isRunning) {
14686
+ clearInterval(checkInterval);
14687
+ }
14688
+ }, 100);
14689
+ return;
14690
+ }
14685
14691
  if (this.isRunning && this.isEnabled && !document.hidden) {
14692
+ // 标记任务开始
14693
+ this.isCurrentTaskCompleted = false;
14686
14694
  // 记录定时开始时间
14687
14695
  const scheduleStartTime = performance.now();
14688
14696
  if (!this.options.silentMode) {
@@ -14750,25 +14758,36 @@ class ScreenshotManager {
14750
14758
  console.error('📸 [轮询] ❌ 处理二进制数据失败:', error);
14751
14759
  }
14752
14760
  }
14761
+ // 任务完成(无压缩模式)
14762
+ this.isCurrentTaskCompleted = true;
14753
14763
  }
14754
14764
  else if (this.currentBinaryConfig && this.options.compress) {
14755
14765
  // 启用了压缩,等待 Worker 压缩完成后在 onmessage 中发送
14766
+ // 任务完成标志会在压缩完成的回调中设置
14756
14767
  if (!this.options.silentMode) {
14757
14768
  console.log('📸 [轮询] 等待 Worker 压缩完成后发送到 iframe...');
14758
14769
  }
14759
14770
  }
14771
+ else {
14772
+ // 没有二进制配置,任务完成
14773
+ this.isCurrentTaskCompleted = true;
14774
+ }
14760
14775
  }
14761
14776
  catch (error) {
14762
14777
  if (!this.options.silentMode) {
14763
14778
  console.error('📸 [轮询] 截图失败:', error);
14764
14779
  }
14780
+ // 任务失败,标记为完成
14781
+ this.isCurrentTaskCompleted = true;
14765
14782
  }
14766
14783
  }
14767
- // 如果还在运行,安排下一次截图
14768
- if (this.isRunning) {
14784
+ // 如果还在运行且任务已完成,安排下一次截图
14785
+ if (this.isRunning && this.isCurrentTaskCompleted) {
14769
14786
  this.screenshotTimer = setTimeout(scheduleNext, currentInterval);
14770
14787
  }
14771
14788
  };
14789
+ // 保存 scheduleNext 函数引用,以便在压缩完成的回调中使用
14790
+ this.scheduleNextFn = scheduleNext;
14772
14791
  // 立即开始第一次
14773
14792
  scheduleNext();
14774
14793
  // 注意:不再立即执行一次,因为已经在 takeScreenshotAndUpload 中执行了
@@ -14788,6 +14807,9 @@ class ScreenshotManager {
14788
14807
  clearInterval(this.screenshotTimer);
14789
14808
  this.screenshotTimer = null;
14790
14809
  }
14810
+ // 清理任务状态
14811
+ this.isCurrentTaskCompleted = true;
14812
+ this.scheduleNextFn = null;
14791
14813
  }
14792
14814
  /**
14793
14815
  * 手动截图一次(允许在未启用时也执行,用于测试)
@@ -14846,20 +14868,12 @@ class ScreenshotManager {
14846
14868
  if (this.options.preloadImages && this.options.enableCORS && selectedEngine === 'modern-screenshot') {
14847
14869
  // 清理过期缓存
14848
14870
  this.cleanExpiredCache();
14849
- // 如果启用 IndexedDB,也清理 IndexedDB 缓存
14850
- if (this.options.useIndexedDB) {
14851
- await this.cleanIndexedDBCache();
14852
- }
14853
14871
  await this.preprocessNetworkImages(this.targetElement);
14854
14872
  await this.waitForImagesToLoad(this.targetElement);
14855
14873
  }
14856
14874
  else {
14857
14875
  // 即使不预加载,也清理一下过期缓存
14858
14876
  this.cleanExpiredCache();
14859
- // 如果启用 IndexedDB,也清理 IndexedDB 缓存
14860
- if (this.options.useIndexedDB) {
14861
- await this.cleanIndexedDBCache();
14862
- }
14863
14877
  }
14864
14878
  let dataUrl;
14865
14879
  // 根据选择的引擎进行截图
@@ -15652,18 +15666,6 @@ class ScreenshotManager {
15652
15666
  }
15653
15667
  return cachedDataUrl;
15654
15668
  }
15655
- // 检查 IndexedDB 缓存(如果启用)
15656
- if (this.options.useIndexedDB) {
15657
- const indexedDBCache = await this.getIndexedDBCache(url);
15658
- if (indexedDBCache) {
15659
- // 同步到内存缓存
15660
- this.setCachedImage(url, indexedDBCache);
15661
- if (!this.options.silentMode) {
15662
- console.log(`📸 ✅ 使用 IndexedDB 缓存图片: ${url.substring(0, 50)}...`);
15663
- }
15664
- return indexedDBCache;
15665
- }
15666
- }
15667
15669
  try {
15668
15670
  // 构建代理请求参数
15669
15671
  const params = new URLSearchParams({
@@ -15703,10 +15705,6 @@ class ScreenshotManager {
15703
15705
  const dataUrl = await this.blobToDataUrl(blob);
15704
15706
  // 缓存结果(带时间戳,10分钟有效)
15705
15707
  this.setCachedImage(url, dataUrl);
15706
- // 如果启用 IndexedDB,也保存到 IndexedDB
15707
- if (this.options.useIndexedDB) {
15708
- await this.setIndexedDBCache(url, dataUrl);
15709
- }
15710
15708
  return dataUrl;
15711
15709
  }
15712
15710
  catch (fetchError) {
@@ -15739,18 +15737,6 @@ class ScreenshotManager {
15739
15737
  }
15740
15738
  return cachedDataUrl;
15741
15739
  }
15742
- // 检查 IndexedDB 缓存(如果启用)
15743
- if (this.options.useIndexedDB) {
15744
- const indexedDBCache = await this.getIndexedDBCache(url);
15745
- if (indexedDBCache) {
15746
- // 同步到内存缓存
15747
- this.setCachedImage(url, indexedDBCache);
15748
- if (!this.options.silentMode) {
15749
- console.log(`📸 ✅ 使用 IndexedDB 缓存图片(无代理模式): ${url.substring(0, 50)}...`);
15750
- }
15751
- return indexedDBCache;
15752
- }
15753
- }
15754
15740
  // 检查是否正在下载(避免重复下载)
15755
15741
  if (this.imageDownloadQueue.has(url)) {
15756
15742
  // 如果已经在下载队列中,等待现有下载完成
@@ -15878,14 +15864,10 @@ class ScreenshotManager {
15878
15864
  console.log(`📸 使用元素实际尺寸: ${elementWidth}x${elementHeight}`);
15879
15865
  }
15880
15866
  }
15881
- // 缩放配置:移动设备使用更低的缩放比例,进一步减少图片大小
15867
+ // 缩放配置:使用外部传递的参数
15882
15868
  // scale < 1 会降低图片分辨率,减少 base64 大小
15883
- if (this.options.scale !== 1) {
15884
- contextOptions.scale = isMobile ? 0.7 : this.options.scale; // 移动设备:0.8 -> 0.7
15885
- }
15886
- else if (isMobile) {
15887
- // 如果未指定 scale,移动设备默认使用 0.7
15888
- contextOptions.scale = 0.7;
15869
+ if (this.options.scale !== undefined && this.options.scale !== 1) {
15870
+ contextOptions.scale = this.options.scale;
15889
15871
  }
15890
15872
  // 优化:复用 context,避免频繁创建和销毁(性能提升 20%+)
15891
15873
  // 只在元素变化、配置变化或内容变化时重新创建 context
@@ -16083,28 +16065,83 @@ class ScreenshotManager {
16083
16065
  if (!this.options.silentMode) {
16084
16066
  console.log(`📸 使用 ${outputFormat.toUpperCase()} 格式截图(直接输出,无需转换)...`);
16085
16067
  }
16086
- // 根据输出格式选择对应的 API
16087
- // modern-screenshot 内部已经处理了超时,不需要额外的 Promise.race
16088
- if (outputFormat === 'webp') {
16089
- // 使用 domToWebp,直接输出 WebP 格式,无需转换
16090
- dataUrl = await domToWebp(this.screenshotContext);
16091
- }
16092
- else if (outputFormat === 'jpeg') {
16093
- // 使用 domToJpeg,直接输出 JPEG 格式,无需转换
16094
- dataUrl = await domToJpeg(this.screenshotContext);
16095
- }
16096
- else {
16097
- // 默认使用 domToPng
16098
- dataUrl = await domToPng(this.screenshotContext);
16099
- }
16100
- // 验证截图结果
16101
- if (!dataUrl || dataUrl.length < 100) {
16102
- throw new Error('生成的截图数据无效或过短');
16068
+ // 尝试使用 Worker 模式(context)
16069
+ try {
16070
+ // 根据输出格式选择对应的 API
16071
+ // modern-screenshot 内部已经处理了超时,不需要额外的 Promise.race
16072
+ if (outputFormat === 'webp') {
16073
+ // 使用 domToWebp,直接输出 WebP 格式,无需转换
16074
+ dataUrl = await domToWebp(this.screenshotContext);
16075
+ }
16076
+ else if (outputFormat === 'jpeg') {
16077
+ // 使用 domToJpeg,直接输出 JPEG 格式,无需转换
16078
+ dataUrl = await domToJpeg(this.screenshotContext);
16079
+ }
16080
+ else {
16081
+ // 默认使用 domToPng
16082
+ dataUrl = await domToPng(this.screenshotContext);
16083
+ }
16084
+ // 验证截图结果
16085
+ if (!dataUrl || dataUrl.length < 100) {
16086
+ throw new Error('生成的截图数据无效或过短');
16087
+ }
16088
+ if (!this.options.silentMode) {
16089
+ console.log(`📸 ✅ modern-screenshot 截图成功(Worker 模式,${outputFormat.toUpperCase()} 格式)`);
16090
+ }
16091
+ return dataUrl;
16103
16092
  }
16104
- if (!this.options.silentMode) {
16105
- console.log(`📸 modern-screenshot 截图成功(${outputFormat.toUpperCase()} 格式,直接输出,无需转换)`);
16093
+ catch (workerError) {
16094
+ // Worker 模式失败,回退到普通模式(参考用户代码)
16095
+ if (!this.options.silentMode) {
16096
+ console.warn('📸 Worker 模式失败,回退到普通模式:', workerError);
16097
+ }
16098
+ // 销毁失败的 context
16099
+ if (this.screenshotContext) {
16100
+ try {
16101
+ destroyContext(this.screenshotContext);
16102
+ }
16103
+ catch {
16104
+ // 忽略销毁错误
16105
+ }
16106
+ this.screenshotContext = null;
16107
+ }
16108
+ // 回退到普通模式(直接使用 domToWebp,不传 context)
16109
+ // 使用外部传递的参数,并给出合理的默认值
16110
+ const fallbackOptions = {
16111
+ scale: this.options.scale ?? 1, // 使用外部参数,默认 1
16112
+ backgroundColor: '#ffffff', // 默认白色背景
16113
+ type: `image/${outputFormat}`, // 使用配置的输出格式
16114
+ quality: this.options.quality ?? 0.8, // 使用外部参数,默认 0.4
16115
+ drawImageInterval: 20, // 默认 20ms,减少主线程阻塞
16116
+ features: {
16117
+ copyScrollbar: false,
16118
+ removeAbnormalAttributes: true,
16119
+ removeControlCharacter: true,
16120
+ fixSvgXmlDecode: true,
16121
+ restoreScrollPosition: false,
16122
+ },
16123
+ timeout: Math.max((this.options.interval ?? 5000) * 6, 10000), // 使用外部参数计算超时,默认 10 秒
16124
+ };
16125
+ // 限制 timeout 最多 15 秒
16126
+ fallbackOptions.timeout = Math.min(fallbackOptions.timeout, 15000);
16127
+ if (outputFormat === 'webp') {
16128
+ dataUrl = await domToWebp(element, fallbackOptions);
16129
+ }
16130
+ else if (outputFormat === 'jpeg') {
16131
+ dataUrl = await domToJpeg(element, fallbackOptions);
16132
+ }
16133
+ else {
16134
+ dataUrl = await domToPng(element, fallbackOptions);
16135
+ }
16136
+ // 验证截图结果
16137
+ if (!dataUrl || dataUrl.length < 100) {
16138
+ throw new Error('生成的截图数据无效或过短');
16139
+ }
16140
+ if (!this.options.silentMode) {
16141
+ console.log(`📸 ✅ modern-screenshot 截图成功(普通模式,${outputFormat.toUpperCase()} 格式)`);
16142
+ }
16143
+ return dataUrl;
16106
16144
  }
16107
- return dataUrl;
16108
16145
  }
16109
16146
  catch (error) {
16110
16147
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -16216,94 +16253,6 @@ class ScreenshotManager {
16216
16253
  threshold: 0.01
16217
16254
  });
16218
16255
  }
16219
- /**
16220
- * 初始化 IndexedDB(持久化缓存)
16221
- */
16222
- async initIndexedDB() {
16223
- return new Promise((resolve, reject) => {
16224
- const request = indexedDB.open('screenshot-cache', 1);
16225
- request.onerror = () => reject(request.error);
16226
- request.onsuccess = () => {
16227
- this.indexedDBCache = request.result;
16228
- this.indexedDBReady = true;
16229
- // 初始化后立即清理过期和超大小的缓存
16230
- this.cleanIndexedDBCache().catch(() => {
16231
- // 清理失败不影响功能
16232
- });
16233
- resolve();
16234
- };
16235
- request.onupgradeneeded = (event) => {
16236
- const db = event.target.result;
16237
- if (!db.objectStoreNames.contains('images')) {
16238
- const store = db.createObjectStore('images', { keyPath: 'url' });
16239
- // 创建索引用于按时间排序
16240
- store.createIndex('timestamp', 'timestamp', { unique: false });
16241
- }
16242
- };
16243
- });
16244
- }
16245
- /**
16246
- * 从 IndexedDB 获取缓存
16247
- */
16248
- async getIndexedDBCache(url) {
16249
- if (!this.indexedDBReady || !this.indexedDBCache) {
16250
- return null;
16251
- }
16252
- try {
16253
- const transaction = this.indexedDBCache.transaction(['images'], 'readonly');
16254
- const store = transaction.objectStore('images');
16255
- const request = store.get(url);
16256
- return new Promise((resolve) => {
16257
- request.onsuccess = () => {
16258
- const result = request.result;
16259
- if (result) {
16260
- const now = Date.now();
16261
- const age = now - result.timestamp;
16262
- // 检查是否超过最大缓存时间
16263
- if (age > this.options.maxCacheAge) {
16264
- // 缓存过期,删除
16265
- this.deleteIndexedDBCache(url);
16266
- resolve(null);
16267
- return;
16268
- }
16269
- // 返回缓存数据(即使超过 imageCacheTTL,只要未超过 maxCacheAge 仍可使用)
16270
- resolve(result.dataUrl);
16271
- }
16272
- else {
16273
- resolve(null);
16274
- }
16275
- };
16276
- request.onerror = () => resolve(null);
16277
- });
16278
- }
16279
- catch {
16280
- return null;
16281
- }
16282
- }
16283
- /**
16284
- * 设置 IndexedDB 缓存(带大小和时间控制)
16285
- */
16286
- async setIndexedDBCache(url, dataUrl) {
16287
- if (!this.indexedDBReady || !this.indexedDBCache) {
16288
- return;
16289
- }
16290
- try {
16291
- // 计算当前缓存大小
16292
- const currentSize = await this.getIndexedDBCacheSize();
16293
- const newItemSize = this.estimateDataUrlSize(dataUrl);
16294
- const maxSizeBytes = (this.options.maxCacheSize || 50) * 1024 * 1024; // 转换为字节
16295
- // 如果添加新项后超过限制,清理最旧的数据
16296
- if (currentSize + newItemSize > maxSizeBytes) {
16297
- await this.cleanIndexedDBCacheBySize(maxSizeBytes - newItemSize);
16298
- }
16299
- const transaction = this.indexedDBCache.transaction(['images'], 'readwrite');
16300
- const store = transaction.objectStore('images');
16301
- store.put({ url, dataUrl, timestamp: Date.now() });
16302
- }
16303
- catch {
16304
- // IndexedDB 写入失败,忽略
16305
- }
16306
- }
16307
16256
  /**
16308
16257
  * 估算 data URL 的大小(字节)
16309
16258
  */
@@ -16314,156 +16263,32 @@ class ScreenshotManager {
16314
16263
  return Math.ceil(base64Data.length * 0.75) + 30;
16315
16264
  }
16316
16265
  /**
16317
- * 获取 IndexedDB 当前缓存大小(字节)
16266
+ * 获取 IndexedDB 当前缓存大小(已移除,不再使用)
16267
+ * @deprecated IndexedDB 已移除,不再使用
16318
16268
  */
16319
16269
  async getIndexedDBCacheSize() {
16320
- if (!this.indexedDBReady || !this.indexedDBCache) {
16321
- return 0;
16322
- }
16323
- try {
16324
- const transaction = this.indexedDBCache.transaction(['images'], 'readonly');
16325
- const store = transaction.objectStore('images');
16326
- const request = store.getAll();
16327
- return new Promise((resolve) => {
16328
- request.onsuccess = () => {
16329
- const items = request.result || [];
16330
- let totalSize = 0;
16331
- items.forEach((item) => {
16332
- totalSize += this.estimateDataUrlSize(item.dataUrl);
16333
- });
16334
- resolve(totalSize);
16335
- };
16336
- request.onerror = () => resolve(0);
16337
- });
16338
- }
16339
- catch {
16340
- return 0;
16341
- }
16270
+ return 0;
16342
16271
  }
16343
16272
  /**
16344
- * 清理 IndexedDB 缓存(按时间和大小)
16273
+ * 清理 IndexedDB 缓存(已移除,不再使用)
16274
+ * @deprecated IndexedDB 已移除,不再使用
16345
16275
  */
16346
16276
  async cleanIndexedDBCache() {
16347
- if (!this.indexedDBReady || !this.indexedDBCache) {
16348
- return;
16349
- }
16350
- try {
16351
- const transaction = this.indexedDBCache.transaction(['images'], 'readwrite');
16352
- const store = transaction.objectStore('images');
16353
- const index = store.index('timestamp');
16354
- const request = index.getAll();
16355
- return new Promise((resolve) => {
16356
- request.onsuccess = () => {
16357
- const items = request.result || [];
16358
- const now = Date.now();
16359
- const expiredUrls = [];
16360
- let currentSize = 0;
16361
- const maxSizeBytes = (this.options.maxCacheSize || 50) * 1024 * 1024;
16362
- // 按时间排序(最旧的在前)
16363
- items.sort((a, b) => a.timestamp - b.timestamp);
16364
- // 清理过期数据
16365
- items.forEach((item) => {
16366
- const age = now - item.timestamp;
16367
- if (age > this.options.maxCacheAge) {
16368
- expiredUrls.push(item.url);
16369
- }
16370
- else {
16371
- currentSize += this.estimateDataUrlSize(item.dataUrl);
16372
- }
16373
- });
16374
- // 如果仍然超过大小限制,删除最旧的数据
16375
- const urlsToDelete = [...expiredUrls];
16376
- if (currentSize > maxSizeBytes) {
16377
- for (const item of items) {
16378
- if (expiredUrls.includes(item.url))
16379
- continue;
16380
- currentSize -= this.estimateDataUrlSize(item.dataUrl);
16381
- urlsToDelete.push(item.url);
16382
- if (currentSize <= maxSizeBytes) {
16383
- break;
16384
- }
16385
- }
16386
- }
16387
- // 删除过期和超大小的数据
16388
- urlsToDelete.forEach((url) => {
16389
- store.delete(url);
16390
- });
16391
- if (urlsToDelete.length > 0 && !this.options.silentMode) {
16392
- console.log(`📸 IndexedDB 清理了 ${urlsToDelete.length} 个缓存项(过期或超大小)`);
16393
- }
16394
- resolve();
16395
- };
16396
- request.onerror = () => resolve();
16397
- });
16398
- }
16399
- catch {
16400
- // 清理失败,忽略
16401
- }
16277
+ // IndexedDB 已移除,不再使用
16402
16278
  }
16403
16279
  /**
16404
- * 按大小清理 IndexedDB 缓存
16280
+ * 按大小清理 IndexedDB 缓存(已移除,不再使用)
16281
+ * @deprecated IndexedDB 已移除,不再使用
16405
16282
  */
16406
- async cleanIndexedDBCacheBySize(targetSize) {
16407
- if (!this.indexedDBReady || !this.indexedDBCache) {
16408
- return;
16409
- }
16410
- try {
16411
- const transaction = this.indexedDBCache.transaction(['images'], 'readwrite');
16412
- const store = transaction.objectStore('images');
16413
- const index = store.index('timestamp');
16414
- const request = index.getAll();
16415
- return new Promise((resolve) => {
16416
- request.onsuccess = () => {
16417
- const items = request.result || [];
16418
- // 按时间排序(最旧的在前)
16419
- items.sort((a, b) => a.timestamp - b.timestamp);
16420
- let currentSize = 0;
16421
- const urlsToDelete = [];
16422
- // 计算当前大小
16423
- items.forEach((item) => {
16424
- currentSize += this.estimateDataUrlSize(item.dataUrl);
16425
- });
16426
- // 如果超过目标大小,删除最旧的数据
16427
- if (currentSize > targetSize) {
16428
- for (const item of items) {
16429
- if (currentSize <= targetSize) {
16430
- break;
16431
- }
16432
- currentSize -= this.estimateDataUrlSize(item.dataUrl);
16433
- urlsToDelete.push(item.url);
16434
- }
16435
- }
16436
- // 删除数据
16437
- urlsToDelete.forEach((url) => {
16438
- store.delete(url);
16439
- });
16440
- if (urlsToDelete.length > 0 && !this.options.silentMode) {
16441
- console.log(`📸 IndexedDB 清理了 ${urlsToDelete.length} 个缓存项(超过大小限制)`);
16442
- }
16443
- resolve();
16444
- };
16445
- request.onerror = () => resolve();
16446
- });
16447
- }
16448
- catch {
16449
- // 清理失败,忽略
16450
- }
16283
+ async cleanIndexedDBCacheBySize(_targetSize) {
16284
+ // IndexedDB 已移除,不再使用
16451
16285
  }
16452
16286
  /**
16453
- * 删除 IndexedDB 缓存
16287
+ * 删除 IndexedDB 缓存(已移除,不再使用)
16288
+ * @deprecated IndexedDB 已移除,不再使用
16454
16289
  */
16455
- async deleteIndexedDBCache(url) {
16456
- if (!this.indexedDBReady || !this.indexedDBCache) {
16457
- return;
16458
- }
16459
- try {
16460
- const transaction = this.indexedDBCache.transaction(['images'], 'readwrite');
16461
- const store = transaction.objectStore('images');
16462
- store.delete(url);
16463
- }
16464
- catch {
16465
- // 忽略错误
16466
- }
16290
+ async deleteIndexedDBCache(_url) {
16291
+ // IndexedDB 已移除,不再使用
16467
16292
  }
16468
16293
  /**
16469
16294
  * 检查元素是否在可视区域内(优化:使用 Intersection Observer 或 getBoundingClientRect)
@@ -16678,10 +16503,6 @@ class ScreenshotManager {
16678
16503
  const dataUrl = await this.blobToDataUrl(blob);
16679
16504
  // 缓存结果(带时间戳,10分钟有效)
16680
16505
  this.setCachedImage(url, dataUrl);
16681
- // 如果启用 IndexedDB,也保存到 IndexedDB
16682
- if (this.options.useIndexedDB) {
16683
- await this.setIndexedDBCache(url, dataUrl);
16684
- }
16685
16506
  return dataUrl;
16686
16507
  }
16687
16508
  // 如果下载失败,返回原 URL,让 modern-screenshot 自己处理
@@ -17160,6 +16981,16 @@ class ScreenshotManager {
17160
16981
  this.sendCompressedScreenshotToIframe(compressed.dataUrl, scheduleStartTime, compressEndTime);
17161
16982
  }
17162
16983
  }
16984
+ // 任务完成(压缩模式)- 无论是否成功,都要标记为完成
16985
+ this.isCurrentTaskCompleted = true;
16986
+ // 如果还在运行,触发下一次任务
16987
+ if (this.isRunning && this.scheduleNextFn) {
16988
+ const currentInterval = this.dynamicInterval || this.options.interval;
16989
+ this.screenshotTimer = setTimeout(() => {
16990
+ this.screenshotTimer = null;
16991
+ this.scheduleNextFn?.();
16992
+ }, currentInterval);
16993
+ }
17163
16994
  }
17164
16995
  };
17165
16996
  newWorker.onerror = (e) => {
@@ -17189,6 +17020,16 @@ class ScreenshotManager {
17189
17020
  }
17190
17021
  }
17191
17022
  }
17023
+ // Worker 错误时,任务也完成
17024
+ this.isCurrentTaskCompleted = true;
17025
+ // 如果还在运行,触发下一次任务
17026
+ if (this.isRunning && this.scheduleNextFn) {
17027
+ const currentInterval = this.dynamicInterval || this.options.interval;
17028
+ this.screenshotTimer = setTimeout(() => {
17029
+ this.screenshotTimer = null;
17030
+ this.scheduleNextFn?.();
17031
+ }, currentInterval);
17032
+ }
17192
17033
  };
17193
17034
  // 注意:不要立即 revokeObjectURL,因为 Worker 需要这个 URL 保持有效
17194
17035
  // 在 destroy() 方法中清理 Worker 时再 revoke
@@ -17691,12 +17532,7 @@ class ScreenshotManager {
17691
17532
  this.intersectionObserver = null;
17692
17533
  this.visibleElementsCache.clear();
17693
17534
  }
17694
- // 关闭 IndexedDB
17695
- if (this.indexedDBCache) {
17696
- this.indexedDBCache.close();
17697
- this.indexedDBCache = null;
17698
- this.indexedDBReady = false;
17699
- }
17535
+ // IndexedDB 已移除,不再使用
17700
17536
  // 清理图片代理缓存
17701
17537
  this.imageProxyCache.clear();
17702
17538
  // 清理截图历史记录(释放大量内存)
@@ -17785,12 +17621,6 @@ class ScreenshotManager {
17785
17621
  // 每2分钟清理一次过期缓存(从5分钟改为2分钟,更频繁)
17786
17622
  setInterval(() => {
17787
17623
  this.cleanExpiredCache();
17788
- // 如果启用 IndexedDB,也清理 IndexedDB 缓存
17789
- if (this.options.useIndexedDB) {
17790
- this.cleanIndexedDBCache().catch(() => {
17791
- // 清理失败,忽略
17792
- });
17793
- }
17794
17624
  if (!this.options.silentMode) {
17795
17625
  const memoryCacheSize = this.imageProxyCache.size;
17796
17626
  const memoryCacheSizeMB = Array.from(this.imageProxyCache.values())