@qzsy/dsh-bridge 3.5.3 → 3.5.4

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,19 @@
1
1
  # 更新日志
2
2
 
3
+ ## [v3.5.4] - 2026-11-02
4
+
5
+ ### 🐛 修复
6
+
7
+ - **接住宿主自己弹出的推理菜单**:选完模型后宿主的推理菜单可能已经挂出(层级不一定是 `body` 直接子节点);现在不再依赖我们点「推理等级」,过渡期间直接查找并 sheet 化。
8
+ - **推理菜单向上找定位层**:从每个 `[role="menu"]` 向上找最近的 `fixed/absolute` 承载层,兼容嵌套 portal。
9
+ - **给宿主 300ms 自动衔接窗口**:先等宿主自己切到推理菜单;没出现才回退到「重开父菜单 → 点推理等级」。
10
+ - 这样选完模型会稳定进入推理 sheet;当前模型/不同模型两条路径都覆盖。
11
+
12
+ ### 🧪 验证
13
+
14
+ - 独立宿主模拟 4/4;待安装后验证真实 DSH。
15
+
16
+
3
17
  ## [v3.5.3] - 2026-11-02
4
18
 
5
19
  ### 🐛 修复
package/client/client.js CHANGED
@@ -677,23 +677,25 @@ function setupMobileDrawer() {
677
677
  };
678
678
  const findReasoningPortal = () => {
679
679
  if (!isMobileViewport()) return null;
680
- const candidates = [];
681
- for (const el of document.body.children) {
682
- if (!(el instanceof HTMLElement)) continue;
683
- if (el === modelBackdrop || el === modelClone || el.classList.contains(HEADER_CLASS) || el.classList.contains(BACKDROP_CLASS)) continue;
684
- if (el.hasAttribute("data-qzsy-model-sheet") || el.hasAttribute("data-qzsy-model-sheet-clone")) continue;
685
- const s = getComputedStyle(el);
686
- if (s.display === "none") continue;
687
- if (s.position !== "fixed" && s.position !== "absolute") continue;
688
- const menu = el.querySelector('[role="menu"]');
689
- if (!menu) continue;
680
+ const roots = /* @__PURE__ */ new Set();
681
+ for (const menu of document.querySelectorAll('[role="menu"]')) {
690
682
  const radios = menu.querySelectorAll('button[role="menuitemradio"], [role="menuitemradio"], input[type="radio"]').length;
691
683
  if (radios < 1) continue;
692
684
  const text = menu.innerText || "";
693
685
  if (/搜索模型|Search model|DeepSeek|claude|模型与推理等级/i.test(text)) continue;
694
- candidates.push(el);
686
+ let el = menu;
687
+ while (el && el !== document.body) {
688
+ if (el.hasAttribute("data-qzsy-model-sheet") || el.hasAttribute("data-qzsy-model-sheet-clone")) break;
689
+ const s = getComputedStyle(el);
690
+ if (s.display === "none") break;
691
+ if (s.position === "fixed" || s.position === "absolute") {
692
+ roots.add(el);
693
+ break;
694
+ }
695
+ el = el.parentElement;
696
+ }
695
697
  }
696
- return candidates[candidates.length - 1] || null;
698
+ return [...roots].pop() || null;
697
699
  };
698
700
  const hostBackdropPresent = () => {
699
701
  for (const el of document.body.children) {
@@ -930,6 +932,12 @@ function setupMobileDrawer() {
930
932
  document.body.appendChild(modelClone);
931
933
  window.setTimeout(() => {
932
934
  if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
935
+ const hostReason = findReasoningPortal();
936
+ if (hostReason) {
937
+ reasoningStep = true;
938
+ applyModelSheet(hostReason);
939
+ return;
940
+ }
933
941
  const findReasonCell = () => [...document.querySelectorAll('button[role="menuitem"]')].find((b) => /推理等级|Reasoning effort|Reasoning level|推理强度/i.test(b.innerText || ""));
934
942
  const clickReasonCell = (cell) => {
935
943
  if (!cell) return;
@@ -998,7 +1006,7 @@ function setupMobileDrawer() {
998
1006
  return;
999
1007
  }
1000
1008
  reopenParent();
1001
- }, 140);
1009
+ }, 300);
1002
1010
  };
1003
1011
  const syncModelSheet = () => {
1004
1012
  if (disposed) return;
@@ -1007,12 +1015,11 @@ function setupMobileDrawer() {
1007
1015
  return;
1008
1016
  }
1009
1017
  if (modelAdvancing) {
1010
- if (reasoningStep) {
1011
- const next = findReasoningPortal();
1012
- if (next) {
1013
- applyModelSheet(next);
1014
- return;
1015
- }
1018
+ const next = findReasoningPortal();
1019
+ if (next) {
1020
+ reasoningStep = true;
1021
+ applyModelSheet(next);
1022
+ return;
1016
1023
  }
1017
1024
  if (modelPortal && !visiblePortal(modelPortal)) modelPortal = null;
1018
1025
  return;
package/client/mobile.js CHANGED
@@ -501,24 +501,26 @@ export function setupMobileDrawer() {
501
501
 
502
502
  const findReasoningPortal = () => {
503
503
  if (!isMobileViewport()) return null;
504
- const candidates = [];
505
- for (const el of document.body.children) {
506
- if (!(el instanceof HTMLElement)) continue;
507
- if (el === modelBackdrop || el === modelClone || el.classList.contains(HEADER_CLASS) || el.classList.contains(BACKDROP_CLASS)) continue;
508
- if (el.hasAttribute('data-qzsy-model-sheet') || el.hasAttribute('data-qzsy-model-sheet-clone')) continue;
509
- const s = getComputedStyle(el);
510
- if (s.display === 'none') continue;
511
- if (s.position !== 'fixed' && s.position !== 'absolute') continue;
512
- const menu = el.querySelector('[role="menu"]');
513
- if (!menu) continue;
504
+ const roots = new Set();
505
+ for (const menu of document.querySelectorAll('[role="menu"]')) {
514
506
  const radios = menu.querySelectorAll('button[role="menuitemradio"], [role="menuitemradio"], input[type="radio"]').length;
515
507
  if (radios < 1) continue;
516
508
  const text = menu.innerText || '';
517
- // 推理等级菜单只含 Off / Low / High / Max 等档位,不含模型搜索与模型分组
518
509
  if (/搜索模型|Search model|DeepSeek|claude|模型与推理等级/i.test(text)) continue;
519
- candidates.push(el);
510
+ // 推理菜单可能不是 body 直接子节点,向上找最近的 fixed/absolute 承载层
511
+ let el = menu;
512
+ while (el && el !== document.body) {
513
+ if (el.hasAttribute('data-qzsy-model-sheet') || el.hasAttribute('data-qzsy-model-sheet-clone')) break;
514
+ const s = getComputedStyle(el);
515
+ if (s.display === 'none') break;
516
+ if (s.position === 'fixed' || s.position === 'absolute') {
517
+ roots.add(el);
518
+ break;
519
+ }
520
+ el = el.parentElement;
521
+ }
520
522
  }
521
- return candidates[candidates.length - 1] || null;
523
+ return [...roots].pop() || null;
522
524
  };
523
525
 
524
526
  const hostBackdropPresent = () => {
@@ -756,6 +758,13 @@ export function setupMobileDrawer() {
756
758
 
757
759
  window.setTimeout(() => {
758
760
  if (disposed || token !== modelAdvanceToken || !modelAdvancing) return;
761
+ // 先给宿主一点时间自己切到推理菜单;出现了就直接 sheet 化
762
+ const hostReason = findReasoningPortal();
763
+ if (hostReason) {
764
+ reasoningStep = true;
765
+ applyModelSheet(hostReason);
766
+ return;
767
+ }
759
768
  const findReasonCell = () => [...document.querySelectorAll('button[role="menuitem"]')].find((b) => /推理等级|Reasoning effort|Reasoning level|推理强度/i.test(b.innerText || ''));
760
769
  const clickReasonCell = (cell) => {
761
770
  if (!cell) return;
@@ -820,7 +829,7 @@ export function setupMobileDrawer() {
820
829
  return;
821
830
  }
822
831
  reopenParent();
823
- }, 140);
832
+ }, 300);
824
833
  };
825
834
 
826
835
  const syncModelSheet = () => {
@@ -830,12 +839,12 @@ export function setupMobileDrawer() {
830
839
  return;
831
840
  }
832
841
  if (modelAdvancing) {
833
- if (reasoningStep) {
834
- const next = findReasoningPortal();
835
- if (next) {
836
- applyModelSheet(next);
837
- return;
838
- }
842
+ // 宿主可能在选中模型后自己就把推理菜单挂出来了;先直接接住
843
+ const next = findReasoningPortal();
844
+ if (next) {
845
+ reasoningStep = true;
846
+ applyModelSheet(next);
847
+ return;
839
848
  }
840
849
  if (modelPortal && !visiblePortal(modelPortal)) modelPortal = null;
841
850
  return;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@qzsy/dsh-bridge",
3
- "version": "3.5.3",
3
+ "version": "3.5.4",
4
4
  "description": "DSH 远程隧道云官方客户端插件:一条命令把本机 DeepSeek Harness 接入云端隧道(设备码绑定、短期令牌、本地门禁代理、HTTP/SSE/WebSocket 反向隧道)。",
5
- "releaseNotes": "【v3.5.3】模型 → 思考级别连招完成\\n• 推理菜单延迟挂载也接得住:点击后每 50ms 轮询,直到推理 sheet 出现\\n• 识别不再要求宽高 > 0,避免透明过渡期漏掉 portal\\n• 选完模型稳定进入推理等级 sheet,选完才关闭",
5
+ "releaseNotes": "【v3.5.4】模型 → 思考级别连招\\n• 接住宿主自己弹出的推理菜单,嵌套 portal 也能 sheet 化\\n• 给宿主 300ms 自动衔接窗口,没出现才回退点「推理等级」\\n• 当前模型 / 不同模型两条路径统一,推理选完才关闭",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",
8
8
  "exports": {