customer-chat-sdk 1.0.35 → 1.0.37
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,19 @@ export declare class ScreenshotManager {
|
|
|
227
227
|
*/
|
|
228
228
|
private waitForImagesToLoad;
|
|
229
229
|
/**
|
|
230
|
-
* 等待 CSS
|
|
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
|
+
*/
|
|
242
|
+
private waitForAllStylesLoaded;
|
|
237
243
|
/**
|
|
238
244
|
* 计算压缩后的尺寸
|
|
239
245
|
*/
|
|
@@ -276,6 +282,7 @@ export declare class ScreenshotManager {
|
|
|
276
282
|
private getCachedImage;
|
|
277
283
|
/**
|
|
278
284
|
* 设置缓存的图片(带时间戳)
|
|
285
|
+
* 添加内存大小限制,防止缓存无限增长
|
|
279
286
|
*/
|
|
280
287
|
private setCachedImage;
|
|
281
288
|
/**
|
|
@@ -284,6 +291,7 @@ export declare class ScreenshotManager {
|
|
|
284
291
|
private cleanExpiredCache;
|
|
285
292
|
/**
|
|
286
293
|
* 定期清理过期缓存(可选,在截图时也会自动清理)
|
|
294
|
+
* 增加清理频率,防止内存积累
|
|
287
295
|
*/
|
|
288
296
|
private startCacheCleanup;
|
|
289
297
|
/**
|
|
@@ -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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"}
|
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -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 === '
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
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
|
-
|
|
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,37 @@ class ScreenshotManager {
|
|
|
7124
7171
|
logging: !this.options.silentMode,
|
|
7125
7172
|
width: finalWidth,
|
|
7126
7173
|
height: finalHeight,
|
|
7174
|
+
// 关键配置:确保样式正确渲染
|
|
7175
|
+
// 注意:foreignObjectRendering 可能导致样式问题,改为 true 或移除此选项
|
|
7176
|
+
// foreignObjectRendering: false, // 移除或设为 true,让 html2canvas 自动选择最佳渲染方式
|
|
7177
|
+
onclone: (clonedDoc, _clonedElement) => {
|
|
7178
|
+
// 在克隆的文档中,确保所有样式都正确应用
|
|
7179
|
+
// html2canvas 会自动处理样式,但我们可以确保样式表被正确加载
|
|
7180
|
+
// 确保克隆文档的样式表链接正确
|
|
7181
|
+
const originalLinks = document.querySelectorAll('link[rel="stylesheet"]');
|
|
7182
|
+
const clonedLinks = clonedDoc.querySelectorAll('link[rel="stylesheet"]');
|
|
7183
|
+
// 如果克隆文档缺少样式表链接,从原始文档复制
|
|
7184
|
+
if (clonedLinks.length < originalLinks.length) {
|
|
7185
|
+
originalLinks.forEach((link) => {
|
|
7186
|
+
const href = link.getAttribute('href');
|
|
7187
|
+
if (href && !clonedDoc.querySelector(`link[href="${href}"]`)) {
|
|
7188
|
+
const newLink = clonedDoc.createElement('link');
|
|
7189
|
+
newLink.rel = 'stylesheet';
|
|
7190
|
+
newLink.href = href;
|
|
7191
|
+
clonedDoc.head.appendChild(newLink);
|
|
7192
|
+
}
|
|
7193
|
+
});
|
|
7194
|
+
}
|
|
7195
|
+
// 确保内联样式被正确复制
|
|
7196
|
+
const originalStyle = document.querySelector('style');
|
|
7197
|
+
if (originalStyle && originalStyle.textContent) {
|
|
7198
|
+
const clonedStyle = clonedDoc.createElement('style');
|
|
7199
|
+
clonedStyle.textContent = originalStyle.textContent;
|
|
7200
|
+
if (!clonedDoc.head.querySelector('style')) {
|
|
7201
|
+
clonedDoc.head.appendChild(clonedStyle);
|
|
7202
|
+
}
|
|
7203
|
+
}
|
|
7204
|
+
},
|
|
7127
7205
|
// 性能优化
|
|
7128
7206
|
removeContainer: true, // 截图后移除临时容器
|
|
7129
7207
|
imageTimeout: this.options.imageLoadTimeout || 5000,
|
|
@@ -7140,7 +7218,13 @@ class ScreenshotManager {
|
|
|
7140
7218
|
// html2canvas 不支持直接的 proxy 选项,需要通过 onclone 钩子处理图片
|
|
7141
7219
|
// 如果配置了代理服务器,在克隆时替换图片 URL
|
|
7142
7220
|
if (this.options.useProxy && this.options.proxyUrl && this.options.proxyUrl.trim() !== '') {
|
|
7143
|
-
|
|
7221
|
+
// 保存原始的 onclone
|
|
7222
|
+
const originalOnclone = options.onclone;
|
|
7223
|
+
options.onclone = (clonedDoc, clonedElement) => {
|
|
7224
|
+
// 先执行原始的样式处理逻辑
|
|
7225
|
+
if (originalOnclone) {
|
|
7226
|
+
originalOnclone(clonedDoc, clonedElement);
|
|
7227
|
+
}
|
|
7144
7228
|
// 在克隆的文档中,替换所有跨域图片的 src
|
|
7145
7229
|
const images = clonedDoc.querySelectorAll('img');
|
|
7146
7230
|
images.forEach((img) => {
|
|
@@ -7530,37 +7614,49 @@ class ScreenshotManager {
|
|
|
7530
7614
|
throw new Error('无法获取 canvas context');
|
|
7531
7615
|
}
|
|
7532
7616
|
const img = new Image();
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7617
|
+
let convertedDataUrl;
|
|
7618
|
+
try {
|
|
7619
|
+
await new Promise((resolve, reject) => {
|
|
7620
|
+
img.onload = () => {
|
|
7621
|
+
canvas.width = img.width;
|
|
7622
|
+
canvas.height = img.height;
|
|
7623
|
+
ctx.drawImage(img, 0, 0);
|
|
7624
|
+
resolve();
|
|
7625
|
+
};
|
|
7626
|
+
img.onerror = reject;
|
|
7627
|
+
img.src = dataUrl;
|
|
7628
|
+
});
|
|
7629
|
+
let mimeType = 'image/jpeg';
|
|
7630
|
+
// 使用与 createContext 相同的质量设置
|
|
7631
|
+
let conversionQuality = finalQuality;
|
|
7632
|
+
if (this.options.outputFormat === 'webp' && !isMobile) {
|
|
7633
|
+
try {
|
|
7634
|
+
const testCanvas = document.createElement('canvas');
|
|
7635
|
+
testCanvas.width = 1;
|
|
7636
|
+
testCanvas.height = 1;
|
|
7637
|
+
const testDataUrl = testCanvas.toDataURL('image/webp');
|
|
7638
|
+
if (testDataUrl.indexOf('webp') !== -1) {
|
|
7639
|
+
mimeType = 'image/webp';
|
|
7640
|
+
}
|
|
7641
|
+
}
|
|
7642
|
+
catch {
|
|
7643
|
+
mimeType = 'image/jpeg';
|
|
7554
7644
|
}
|
|
7555
7645
|
}
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7646
|
+
// 使用优化后的质量进行格式转换
|
|
7647
|
+
convertedDataUrl = mimeType === 'image/png'
|
|
7648
|
+
? canvas.toDataURL(mimeType)
|
|
7649
|
+
: canvas.toDataURL(mimeType, conversionQuality);
|
|
7650
|
+
}
|
|
7651
|
+
finally {
|
|
7652
|
+
// 清理资源,释放内存
|
|
7653
|
+
img.src = ''; // 清除图片引用
|
|
7654
|
+
img.onload = null;
|
|
7655
|
+
img.onerror = null;
|
|
7656
|
+
canvas.width = 0; // 清空 canvas
|
|
7657
|
+
canvas.height = 0;
|
|
7658
|
+
ctx.clearRect(0, 0, 0, 0); // 清除绘制内容
|
|
7559
7659
|
}
|
|
7560
|
-
// 使用优化后的质量进行格式转换
|
|
7561
|
-
const convertedDataUrl = mimeType === 'image/png'
|
|
7562
|
-
? canvas.toDataURL(mimeType)
|
|
7563
|
-
: canvas.toDataURL(mimeType, conversionQuality);
|
|
7564
7660
|
return convertedDataUrl;
|
|
7565
7661
|
}
|
|
7566
7662
|
return dataUrl;
|
|
@@ -7581,15 +7677,34 @@ class ScreenshotManager {
|
|
|
7581
7677
|
throw error;
|
|
7582
7678
|
}
|
|
7583
7679
|
finally {
|
|
7584
|
-
//
|
|
7680
|
+
// 每次截图后立即清理 context,释放 Worker 和内存
|
|
7681
|
+
// 这是防止内存泄漏的关键步骤
|
|
7585
7682
|
if (this.screenshotContext) {
|
|
7586
7683
|
try {
|
|
7587
7684
|
destroyContext(this.screenshotContext);
|
|
7685
|
+
if (!this.options.silentMode) {
|
|
7686
|
+
console.log('📸 ✅ modern-screenshot context 已清理');
|
|
7687
|
+
}
|
|
7588
7688
|
}
|
|
7589
7689
|
catch (e) {
|
|
7590
|
-
|
|
7690
|
+
if (!this.options.silentMode) {
|
|
7691
|
+
console.warn('📸 ⚠️ 清理 context 失败:', e);
|
|
7692
|
+
}
|
|
7693
|
+
}
|
|
7694
|
+
finally {
|
|
7695
|
+
// 确保 context 引用被清除
|
|
7696
|
+
this.screenshotContext = null;
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7699
|
+
// 强制触发垃圾回收(如果可能)
|
|
7700
|
+
// 注意:这需要浏览器支持,不是所有浏览器都有效
|
|
7701
|
+
if (typeof window !== 'undefined' && window.gc && typeof window.gc === 'function') {
|
|
7702
|
+
try {
|
|
7703
|
+
window.gc();
|
|
7704
|
+
}
|
|
7705
|
+
catch {
|
|
7706
|
+
// 忽略 GC 错误
|
|
7591
7707
|
}
|
|
7592
|
-
this.screenshotContext = null;
|
|
7593
7708
|
}
|
|
7594
7709
|
}
|
|
7595
7710
|
}
|
|
@@ -8164,15 +8279,42 @@ class ScreenshotManager {
|
|
|
8164
8279
|
}
|
|
8165
8280
|
}
|
|
8166
8281
|
/**
|
|
8167
|
-
* 等待 CSS
|
|
8282
|
+
* 等待 CSS 和字体加载完成
|
|
8283
|
+
* html2canvas 需要确保所有样式表都加载完成才能正确截图
|
|
8168
8284
|
*/
|
|
8169
8285
|
async waitForStylesAndFonts() {
|
|
8170
|
-
//
|
|
8171
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8286
|
+
// 等待所有样式表加载完成
|
|
8287
|
+
const styleSheets = Array.from(document.styleSheets);
|
|
8288
|
+
const styleSheetPromises = styleSheets.map((sheet) => {
|
|
8289
|
+
return new Promise((resolve) => {
|
|
8290
|
+
try {
|
|
8291
|
+
// 检查样式表是否已加载
|
|
8292
|
+
// 尝试访问 cssRules,如果成功说明样式表已加载
|
|
8293
|
+
const rules = sheet.cssRules;
|
|
8294
|
+
if (rules) {
|
|
8295
|
+
resolve();
|
|
8296
|
+
}
|
|
8297
|
+
else {
|
|
8298
|
+
// 如果样式表还在加载,等待一下
|
|
8299
|
+
setTimeout(() => resolve(), 100);
|
|
8300
|
+
}
|
|
8301
|
+
}
|
|
8302
|
+
catch (e) {
|
|
8303
|
+
// 跨域样式表可能无法访问,忽略错误
|
|
8174
8304
|
resolve();
|
|
8175
|
-
}
|
|
8305
|
+
}
|
|
8306
|
+
});
|
|
8307
|
+
});
|
|
8308
|
+
await Promise.all(styleSheetPromises);
|
|
8309
|
+
// 使用 requestAnimationFrame 确保 DOM 已渲染
|
|
8310
|
+
await new Promise((resolve) => {
|
|
8311
|
+
requestAnimationFrame(() => {
|
|
8312
|
+
requestAnimationFrame(() => {
|
|
8313
|
+
// 额外等待,确保样式应用完成
|
|
8314
|
+
setTimeout(() => {
|
|
8315
|
+
resolve();
|
|
8316
|
+
}, 100); // 增加到100ms,确保样式加载
|
|
8317
|
+
});
|
|
8176
8318
|
});
|
|
8177
8319
|
});
|
|
8178
8320
|
}
|
|
@@ -8191,6 +8333,75 @@ class ScreenshotManager {
|
|
|
8191
8333
|
// 忽略错误
|
|
8192
8334
|
}
|
|
8193
8335
|
}
|
|
8336
|
+
/**
|
|
8337
|
+
* 等待所有样式表加载完成(html2canvas 专用)
|
|
8338
|
+
* 增强版:确保所有样式表都完全加载
|
|
8339
|
+
*/
|
|
8340
|
+
async waitForAllStylesLoaded() {
|
|
8341
|
+
const styleSheets = Array.from(document.styleSheets);
|
|
8342
|
+
const promises = [];
|
|
8343
|
+
styleSheets.forEach((sheet) => {
|
|
8344
|
+
promises.push(new Promise((resolve) => {
|
|
8345
|
+
try {
|
|
8346
|
+
// 尝试访问样式表规则,如果成功说明样式表已加载
|
|
8347
|
+
const rules = sheet.cssRules;
|
|
8348
|
+
if (rules && rules.length > 0) {
|
|
8349
|
+
// 样式表已加载且有规则
|
|
8350
|
+
resolve();
|
|
8351
|
+
}
|
|
8352
|
+
else if (rules) {
|
|
8353
|
+
// 样式表已加载但可能没有规则(空样式表)
|
|
8354
|
+
resolve();
|
|
8355
|
+
}
|
|
8356
|
+
else {
|
|
8357
|
+
// 等待样式表加载
|
|
8358
|
+
let checkCount = 0;
|
|
8359
|
+
const maxChecks = 100; // 最多检查100次(5秒)
|
|
8360
|
+
const checkInterval = setInterval(() => {
|
|
8361
|
+
checkCount++;
|
|
8362
|
+
try {
|
|
8363
|
+
const currentRules = sheet.cssRules;
|
|
8364
|
+
if (currentRules) {
|
|
8365
|
+
clearInterval(checkInterval);
|
|
8366
|
+
resolve();
|
|
8367
|
+
}
|
|
8368
|
+
else if (checkCount >= maxChecks) {
|
|
8369
|
+
// 超时,可能样式表无法访问
|
|
8370
|
+
clearInterval(checkInterval);
|
|
8371
|
+
resolve();
|
|
8372
|
+
}
|
|
8373
|
+
}
|
|
8374
|
+
catch {
|
|
8375
|
+
// 跨域样式表无法访问,直接 resolve
|
|
8376
|
+
clearInterval(checkInterval);
|
|
8377
|
+
resolve();
|
|
8378
|
+
}
|
|
8379
|
+
}, 50);
|
|
8380
|
+
// 超时保护(5秒)
|
|
8381
|
+
setTimeout(() => {
|
|
8382
|
+
clearInterval(checkInterval);
|
|
8383
|
+
resolve();
|
|
8384
|
+
}, 5000);
|
|
8385
|
+
}
|
|
8386
|
+
}
|
|
8387
|
+
catch (e) {
|
|
8388
|
+
// 跨域样式表无法访问,直接 resolve
|
|
8389
|
+
resolve();
|
|
8390
|
+
}
|
|
8391
|
+
}));
|
|
8392
|
+
});
|
|
8393
|
+
await Promise.all(promises);
|
|
8394
|
+
// 额外等待,确保样式应用(增加到300ms,确保样式完全应用)
|
|
8395
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
8396
|
+
// 再次使用 requestAnimationFrame 确保渲染完成
|
|
8397
|
+
await new Promise(resolve => {
|
|
8398
|
+
requestAnimationFrame(() => {
|
|
8399
|
+
requestAnimationFrame(() => {
|
|
8400
|
+
setTimeout(() => resolve(), 100);
|
|
8401
|
+
});
|
|
8402
|
+
});
|
|
8403
|
+
});
|
|
8404
|
+
}
|
|
8194
8405
|
/**
|
|
8195
8406
|
* 计算压缩后的尺寸
|
|
8196
8407
|
*/
|
|
@@ -8407,6 +8618,11 @@ class ScreenshotManager {
|
|
|
8407
8618
|
}
|
|
8408
8619
|
// 清理图片代理缓存
|
|
8409
8620
|
this.imageProxyCache.clear();
|
|
8621
|
+
// 清理截图历史记录(释放大量内存)
|
|
8622
|
+
this.screenshotHistory.length = 0;
|
|
8623
|
+
// 清理图片下载队列
|
|
8624
|
+
this.imageDownloadQueue.clear();
|
|
8625
|
+
this.activeDownloads.clear();
|
|
8410
8626
|
}
|
|
8411
8627
|
/**
|
|
8412
8628
|
* 获取缓存的图片(检查是否过期)
|
|
@@ -8428,8 +8644,34 @@ class ScreenshotManager {
|
|
|
8428
8644
|
}
|
|
8429
8645
|
/**
|
|
8430
8646
|
* 设置缓存的图片(带时间戳)
|
|
8647
|
+
* 添加内存大小限制,防止缓存无限增长
|
|
8431
8648
|
*/
|
|
8432
8649
|
setCachedImage(url, dataUrl) {
|
|
8650
|
+
// 估算当前缓存总大小(MB)
|
|
8651
|
+
let totalSizeMB = 0;
|
|
8652
|
+
this.imageProxyCache.forEach((cached) => {
|
|
8653
|
+
totalSizeMB += cached.dataUrl.length * 0.75 / (1024 * 1024); // base64 转字节再转MB
|
|
8654
|
+
});
|
|
8655
|
+
// 添加新项的大小
|
|
8656
|
+
const newItemSizeMB = dataUrl.length * 0.75 / (1024 * 1024);
|
|
8657
|
+
const maxCacheSizeMB = 100; // 最大100MB内存缓存
|
|
8658
|
+
// 如果超过限制,清理最旧的缓存
|
|
8659
|
+
if (totalSizeMB + newItemSizeMB > maxCacheSizeMB) {
|
|
8660
|
+
const sortedEntries = Array.from(this.imageProxyCache.entries())
|
|
8661
|
+
.sort((a, b) => a[1].timestamp - b[1].timestamp); // 按时间排序
|
|
8662
|
+
let currentSizeMB = totalSizeMB;
|
|
8663
|
+
for (const [key, value] of sortedEntries) {
|
|
8664
|
+
if (currentSizeMB + newItemSizeMB <= maxCacheSizeMB) {
|
|
8665
|
+
break;
|
|
8666
|
+
}
|
|
8667
|
+
const itemSizeMB = value.dataUrl.length * 0.75 / (1024 * 1024);
|
|
8668
|
+
this.imageProxyCache.delete(key);
|
|
8669
|
+
currentSizeMB -= itemSizeMB;
|
|
8670
|
+
if (!this.options.silentMode) {
|
|
8671
|
+
console.log(`📸 清理内存缓存(超过限制): ${key.substring(0, 50)}...`);
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
}
|
|
8433
8675
|
this.imageProxyCache.set(url, {
|
|
8434
8676
|
dataUrl,
|
|
8435
8677
|
timestamp: Date.now()
|
|
@@ -8456,9 +8698,10 @@ class ScreenshotManager {
|
|
|
8456
8698
|
}
|
|
8457
8699
|
/**
|
|
8458
8700
|
* 定期清理过期缓存(可选,在截图时也会自动清理)
|
|
8701
|
+
* 增加清理频率,防止内存积累
|
|
8459
8702
|
*/
|
|
8460
8703
|
startCacheCleanup() {
|
|
8461
|
-
// 每5
|
|
8704
|
+
// 每2分钟清理一次过期缓存(从5分钟改为2分钟,更频繁)
|
|
8462
8705
|
setInterval(() => {
|
|
8463
8706
|
this.cleanExpiredCache();
|
|
8464
8707
|
// 如果启用 IndexedDB,也清理 IndexedDB 缓存
|
|
@@ -8469,9 +8712,12 @@ class ScreenshotManager {
|
|
|
8469
8712
|
}
|
|
8470
8713
|
if (!this.options.silentMode) {
|
|
8471
8714
|
const memoryCacheSize = this.imageProxyCache.size;
|
|
8472
|
-
|
|
8715
|
+
const memoryCacheSizeMB = Array.from(this.imageProxyCache.values())
|
|
8716
|
+
.reduce((sum, cached) => sum + cached.dataUrl.length * 0.75 / (1024 * 1024), 0);
|
|
8717
|
+
const historySizeMB = this.screenshotHistory.reduce((sum, item) => sum + item.length * 0.75 / (1024 * 1024), 0);
|
|
8718
|
+
console.log(`📸 清理过期缓存,内存缓存: ${memoryCacheSize} 项,${memoryCacheSizeMB.toFixed(2)} MB,历史记录: ${historySizeMB.toFixed(2)} MB`);
|
|
8473
8719
|
}
|
|
8474
|
-
},
|
|
8720
|
+
}, 120000); // 2分钟(从5分钟改为2分钟,更频繁清理)
|
|
8475
8721
|
}
|
|
8476
8722
|
/**
|
|
8477
8723
|
* 获取状态
|