customer-chat-sdk 1.0.25 → 1.0.26
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 +4 -3
- package/dist/core/ScreenshotManager.d.ts.map +1 -1
- package/dist/customer-sdk.cjs.js +213 -86
- package/dist/customer-sdk.esm.js +213 -86
- package/dist/customer-sdk.min.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ export interface ScreenshotOptions {
|
|
|
12
12
|
outputFormat?: 'webp' | 'jpeg' | 'png';
|
|
13
13
|
enableCORS?: boolean;
|
|
14
14
|
proxyUrl?: string;
|
|
15
|
-
engine?: '
|
|
15
|
+
engine?: 'modern-screenshot';
|
|
16
16
|
corsMode?: 'simple' | 'smart' | 'proxy' | 'blob' | 'canvas-proxy' | 'ignore' | 'test-first';
|
|
17
17
|
silentMode?: boolean;
|
|
18
18
|
maxRetries?: number;
|
|
@@ -47,6 +47,7 @@ export declare class ScreenshotManager {
|
|
|
47
47
|
private currentUploadConfig;
|
|
48
48
|
private worker;
|
|
49
49
|
private screenshotTimer;
|
|
50
|
+
private screenshotContext;
|
|
50
51
|
private messageHandler;
|
|
51
52
|
private dynamicInterval;
|
|
52
53
|
private expirationTimer;
|
|
@@ -95,9 +96,9 @@ export declare class ScreenshotManager {
|
|
|
95
96
|
*/
|
|
96
97
|
private takeScreenshot;
|
|
97
98
|
/**
|
|
98
|
-
* 使用
|
|
99
|
+
* 使用 modern-screenshot 截图(启用 Worker)
|
|
99
100
|
*/
|
|
100
|
-
private
|
|
101
|
+
private takeScreenshotWithModernScreenshot;
|
|
101
102
|
/**
|
|
102
103
|
* 预处理网络图片
|
|
103
104
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"
|
|
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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var modernScreenshot = require('modern-screenshot');
|
|
6
6
|
|
|
7
7
|
// 直接使用base64字符串,避免打包后路径问题
|
|
8
8
|
const iconImage = 'data:image/png;base64,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';
|
|
@@ -925,7 +925,29 @@ class IframeManager {
|
|
|
925
925
|
}
|
|
926
926
|
}
|
|
927
927
|
|
|
928
|
-
// @ts-ignore -
|
|
928
|
+
// @ts-ignore - modern-screenshot may not have type definitions
|
|
929
|
+
// Worker URL 将在创建上下文时动态获取
|
|
930
|
+
// 在 Vite 环境中可以使用: import workerUrl from 'modern-screenshot/worker?url'
|
|
931
|
+
// 在 Rollup 中,modern-screenshot 会自动处理 worker URL
|
|
932
|
+
let workerUrl = undefined;
|
|
933
|
+
// 尝试动态获取 worker URL(仅在支持的环境中)
|
|
934
|
+
async function getWorkerUrl() {
|
|
935
|
+
if (workerUrl) {
|
|
936
|
+
return workerUrl;
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
// 尝试使用 Vite 的 ?url 语法(仅在 Vite 环境中有效)
|
|
940
|
+
// @ts-ignore - Vite 特有的 ?url 语法
|
|
941
|
+
const workerModule = await import('modern-screenshot/worker?url');
|
|
942
|
+
workerUrl = workerModule.default || workerModule;
|
|
943
|
+
return workerUrl;
|
|
944
|
+
}
|
|
945
|
+
catch {
|
|
946
|
+
// Rollup 或其他构建工具不支持 ?url 语法
|
|
947
|
+
// modern-screenshot 会自动处理 worker URL,返回 undefined 即可
|
|
948
|
+
return undefined;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
929
951
|
/**
|
|
930
952
|
* 截图管理器
|
|
931
953
|
* 负责页面截图、压缩和上传功能
|
|
@@ -947,6 +969,8 @@ class ScreenshotManager {
|
|
|
947
969
|
// WebWorker 相关
|
|
948
970
|
this.worker = null;
|
|
949
971
|
this.screenshotTimer = null;
|
|
972
|
+
// modern-screenshot Worker 上下文(用于复用,避免频繁创建和销毁)
|
|
973
|
+
this.screenshotContext = null;
|
|
950
974
|
// PostMessage 监听器
|
|
951
975
|
this.messageHandler = null;
|
|
952
976
|
// 动态轮询间隔(由 iframe 消息控制)
|
|
@@ -970,7 +994,7 @@ class ScreenshotManager {
|
|
|
970
994
|
outputFormat: options.outputFormat ?? 'webp',
|
|
971
995
|
enableCORS: options.enableCORS ?? true,
|
|
972
996
|
proxyUrl: options.proxyUrl ?? '',
|
|
973
|
-
engine: options.engine ?? '
|
|
997
|
+
engine: options.engine ?? 'modern-screenshot',
|
|
974
998
|
corsMode: options.corsMode ?? 'canvas-proxy',
|
|
975
999
|
silentMode: options.silentMode ?? false,
|
|
976
1000
|
maxRetries: options.maxRetries ?? 2
|
|
@@ -982,6 +1006,16 @@ class ScreenshotManager {
|
|
|
982
1006
|
* 设置目标元素
|
|
983
1007
|
*/
|
|
984
1008
|
setTargetElement(element) {
|
|
1009
|
+
// 如果元素改变了,清理旧的 Worker 上下文
|
|
1010
|
+
if (this.targetElement !== element && this.screenshotContext) {
|
|
1011
|
+
try {
|
|
1012
|
+
modernScreenshot.destroyContext(this.screenshotContext);
|
|
1013
|
+
}
|
|
1014
|
+
catch (e) {
|
|
1015
|
+
// 忽略清理错误
|
|
1016
|
+
}
|
|
1017
|
+
this.screenshotContext = null;
|
|
1018
|
+
}
|
|
985
1019
|
this.targetElement = element;
|
|
986
1020
|
}
|
|
987
1021
|
/**
|
|
@@ -1270,10 +1304,10 @@ class ScreenshotManager {
|
|
|
1270
1304
|
this.waitForStylesAndFonts(),
|
|
1271
1305
|
this.waitForFonts()
|
|
1272
1306
|
]);
|
|
1273
|
-
// 选择截图引擎(仅支持
|
|
1274
|
-
const selectedEngine = '
|
|
1307
|
+
// 选择截图引擎(仅支持 modern-screenshot)
|
|
1308
|
+
const selectedEngine = 'modern-screenshot';
|
|
1275
1309
|
if (!this.options.silentMode) {
|
|
1276
|
-
console.log(`📸 使用截图引擎: ${selectedEngine}`);
|
|
1310
|
+
console.log(`📸 使用截图引擎: ${selectedEngine} (Worker 模式)`);
|
|
1277
1311
|
}
|
|
1278
1312
|
// 预处理网络图片
|
|
1279
1313
|
if (this.options.enableCORS) {
|
|
@@ -1283,8 +1317,8 @@ class ScreenshotManager {
|
|
|
1283
1317
|
let dataUrl;
|
|
1284
1318
|
// 等待一小段时间,确保 DOM 更新完成
|
|
1285
1319
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
1286
|
-
// 使用
|
|
1287
|
-
dataUrl = await this.
|
|
1320
|
+
// 使用 modern-screenshot 截图(启用 Worker)
|
|
1321
|
+
dataUrl = await this.takeScreenshotWithModernScreenshot(this.targetElement);
|
|
1288
1322
|
const timestamp = Date.now();
|
|
1289
1323
|
// 更新状态
|
|
1290
1324
|
this.screenshotCount++;
|
|
@@ -1349,11 +1383,11 @@ class ScreenshotManager {
|
|
|
1349
1383
|
}
|
|
1350
1384
|
}
|
|
1351
1385
|
/**
|
|
1352
|
-
* 使用
|
|
1386
|
+
* 使用 modern-screenshot 截图(启用 Worker)
|
|
1353
1387
|
*/
|
|
1354
|
-
async
|
|
1388
|
+
async takeScreenshotWithModernScreenshot(element) {
|
|
1355
1389
|
if (!this.options.silentMode) {
|
|
1356
|
-
console.log('📸 使用
|
|
1390
|
+
console.log('📸 使用 modern-screenshot 引擎截图(Worker 模式)...');
|
|
1357
1391
|
}
|
|
1358
1392
|
const { width, height } = this.calculateCompressedSize(element.scrollWidth, element.scrollHeight, this.options.maxWidth, this.options.maxHeight);
|
|
1359
1393
|
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
@@ -1361,85 +1395,164 @@ class ScreenshotManager {
|
|
|
1361
1395
|
const mobileQuality = isMobile || isLowEndDevice ? Math.max(this.options.quality * 0.7, 0.3) : this.options.quality;
|
|
1362
1396
|
const mobileWidth = isMobile ? Math.min(width, 1280) : width;
|
|
1363
1397
|
const mobileHeight = isMobile ? Math.min(height, 720) : height;
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
height: mobileHeight,
|
|
1372
|
-
scrollX: 0,
|
|
1373
|
-
scrollY: 0,
|
|
1374
|
-
removeContainer: false,
|
|
1375
|
-
foreignObjectRendering: false,
|
|
1376
|
-
onclone: (clonedDoc) => {
|
|
1377
|
-
const clonedImages = clonedDoc.querySelectorAll('img');
|
|
1378
|
-
clonedImages.forEach((img) => {
|
|
1379
|
-
if (img.src && !img.src.startsWith('data:') && !img.src.startsWith('blob:')) {
|
|
1380
|
-
try {
|
|
1381
|
-
const imgUrl = new URL(img.src, window.location.href);
|
|
1382
|
-
const currentOrigin = window.location.origin;
|
|
1383
|
-
if (this.options.proxyUrl && img.src.includes(this.options.proxyUrl.split('/api/image-proxy')[0])) {
|
|
1384
|
-
return;
|
|
1385
|
-
}
|
|
1386
|
-
if (imgUrl.origin !== currentOrigin) {
|
|
1387
|
-
if (this.options.corsMode === 'canvas-proxy') {
|
|
1388
|
-
const cachedDataUrl = this.imageProxyCache.get(img.src);
|
|
1389
|
-
if (cachedDataUrl) {
|
|
1390
|
-
img.src = cachedDataUrl;
|
|
1391
|
-
}
|
|
1392
|
-
else if (img.crossOrigin) {
|
|
1393
|
-
img.removeAttribute('crossOrigin');
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
else if (!img.crossOrigin) {
|
|
1397
|
-
img.crossOrigin = 'anonymous';
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
catch (e) {
|
|
1402
|
-
// URL 解析失败,跳过
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
});
|
|
1406
|
-
},
|
|
1407
|
-
ignoreElements: (element) => {
|
|
1408
|
-
const htmlElement = element;
|
|
1409
|
-
return element.classList.contains('van-popup') ||
|
|
1410
|
-
element.classList.contains('van-overlay') ||
|
|
1411
|
-
element.classList.contains('van-toast') ||
|
|
1412
|
-
element.classList.contains('van-dialog') ||
|
|
1413
|
-
element.classList.contains('van-loading') ||
|
|
1414
|
-
htmlElement.style.display === 'none' ||
|
|
1415
|
-
htmlElement.style.visibility === 'hidden';
|
|
1416
|
-
},
|
|
1417
|
-
imageTimeout: 15000
|
|
1418
|
-
});
|
|
1419
|
-
let mimeType = 'image/jpeg';
|
|
1420
|
-
let finalQuality = mobileQuality;
|
|
1421
|
-
if (this.options.outputFormat === 'webp' && !isMobile) {
|
|
1398
|
+
// 处理跨域图片的函数
|
|
1399
|
+
const handleCrossOriginImage = async (url) => {
|
|
1400
|
+
// 如果是 data URL 或 blob URL,直接返回
|
|
1401
|
+
if (url.startsWith('data:') || url.startsWith('blob:')) {
|
|
1402
|
+
return url;
|
|
1403
|
+
}
|
|
1404
|
+
// 如果是同源图片,直接返回
|
|
1422
1405
|
try {
|
|
1423
|
-
const
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
const testDataUrl = testCanvas.toDataURL('image/webp');
|
|
1427
|
-
if (testDataUrl.indexOf('webp') !== -1) {
|
|
1428
|
-
mimeType = 'image/webp';
|
|
1406
|
+
const imgUrl = new URL(url, window.location.href);
|
|
1407
|
+
if (imgUrl.origin === window.location.origin) {
|
|
1408
|
+
return url;
|
|
1429
1409
|
}
|
|
1430
1410
|
}
|
|
1431
|
-
catch {
|
|
1432
|
-
|
|
1411
|
+
catch (e) {
|
|
1412
|
+
// URL 解析失败,继续处理
|
|
1413
|
+
}
|
|
1414
|
+
// 如果配置了代理服务器,使用代理处理跨域图片
|
|
1415
|
+
if (this.options.proxyUrl && this.options.proxyUrl.trim() !== '') {
|
|
1416
|
+
// 检查缓存
|
|
1417
|
+
if (this.imageProxyCache.has(url)) {
|
|
1418
|
+
return this.imageProxyCache.get(url);
|
|
1419
|
+
}
|
|
1420
|
+
try {
|
|
1421
|
+
// 构建代理请求参数
|
|
1422
|
+
const params = new URLSearchParams({
|
|
1423
|
+
url: url,
|
|
1424
|
+
maxWidth: String(this.options.maxWidth || 1600),
|
|
1425
|
+
maxHeight: String(this.options.maxHeight || 900),
|
|
1426
|
+
quality: String(Math.round((this.options.quality || 0.4) * 100)),
|
|
1427
|
+
format: this.options.outputFormat || 'webp'
|
|
1428
|
+
});
|
|
1429
|
+
let baseUrl = this.options.proxyUrl;
|
|
1430
|
+
baseUrl = baseUrl.replace(/[?&]$/, '');
|
|
1431
|
+
const proxyUrl = `${baseUrl}?${params.toString()}`;
|
|
1432
|
+
// 请求代理服务器
|
|
1433
|
+
const response = await fetch(proxyUrl, {
|
|
1434
|
+
method: 'GET',
|
|
1435
|
+
mode: 'cors',
|
|
1436
|
+
credentials: 'omit',
|
|
1437
|
+
headers: {
|
|
1438
|
+
'Accept': 'image/*'
|
|
1439
|
+
},
|
|
1440
|
+
cache: 'no-cache'
|
|
1441
|
+
});
|
|
1442
|
+
if (!response.ok) {
|
|
1443
|
+
throw new Error(`代理请求失败: ${response.status}`);
|
|
1444
|
+
}
|
|
1445
|
+
const blob = await response.blob();
|
|
1446
|
+
const dataUrl = await this.blobToDataUrl(blob);
|
|
1447
|
+
// 缓存结果
|
|
1448
|
+
this.imageProxyCache.set(url, dataUrl);
|
|
1449
|
+
return dataUrl;
|
|
1450
|
+
}
|
|
1451
|
+
catch (error) {
|
|
1452
|
+
if (!this.options.silentMode) {
|
|
1453
|
+
console.warn(`📸 代理处理图片失败: ${url.substring(0, 100)}...`, error);
|
|
1454
|
+
}
|
|
1455
|
+
// 失败时返回原 URL
|
|
1456
|
+
return url;
|
|
1457
|
+
}
|
|
1433
1458
|
}
|
|
1459
|
+
// 如果没有配置代理,尝试使用 CORS
|
|
1460
|
+
if (this.options.enableCORS) {
|
|
1461
|
+
return url;
|
|
1462
|
+
}
|
|
1463
|
+
// 默认返回原 URL
|
|
1464
|
+
return url;
|
|
1465
|
+
};
|
|
1466
|
+
// 如果还没有创建 Worker 上下文,则创建
|
|
1467
|
+
if (!this.screenshotContext) {
|
|
1468
|
+
const workerNumber = isMobile || isLowEndDevice ? 1 : 2;
|
|
1469
|
+
// 构建 createContext 配置
|
|
1470
|
+
const contextOptions = {
|
|
1471
|
+
workerNumber,
|
|
1472
|
+
quality: mobileQuality,
|
|
1473
|
+
fetchFn: handleCrossOriginImage, // 使用代理服务器处理跨域图片
|
|
1474
|
+
fetch: {
|
|
1475
|
+
requestInit: {
|
|
1476
|
+
cache: 'no-cache',
|
|
1477
|
+
},
|
|
1478
|
+
bypassingCache: true,
|
|
1479
|
+
},
|
|
1480
|
+
};
|
|
1481
|
+
// 如果指定了尺寸,添加尺寸配置
|
|
1482
|
+
if (mobileWidth && mobileHeight) {
|
|
1483
|
+
contextOptions.width = mobileWidth;
|
|
1484
|
+
contextOptions.height = mobileHeight;
|
|
1485
|
+
}
|
|
1486
|
+
// 如果指定了缩放比例,添加缩放配置
|
|
1487
|
+
if (this.options.scale !== 1) {
|
|
1488
|
+
contextOptions.scale = isMobile ? 0.8 : this.options.scale;
|
|
1489
|
+
}
|
|
1490
|
+
// 尝试设置 workerUrl(如果可用)
|
|
1491
|
+
const resolvedWorkerUrl = await getWorkerUrl();
|
|
1492
|
+
if (resolvedWorkerUrl) {
|
|
1493
|
+
contextOptions.workerUrl = resolvedWorkerUrl;
|
|
1494
|
+
}
|
|
1495
|
+
// 创建 Worker 上下文
|
|
1496
|
+
this.screenshotContext = await modernScreenshot.createContext(element, contextOptions);
|
|
1497
|
+
}
|
|
1498
|
+
try {
|
|
1499
|
+
// 使用 Worker 上下文进行截图
|
|
1500
|
+
const dataUrl = await modernScreenshot.domToPng(this.screenshotContext);
|
|
1501
|
+
// 根据输出格式转换
|
|
1502
|
+
if (this.options.outputFormat !== 'png') {
|
|
1503
|
+
// modern-screenshot 默认输出 PNG,如果需要其他格式,需要转换
|
|
1504
|
+
const canvas = document.createElement('canvas');
|
|
1505
|
+
const ctx = canvas.getContext('2d');
|
|
1506
|
+
if (!ctx) {
|
|
1507
|
+
throw new Error('无法获取 canvas context');
|
|
1508
|
+
}
|
|
1509
|
+
const img = new Image();
|
|
1510
|
+
await new Promise((resolve, reject) => {
|
|
1511
|
+
img.onload = () => {
|
|
1512
|
+
canvas.width = img.width;
|
|
1513
|
+
canvas.height = img.height;
|
|
1514
|
+
ctx.drawImage(img, 0, 0);
|
|
1515
|
+
resolve();
|
|
1516
|
+
};
|
|
1517
|
+
img.onerror = reject;
|
|
1518
|
+
img.src = dataUrl;
|
|
1519
|
+
});
|
|
1520
|
+
let mimeType = 'image/jpeg';
|
|
1521
|
+
let finalQuality = mobileQuality;
|
|
1522
|
+
if (this.options.outputFormat === 'webp' && !isMobile) {
|
|
1523
|
+
try {
|
|
1524
|
+
const testCanvas = document.createElement('canvas');
|
|
1525
|
+
testCanvas.width = 1;
|
|
1526
|
+
testCanvas.height = 1;
|
|
1527
|
+
const testDataUrl = testCanvas.toDataURL('image/webp');
|
|
1528
|
+
if (testDataUrl.indexOf('webp') !== -1) {
|
|
1529
|
+
mimeType = 'image/webp';
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
catch {
|
|
1533
|
+
mimeType = 'image/jpeg';
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
const convertedDataUrl = mimeType === 'image/png'
|
|
1537
|
+
? canvas.toDataURL(mimeType)
|
|
1538
|
+
: canvas.toDataURL(mimeType, finalQuality);
|
|
1539
|
+
return convertedDataUrl;
|
|
1540
|
+
}
|
|
1541
|
+
return dataUrl;
|
|
1434
1542
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1543
|
+
catch (error) {
|
|
1544
|
+
// 如果截图失败,清理上下文以便下次重新创建
|
|
1545
|
+
if (this.screenshotContext) {
|
|
1546
|
+
try {
|
|
1547
|
+
modernScreenshot.destroyContext(this.screenshotContext);
|
|
1548
|
+
}
|
|
1549
|
+
catch (e) {
|
|
1550
|
+
// 忽略清理错误
|
|
1551
|
+
}
|
|
1552
|
+
this.screenshotContext = null;
|
|
1553
|
+
}
|
|
1554
|
+
throw error;
|
|
1438
1555
|
}
|
|
1439
|
-
const dataUrl = mimeType === 'image/png'
|
|
1440
|
-
? canvas.toDataURL(mimeType)
|
|
1441
|
-
: canvas.toDataURL(mimeType, finalQuality);
|
|
1442
|
-
return dataUrl;
|
|
1443
1556
|
}
|
|
1444
1557
|
/**
|
|
1445
1558
|
* 预处理网络图片
|
|
@@ -1552,7 +1665,7 @@ class ScreenshotManager {
|
|
|
1552
1665
|
throw new Error(`代理请求失败: ${response.status} ${response.statusText}${errorText ? ` - ${errorText.substring(0, 200)}` : ''}`);
|
|
1553
1666
|
}
|
|
1554
1667
|
const blob = await response.blob();
|
|
1555
|
-
// 将 blob 转换为 data URL(用于
|
|
1668
|
+
// 将 blob 转换为 data URL(用于 modern-screenshot 兼容性)
|
|
1556
1669
|
const dataUrl = await this.blobToDataUrl(blob);
|
|
1557
1670
|
if (!this.options.silentMode) {
|
|
1558
1671
|
console.log(`📸 ✅ 代理模式成功(已转换为 data URL): ${imageUrl.substring(0, 100)}...`);
|
|
@@ -1682,7 +1795,9 @@ class ScreenshotManager {
|
|
|
1682
1795
|
newWorker.onerror = (e) => {
|
|
1683
1796
|
console.error('📸 WebWorker 错误:', e);
|
|
1684
1797
|
};
|
|
1685
|
-
URL
|
|
1798
|
+
// 注意:不要立即 revokeObjectURL,因为 Worker 需要这个 URL 保持有效
|
|
1799
|
+
// 在 destroy() 方法中清理 Worker 时再 revoke
|
|
1800
|
+
// URL.revokeObjectURL(workerUrl) // 已移除,在 destroy 时清理
|
|
1686
1801
|
return newWorker;
|
|
1687
1802
|
}
|
|
1688
1803
|
catch (err) {
|
|
@@ -1813,6 +1928,16 @@ class ScreenshotManager {
|
|
|
1813
1928
|
this.worker.terminate();
|
|
1814
1929
|
this.worker = null;
|
|
1815
1930
|
}
|
|
1931
|
+
// 清理 modern-screenshot Worker 上下文
|
|
1932
|
+
if (this.screenshotContext) {
|
|
1933
|
+
try {
|
|
1934
|
+
modernScreenshot.destroyContext(this.screenshotContext);
|
|
1935
|
+
}
|
|
1936
|
+
catch (e) {
|
|
1937
|
+
// 忽略清理错误
|
|
1938
|
+
}
|
|
1939
|
+
this.screenshotContext = null;
|
|
1940
|
+
}
|
|
1816
1941
|
if (this.expirationTimer) {
|
|
1817
1942
|
clearTimeout(this.expirationTimer);
|
|
1818
1943
|
this.expirationTimer = null;
|
|
@@ -1822,6 +1947,8 @@ class ScreenshotManager {
|
|
|
1822
1947
|
this.messageHandler = null;
|
|
1823
1948
|
}
|
|
1824
1949
|
this.removeGlobalErrorHandlers();
|
|
1950
|
+
// 清理图片代理缓存
|
|
1951
|
+
this.imageProxyCache.clear();
|
|
1825
1952
|
}
|
|
1826
1953
|
/**
|
|
1827
1954
|
* 获取状态
|