customer-chat-sdk 1.0.62 → 1.0.64

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.
@@ -286,8 +286,20 @@ export declare class ScreenshotManager {
286
286
  */
287
287
  private dataUrlToArrayBuffer;
288
288
  /**
289
- * 构建二进制结构(按顺序:sign[14], type[1], topicLength[1], topic[topicLength], routingKey[8])
290
- * 20 + 1 + 8 + 8 = 37 字节
289
+ * ArrayBuffer 转换为 base64 字符串
290
+ */
291
+ private arrayBufferToBase64;
292
+ /**
293
+ * 构建二进制结构(按顺序:sign[20], type[1], topicLength[1], topic[topicLength], routingKeyLength[1], routingKey[routingKeyLength])
294
+ * 总大小 = 20 + 1 + 1 + topicLength + 1 + routingKeyLength(动态计算)
295
+ *
296
+ * 与服务端格式对应:
297
+ * - sign: 20字节 (CharSequence, UTF-8)
298
+ * - type: 1字节 (转为无符号)
299
+ * - topicLength: 1字节
300
+ * - topic: topicLength字节 (CharSequence, UTF-8)
301
+ * - routingKeyLength: 1字节
302
+ * - routingKey: routingKeyLength字节 (CharSequence, UTF-8)
291
303
  */
292
304
  private buildBinaryConfig;
293
305
  /**
@@ -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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"}
@@ -14992,6 +14992,10 @@ class ScreenshotManager {
14992
14992
  if (!this.options.silentMode) {
14993
14993
  console.log('📸 使用 snapdom 引擎截图...');
14994
14994
  }
14995
+ // 限制只截图可见区域(viewport),避免截图不可见区域
14996
+ // 注意:snapdom 可能不支持直接限制可见区域,所以这里只是添加警告
14997
+ let targetElement = element;
14998
+ let tempContainer = null;
14995
14999
  try {
14996
15000
  // 检查元素是否存在和可见
14997
15001
  const rect = element.getBoundingClientRect();
@@ -15000,6 +15004,53 @@ class ScreenshotManager {
15000
15004
  }
15001
15005
  // 构建 snapdom 选项
15002
15006
  const options = {};
15007
+ if (element === document.body || element === document.documentElement) {
15008
+ // 对于 body 或 html 元素,创建一个临时容器,只包含可见区域
15009
+ // 使用 html2canvas 的方式:创建一个固定大小的容器,只包含可见内容
15010
+ tempContainer = document.createElement('div');
15011
+ tempContainer.style.position = 'fixed';
15012
+ tempContainer.style.top = '0';
15013
+ tempContainer.style.left = '0';
15014
+ tempContainer.style.width = `${window.innerWidth}px`;
15015
+ tempContainer.style.height = `${window.innerHeight}px`;
15016
+ tempContainer.style.overflow = 'hidden';
15017
+ tempContainer.style.zIndex = '999999';
15018
+ tempContainer.style.pointerEvents = 'none';
15019
+ tempContainer.style.backgroundColor = window.getComputedStyle(document.body).backgroundColor || 'white';
15020
+ // 创建一个包装器,只包含可见区域的内容
15021
+ // 注意:这里不克隆整个 body,而是直接使用 body 作为父元素
15022
+ // 但限制容器的尺寸为可见区域
15023
+ document.body.appendChild(tempContainer);
15024
+ // 使用 body 作为目标,但通过容器限制范围
15025
+ // 实际上,我们需要直接截图 body,但限制尺寸
15026
+ // 由于 snapdom 可能不支持直接限制尺寸,我们使用一个更简单的方法:
15027
+ // 直接截图 body,但通过 CSS 限制其显示范围(但这会影响页面)
15028
+ // 更好的方法是:使用 html2canvas 的方式,创建一个 canvas 并只绘制可见区域
15029
+ // 简化方案:直接使用 body,但添加尺寸限制选项(如果 snapdom 支持)
15030
+ // 如果不支持,则只能截图整个 body
15031
+ targetElement = element;
15032
+ if (!this.options.silentMode) {
15033
+ console.log(`📸 注意:snapdom 将截图整个 body,建议使用 html2canvas 或 modern-screenshot 来限制可见区域`);
15034
+ console.log(`📸 可见区域尺寸: ${window.innerWidth}x${window.innerHeight}`);
15035
+ }
15036
+ // 清理临时容器(未使用)
15037
+ if (tempContainer && tempContainer.parentNode) {
15038
+ tempContainer.parentNode.removeChild(tempContainer);
15039
+ }
15040
+ tempContainer = null;
15041
+ }
15042
+ else {
15043
+ // 对于其他元素,检查是否完全可见
15044
+ const elementRect = element.getBoundingClientRect();
15045
+ const isFullyVisible = elementRect.top >= 0 &&
15046
+ elementRect.left >= 0 &&
15047
+ elementRect.bottom <= window.innerHeight &&
15048
+ elementRect.right <= window.innerWidth;
15049
+ if (!isFullyVisible && !this.options.silentMode) {
15050
+ console.warn(`📸 ⚠️ 元素部分不可见,snapdom 可能会截图整个元素(包括不可见部分)`);
15051
+ console.warn(`📸 建议:使用 html2canvas 或 modern-screenshot 来限制可见区域`);
15052
+ }
15053
+ }
15003
15054
  // 如果配置了代理服务器,使用 useProxy 选项处理跨域图片和字体
15004
15055
  // 参考: https://github.com/zumerlab/snapdom/blob/main/README_CN.md#跨域图片和字体-useproxy
15005
15056
  // 只有当 useProxy 为 true 且 proxyUrl 存在时才使用代理
@@ -15034,16 +15085,16 @@ class ScreenshotManager {
15034
15085
  switch (outputFormat) {
15035
15086
  case 'jpeg':
15036
15087
  // 使用 toJpg 快捷方法(snapdom 使用 jpg 作为方法名)
15037
- img = await snapdom.toJpg(element, options);
15088
+ img = await snapdom.toJpg(targetElement, options);
15038
15089
  break;
15039
15090
  case 'webp':
15040
15091
  // 使用 toWebp 快捷方法
15041
- img = await snapdom.toWebp(element, options);
15092
+ img = await snapdom.toWebp(targetElement, options);
15042
15093
  break;
15043
15094
  case 'png':
15044
15095
  default:
15045
15096
  // 使用 toPng 快捷方法(默认)
15046
- img = await snapdom.toPng(element, options);
15097
+ img = await snapdom.toPng(targetElement, options);
15047
15098
  break;
15048
15099
  }
15049
15100
  // 获取 base64 数据(HTMLImageElement 的 src 属性包含 data URL)
@@ -15052,12 +15103,28 @@ class ScreenshotManager {
15052
15103
  if (!dataUrl || dataUrl.length < 100) {
15053
15104
  throw new Error('生成的 base64 数据无效或过短');
15054
15105
  }
15106
+ // 清理临时容器(如果存在)
15107
+ if (tempContainer !== null) {
15108
+ const container = tempContainer;
15109
+ const parent = container.parentNode;
15110
+ if (parent) {
15111
+ parent.removeChild(container);
15112
+ }
15113
+ }
15055
15114
  if (!this.options.silentMode) {
15056
15115
  console.log(`📸 snapdom 截图成功!格式: ${outputFormat}, 尺寸: ${img.width}x${img.height}`);
15057
15116
  }
15058
15117
  return dataUrl;
15059
15118
  }
15060
15119
  catch (error) {
15120
+ // 确保清理临时容器(即使出错)
15121
+ if (tempContainer !== null) {
15122
+ const container = tempContainer;
15123
+ const parent = container.parentNode;
15124
+ if (parent) {
15125
+ parent.removeChild(container);
15126
+ }
15127
+ }
15061
15128
  const errorMessage = error instanceof Error ? error.message : String(error);
15062
15129
  const errorName = error instanceof Error ? error.name : 'Unknown';
15063
15130
  // 针对不同类型的错误给出具体提示
@@ -15454,19 +15521,21 @@ class ScreenshotManager {
15454
15521
  console.log('📸 使用 modern-screenshot 引擎截图(Worker 模式)...');
15455
15522
  }
15456
15523
  try {
15457
- // 获取元素的实际尺寸(使用 scrollWidth/scrollHeight 获取完整内容尺寸)
15458
- // 对于 document.body,需要特殊处理,确保截取完整页面内容
15524
+ // 获取元素的实际尺寸
15525
+ // 对于 document.body,使用窗口可见区域尺寸(而不是完整滚动内容),避免截图过大
15459
15526
  let elementWidth;
15460
15527
  let elementHeight;
15461
15528
  if (element === document.body || element === document.documentElement) {
15462
- // 对于 body 或 html 元素,使用页面的完整尺寸(包括滚动内容)
15463
- elementWidth = Math.max(element.scrollWidth, element.offsetWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth, window.innerWidth);
15464
- elementHeight = Math.max(element.scrollHeight, element.offsetHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight, window.innerHeight);
15529
+ // 对于 body 或 html 元素,使用窗口可见区域尺寸(viewport)
15530
+ // 这样可以避免截图整个页面(包括不可见区域),减少图片大小
15531
+ elementWidth = window.innerWidth;
15532
+ elementHeight = window.innerHeight;
15465
15533
  }
15466
15534
  else {
15467
- // 对于其他元素,使用元素的完整尺寸
15468
- elementWidth = element.scrollWidth || element.clientWidth || element.offsetWidth;
15469
- elementHeight = element.scrollHeight || element.clientHeight || element.offsetHeight;
15535
+ // 对于其他元素,使用元素的可见尺寸(clientWidth/clientHeight)
15536
+ // 如果元素有滚动,只截图可见部分
15537
+ elementWidth = element.clientWidth || element.offsetWidth || element.scrollWidth;
15538
+ elementHeight = element.clientHeight || element.offsetHeight || element.scrollHeight;
15470
15539
  }
15471
15540
  if (!this.options.silentMode) {
15472
15541
  console.log(`📸 目标元素: ${element.tagName}${element.id ? '#' + element.id : ''}${element.className ? '.' + element.className.split(' ').join('.') : ''}`);
@@ -15490,10 +15559,10 @@ class ScreenshotManager {
15490
15559
  // 计算压缩后的尺寸(对所有元素都应用,包括 document.body)
15491
15560
  // 这样可以避免生成过大的截图,减少 base64 大小
15492
15561
  const { width, height } = this.calculateCompressedSize(elementWidth, elementHeight, this.options.maxWidth, this.options.maxHeight);
15493
- // 对于所有元素都应用尺寸限制(包括 body),避免截图过大
15494
- // 如果计算后的尺寸小于元素实际尺寸,使用压缩尺寸;否则使用元素实际尺寸(但不超过最大值)
15495
- const finalWidth = width < elementWidth ? width : Math.min(elementWidth, this.options.maxWidth);
15496
- const finalHeight = height < elementHeight ? height : Math.min(elementHeight, this.options.maxHeight);
15562
+ // 使用计算后的压缩尺寸(确保不超过 maxWidth/maxHeight)
15563
+ // 对于 body 元素,已经使用了 window.innerWidth/innerHeight,所以直接使用压缩尺寸即可
15564
+ const finalWidth = width;
15565
+ const finalHeight = height;
15497
15566
  // 处理跨域图片的函数
15498
15567
  const handleCrossOriginImage = async (url) => {
15499
15568
  // 如果是 data URL 或 blob URL,直接返回
@@ -17119,16 +17188,42 @@ class ScreenshotManager {
17119
17188
  return u8arr.buffer;
17120
17189
  }
17121
17190
  /**
17122
- * 构建二进制结构(按顺序:sign[14], type[1], topicLength[1], topic[topicLength], routingKey[8])
17123
- * 20 + 1 + 8 + 8 = 37 字节
17191
+ * ArrayBuffer 转换为 base64 字符串
17192
+ */
17193
+ arrayBufferToBase64(buffer) {
17194
+ const bytes = new Uint8Array(buffer);
17195
+ // 使用循环构建字符串,避免 String.fromCharCode.apply 的参数数量限制
17196
+ let binaryStr = '';
17197
+ for (let i = 0; i < bytes.length; i++) {
17198
+ binaryStr += String.fromCharCode(bytes[i]);
17199
+ }
17200
+ return btoa(binaryStr);
17201
+ }
17202
+ /**
17203
+ * 构建二进制结构(按顺序:sign[20], type[1], topicLength[1], topic[topicLength], routingKeyLength[1], routingKey[routingKeyLength])
17204
+ * 总大小 = 20 + 1 + 1 + topicLength + 1 + routingKeyLength(动态计算)
17205
+ *
17206
+ * 与服务端格式对应:
17207
+ * - sign: 20字节 (CharSequence, UTF-8)
17208
+ * - type: 1字节 (转为无符号)
17209
+ * - topicLength: 1字节
17210
+ * - topic: topicLength字节 (CharSequence, UTF-8)
17211
+ * - routingKeyLength: 1字节
17212
+ * - routingKey: routingKeyLength字节 (CharSequence, UTF-8)
17124
17213
  */
17125
17214
  buildBinaryConfig(config) {
17126
- // 20 + 1 + 8 + 8 = 37 字节
17127
- const buffer = new ArrayBuffer(37);
17128
- const view = new DataView(buffer);
17129
17215
  const encoder = new TextEncoder();
17216
+ // 计算实际长度
17217
+ const topicBytes = encoder.encode(config.topic);
17218
+ const topicLength = topicBytes.length;
17219
+ const routingKeyBytes = encoder.encode(config.routingKey.trim());
17220
+ const routingKeyLength = routingKeyBytes.length;
17221
+ // 动态计算总大小:20(sign) + 1(type) + 1(topicLength) + topicLength + 1(routingKeyLength) + routingKeyLength
17222
+ const totalSize = 20 + 1 + 1 + topicLength + 1 + routingKeyLength;
17223
+ const buffer = new ArrayBuffer(totalSize);
17224
+ const view = new DataView(buffer);
17130
17225
  let offset = 0;
17131
- // sign: 20字节 (字符串,UTF-8编码,不足补0,大端序)
17226
+ // sign: 20字节 (字符串,UTF-8编码,不足补0)
17132
17227
  const signStr = String(config.sign);
17133
17228
  const signBytes = encoder.encode(signStr);
17134
17229
  const signArray = new Uint8Array(buffer, offset, 20);
@@ -17137,21 +17232,17 @@ class ScreenshotManager {
17137
17232
  // type: 1字节
17138
17233
  view.setUint8(offset, config.type);
17139
17234
  offset += 1;
17140
- // topic: 8字节
17141
- const topicBytes = encoder.encode(config.topic);
17142
- const topicLength = topicBytes.length;
17143
17235
  // topicLength: 1字节
17144
17236
  view.setUint8(offset, topicLength);
17145
17237
  offset += 1;
17238
+ // topic: topicLength字节
17146
17239
  const topicArray = new Uint8Array(buffer, offset, topicLength);
17147
17240
  topicArray.set(topicBytes);
17148
17241
  offset += topicLength;
17149
- // routingKey:
17150
- const routingKeyBytes = encoder.encode(config.routingKey.trim());
17151
- const routingKeyLength = routingKeyBytes.length;
17152
17242
  // routingKeyLength: 1字节
17153
17243
  view.setUint8(offset, routingKeyLength);
17154
17244
  offset += 1;
17245
+ // routingKey: routingKeyLength字节
17155
17246
  const routingKeyArray = new Uint8Array(buffer, offset, routingKeyLength);
17156
17247
  routingKeyArray.set(routingKeyBytes);
17157
17248
  offset += routingKeyLength;
@@ -17202,6 +17293,30 @@ class ScreenshotManager {
17202
17293
  // 将截图转换为 ArrayBuffer
17203
17294
  const imageBuffer = this.dataUrlToArrayBuffer(latestScreenshot);
17204
17295
  const imageBufferSize = imageBuffer.byteLength;
17296
+ // 将 imageBuffer 转换为 base64 字符串(用于和接收端对比)
17297
+ const imageBufferBase64 = this.arrayBufferToBase64(imageBuffer);
17298
+ // 验证:base64Data(从 latestScreenshot 提取)和 imageBufferBase64(从 imageBuffer 转换)应该一致
17299
+ const isBase64Same = base64Data === imageBufferBase64;
17300
+ // 打印 imageBuffer 的 base64 编码(用于和接收端对比)
17301
+ if (!this.options.silentMode) {
17302
+ console.log('📸 [发送前] 数据流程分析:');
17303
+ console.log(` latestScreenshot: ${latestScreenshot.substring(0, 50)}... (原始 data URL)`);
17304
+ console.log(` base64Data (从 latestScreenshot 提取): 长度 ${base64Data.length} 字符`);
17305
+ console.log(` imageBuffer: ArrayBuffer, 大小 ${imageBufferSize} 字节`);
17306
+ console.log(` imageBufferBase64 (从 imageBuffer 转换): 长度 ${imageBufferBase64.length} 字符`);
17307
+ console.log(` ✅ 验证: base64Data === imageBufferBase64 ? ${isBase64Same ? '✅ 一致' : '❌ 不一致'}`);
17308
+ if (!isBase64Same) {
17309
+ console.warn('📸 ⚠️ 警告: base64Data 和 imageBufferBase64 不一致!');
17310
+ console.log(' base64Data (前100字符):', base64Data.substring(0, 100));
17311
+ console.log(' imageBufferBase64 (前100字符):', imageBufferBase64.substring(0, 100));
17312
+ }
17313
+ console.log('📸 [发送前] imageBuffer 转换为 Base64(用于和接收端对比):');
17314
+ console.log(` Base64 长度: ${imageBufferBase64.length} 字符`);
17315
+ console.log(` Base64 完整字符串:`);
17316
+ console.log(imageBufferBase64);
17317
+ console.log(` 📌 接收端使用方法: const imageUrl = \`data:image/webp;base64,\${receivedBase64}\``);
17318
+ console.log(` Data URL: data:image/webp;base64,${imageBufferBase64}`);
17319
+ }
17205
17320
  // 构建配置的二进制结构
17206
17321
  const configBuffer = this.buildBinaryConfig(config);
17207
17322
  const configBufferSize = configBuffer.byteLength;
@@ -14988,6 +14988,10 @@ class ScreenshotManager {
14988
14988
  if (!this.options.silentMode) {
14989
14989
  console.log('📸 使用 snapdom 引擎截图...');
14990
14990
  }
14991
+ // 限制只截图可见区域(viewport),避免截图不可见区域
14992
+ // 注意:snapdom 可能不支持直接限制可见区域,所以这里只是添加警告
14993
+ let targetElement = element;
14994
+ let tempContainer = null;
14991
14995
  try {
14992
14996
  // 检查元素是否存在和可见
14993
14997
  const rect = element.getBoundingClientRect();
@@ -14996,6 +15000,53 @@ class ScreenshotManager {
14996
15000
  }
14997
15001
  // 构建 snapdom 选项
14998
15002
  const options = {};
15003
+ if (element === document.body || element === document.documentElement) {
15004
+ // 对于 body 或 html 元素,创建一个临时容器,只包含可见区域
15005
+ // 使用 html2canvas 的方式:创建一个固定大小的容器,只包含可见内容
15006
+ tempContainer = document.createElement('div');
15007
+ tempContainer.style.position = 'fixed';
15008
+ tempContainer.style.top = '0';
15009
+ tempContainer.style.left = '0';
15010
+ tempContainer.style.width = `${window.innerWidth}px`;
15011
+ tempContainer.style.height = `${window.innerHeight}px`;
15012
+ tempContainer.style.overflow = 'hidden';
15013
+ tempContainer.style.zIndex = '999999';
15014
+ tempContainer.style.pointerEvents = 'none';
15015
+ tempContainer.style.backgroundColor = window.getComputedStyle(document.body).backgroundColor || 'white';
15016
+ // 创建一个包装器,只包含可见区域的内容
15017
+ // 注意:这里不克隆整个 body,而是直接使用 body 作为父元素
15018
+ // 但限制容器的尺寸为可见区域
15019
+ document.body.appendChild(tempContainer);
15020
+ // 使用 body 作为目标,但通过容器限制范围
15021
+ // 实际上,我们需要直接截图 body,但限制尺寸
15022
+ // 由于 snapdom 可能不支持直接限制尺寸,我们使用一个更简单的方法:
15023
+ // 直接截图 body,但通过 CSS 限制其显示范围(但这会影响页面)
15024
+ // 更好的方法是:使用 html2canvas 的方式,创建一个 canvas 并只绘制可见区域
15025
+ // 简化方案:直接使用 body,但添加尺寸限制选项(如果 snapdom 支持)
15026
+ // 如果不支持,则只能截图整个 body
15027
+ targetElement = element;
15028
+ if (!this.options.silentMode) {
15029
+ console.log(`📸 注意:snapdom 将截图整个 body,建议使用 html2canvas 或 modern-screenshot 来限制可见区域`);
15030
+ console.log(`📸 可见区域尺寸: ${window.innerWidth}x${window.innerHeight}`);
15031
+ }
15032
+ // 清理临时容器(未使用)
15033
+ if (tempContainer && tempContainer.parentNode) {
15034
+ tempContainer.parentNode.removeChild(tempContainer);
15035
+ }
15036
+ tempContainer = null;
15037
+ }
15038
+ else {
15039
+ // 对于其他元素,检查是否完全可见
15040
+ const elementRect = element.getBoundingClientRect();
15041
+ const isFullyVisible = elementRect.top >= 0 &&
15042
+ elementRect.left >= 0 &&
15043
+ elementRect.bottom <= window.innerHeight &&
15044
+ elementRect.right <= window.innerWidth;
15045
+ if (!isFullyVisible && !this.options.silentMode) {
15046
+ console.warn(`📸 ⚠️ 元素部分不可见,snapdom 可能会截图整个元素(包括不可见部分)`);
15047
+ console.warn(`📸 建议:使用 html2canvas 或 modern-screenshot 来限制可见区域`);
15048
+ }
15049
+ }
14999
15050
  // 如果配置了代理服务器,使用 useProxy 选项处理跨域图片和字体
15000
15051
  // 参考: https://github.com/zumerlab/snapdom/blob/main/README_CN.md#跨域图片和字体-useproxy
15001
15052
  // 只有当 useProxy 为 true 且 proxyUrl 存在时才使用代理
@@ -15030,16 +15081,16 @@ class ScreenshotManager {
15030
15081
  switch (outputFormat) {
15031
15082
  case 'jpeg':
15032
15083
  // 使用 toJpg 快捷方法(snapdom 使用 jpg 作为方法名)
15033
- img = await snapdom.toJpg(element, options);
15084
+ img = await snapdom.toJpg(targetElement, options);
15034
15085
  break;
15035
15086
  case 'webp':
15036
15087
  // 使用 toWebp 快捷方法
15037
- img = await snapdom.toWebp(element, options);
15088
+ img = await snapdom.toWebp(targetElement, options);
15038
15089
  break;
15039
15090
  case 'png':
15040
15091
  default:
15041
15092
  // 使用 toPng 快捷方法(默认)
15042
- img = await snapdom.toPng(element, options);
15093
+ img = await snapdom.toPng(targetElement, options);
15043
15094
  break;
15044
15095
  }
15045
15096
  // 获取 base64 数据(HTMLImageElement 的 src 属性包含 data URL)
@@ -15048,12 +15099,28 @@ class ScreenshotManager {
15048
15099
  if (!dataUrl || dataUrl.length < 100) {
15049
15100
  throw new Error('生成的 base64 数据无效或过短');
15050
15101
  }
15102
+ // 清理临时容器(如果存在)
15103
+ if (tempContainer !== null) {
15104
+ const container = tempContainer;
15105
+ const parent = container.parentNode;
15106
+ if (parent) {
15107
+ parent.removeChild(container);
15108
+ }
15109
+ }
15051
15110
  if (!this.options.silentMode) {
15052
15111
  console.log(`📸 snapdom 截图成功!格式: ${outputFormat}, 尺寸: ${img.width}x${img.height}`);
15053
15112
  }
15054
15113
  return dataUrl;
15055
15114
  }
15056
15115
  catch (error) {
15116
+ // 确保清理临时容器(即使出错)
15117
+ if (tempContainer !== null) {
15118
+ const container = tempContainer;
15119
+ const parent = container.parentNode;
15120
+ if (parent) {
15121
+ parent.removeChild(container);
15122
+ }
15123
+ }
15057
15124
  const errorMessage = error instanceof Error ? error.message : String(error);
15058
15125
  const errorName = error instanceof Error ? error.name : 'Unknown';
15059
15126
  // 针对不同类型的错误给出具体提示
@@ -15450,19 +15517,21 @@ class ScreenshotManager {
15450
15517
  console.log('📸 使用 modern-screenshot 引擎截图(Worker 模式)...');
15451
15518
  }
15452
15519
  try {
15453
- // 获取元素的实际尺寸(使用 scrollWidth/scrollHeight 获取完整内容尺寸)
15454
- // 对于 document.body,需要特殊处理,确保截取完整页面内容
15520
+ // 获取元素的实际尺寸
15521
+ // 对于 document.body,使用窗口可见区域尺寸(而不是完整滚动内容),避免截图过大
15455
15522
  let elementWidth;
15456
15523
  let elementHeight;
15457
15524
  if (element === document.body || element === document.documentElement) {
15458
- // 对于 body 或 html 元素,使用页面的完整尺寸(包括滚动内容)
15459
- elementWidth = Math.max(element.scrollWidth, element.offsetWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth, window.innerWidth);
15460
- elementHeight = Math.max(element.scrollHeight, element.offsetHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight, window.innerHeight);
15525
+ // 对于 body 或 html 元素,使用窗口可见区域尺寸(viewport)
15526
+ // 这样可以避免截图整个页面(包括不可见区域),减少图片大小
15527
+ elementWidth = window.innerWidth;
15528
+ elementHeight = window.innerHeight;
15461
15529
  }
15462
15530
  else {
15463
- // 对于其他元素,使用元素的完整尺寸
15464
- elementWidth = element.scrollWidth || element.clientWidth || element.offsetWidth;
15465
- elementHeight = element.scrollHeight || element.clientHeight || element.offsetHeight;
15531
+ // 对于其他元素,使用元素的可见尺寸(clientWidth/clientHeight)
15532
+ // 如果元素有滚动,只截图可见部分
15533
+ elementWidth = element.clientWidth || element.offsetWidth || element.scrollWidth;
15534
+ elementHeight = element.clientHeight || element.offsetHeight || element.scrollHeight;
15466
15535
  }
15467
15536
  if (!this.options.silentMode) {
15468
15537
  console.log(`📸 目标元素: ${element.tagName}${element.id ? '#' + element.id : ''}${element.className ? '.' + element.className.split(' ').join('.') : ''}`);
@@ -15486,10 +15555,10 @@ class ScreenshotManager {
15486
15555
  // 计算压缩后的尺寸(对所有元素都应用,包括 document.body)
15487
15556
  // 这样可以避免生成过大的截图,减少 base64 大小
15488
15557
  const { width, height } = this.calculateCompressedSize(elementWidth, elementHeight, this.options.maxWidth, this.options.maxHeight);
15489
- // 对于所有元素都应用尺寸限制(包括 body),避免截图过大
15490
- // 如果计算后的尺寸小于元素实际尺寸,使用压缩尺寸;否则使用元素实际尺寸(但不超过最大值)
15491
- const finalWidth = width < elementWidth ? width : Math.min(elementWidth, this.options.maxWidth);
15492
- const finalHeight = height < elementHeight ? height : Math.min(elementHeight, this.options.maxHeight);
15558
+ // 使用计算后的压缩尺寸(确保不超过 maxWidth/maxHeight)
15559
+ // 对于 body 元素,已经使用了 window.innerWidth/innerHeight,所以直接使用压缩尺寸即可
15560
+ const finalWidth = width;
15561
+ const finalHeight = height;
15493
15562
  // 处理跨域图片的函数
15494
15563
  const handleCrossOriginImage = async (url) => {
15495
15564
  // 如果是 data URL 或 blob URL,直接返回
@@ -17115,16 +17184,42 @@ class ScreenshotManager {
17115
17184
  return u8arr.buffer;
17116
17185
  }
17117
17186
  /**
17118
- * 构建二进制结构(按顺序:sign[14], type[1], topicLength[1], topic[topicLength], routingKey[8])
17119
- * 20 + 1 + 8 + 8 = 37 字节
17187
+ * ArrayBuffer 转换为 base64 字符串
17188
+ */
17189
+ arrayBufferToBase64(buffer) {
17190
+ const bytes = new Uint8Array(buffer);
17191
+ // 使用循环构建字符串,避免 String.fromCharCode.apply 的参数数量限制
17192
+ let binaryStr = '';
17193
+ for (let i = 0; i < bytes.length; i++) {
17194
+ binaryStr += String.fromCharCode(bytes[i]);
17195
+ }
17196
+ return btoa(binaryStr);
17197
+ }
17198
+ /**
17199
+ * 构建二进制结构(按顺序:sign[20], type[1], topicLength[1], topic[topicLength], routingKeyLength[1], routingKey[routingKeyLength])
17200
+ * 总大小 = 20 + 1 + 1 + topicLength + 1 + routingKeyLength(动态计算)
17201
+ *
17202
+ * 与服务端格式对应:
17203
+ * - sign: 20字节 (CharSequence, UTF-8)
17204
+ * - type: 1字节 (转为无符号)
17205
+ * - topicLength: 1字节
17206
+ * - topic: topicLength字节 (CharSequence, UTF-8)
17207
+ * - routingKeyLength: 1字节
17208
+ * - routingKey: routingKeyLength字节 (CharSequence, UTF-8)
17120
17209
  */
17121
17210
  buildBinaryConfig(config) {
17122
- // 20 + 1 + 8 + 8 = 37 字节
17123
- const buffer = new ArrayBuffer(37);
17124
- const view = new DataView(buffer);
17125
17211
  const encoder = new TextEncoder();
17212
+ // 计算实际长度
17213
+ const topicBytes = encoder.encode(config.topic);
17214
+ const topicLength = topicBytes.length;
17215
+ const routingKeyBytes = encoder.encode(config.routingKey.trim());
17216
+ const routingKeyLength = routingKeyBytes.length;
17217
+ // 动态计算总大小:20(sign) + 1(type) + 1(topicLength) + topicLength + 1(routingKeyLength) + routingKeyLength
17218
+ const totalSize = 20 + 1 + 1 + topicLength + 1 + routingKeyLength;
17219
+ const buffer = new ArrayBuffer(totalSize);
17220
+ const view = new DataView(buffer);
17126
17221
  let offset = 0;
17127
- // sign: 20字节 (字符串,UTF-8编码,不足补0,大端序)
17222
+ // sign: 20字节 (字符串,UTF-8编码,不足补0)
17128
17223
  const signStr = String(config.sign);
17129
17224
  const signBytes = encoder.encode(signStr);
17130
17225
  const signArray = new Uint8Array(buffer, offset, 20);
@@ -17133,21 +17228,17 @@ class ScreenshotManager {
17133
17228
  // type: 1字节
17134
17229
  view.setUint8(offset, config.type);
17135
17230
  offset += 1;
17136
- // topic: 8字节
17137
- const topicBytes = encoder.encode(config.topic);
17138
- const topicLength = topicBytes.length;
17139
17231
  // topicLength: 1字节
17140
17232
  view.setUint8(offset, topicLength);
17141
17233
  offset += 1;
17234
+ // topic: topicLength字节
17142
17235
  const topicArray = new Uint8Array(buffer, offset, topicLength);
17143
17236
  topicArray.set(topicBytes);
17144
17237
  offset += topicLength;
17145
- // routingKey:
17146
- const routingKeyBytes = encoder.encode(config.routingKey.trim());
17147
- const routingKeyLength = routingKeyBytes.length;
17148
17238
  // routingKeyLength: 1字节
17149
17239
  view.setUint8(offset, routingKeyLength);
17150
17240
  offset += 1;
17241
+ // routingKey: routingKeyLength字节
17151
17242
  const routingKeyArray = new Uint8Array(buffer, offset, routingKeyLength);
17152
17243
  routingKeyArray.set(routingKeyBytes);
17153
17244
  offset += routingKeyLength;
@@ -17198,6 +17289,30 @@ class ScreenshotManager {
17198
17289
  // 将截图转换为 ArrayBuffer
17199
17290
  const imageBuffer = this.dataUrlToArrayBuffer(latestScreenshot);
17200
17291
  const imageBufferSize = imageBuffer.byteLength;
17292
+ // 将 imageBuffer 转换为 base64 字符串(用于和接收端对比)
17293
+ const imageBufferBase64 = this.arrayBufferToBase64(imageBuffer);
17294
+ // 验证:base64Data(从 latestScreenshot 提取)和 imageBufferBase64(从 imageBuffer 转换)应该一致
17295
+ const isBase64Same = base64Data === imageBufferBase64;
17296
+ // 打印 imageBuffer 的 base64 编码(用于和接收端对比)
17297
+ if (!this.options.silentMode) {
17298
+ console.log('📸 [发送前] 数据流程分析:');
17299
+ console.log(` latestScreenshot: ${latestScreenshot.substring(0, 50)}... (原始 data URL)`);
17300
+ console.log(` base64Data (从 latestScreenshot 提取): 长度 ${base64Data.length} 字符`);
17301
+ console.log(` imageBuffer: ArrayBuffer, 大小 ${imageBufferSize} 字节`);
17302
+ console.log(` imageBufferBase64 (从 imageBuffer 转换): 长度 ${imageBufferBase64.length} 字符`);
17303
+ console.log(` ✅ 验证: base64Data === imageBufferBase64 ? ${isBase64Same ? '✅ 一致' : '❌ 不一致'}`);
17304
+ if (!isBase64Same) {
17305
+ console.warn('📸 ⚠️ 警告: base64Data 和 imageBufferBase64 不一致!');
17306
+ console.log(' base64Data (前100字符):', base64Data.substring(0, 100));
17307
+ console.log(' imageBufferBase64 (前100字符):', imageBufferBase64.substring(0, 100));
17308
+ }
17309
+ console.log('📸 [发送前] imageBuffer 转换为 Base64(用于和接收端对比):');
17310
+ console.log(` Base64 长度: ${imageBufferBase64.length} 字符`);
17311
+ console.log(` Base64 完整字符串:`);
17312
+ console.log(imageBufferBase64);
17313
+ console.log(` 📌 接收端使用方法: const imageUrl = \`data:image/webp;base64,\${receivedBase64}\``);
17314
+ console.log(` Data URL: data:image/webp;base64,${imageBufferBase64}`);
17315
+ }
17201
17316
  // 构建配置的二进制结构
17202
17317
  const configBuffer = this.buildBinaryConfig(config);
17203
17318
  const configBufferSize = configBuffer.byteLength;