@qzsy/dsh-bridge 3.5.1 → 3.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # 更新日志
2
2
 
3
+ ## [v3.5.3] - 2026-11-02
4
+
5
+ ### 🐛 修复
6
+
7
+ - **推理菜单延迟挂载也能接住**:宿主的推理菜单 portal 在过渡期可能是 0 尺寸/延迟一帧才可测量,原来只在 `MutationObserver` 触发时找一次会漏掉;现在点击「推理等级」后每 50ms 轮询、最多 1.2s,直到推理 sheet 出现。
8
+ - **推理 portal 识别放宽**:不再要求宽高 > 0,只要求 `display` 可见且 `position: fixed/absolute`,避免透明过渡期被误判为不存在。
9
+ - 清掉旧 sheet 快照和过渡遮罩的时机保持到推理 sheet 真正套用之后。
10
+
11
+ ### 🧪 验证
12
+
13
+ - 独立宿主模拟 4/4;待安装后验证真实 DSH 的完整链路。
14
+
15
+
16
+ ## [v3.5.2] - 2026-11-02
17
+
18
+ ### 🐛 修复
19
+
20
+ - **复用宿主已打开的父菜单**:选完不同模型后,宿主通常会把父级「模型 / 推理等级」popover 留在 DOM 里;现在直接点其中的「推理等级」,不再多点一次触发按钮把父菜单关掉。
21
+ - **当前模型的关闭/重开**:只有模型子菜单还开着(选的是当前模型)时,才先点一次触发按钮收起,再点一次打开父菜单,然后进入推理等级。
22
+ - 保持旧 sheet 快照和透明隐藏,推理 sheet 出现后再清理。
23
+
24
+ ### 🧪 验证
25
+
26
+ - 独立宿主模拟 4/4;待安装后分别验证「选不同模型」和「选当前模型」两条真实链路。
27
+
28
+
3
29
  ## [v3.5.1] - 2026-11-02
4
30
 
5
31
  ### 🐛 修复
package/client/client.js CHANGED
@@ -682,8 +682,8 @@ function setupMobileDrawer() {
682
682
  if (!(el instanceof HTMLElement)) continue;
683
683
  if (el === modelBackdrop || el === modelClone || el.classList.contains(HEADER_CLASS) || el.classList.contains(BACKDROP_CLASS)) continue;
684
684
  if (el.hasAttribute("data-qzsy-model-sheet") || el.hasAttribute("data-qzsy-model-sheet-clone")) continue;
685
- if (!visiblePortal(el)) continue;
686
685
  const s = getComputedStyle(el);
686
+ if (s.display === "none") continue;
687
687
  if (s.position !== "fixed" && s.position !== "absolute") continue;
688
688
  const menu = el.querySelector('[role="menu"]');
689
689
  if (!menu) continue;
@@ -930,53 +930,74 @@ function setupMobileDrawer() {
930
930
  document.body.appendChild(modelClone);
931
931
  window.setTimeout(() => {
932
932
  if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
933
- if (modelPortal && visiblePortal(modelPortal)) {
933
+ const findReasonCell = () => [...document.querySelectorAll('button[role="menuitem"]')].find((b) => /推理等级|Reasoning effort|Reasoning level|推理强度/i.test(b.innerText || ""));
934
+ const clickReasonCell = (cell) => {
935
+ if (!cell) return;
936
+ reasoningStep = true;
934
937
  try {
935
- dismissHostMenus();
938
+ cell.click();
936
939
  } catch {
937
940
  }
938
- window.setTimeout(() => openParentAndReason(), 100);
939
- return;
940
- }
941
- openParentAndReason();
942
- function openParentAndReason() {
943
- if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
944
- const trigger = document.querySelector(
945
- 'button[aria-haspopup="menu"][aria-label*="\u9009\u62E9\u6A21\u578B"], button[aria-haspopup="menu"][aria-label*="Select model"], button[aria-haspopup="menu"][aria-label*="\u6A21\u578B"]'
946
- );
947
- try {
948
- trigger?.click();
949
- } catch {
950
- }
951
- let attempts = 0;
952
- const openReasonCell = () => {
953
- if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
954
- const cell = [...document.querySelectorAll('button[role="menuitem"]')].find((b) => /推理等级|Reasoning effort|Reasoning level|推理强度/i.test(b.innerText || ""));
955
- if (cell) {
956
- reasoningStep = true;
957
- try {
958
- cell.click();
959
- } catch {
960
- }
961
- window.setTimeout(() => {
962
- if (disposed || token !== modelAdvanceToken) return;
963
- if (!modelPortal) {
964
- cancelAdvanceOverlay();
965
- clearModelSheet();
966
- }
967
- }, 1200);
941
+ let reasonTries = 0;
942
+ const applyReasonPortal = () => {
943
+ if (disposed || token !== modelAdvanceToken) return;
944
+ if (modelPortal && modelPortal.getAttribute("data-qzsy-model-step") === "reasoning") return;
945
+ const found = findReasoningPortal();
946
+ if (found) {
947
+ applyModelSheet(found);
968
948
  return;
969
949
  }
970
- attempts += 1;
971
- if (attempts > 20) {
950
+ reasonTries += 1;
951
+ if (reasonTries > 24) {
972
952
  cancelAdvanceOverlay();
973
953
  clearModelSheet();
974
954
  return;
975
955
  }
976
- window.setTimeout(openReasonCell, 60);
956
+ window.setTimeout(applyReasonPortal, 50);
977
957
  };
978
- window.setTimeout(openReasonCell, 300);
958
+ window.setTimeout(applyReasonPortal, 30);
959
+ };
960
+ let attempts = 0;
961
+ const pollReasonCell = () => {
962
+ if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
963
+ const cell = findReasonCell();
964
+ if (cell) {
965
+ clickReasonCell(cell);
966
+ return;
967
+ }
968
+ attempts += 1;
969
+ if (attempts > 20) {
970
+ cancelAdvanceOverlay();
971
+ clearModelSheet();
972
+ return;
973
+ }
974
+ window.setTimeout(pollReasonCell, 60);
975
+ };
976
+ const initialCell = findReasonCell();
977
+ if (initialCell) {
978
+ clickReasonCell(initialCell);
979
+ return;
980
+ }
981
+ const trigger = document.querySelector(
982
+ 'button[aria-haspopup="menu"][aria-label*="\u9009\u62E9\u6A21\u578B"], button[aria-haspopup="menu"][aria-label*="Select model"], button[aria-haspopup="menu"][aria-label*="\u6A21\u578B"]'
983
+ );
984
+ const reopenParent = () => {
985
+ if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
986
+ try {
987
+ trigger?.click();
988
+ } catch {
989
+ }
990
+ window.setTimeout(pollReasonCell, 300);
991
+ };
992
+ if (modelPortal && visiblePortal(modelPortal)) {
993
+ try {
994
+ trigger?.click();
995
+ } catch {
996
+ }
997
+ window.setTimeout(reopenParent, 180);
998
+ return;
979
999
  }
1000
+ reopenParent();
980
1001
  }, 140);
981
1002
  };
982
1003
  const syncModelSheet = () => {
package/client/mobile.js CHANGED
@@ -506,8 +506,8 @@ export function setupMobileDrawer() {
506
506
  if (!(el instanceof HTMLElement)) continue;
507
507
  if (el === modelBackdrop || el === modelClone || el.classList.contains(HEADER_CLASS) || el.classList.contains(BACKDROP_CLASS)) continue;
508
508
  if (el.hasAttribute('data-qzsy-model-sheet') || el.hasAttribute('data-qzsy-model-sheet-clone')) continue;
509
- if (!visiblePortal(el)) continue;
510
509
  const s = getComputedStyle(el);
510
+ if (s.display === 'none') continue;
511
511
  if (s.position !== 'fixed' && s.position !== 'absolute') continue;
512
512
  const menu = el.querySelector('[role="menu"]');
513
513
  if (!menu) continue;
@@ -756,48 +756,70 @@ export function setupMobileDrawer() {
756
756
 
757
757
  window.setTimeout(() => {
758
758
  if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
759
- // 选中的是当前模型时宿主可能不关菜单,先按 Esc/外部点击收掉
760
- if (modelPortal && visiblePortal(modelPortal)) {
761
- try { dismissHostMenus(); } catch { /* 忽略 */ }
762
- window.setTimeout(() => openParentAndReason(), 100);
763
- return;
764
- }
765
- openParentAndReason();
766
-
767
- function openParentAndReason() {
768
- if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
769
- const trigger = document.querySelector(
770
- 'button[aria-haspopup="menu"][aria-label*="选择模型"], button[aria-haspopup="menu"][aria-label*="Select model"], button[aria-haspopup="menu"][aria-label*="模型"]',
771
- );
772
- try { trigger?.click(); } catch { /* 忽略 */ }
773
- let attempts = 0;
774
- const openReasonCell = () => {
775
- if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
776
- const cell = [...document.querySelectorAll('button[role="menuitem"]')].find((b) => /推理等级|Reasoning effort|Reasoning level|推理强度/i.test(b.innerText || ''));
777
- if (cell) {
778
- reasoningStep = true;
779
- try { cell.click(); } catch { /* 忽略 */ }
780
- window.setTimeout(() => {
781
- if (disposed || token !== modelAdvanceToken) return;
782
- if (!modelPortal) {
783
- // 推理菜单没出现:撤掉 overlay,避免卡在半路
784
- cancelAdvanceOverlay();
785
- clearModelSheet();
786
- }
787
- }, 1200);
759
+ const findReasonCell = () => [...document.querySelectorAll('button[role="menuitem"]')].find((b) => /推理等级|Reasoning effort|Reasoning level|推理强度/i.test(b.innerText || ''));
760
+ const clickReasonCell = (cell) => {
761
+ if (!cell) return;
762
+ reasoningStep = true;
763
+ try { cell.click(); } catch { /* 忽略 */ }
764
+ let reasonTries = 0;
765
+ const applyReasonPortal = () => {
766
+ if (disposed || token !== modelAdvanceToken) return;
767
+ if (modelPortal && modelPortal.getAttribute('data-qzsy-model-step') === 'reasoning') return;
768
+ const found = findReasoningPortal();
769
+ if (found) {
770
+ applyModelSheet(found);
788
771
  return;
789
772
  }
790
- attempts += 1;
791
- if (attempts > 20) {
773
+ reasonTries += 1;
774
+ if (reasonTries > 24) {
792
775
  cancelAdvanceOverlay();
793
776
  clearModelSheet();
794
777
  return;
795
778
  }
796
- window.setTimeout(openReasonCell, 60);
779
+ window.setTimeout(applyReasonPortal, 50);
797
780
  };
798
- // 等父菜单的进入动画/事件绑定完成再点「推理等级」
799
- window.setTimeout(openReasonCell, 300);
781
+ window.setTimeout(applyReasonPortal, 30);
782
+ };
783
+ let attempts = 0;
784
+ const pollReasonCell = () => {
785
+ if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
786
+ const cell = findReasonCell();
787
+ if (cell) {
788
+ clickReasonCell(cell);
789
+ return;
790
+ }
791
+ attempts += 1;
792
+ if (attempts > 20) {
793
+ cancelAdvanceOverlay();
794
+ clearModelSheet();
795
+ return;
796
+ }
797
+ window.setTimeout(pollReasonCell, 60);
798
+ };
799
+
800
+ // 不同模型:宿主通常已经把父级「模型 / 推理等级」popover 留在 DOM 里(视觉上被隐藏)
801
+ const initialCell = findReasonCell();
802
+ if (initialCell) {
803
+ clickReasonCell(initialCell);
804
+ return;
805
+ }
806
+
807
+ const trigger = document.querySelector(
808
+ 'button[aria-haspopup="menu"][aria-label*="选择模型"], button[aria-haspopup="menu"][aria-label*="Select model"], button[aria-haspopup="menu"][aria-label*="模型"]',
809
+ );
810
+ const reopenParent = () => {
811
+ if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
812
+ try { trigger?.click(); } catch { /* 忽略 */ }
813
+ window.setTimeout(pollReasonCell, 300);
814
+ };
815
+
816
+ // 当前模型:宿主可能不关模型子菜单,先关一次再打开父级
817
+ if (modelPortal && visiblePortal(modelPortal)) {
818
+ try { trigger?.click(); } catch { /* 忽略 */ }
819
+ window.setTimeout(reopenParent, 180);
820
+ return;
800
821
  }
822
+ reopenParent();
801
823
  }, 140);
802
824
  };
803
825
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@qzsy/dsh-bridge",
3
- "version": "3.5.1",
3
+ "version": "3.5.3",
4
4
  "description": "DSH 远程隧道云官方客户端插件:一条命令把本机 DeepSeek Harness 接入云端隧道(设备码绑定、短期令牌、本地门禁代理、HTTP/SSE/WebSocket 反向隧道)。",
5
- "releaseNotes": "【v3.5.1】模型 → 思考级别连招修复\\n• 选中当前模型时先收起旧菜单,再进入推理等级,不再卡住\\n• 等父菜单动画/事件就绪后再点推理等级,避免 sheet 不出现\\n• 过渡期宿主菜单改透明隐藏,程序化点击可靠",
5
+ "releaseNotes": "【v3.5.3】模型 → 思考级别连招完成\\n• 推理菜单延迟挂载也接得住:点击后每 50ms 轮询,直到推理 sheet 出现\\n• 识别不再要求宽高 > 0,避免透明过渡期漏掉 portal\\n• 选完模型稳定进入推理等级 sheet,选完才关闭",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",
8
8
  "exports": {