customer-chat-sdk 1.1.1 → 1.1.3

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.
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  // 直接使用base64字符串,避免打包后路径问题
6
6
  const iconImage = 'data:image/png;base64,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';
7
7
  class IconManager {
8
- constructor(position, debug = false) {
8
+ constructor(position, debug = false, target) {
9
9
  this.iconElement = null;
10
10
  this.badgeElement = null;
11
11
  this.onClickCallback = null;
@@ -23,8 +23,11 @@ class IconManager {
23
23
  this.iconPosition = null; // 图标位置配置
24
24
  this.debug = false; // debug 模式标志
25
25
  this.isClickEnabled = true; // 是否允许点击(iframe 打开时禁用)
26
+ this.target = null; // 图标传送目标元素(可以是 HTMLElement 或选择器字符串)
26
27
  this.iconPosition = position || null;
27
28
  this.debug = debug;
29
+ // 保存 target(可以是 HTMLElement 或字符串选择器)
30
+ this.target = target || null;
28
31
  }
29
32
  /**
30
33
  * 显示悬浮图标
@@ -123,8 +126,18 @@ class IconManager {
123
126
  this.iconElement.appendChild(imgContainer);
124
127
  // 添加拖动和点击事件
125
128
  this.setupDragEvents();
126
- // 添加到页面
127
- document.body.appendChild(this.iconElement);
129
+ // 添加到目标元素(如果 target 是字符串,需要重新查找,因为可能在初始化时元素还不存在)
130
+ const targetElement = this.getTargetElement();
131
+ if (targetElement) {
132
+ targetElement.appendChild(this.iconElement);
133
+ }
134
+ else {
135
+ // 如果目标元素不存在,回退到 document.body
136
+ document.body.appendChild(this.iconElement);
137
+ if (this.debug) {
138
+ console.warn('Target element not found, icon added to document.body');
139
+ }
140
+ }
128
141
  if (this.debug) {
129
142
  console.log('CustomerSDK icon displayed');
130
143
  }
@@ -497,6 +510,44 @@ class IconManager {
497
510
  this.iconElement.style.cursor = 'pointer';
498
511
  }
499
512
  }
513
+ /**
514
+ * 获取目标元素(支持动态查找)
515
+ */
516
+ getTargetElement() {
517
+ if (!this.target) {
518
+ return document.body;
519
+ }
520
+ // 如果 target 是字符串选择器,每次都需要重新查找(支持动态元素)
521
+ if (typeof this.target === 'string') {
522
+ const element = document.querySelector(this.target);
523
+ if (element) {
524
+ return element;
525
+ }
526
+ else {
527
+ // 如果元素不存在,回退到 document.body
528
+ if (this.debug) {
529
+ console.warn(`Target element not found: ${this.target}, falling back to document.body`);
530
+ }
531
+ return document.body;
532
+ }
533
+ }
534
+ // 如果 target 是 HTMLElement
535
+ if (this.target instanceof HTMLElement) {
536
+ // 检查元素是否还在 DOM 中
537
+ if (document.body.contains(this.target)) {
538
+ return this.target;
539
+ }
540
+ else {
541
+ // 如果元素不在 DOM 中,回退到 document.body
542
+ if (this.debug) {
543
+ console.warn('Target element no longer in DOM, falling back to document.body');
544
+ }
545
+ return document.body;
546
+ }
547
+ }
548
+ // 默认回退到 document.body
549
+ return document.body;
550
+ }
500
551
  /**
501
552
  * 创建消息徽章(简化版)
502
553
  */
@@ -576,7 +627,15 @@ class IframeManager {
576
627
  */
577
628
  async init() {
578
629
  try {
630
+ // PC模式:预创建遮罩层(隐藏状态),避免后续移动DOM导致iframe重新加载
631
+ const actualMode = this.getActualMode();
632
+ const isPC = actualMode === 'popup';
633
+ if (isPC) {
634
+ // 创建遮罩层但不立即显示(showImmediately = false)
635
+ this.createOverlay(false);
636
+ }
579
637
  // 创建隐藏的iframe(预连接到SSE)
638
+ // 注意:createIframe 会将容器添加到遮罩层(PC模式)或 body(移动端)
580
639
  this.createIframe();
581
640
  this.isCreated = true;
582
641
  if (this.debug) {
@@ -604,10 +663,30 @@ class IframeManager {
604
663
  const isPC = actualMode === 'popup';
605
664
  // PC模式:创建或显示遮罩层
606
665
  if (isPC) {
607
- this.createOverlay(); // createOverlay 内部会检查是否已存在
666
+ // 如果遮罩层不存在,创建并显示;如果已存在,直接显示
667
+ this.createOverlay(true); // showImmediately = true,立即显示
608
668
  }
609
669
  // 显示已创建的容器
670
+ // 关键优化:容器在 init() 时已经添加到正确位置,这里只改变样式,不移动 DOM
671
+ // 这样可以避免 iframe 重新加载
610
672
  if (this.containerElement) {
673
+ // 确保容器在 DOM 中(理论上已经在 init() 时添加,但为了安全)
674
+ if (!this.containerElement.parentNode) {
675
+ // 如果容器不在 DOM 中(异常情况),根据模式添加到正确位置
676
+ if (isPC && this.overlayElement) {
677
+ // PC模式:确保遮罩层在 DOM 中,然后将容器添加到遮罩层
678
+ if (!this.overlayElement.parentNode) {
679
+ document.body.appendChild(this.overlayElement);
680
+ }
681
+ this.overlayElement.appendChild(this.containerElement);
682
+ }
683
+ else {
684
+ // 移动端模式:直接添加到 body
685
+ document.body.appendChild(this.containerElement);
686
+ }
687
+ }
688
+ // 注意:不再检查容器是否在遮罩层内,因为 init() 时已经正确放置
689
+ // 如果移动容器会导致 iframe 重新加载,所以不移动
611
690
  if (isPC) {
612
691
  // PC模式:配置为居中弹窗样式
613
692
  Object.assign(this.containerElement.style, {
@@ -619,17 +698,18 @@ class IframeManager {
619
698
  opacity: '1',
620
699
  display: 'block'
621
700
  });
622
- // 关键优化:避免重复移动容器导致 iframe 重新加载
623
- // 只有当容器不在遮罩层内时才移动,且确保遮罩层在 DOM 中
701
+ // 显示遮罩层(确保在 DOM 中)
624
702
  if (this.overlayElement) {
625
- // 如果遮罩层不在 DOM 中,先添加到 DOM
703
+ // 确保遮罩层在 DOM
626
704
  if (!this.overlayElement.parentNode) {
627
705
  document.body.appendChild(this.overlayElement);
628
706
  }
629
- // 只有当容器不在遮罩层内时才移动(避免重复移动导致 iframe 重新加载)
630
- if (this.containerElement.parentNode !== this.overlayElement) {
631
- this.overlayElement.appendChild(this.containerElement);
632
- }
707
+ // 显示遮罩层(只改变样式,不移动)
708
+ Object.assign(this.overlayElement.style, {
709
+ visibility: 'visible',
710
+ opacity: '1',
711
+ display: 'flex'
712
+ });
633
713
  }
634
714
  }
635
715
  else {
@@ -710,11 +790,16 @@ class IframeManager {
710
790
  */
711
791
  destroy() {
712
792
  this.hide();
793
+ // 移除容器和遮罩层
713
794
  if (this.containerElement) {
714
795
  this.containerElement.remove();
715
796
  this.containerElement = null;
716
797
  this.iframeElement = null;
717
798
  }
799
+ if (this.overlayElement) {
800
+ this.overlayElement.remove();
801
+ this.overlayElement = null;
802
+ }
718
803
  this.isCreated = false;
719
804
  if (this.debug) {
720
805
  console.log('CustomerSDK container destroyed');
@@ -736,25 +821,23 @@ class IframeManager {
736
821
  }
737
822
  /**
738
823
  * 创建遮罩层(PC模式使用)
824
+ * @param showImmediately 是否立即显示,默认 false(用于 init() 时创建但不显示)
739
825
  */
740
- createOverlay() {
741
- // 如果遮罩层已存在,直接显示即可
742
- if (this.overlayElement && this.overlayElement.parentNode) {
743
- Object.assign(this.overlayElement.style, {
744
- visibility: 'visible',
745
- opacity: '1',
746
- display: 'flex'
747
- });
748
- return;
749
- }
750
- // 如果遮罩层存在但不在 DOM 中,重新添加到 DOM
751
- if (this.overlayElement && !this.overlayElement.parentNode) {
752
- document.body.appendChild(this.overlayElement);
753
- Object.assign(this.overlayElement.style, {
754
- visibility: 'visible',
755
- opacity: '1',
756
- display: 'flex'
757
- });
826
+ createOverlay(showImmediately = false) {
827
+ // 如果遮罩层已存在
828
+ if (this.overlayElement) {
829
+ // 如果不在 DOM 中,添加到 DOM
830
+ if (!this.overlayElement.parentNode) {
831
+ document.body.appendChild(this.overlayElement);
832
+ }
833
+ // 根据参数决定是否显示
834
+ if (showImmediately) {
835
+ Object.assign(this.overlayElement.style, {
836
+ visibility: 'visible',
837
+ opacity: '1',
838
+ display: 'flex'
839
+ });
840
+ }
758
841
  return;
759
842
  }
760
843
  // 创建新的遮罩层
@@ -768,10 +851,12 @@ class IframeManager {
768
851
  height: '100%',
769
852
  backgroundColor: 'rgba(0, 0, 0, 0.5)',
770
853
  zIndex: '999998',
771
- display: 'flex',
854
+ display: showImmediately ? 'flex' : 'none', // 根据参数决定初始显示状态
772
855
  alignItems: 'center',
773
856
  justifyContent: 'center',
774
- cursor: this.config.allowClose ? 'pointer' : 'default'
857
+ cursor: this.config.allowClose ? 'pointer' : 'default',
858
+ visibility: showImmediately ? 'visible' : 'hidden',
859
+ opacity: showImmediately ? '1' : '0'
775
860
  });
776
861
  // 点击遮罩层关闭(只添加一次事件监听器)
777
862
  if (this.config.allowClose) {
@@ -901,8 +986,16 @@ class IframeManager {
901
986
  this.injectMobileStyles();
902
987
  }
903
988
  });
904
- // 添加到body(预连接SSE,但不显示)
905
- document.body.appendChild(this.containerElement);
989
+ // 关键优化:PC模式将容器添加到遮罩层(如果遮罩层已创建),避免后续移动DOM导致iframe重新加载
990
+ // 移动端直接添加到body
991
+ if (isPC && this.overlayElement) {
992
+ // PC模式:添加到遮罩层(遮罩层已在 init() 中创建)
993
+ this.overlayElement.appendChild(this.containerElement);
994
+ }
995
+ else {
996
+ // 移动端模式:直接添加到body
997
+ document.body.appendChild(this.containerElement);
998
+ }
906
999
  if (this.debug) {
907
1000
  console.log('CustomerSDK container created (hidden, ready for SSE)');
908
1001
  }
@@ -14544,7 +14637,7 @@ class ScreenshotManager {
14544
14637
  useProxy: options.useProxy ?? true, // 默认启用代理(如果配置了proxyUrl)
14545
14638
  engine: options.engine ?? 'modern-screenshot',
14546
14639
  corsMode: options.corsMode ?? 'canvas-proxy',
14547
- silentMode: options.silentMode !== undefined ? options.silentMode : false, // 默认 false,显示日志
14640
+ debug: options.debug !== undefined ? options.debug : false, // 默认 false,不输出日志
14548
14641
  maxRetries: options.maxRetries ?? 2,
14549
14642
  preloadImages: options.preloadImages ?? false, // 默认不预加载,按需加载
14550
14643
  maxConcurrentDownloads: options.maxConcurrentDownloads ?? 10, // 增加并发数
@@ -14564,9 +14657,9 @@ class ScreenshotManager {
14564
14657
  this.setupMessageListener();
14565
14658
  this.setupVisibilityChangeListener();
14566
14659
  // 打印初始化信息
14567
- if (!this.options.silentMode) {
14660
+ if (this.options.debug) {
14568
14661
  console.log('📸 ScreenshotManager 初始化完成');
14569
- console.log(`📸 配置: interval=${this.options.interval}ms, engine=${this.options.engine}, silentMode=${this.options.silentMode}`);
14662
+ console.log(`📸 配置: interval=${this.options.interval}ms, engine=${this.options.engine}, debug=${this.options.debug}`);
14570
14663
  console.log('📸 等待 iframe 发送 checkScreenshot 消息...');
14571
14664
  }
14572
14665
  // 启动缓存清理定时器
@@ -14589,7 +14682,7 @@ class ScreenshotManager {
14589
14682
  if (this.screenshotContext) {
14590
14683
  try {
14591
14684
  destroyContext(this.screenshotContext);
14592
- if (!this.options.silentMode) {
14685
+ if (this.options.debug) {
14593
14686
  console.log('📸 目标元素变化,清理 context');
14594
14687
  }
14595
14688
  }
@@ -14612,7 +14705,7 @@ class ScreenshotManager {
14612
14705
  this.handleIframeMessage(event);
14613
14706
  };
14614
14707
  window.addEventListener('message', this.messageHandler);
14615
- if (!this.options.silentMode) {
14708
+ if (this.options.debug) {
14616
14709
  console.log('📸 消息监听器已设置,正在监听 iframe 消息...');
14617
14710
  }
14618
14711
  }
@@ -14622,12 +14715,12 @@ class ScreenshotManager {
14622
14715
  setupVisibilityChangeListener() {
14623
14716
  document.addEventListener('visibilitychange', () => {
14624
14717
  if (document.hidden) {
14625
- if (!this.options.silentMode) {
14718
+ if (this.options.debug) {
14626
14719
  console.log('📸 页面隐藏,截图轮询已暂停');
14627
14720
  }
14628
14721
  }
14629
14722
  else {
14630
- if (!this.options.silentMode) {
14723
+ if (this.options.debug) {
14631
14724
  console.log('📸 页面显示,截图轮询已恢复');
14632
14725
  }
14633
14726
  }
@@ -14644,7 +14737,7 @@ class ScreenshotManager {
14644
14737
  }
14645
14738
  // 如果提供了发送消息的回调,保存它(用于后续发送二进制数据)
14646
14739
  // 注意:消息来源验证在 setupMessageListener 中处理
14647
- if (!this.options.silentMode) {
14740
+ if (this.options.debug) {
14648
14741
  console.log('📸 [iframe] 收到消息:', event.data);
14649
14742
  }
14650
14743
  // 尝试解析为二进制配置(新格式)
@@ -14658,7 +14751,7 @@ class ScreenshotManager {
14658
14751
  if (isValid) {
14659
14752
  // 启用截图功能
14660
14753
  if (!this.isEnabled) {
14661
- if (!this.options.silentMode) {
14754
+ if (this.options.debug) {
14662
14755
  console.log('📸 [iframe] 启用截图功能(二进制模式)');
14663
14756
  }
14664
14757
  this.isEnabled = true;
@@ -14668,7 +14761,7 @@ class ScreenshotManager {
14668
14761
  // 计算剩余有效时间(毫秒)
14669
14762
  const remainingTime = binaryConfig.ttl - currentTime;
14670
14763
  // 启动或更新截图轮询
14671
- if (!this.options.silentMode) {
14764
+ if (this.options.debug) {
14672
14765
  const remainingMinutes = Math.ceil(remainingTime / 60000);
14673
14766
  console.log(`📸 [iframe] 设置轮询间隔: ${this.dynamicInterval}ms,剩余有效时间: ${remainingMinutes}分钟`);
14674
14767
  }
@@ -14680,7 +14773,7 @@ class ScreenshotManager {
14680
14773
  this.expirationTimer = null;
14681
14774
  }
14682
14775
  this.expirationTimer = setTimeout(() => {
14683
- if (!this.options.silentMode) {
14776
+ if (this.options.debug) {
14684
14777
  console.log('📸 [iframe] 二进制配置已过期,停止截图');
14685
14778
  }
14686
14779
  this.stopScreenshot();
@@ -14691,7 +14784,7 @@ class ScreenshotManager {
14691
14784
  }
14692
14785
  else {
14693
14786
  // 禁用截图功能(ttl == 0 或已过期)
14694
- if (!this.options.silentMode) {
14787
+ if (this.options.debug) {
14695
14788
  if (binaryConfig.ttl === 0) {
14696
14789
  console.log('📸 [iframe] ttl == 0,禁用截图功能');
14697
14790
  }
@@ -14710,11 +14803,15 @@ class ScreenshotManager {
14710
14803
  return;
14711
14804
  }
14712
14805
  // 如果不是二进制配置格式,记录错误
14713
- console.error('📸 [iframe] 解析配置失败:未识别的配置格式');
14806
+ if (this.options.debug) {
14807
+ console.error('📸 [iframe] 解析配置失败:未识别的配置格式');
14808
+ }
14714
14809
  this.uploadError = '解析配置失败:仅支持二进制配置格式';
14715
14810
  }
14716
14811
  catch (error) {
14717
- console.error('📸 [iframe] 处理消息失败:', error);
14812
+ if (this.options.debug) {
14813
+ console.error('📸 [iframe] 处理消息失败:', error);
14814
+ }
14718
14815
  this.uploadError = error instanceof Error ? error.message : String(error);
14719
14816
  }
14720
14817
  }
@@ -14753,17 +14850,19 @@ class ScreenshotManager {
14753
14850
  */
14754
14851
  startScreenshot(customInterval) {
14755
14852
  if (!this.isEnabled) {
14756
- console.warn('📸 截图功能已禁用,无法启动');
14853
+ if (this.options.debug) {
14854
+ console.warn('📸 截图功能已禁用,无法启动');
14855
+ }
14757
14856
  return;
14758
14857
  }
14759
14858
  const currentInterval = customInterval || this.dynamicInterval || this.options.interval;
14760
14859
  if (this.isRunning) {
14761
- if (!this.options.silentMode) {
14860
+ if (this.options.debug) {
14762
14861
  console.log(`📸 更新轮询间隔: ${currentInterval}ms`);
14763
14862
  }
14764
14863
  this.stopScreenshot();
14765
14864
  }
14766
- if (!this.options.silentMode) {
14865
+ if (this.options.debug) {
14767
14866
  console.log(`📸 开始轮询截图,间隔: ${currentInterval}ms`);
14768
14867
  }
14769
14868
  this.isRunning = true;
@@ -14776,7 +14875,7 @@ class ScreenshotManager {
14776
14875
  const scheduleNext = async () => {
14777
14876
  // 如果上次任务还没完成,等待完成
14778
14877
  if (!this.isCurrentTaskCompleted) {
14779
- if (!this.options.silentMode) {
14878
+ if (this.options.debug) {
14780
14879
  console.log('📸 [定时] 等待上次任务完成...');
14781
14880
  }
14782
14881
  // 每100ms检查一次任务是否完成
@@ -14796,7 +14895,7 @@ class ScreenshotManager {
14796
14895
  this.isCurrentTaskCompleted = false;
14797
14896
  // 记录定时开始时间
14798
14897
  const scheduleStartTime = performance.now();
14799
- if (!this.options.silentMode) {
14898
+ if (this.options.debug) {
14800
14899
  console.log(`📸 [定时开始] 开始新一轮截图任务`);
14801
14900
  }
14802
14901
  try {
@@ -14828,7 +14927,7 @@ class ScreenshotManager {
14828
14927
  const combinedBufferSize = combinedBuffer.byteLength;
14829
14928
  const combineTime = performance.now() - combineStartTime;
14830
14929
  // 打印大小信息
14831
- if (!this.options.silentMode) {
14930
+ if (this.options.debug) {
14832
14931
  console.log('📸 [轮询-大小统计]');
14833
14932
  console.log(` Base64 大小: ${base64Size} 字符`);
14834
14933
  console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
@@ -14849,7 +14948,7 @@ class ScreenshotManager {
14849
14948
  const sendCallbackTime = performance.now() - sendCallbackStartTime;
14850
14949
  const totalSendTime = performance.now() - sendStartTime;
14851
14950
  const totalTime = performance.now() - scheduleStartTime;
14852
- if (!this.options.silentMode) {
14951
+ if (this.options.debug) {
14853
14952
  console.log('📸 [轮询] ✅ 二进制数据已发送到 iframe');
14854
14953
  console.log(` ⏱️ 发送回调耗时: ${sendCallbackTime.toFixed(2)}ms`);
14855
14954
  console.log(` ⏱️ 发送阶段总耗时: ${totalSendTime.toFixed(2)}ms`);
@@ -14858,7 +14957,9 @@ class ScreenshotManager {
14858
14957
  }
14859
14958
  }
14860
14959
  catch (error) {
14861
- console.error('📸 [轮询] ❌ 处理二进制数据失败:', error);
14960
+ if (this.options.debug) {
14961
+ console.error('📸 [轮询] ❌ 处理二进制数据失败:', error);
14962
+ }
14862
14963
  }
14863
14964
  }
14864
14965
  // 任务完成(无压缩模式)
@@ -14867,7 +14968,7 @@ class ScreenshotManager {
14867
14968
  else if (this.currentBinaryConfig && this.options.compress) {
14868
14969
  // 启用了压缩,等待 Worker 压缩完成后在 onmessage 中发送
14869
14970
  // 任务完成标志会在压缩完成的回调中设置
14870
- if (!this.options.silentMode) {
14971
+ if (this.options.debug) {
14871
14972
  console.log('📸 [轮询] 等待 Worker 压缩完成后发送到 iframe...');
14872
14973
  }
14873
14974
  }
@@ -14877,7 +14978,7 @@ class ScreenshotManager {
14877
14978
  }
14878
14979
  }
14879
14980
  catch (error) {
14880
- if (!this.options.silentMode) {
14981
+ if (this.options.debug) {
14881
14982
  console.error('📸 [轮询] 截图失败:', error);
14882
14983
  }
14883
14984
  // 任务失败,标记为完成
@@ -14902,7 +15003,7 @@ class ScreenshotManager {
14902
15003
  if (!this.isRunning) {
14903
15004
  return;
14904
15005
  }
14905
- if (!this.options.silentMode) {
15006
+ if (this.options.debug) {
14906
15007
  console.log('📸 停止轮询截图');
14907
15008
  }
14908
15009
  this.isRunning = false;
@@ -14919,7 +15020,9 @@ class ScreenshotManager {
14919
15020
  */
14920
15021
  async captureOnce(force = false) {
14921
15022
  if (!this.isEnabled && !force) {
14922
- console.warn('📸 截图功能已禁用,无法执行截图。如需测试,请先调用 enable(true) 启用截图功能');
15023
+ if (this.options.debug) {
15024
+ console.warn('📸 截图功能已禁用,无法执行截图。如需测试,请先调用 enable(true) 启用截图功能');
15025
+ }
14923
15026
  return false;
14924
15027
  }
14925
15028
  return await this.takeScreenshot();
@@ -14929,14 +15032,16 @@ class ScreenshotManager {
14929
15032
  */
14930
15033
  async takeScreenshot(scheduleStartTime) {
14931
15034
  if (!this.targetElement) {
14932
- console.warn('📸 目标元素不存在');
15035
+ if (this.options.debug) {
15036
+ console.warn('📸 目标元素不存在');
15037
+ }
14933
15038
  return false;
14934
15039
  }
14935
15040
  // 记录截图开始时间
14936
15041
  const screenshotStartTime = performance.now();
14937
15042
  this.setupGlobalErrorHandlers();
14938
15043
  try {
14939
- if (!this.options.silentMode) {
15044
+ if (this.options.debug) {
14940
15045
  console.log(`📸 开始截图 #${this.screenshotCount + 1}...`);
14941
15046
  if (scheduleStartTime) {
14942
15047
  const waitTime = screenshotStartTime - scheduleStartTime;
@@ -14950,7 +15055,7 @@ class ScreenshotManager {
14950
15055
  this.waitForFonts()
14951
15056
  ]);
14952
15057
  const waitStylesTime = performance.now() - waitStylesStartTime;
14953
- if (!this.options.silentMode) {
15058
+ if (this.options.debug) {
14954
15059
  console.log(` ⏱️ 等待样式和字体加载耗时: ${waitStylesTime.toFixed(2)}ms`);
14955
15060
  }
14956
15061
  // 等待元素完全渲染(特别是对于 modern-screenshot)
@@ -14959,12 +15064,12 @@ class ScreenshotManager {
14959
15064
  requestAnimationFrame(() => resolve());
14960
15065
  }));
14961
15066
  const waitRenderTime = performance.now() - waitRenderStartTime;
14962
- if (!this.options.silentMode) {
15067
+ if (this.options.debug) {
14963
15068
  console.log(` ⏱️ 等待渲染完成耗时: ${waitRenderTime.toFixed(2)}ms`);
14964
15069
  }
14965
15070
  // 选择截图引擎
14966
15071
  const selectedEngine = this.options.engine || 'modern-screenshot';
14967
- if (!this.options.silentMode) {
15072
+ if (this.options.debug) {
14968
15073
  console.log(`📸 使用截图引擎: ${selectedEngine}`);
14969
15074
  }
14970
15075
  // 优化:如果启用预加载,才预处理图片;否则让引擎按需加载
@@ -15004,7 +15109,7 @@ class ScreenshotManager {
15004
15109
  dataUrl = await this.takeScreenshotWithModernScreenshot(this.targetElement);
15005
15110
  }
15006
15111
  const engineTime = performance.now() - engineStartTime;
15007
- if (!this.options.silentMode) {
15112
+ if (this.options.debug) {
15008
15113
  console.log(` ⏱️ 截图引擎执行耗时: ${engineTime.toFixed(2)}ms`);
15009
15114
  }
15010
15115
  const timestamp = Date.now();
@@ -15018,7 +15123,7 @@ class ScreenshotManager {
15018
15123
  const removed = this.screenshotHistory.shift();
15019
15124
  // 强制 GC(如果可能)
15020
15125
  if (removed && removed.length > 1000000) { // 大于1MB的字符串
15021
- if (!this.options.silentMode) {
15126
+ if (this.options.debug) {
15022
15127
  console.log(`📸 清理旧截图,释放内存: ${Math.round(removed.length * 0.75 / 1024)} KB`);
15023
15128
  }
15024
15129
  }
@@ -15032,7 +15137,7 @@ class ScreenshotManager {
15032
15137
  if (removed) {
15033
15138
  const removedSize = removed.length;
15034
15139
  totalSize -= removedSize;
15035
- if (!this.options.silentMode) {
15140
+ if (this.options.debug) {
15036
15141
  console.warn(`📸 ⚠️ 历史记录总大小超过限制,清理最旧截图: ${Math.round(removedSize * 0.75 / 1024)} KB`);
15037
15142
  }
15038
15143
  }
@@ -15044,7 +15149,7 @@ class ScreenshotManager {
15044
15149
  const screenshotTotalTime = performance.now() - screenshotStartTime;
15045
15150
  // 打印基本信息
15046
15151
  const base64Data = dataUrl.split(',')[1] || '';
15047
- if (!this.options.silentMode) {
15152
+ if (this.options.debug) {
15048
15153
  console.log('📸 截图完成:');
15049
15154
  console.log(`📸 编号: #${this.screenshotCount}`);
15050
15155
  console.log(`📸 时间: ${new Date(timestamp).toLocaleTimeString()}`);
@@ -15063,7 +15168,7 @@ class ScreenshotManager {
15063
15168
  // 确保 Worker 已创建
15064
15169
  if (!this.worker) {
15065
15170
  this.worker = this.createWorker();
15066
- if (!this.options.silentMode) {
15171
+ if (this.options.debug) {
15067
15172
  if (this.worker) {
15068
15173
  console.log('📸 Worker 已创建,准备压缩');
15069
15174
  }
@@ -15075,7 +15180,7 @@ class ScreenshotManager {
15075
15180
  if (this.worker) {
15076
15181
  // 记录压缩开始时间
15077
15182
  const compressStartTime = performance.now();
15078
- if (!this.options.silentMode) {
15183
+ if (this.options.debug) {
15079
15184
  console.log('📸 发送到 WebWorker 进行压缩...');
15080
15185
  }
15081
15186
  // 保存原始 dataUrl 用于后续对比(在 Worker 压缩完成后)
@@ -15098,7 +15203,7 @@ class ScreenshotManager {
15098
15203
  }
15099
15204
  else {
15100
15205
  // Worker 不可用,如果配置了二进制模式,直接发送原始截图
15101
- if (!this.options.silentMode) {
15206
+ if (this.options.debug) {
15102
15207
  console.warn('📸 ⚠️ Worker 不可用,跳过压缩(使用原始截图)');
15103
15208
  }
15104
15209
  // Worker 不可用时,如果配置了二进制模式,立即发送原始截图
@@ -15114,7 +15219,7 @@ class ScreenshotManager {
15114
15219
  data: combinedBuffer
15115
15220
  };
15116
15221
  this.sendToIframeCallback(message);
15117
- if (!this.options.silentMode) {
15222
+ if (this.options.debug) {
15118
15223
  console.log('📸 [Worker 不可用] ✅ 原始截图已发送到 iframe');
15119
15224
  }
15120
15225
  }
@@ -15139,7 +15244,7 @@ class ScreenshotManager {
15139
15244
  console.error('📸 截图失败:', err);
15140
15245
  this.error = errorMessage;
15141
15246
  }
15142
- else if (!this.options.silentMode) {
15247
+ else if (this.options.debug) {
15143
15248
  console.warn('📸 截图遇到跨域问题(已忽略)');
15144
15249
  }
15145
15250
  return false;
@@ -15156,7 +15261,7 @@ class ScreenshotManager {
15156
15261
  * 注意:snapdom 内部使用 worker 进行截图处理,会在后台线程执行,不会阻塞主线程
15157
15262
  */
15158
15263
  async takeScreenshotWithSnapdom(element) {
15159
- if (!this.options.silentMode) {
15264
+ if (this.options.debug) {
15160
15265
  console.log('📸 使用 snapdom 引擎截图...');
15161
15266
  }
15162
15267
  // 限制只截图可见区域(viewport),避免截图不可见区域
@@ -15196,7 +15301,7 @@ class ScreenshotManager {
15196
15301
  // 简化方案:直接使用 body,但添加尺寸限制选项(如果 snapdom 支持)
15197
15302
  // 如果不支持,则只能截图整个 body
15198
15303
  targetElement = element;
15199
- if (!this.options.silentMode) {
15304
+ if (this.options.debug) {
15200
15305
  console.log(`📸 注意:snapdom 将截图整个 body,建议使用 html2canvas 或 modern-screenshot 来限制可见区域`);
15201
15306
  console.log(`📸 可见区域尺寸: ${window.innerWidth}x${window.innerHeight}`);
15202
15307
  }
@@ -15213,7 +15318,7 @@ class ScreenshotManager {
15213
15318
  elementRect.left >= 0 &&
15214
15319
  elementRect.bottom <= window.innerHeight &&
15215
15320
  elementRect.right <= window.innerWidth;
15216
- if (!isFullyVisible && !this.options.silentMode) {
15321
+ if (!isFullyVisible && this.options.debug) {
15217
15322
  console.warn(`📸 ⚠️ 元素部分不可见,snapdom 可能会截图整个元素(包括不可见部分)`);
15218
15323
  console.warn(`📸 建议:使用 html2canvas 或 modern-screenshot 来限制可见区域`);
15219
15324
  }
@@ -15231,12 +15336,12 @@ class ScreenshotManager {
15231
15336
  proxyUrl = proxyUrl.endsWith('?') ? proxyUrl + 'url=' : proxyUrl + '?url=';
15232
15337
  }
15233
15338
  options.useProxy = proxyUrl;
15234
- if (!this.options.silentMode) {
15339
+ if (this.options.debug) {
15235
15340
  console.log(`📸 使用代理服务器处理跨域图片: ${proxyUrl}`);
15236
15341
  }
15237
15342
  }
15238
15343
  else {
15239
- if (!this.options.silentMode) {
15344
+ if (this.options.debug) {
15240
15345
  if (!this.options.useProxy) {
15241
15346
  console.log('📸 代理功能已禁用(useProxy: false)');
15242
15347
  }
@@ -15278,7 +15383,7 @@ class ScreenshotManager {
15278
15383
  parent.removeChild(container);
15279
15384
  }
15280
15385
  }
15281
- if (!this.options.silentMode) {
15386
+ if (this.options.debug) {
15282
15387
  console.log(`📸 snapdom 截图成功!格式: ${outputFormat}, 尺寸: ${img.width}x${img.height}`);
15283
15388
  }
15284
15389
  return dataUrl;
@@ -15296,14 +15401,14 @@ class ScreenshotManager {
15296
15401
  const errorName = error instanceof Error ? error.name : 'Unknown';
15297
15402
  // 针对不同类型的错误给出具体提示
15298
15403
  if (errorName === 'EncodingError' || errorMessage.includes('cannot be decoded')) {
15299
- if (!this.options.silentMode) {
15404
+ if (this.options.debug) {
15300
15405
  console.warn('📸 ⚠️ 图片解码失败 - 这通常是因为跨域图片无法访问');
15301
15406
  console.warn('📸 💡 解决方案:配置 proxyUrl 选项');
15302
15407
  console.warn('📸 📖 参考: https://snapdom.dev/#cors');
15303
15408
  }
15304
15409
  }
15305
15410
  else if (errorMessage.includes('CORS') || errorMessage.includes('cross-origin')) {
15306
- if (!this.options.silentMode) {
15411
+ if (this.options.debug) {
15307
15412
  console.warn('📸 ⚠️ 检测到 CORS 错误,建议配置 proxyUrl 选项');
15308
15413
  }
15309
15414
  }
@@ -15328,7 +15433,7 @@ class ScreenshotManager {
15328
15433
  * - 需要快速截图
15329
15434
  */
15330
15435
  async takeScreenshotWithHtml2Canvas(element) {
15331
- if (!this.options.silentMode) {
15436
+ if (this.options.debug) {
15332
15437
  console.log('📸 使用 html2canvas 引擎截图...');
15333
15438
  }
15334
15439
  try {
@@ -15382,7 +15487,7 @@ class ScreenshotManager {
15382
15487
  : (isMobile ? 0.5 : 0.6), // 用户未配置,使用默认值
15383
15488
  useCORS: this.options.enableCORS,
15384
15489
  allowTaint: !this.options.enableCORS, // 如果启用 CORS,不允许 taint
15385
- logging: !this.options.silentMode, // 使用配置的静默模式
15490
+ logging: this.options.debug, // 使用配置的静默模式
15386
15491
  // foreignObjectRendering: true, // ❌ 移除:可能导致跨域图片不显示
15387
15492
  // removeContainer: true, // ❌ 移除:移到后面,避免重复定义
15388
15493
  // 不设置 width 和 height,让 html2canvas 自动计算
@@ -15520,12 +15625,12 @@ class ScreenshotManager {
15520
15625
  }
15521
15626
  catch (e) {
15522
15627
  // 忽略内联化错误
15523
- if (!this.options.silentMode) {
15628
+ if (this.options.debug) {
15524
15629
  console.warn('📸 iOS 样式内联化失败:', e);
15525
15630
  }
15526
15631
  }
15527
15632
  }
15528
- if (!this.options.silentMode) {
15633
+ if (this.options.debug) {
15529
15634
  const styleLinks = clonedDoc.querySelectorAll('link[rel="stylesheet"]').length;
15530
15635
  const styleTags = clonedDoc.querySelectorAll('style').length;
15531
15636
  console.log(`📸 onclone: 已复制 ${styleLinks} 个样式表链接和 ${styleTags} 个内联样式标签${isIOS ? ' (iOS 模式:已内联化计算样式)' : ''}`);
@@ -15574,7 +15679,7 @@ class ScreenshotManager {
15574
15679
  const cachedDataUrl = this.getCachedImage(originalSrc);
15575
15680
  if (cachedDataUrl) {
15576
15681
  img.src = cachedDataUrl;
15577
- if (!this.options.silentMode) {
15682
+ if (this.options.debug) {
15578
15683
  console.log(`📸 使用缓存的图片: ${originalSrc.substring(0, 50)}...`);
15579
15684
  }
15580
15685
  return;
@@ -15583,7 +15688,7 @@ class ScreenshotManager {
15583
15688
  // 构建代理 URL
15584
15689
  try {
15585
15690
  if (!this.options.proxyUrl) {
15586
- if (!this.options.silentMode) {
15691
+ if (this.options.debug) {
15587
15692
  console.warn(`📸 ⚠️ 未配置代理 URL,跨域图片可能无法显示: ${originalSrc}`);
15588
15693
  }
15589
15694
  return;
@@ -15596,19 +15701,19 @@ class ScreenshotManager {
15596
15701
  baseUrl = baseUrl.replace(/[?&]$/, '');
15597
15702
  const proxyUrl = `${baseUrl}?${params.toString()}`;
15598
15703
  img.src = proxyUrl;
15599
- if (!this.options.silentMode) {
15704
+ if (this.options.debug) {
15600
15705
  console.log(`📸 使用代理 URL 替换跨域图片: ${originalSrc.substring(0, 50)}... -> ${proxyUrl.substring(0, 50)}...`);
15601
15706
  }
15602
15707
  }
15603
15708
  catch (error) {
15604
- if (!this.options.silentMode) {
15709
+ if (this.options.debug) {
15605
15710
  console.warn(`📸 ⚠️ 处理图片 URL 失败: ${originalSrc}`, error);
15606
15711
  }
15607
15712
  }
15608
15713
  });
15609
15714
  };
15610
15715
  }
15611
- if (!this.options.silentMode) {
15716
+ if (this.options.debug) {
15612
15717
  console.log(`📸 html2canvas 配置: 元素尺寸 ${elementWidth}x${elementHeight}, 质量 ${finalQuality.toFixed(2)}, 缩放 ${options.scale}`);
15613
15718
  console.log(`📸 html2canvas 将自动计算截图尺寸(不限制 width/height)`);
15614
15719
  console.log(`📸 用户配置质量: ${this.options.quality}, 实际使用质量: ${finalQuality.toFixed(2)}`);
@@ -15653,7 +15758,7 @@ class ScreenshotManager {
15653
15758
  if (!dataUrl || dataUrl.length < 100) {
15654
15759
  throw new Error('生成的截图数据无效或过短');
15655
15760
  }
15656
- if (!this.options.silentMode) {
15761
+ if (this.options.debug) {
15657
15762
  console.log(`📸 html2canvas 截图成功!格式: ${mimeType}, 尺寸: ${canvas.width}x${canvas.height}, 质量: ${finalQualityForExport?.toFixed(2) || 'N/A (PNG)'}`);
15658
15763
  console.log(`📸 输出格式: ${mimeType}, 用户配置质量: ${this.options.quality}, 实际使用质量: ${finalQualityForExport?.toFixed(2) || 'N/A (PNG)'}`);
15659
15764
  }
@@ -15661,7 +15766,7 @@ class ScreenshotManager {
15661
15766
  }
15662
15767
  catch (error) {
15663
15768
  const errorMessage = error instanceof Error ? error.message : String(error);
15664
- if (!this.options.silentMode) {
15769
+ if (this.options.debug) {
15665
15770
  console.error('📸 html2canvas 截图失败:', errorMessage);
15666
15771
  if (errorMessage.includes('CORS') || errorMessage.includes('cross-origin')) {
15667
15772
  console.warn('📸 💡 建议:配置 proxyUrl 选项处理跨域图片');
@@ -15694,7 +15799,7 @@ class ScreenshotManager {
15694
15799
  if (this.isScreenshotInProgress) {
15695
15800
  // 队列最多保留 1 个请求,避免积压
15696
15801
  if (this.screenshotQueue.length >= 1) {
15697
- if (!this.options.silentMode) {
15802
+ if (this.options.debug) {
15698
15803
  console.log('📸 截图队列已满,跳过当前请求(等待队列处理)');
15699
15804
  }
15700
15805
  // 等待队列中的请求完成
@@ -15723,7 +15828,7 @@ class ScreenshotManager {
15723
15828
  });
15724
15829
  }
15725
15830
  this.isScreenshotInProgress = true;
15726
- if (!this.options.silentMode) {
15831
+ if (this.options.debug) {
15727
15832
  console.log('📸 使用 modern-screenshot 引擎截图(Worker 模式)...');
15728
15833
  }
15729
15834
  try {
@@ -15743,7 +15848,7 @@ class ScreenshotManager {
15743
15848
  elementWidth = element.clientWidth || element.offsetWidth || element.scrollWidth;
15744
15849
  elementHeight = element.clientHeight || element.offsetHeight || element.scrollHeight;
15745
15850
  }
15746
- if (!this.options.silentMode) {
15851
+ if (this.options.debug) {
15747
15852
  console.log(`📸 目标元素: ${element.tagName}${element.id ? '#' + element.id : ''}${element.className ? '.' + element.className.split(' ').join('.') : ''}`);
15748
15853
  console.log(`📸 元素尺寸: ${elementWidth}x${elementHeight}`);
15749
15854
  console.log(`📸 scrollWidth: ${element.scrollWidth}, scrollHeight: ${element.scrollHeight}`);
@@ -15777,7 +15882,7 @@ class ScreenshotManager {
15777
15882
  // 检查内存缓存(优先使用缓存,带过期时间检查)
15778
15883
  const cachedDataUrl = this.getCachedImage(url);
15779
15884
  if (cachedDataUrl) {
15780
- if (!this.options.silentMode) {
15885
+ if (this.options.debug) {
15781
15886
  console.log(`📸 ✅ 使用内存缓存图片: ${url.substring(0, 50)}...`);
15782
15887
  }
15783
15888
  return cachedDataUrl;
@@ -15829,7 +15934,7 @@ class ScreenshotManager {
15829
15934
  }
15830
15935
  }
15831
15936
  catch (error) {
15832
- if (!this.options.silentMode) {
15937
+ if (this.options.debug) {
15833
15938
  console.warn(`📸 代理处理图片失败: ${url.substring(0, 100)}...`, error);
15834
15939
  }
15835
15940
  // 失败时返回原 URL,让 modern-screenshot 自己处理
@@ -15878,7 +15983,7 @@ class ScreenshotManager {
15878
15983
  // 按照 demo 的方式:只在 context 不存在时创建,之后一直复用
15879
15984
  // 不进行复杂的检测和重新创建逻辑
15880
15985
  if (!this.screenshotContext) {
15881
- if (!this.options.silentMode) {
15986
+ if (this.options.debug) {
15882
15987
  console.log(`📸 创建截图 Worker 上下文...`);
15883
15988
  console.log(`📸 Worker 模式: ${workerNumber} 个 Worker`);
15884
15989
  }
@@ -15899,23 +16004,23 @@ class ScreenshotManager {
15899
16004
  // 如果用户指定了 workerUrl,使用指定的 URL
15900
16005
  if (this.options.workerUrl) {
15901
16006
  simpleContextOptions.workerUrl = this.options.workerUrl;
15902
- if (!this.options.silentMode) {
16007
+ if (this.options.debug) {
15903
16008
  console.log(`📸 使用指定的 Worker URL: ${this.options.workerUrl}`);
15904
16009
  }
15905
16010
  }
15906
16011
  else {
15907
- if (!this.options.silentMode) {
16012
+ if (this.options.debug) {
15908
16013
  console.log('📸 Worker URL 未指定,modern-screenshot 将自动处理');
15909
16014
  }
15910
16015
  }
15911
16016
  try {
15912
16017
  this.screenshotContext = await createContext$1(element, simpleContextOptions);
15913
- if (!this.options.silentMode) {
16018
+ if (this.options.debug) {
15914
16019
  console.log('📸 Worker 上下文创建成功');
15915
16020
  }
15916
16021
  }
15917
16022
  catch (error) {
15918
- if (!this.options.silentMode) {
16023
+ if (this.options.debug) {
15919
16024
  console.error('📸 创建 Worker 上下文失败:', error);
15920
16025
  }
15921
16026
  throw error;
@@ -15925,7 +16030,7 @@ class ScreenshotManager {
15925
16030
  // 按照 demo 的方式:使用 domToWebp 时传递配置参数
15926
16031
  let dataUrl;
15927
16032
  const outputFormat = this.options.outputFormat || 'webp';
15928
- if (!this.options.silentMode) {
16033
+ if (this.options.debug) {
15929
16034
  console.log(`📸 使用 ${outputFormat.toUpperCase()} 格式截图(直接输出,无需转换)...`);
15930
16035
  }
15931
16036
  // 构建 domToWebp/domToJpeg/domToPng 的配置参数(和 demo 一致)
@@ -15960,14 +16065,14 @@ class ScreenshotManager {
15960
16065
  if (!dataUrl || dataUrl.length < 100) {
15961
16066
  throw new Error('生成的截图数据无效或过短');
15962
16067
  }
15963
- if (!this.options.silentMode) {
16068
+ if (this.options.debug) {
15964
16069
  console.log(`📸 ✅ modern-screenshot 截图成功(Worker 模式,${outputFormat.toUpperCase()} 格式)`);
15965
16070
  }
15966
16071
  return dataUrl;
15967
16072
  }
15968
16073
  catch (workerError) {
15969
16074
  // Worker 模式失败,回退到普通模式(和 demo 一致)
15970
- if (!this.options.silentMode) {
16075
+ if (this.options.debug) {
15971
16076
  console.warn('📸 Worker 模式失败,回退到普通模式:', workerError);
15972
16077
  }
15973
16078
  // 销毁失败的 context
@@ -16010,7 +16115,7 @@ class ScreenshotManager {
16010
16115
  if (!dataUrl || dataUrl.length < 100) {
16011
16116
  throw new Error('生成的截图数据无效或过短');
16012
16117
  }
16013
- if (!this.options.silentMode) {
16118
+ if (this.options.debug) {
16014
16119
  console.log(`📸 ✅ modern-screenshot 截图成功(普通模式,${outputFormat.toUpperCase()} 格式)`);
16015
16120
  }
16016
16121
  return dataUrl;
@@ -16018,7 +16123,7 @@ class ScreenshotManager {
16018
16123
  }
16019
16124
  catch (error) {
16020
16125
  const errorMessage = error instanceof Error ? error.message : String(error);
16021
- if (!this.options.silentMode) {
16126
+ if (this.options.debug) {
16022
16127
  console.error('📸 modern-screenshot 截图失败:', errorMessage);
16023
16128
  console.error('📸 元素信息:', {
16024
16129
  width: rect.width,
@@ -16042,7 +16147,7 @@ class ScreenshotManager {
16042
16147
  catch (error) {
16043
16148
  // 外层错误处理:确保即使发生错误也释放锁
16044
16149
  const errorMessage = error instanceof Error ? error.message : String(error);
16045
- if (!this.options.silentMode) {
16150
+ if (this.options.debug) {
16046
16151
  console.error('📸 modern-screenshot 截图异常:', errorMessage);
16047
16152
  }
16048
16153
  throw error;
@@ -16096,7 +16201,7 @@ class ScreenshotManager {
16096
16201
  link.crossOrigin = 'anonymous';
16097
16202
  document.head.appendChild(link);
16098
16203
  this.preconnected = true;
16099
- if (!this.options.silentMode) {
16204
+ if (this.options.debug) {
16100
16205
  console.log(`📸 ✅ 已预连接代理服务器: ${proxyOrigin}`);
16101
16206
  }
16102
16207
  }
@@ -16221,7 +16326,7 @@ class ScreenshotManager {
16221
16326
  if (networkImages.length === 0) {
16222
16327
  return;
16223
16328
  }
16224
- if (!this.options.silentMode) {
16329
+ if (this.options.debug) {
16225
16330
  const totalImages = images.length;
16226
16331
  console.log(`📸 发现 ${networkImages.length}/${totalImages} 个可视区域内的跨域图片,开始并行预加载...`);
16227
16332
  }
@@ -16229,7 +16334,7 @@ class ScreenshotManager {
16229
16334
  // 只有当 useProxy 为 true 且 proxyUrl 存在时才使用代理
16230
16335
  const shouldUseProxy = this.options.useProxy && this.options.proxyUrl && this.options.proxyUrl.trim() !== '';
16231
16336
  if (shouldUseProxy) {
16232
- if (!this.options.silentMode) {
16337
+ if (this.options.debug) {
16233
16338
  console.log(`📸 使用代理服务器处理跨域图片: ${this.options.proxyUrl}`);
16234
16339
  }
16235
16340
  // 优化:增加并发数,使用更大的批次
@@ -16257,13 +16362,13 @@ class ScreenshotManager {
16257
16362
  }
16258
16363
  catch (error) {
16259
16364
  // 静默失败,不影响其他图片
16260
- if (!this.options.silentMode) {
16365
+ if (this.options.debug) {
16261
16366
  console.warn(`📸 ❌ 代理预加载失败: ${originalSrc.substring(0, 50)}...`);
16262
16367
  }
16263
16368
  }
16264
16369
  }));
16265
16370
  }));
16266
- if (!this.options.silentMode) {
16371
+ if (this.options.debug) {
16267
16372
  console.log(`📸 ✅ 预处理完成,缓存了 ${this.imageProxyCache.size} 个代理图片`);
16268
16373
  }
16269
16374
  }
@@ -16288,7 +16393,7 @@ class ScreenshotManager {
16288
16393
  // 移除末尾的 ? 或 &(如果有)
16289
16394
  baseUrl = baseUrl.replace(/[?&]$/, '');
16290
16395
  const requestUrl = `${baseUrl}?${params.toString()}`;
16291
- if (!this.options.silentMode) {
16396
+ if (this.options.debug) {
16292
16397
  console.log(`📸 🔄 代理请求 URL: ${requestUrl.substring(0, 200)}...`);
16293
16398
  }
16294
16399
  // 请求代理服务器
@@ -16307,7 +16412,7 @@ class ScreenshotManager {
16307
16412
  const blob = await response.blob();
16308
16413
  // 将 blob 转换为 data URL(用于 modern-screenshot 兼容性)
16309
16414
  const dataUrl = await this.blobToDataUrl(blob);
16310
- if (!this.options.silentMode) {
16415
+ if (this.options.debug) {
16311
16416
  console.log(`📸 ✅ 代理模式成功(已转换为 data URL): ${imageUrl.substring(0, 100)}...`);
16312
16417
  }
16313
16418
  return dataUrl;
@@ -16338,7 +16443,7 @@ class ScreenshotManager {
16338
16443
  if (sizeMB > maxSizeMB) {
16339
16444
  if (this.options.skipLargeImages) {
16340
16445
  // 跳过过大的图片,返回占位符
16341
- if (!this.options.silentMode) {
16446
+ if (this.options.debug) {
16342
16447
  console.warn(`📸 ⚠️ 跳过过大图片(${sizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
16343
16448
  }
16344
16449
  // 返回一个 1x1 的透明占位符,避免截图失败
@@ -16346,7 +16451,7 @@ class ScreenshotManager {
16346
16451
  }
16347
16452
  else {
16348
16453
  // 不跳过,但添加警告
16349
- if (!this.options.silentMode) {
16454
+ if (this.options.debug) {
16350
16455
  console.warn(`📸 ⚠️ 图片较大(${sizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
16351
16456
  }
16352
16457
  }
@@ -16360,14 +16465,14 @@ class ScreenshotManager {
16360
16465
  if (blobSizeMB > maxSizeMB) {
16361
16466
  if (this.options.skipLargeImages) {
16362
16467
  // 跳过过大的图片,返回占位符
16363
- if (!this.options.silentMode) {
16468
+ if (this.options.debug) {
16364
16469
  console.warn(`📸 ⚠️ 跳过过大图片(实际大小 ${blobSizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
16365
16470
  }
16366
16471
  return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
16367
16472
  }
16368
16473
  else {
16369
16474
  // 不跳过,但添加警告
16370
- if (!this.options.silentMode) {
16475
+ if (this.options.debug) {
16371
16476
  console.warn(`📸 ⚠️ 图片较大(实际大小 ${blobSizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
16372
16477
  }
16373
16478
  }
@@ -16383,7 +16488,7 @@ class ScreenshotManager {
16383
16488
  }
16384
16489
  catch (error) {
16385
16490
  // 下载失败,返回原 URL,让 modern-screenshot 自己处理
16386
- if (!this.options.silentMode) {
16491
+ if (this.options.debug) {
16387
16492
  console.warn(`📸 ⚠️ 下载图片失败: ${url.substring(0, 100)}...`, error);
16388
16493
  }
16389
16494
  return url;
@@ -16814,7 +16919,7 @@ class ScreenshotManager {
16814
16919
  // 更新历史记录为压缩后的数据
16815
16920
  this.screenshotHistory[this.screenshotHistory.length - 1] = compressed.dataUrl;
16816
16921
  // 打印压缩统计信息和 base64 对比
16817
- if (!this.options.silentMode) {
16922
+ if (this.options.debug) {
16818
16923
  if (compressed.error) {
16819
16924
  // 压缩失败,使用原始数据
16820
16925
  console.warn('📸 [Worker 压缩] ⚠️ 压缩失败,使用原始截图');
@@ -16868,7 +16973,7 @@ class ScreenshotManager {
16868
16973
  };
16869
16974
  newWorker.onerror = (e) => {
16870
16975
  console.error('📸 WebWorker 错误:', e);
16871
- if (!this.options.silentMode) {
16976
+ if (this.options.debug) {
16872
16977
  console.warn('📸 Worker 压缩失败,使用原始截图');
16873
16978
  }
16874
16979
  // Worker 发生错误时,如果配置了二进制模式,发送原始截图
@@ -16884,7 +16989,7 @@ class ScreenshotManager {
16884
16989
  data: combinedBuffer
16885
16990
  };
16886
16991
  this.sendToIframeCallback(message);
16887
- if (!this.options.silentMode) {
16992
+ if (this.options.debug) {
16888
16993
  console.log('📸 [Worker 错误] ✅ 原始截图已发送到 iframe');
16889
16994
  }
16890
16995
  }
@@ -17049,7 +17154,7 @@ class ScreenshotManager {
17049
17154
  async takeScreenshotAndSendBinary(config) {
17050
17155
  // 如果已经在运行,先停止再重新开始
17051
17156
  if (this.isRunning) {
17052
- if (!this.options.silentMode) {
17157
+ if (this.options.debug) {
17053
17158
  console.log(`📸 更新轮询间隔: ${this.dynamicInterval || this.options.interval}ms`);
17054
17159
  }
17055
17160
  this.stopScreenshot();
@@ -17080,7 +17185,7 @@ class ScreenshotManager {
17080
17185
  // 验证:base64Data(从 latestScreenshot 提取)和 imageBufferBase64(从 imageBuffer 转换)应该一致
17081
17186
  const isBase64Same = base64Data === imageBufferBase64;
17082
17187
  // 打印 imageBuffer 的 base64 编码(用于和接收端对比)
17083
- if (!this.options.silentMode) {
17188
+ if (this.options.debug) {
17084
17189
  console.log('📸 [发送前] 数据流程分析:');
17085
17190
  console.log(` latestScreenshot: ${latestScreenshot.substring(0, 50)}... (原始 data URL)`);
17086
17191
  console.log(` base64Data (从 latestScreenshot 提取): 长度 ${base64Data.length} 字符`);
@@ -17106,7 +17211,7 @@ class ScreenshotManager {
17106
17211
  const combinedBuffer = this.combineBinaryData(configBuffer, imageBuffer);
17107
17212
  const combinedBufferSize = combinedBuffer.byteLength;
17108
17213
  // 打印大小信息
17109
- if (!this.options.silentMode) {
17214
+ if (this.options.debug) {
17110
17215
  console.log('📸 [大小统计]');
17111
17216
  console.log(` Base64 大小: ${base64Size} 字符`);
17112
17217
  console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
@@ -17120,7 +17225,7 @@ class ScreenshotManager {
17120
17225
  data: combinedBuffer
17121
17226
  };
17122
17227
  this.sendToIframeCallback(message);
17123
- if (!this.options.silentMode) {
17228
+ if (this.options.debug) {
17124
17229
  console.log('📸 [iframe] ✅ 二进制数据已发送到 iframe');
17125
17230
  }
17126
17231
  }
@@ -17134,14 +17239,14 @@ class ScreenshotManager {
17134
17239
  }
17135
17240
  }
17136
17241
  else {
17137
- if (!this.options.silentMode) {
17242
+ if (this.options.debug) {
17138
17243
  console.warn('📸 [iframe] 截图完成但未找到截图数据');
17139
17244
  }
17140
17245
  }
17141
17246
  }
17142
17247
  else {
17143
17248
  // 启用了压缩,等待 Worker 压缩完成后在 onmessage 中自动发送
17144
- if (!this.options.silentMode) {
17249
+ if (this.options.debug) {
17145
17250
  console.log('📸 [iframe] 等待 Worker 压缩完成后发送到 iframe...');
17146
17251
  }
17147
17252
  }
@@ -17171,7 +17276,7 @@ class ScreenshotManager {
17171
17276
  const base64Data = dataUrl.split(',')[1] || '';
17172
17277
  const base64Size = base64Data.length;
17173
17278
  // 完整打印发送前的 base64 信息(用于调试)
17174
- if (!this.options.silentMode) {
17279
+ if (this.options.debug) {
17175
17280
  console.log('📸 [发送前] Base64 信息:');
17176
17281
  console.log(` Base64 长度: ${base64Size} 字符`);
17177
17282
  console.log(` 📸 [发送前] 完整 Base64:`);
@@ -17195,7 +17300,7 @@ class ScreenshotManager {
17195
17300
  const combinedBufferSize = combinedBuffer.byteLength;
17196
17301
  const combineTime = performance.now() - combineStartTime;
17197
17302
  // 打印大小信息
17198
- if (!this.options.silentMode) {
17303
+ if (this.options.debug) {
17199
17304
  console.log('📸 [压缩后-大小统计]');
17200
17305
  console.log(` Base64 大小: ${base64Size} 字符`);
17201
17306
  console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
@@ -17218,7 +17323,7 @@ class ScreenshotManager {
17218
17323
  if (scheduleStartTime) {
17219
17324
  totalTime = performance.now() - scheduleStartTime;
17220
17325
  }
17221
- if (!this.options.silentMode) {
17326
+ if (this.options.debug) {
17222
17327
  console.log('📸 [压缩后] ✅ 二进制数据已发送到 iframe');
17223
17328
  console.log(` ⏱️ 发送回调耗时: ${sendCallbackTime.toFixed(2)}ms`);
17224
17329
  console.log(` ⏱️ 发送阶段总耗时: ${totalSendTime.toFixed(2)}ms`);
@@ -17321,8 +17426,8 @@ class ScreenshotManager {
17321
17426
  if (newOptions.compress !== undefined) {
17322
17427
  this.options.compress = newOptions.compress;
17323
17428
  }
17324
- if (newOptions.silentMode !== undefined) {
17325
- this.options.silentMode = newOptions.silentMode;
17429
+ if (newOptions.debug !== undefined) {
17430
+ this.options.debug = newOptions.debug;
17326
17431
  }
17327
17432
  if (newOptions.proxyUrl !== undefined) {
17328
17433
  this.options.proxyUrl = newOptions.proxyUrl;
@@ -17335,7 +17440,7 @@ class ScreenshotManager {
17335
17440
  if (this.screenshotContext) {
17336
17441
  try {
17337
17442
  destroyContext(this.screenshotContext);
17338
- if (!this.options.silentMode) {
17443
+ if (this.options.debug) {
17339
17444
  console.log(`📸 引擎已从 ${oldEngine} 切换到 ${newEngine},已清理旧 context`);
17340
17445
  }
17341
17446
  }
@@ -17344,12 +17449,12 @@ class ScreenshotManager {
17344
17449
  }
17345
17450
  this.screenshotContext = null;
17346
17451
  }
17347
- if (!this.options.silentMode) {
17452
+ if (this.options.debug) {
17348
17453
  console.log(`📸 截图引擎已更新: ${oldEngine} → ${newEngine}`);
17349
17454
  console.log(`📸 下次截图时将使用新引擎: ${newEngine}`);
17350
17455
  }
17351
17456
  }
17352
- else if (!this.options.silentMode) {
17457
+ else if (this.options.debug) {
17353
17458
  console.log('📸 截图配置已更新');
17354
17459
  }
17355
17460
  }
@@ -17449,7 +17554,7 @@ class ScreenshotManager {
17449
17554
  const itemSizeMB = value.dataUrl.length * 0.75 / (1024 * 1024);
17450
17555
  this.imageProxyCache.delete(key);
17451
17556
  currentSizeMB -= itemSizeMB;
17452
- if (!this.options.silentMode) {
17557
+ if (this.options.debug) {
17453
17558
  console.log(`📸 清理内存缓存(超过限制): ${key.substring(0, 50)}...`);
17454
17559
  }
17455
17560
  }
@@ -17474,7 +17579,7 @@ class ScreenshotManager {
17474
17579
  expiredUrls.forEach(url => {
17475
17580
  this.imageProxyCache.delete(url);
17476
17581
  });
17477
- if (expiredUrls.length > 0 && !this.options.silentMode) {
17582
+ if (expiredUrls.length > 0 && this.options.debug) {
17478
17583
  console.log(`📸 清理了 ${expiredUrls.length} 个过期缓存`);
17479
17584
  }
17480
17585
  }
@@ -17486,7 +17591,7 @@ class ScreenshotManager {
17486
17591
  // 每2分钟清理一次过期缓存(从5分钟改为2分钟,更频繁)
17487
17592
  setInterval(() => {
17488
17593
  this.cleanExpiredCache();
17489
- if (!this.options.silentMode) {
17594
+ if (this.options.debug) {
17490
17595
  const memoryCacheSize = this.imageProxyCache.size;
17491
17596
  const memoryCacheSizeMB = Array.from(this.imageProxyCache.values())
17492
17597
  .reduce((sum, cached) => sum + cached.dataUrl.length * 0.75 / (1024 * 1024), 0);
@@ -20826,9 +20931,10 @@ class CustomerServiceSDK {
20826
20931
  agent: config.agent,
20827
20932
  timestamp: Date.now()
20828
20933
  };
20829
- // 创建悬浮图标管理器(支持自定义位置)
20934
+ // 创建悬浮图标管理器(支持自定义位置和传送目标)
20830
20935
  const iconPosition = options?.iconPosition || undefined;
20831
- this.iconManager = new IconManager(iconPosition, this.debug);
20936
+ const iconTarget = options?.target || undefined;
20937
+ this.iconManager = new IconManager(iconPosition, this.debug, iconTarget);
20832
20938
  await this.iconManager.show();
20833
20939
  // 创建iframe管理器(自动检测设备类型)
20834
20940
  this.iframeManager = new IframeManager({
@@ -20868,10 +20974,10 @@ class CustomerServiceSDK {
20868
20974
  // 默认截图目标为 document.body,可以通过配置自定义
20869
20975
  const targetElement = document.body;
20870
20976
  // 传入发送消息到 iframe 的回调函数
20871
- // 将 debug 配置传递给截图管理器(通过 silentMode 的相反值)
20977
+ // 将 debug 配置传递给截图管理器
20872
20978
  const screenshotOptions = {
20873
20979
  ...config.screenshot,
20874
- silentMode: !this.debug // debug=true 时 silentMode=false(显示日志),debug=false 时 silentMode=true(隐藏日志)
20980
+ debug: this.debug // 直接传递 debug 标志
20875
20981
  };
20876
20982
  this.screenshotManager = new ScreenshotManager(targetElement, screenshotOptions, (data) => {
20877
20983
  // 通过 IframeManager 发送消息到 iframe
@@ -21086,8 +21192,9 @@ class CustomerServiceSDK {
21086
21192
  return result.visitorId;
21087
21193
  }
21088
21194
  catch (error) {
21089
- // 错误始终输出
21090
- console.warn('❌ Failed to get device fingerprint, using fallback:', error);
21195
+ if (this.debug) {
21196
+ console.warn('❌ Failed to get device fingerprint, using fallback:', error);
21197
+ }
21091
21198
  const fallbackId = 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
21092
21199
  if (this.debug) {
21093
21200
  console.log('🆔 Fallback Device ID:', fallbackId);