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