@qzsy/dsh-bridge 3.5.2 → 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 +27 -0
- package/client/client.js +40 -22
- package/client/mobile.js +43 -24
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
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
|
+
|
|
17
|
+
## [v3.5.3] - 2026-11-02
|
|
18
|
+
|
|
19
|
+
### 🐛 修复
|
|
20
|
+
|
|
21
|
+
- **推理菜单延迟挂载也能接住**:宿主的推理菜单 portal 在过渡期可能是 0 尺寸/延迟一帧才可测量,原来只在 `MutationObserver` 触发时找一次会漏掉;现在点击「推理等级」后每 50ms 轮询、最多 1.2s,直到推理 sheet 出现。
|
|
22
|
+
- **推理 portal 识别放宽**:不再要求宽高 > 0,只要求 `display` 可见且 `position: fixed/absolute`,避免透明过渡期被误判为不存在。
|
|
23
|
+
- 清掉旧 sheet 快照和过渡遮罩的时机保持到推理 sheet 真正套用之后。
|
|
24
|
+
|
|
25
|
+
### 🧪 验证
|
|
26
|
+
|
|
27
|
+
- 独立宿主模拟 4/4;待安装后验证真实 DSH 的完整链路。
|
|
28
|
+
|
|
29
|
+
|
|
3
30
|
## [v3.5.2] - 2026-11-02
|
|
4
31
|
|
|
5
32
|
### 🐛 修复
|
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
|
|
681
|
-
for (const
|
|
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
|
-
if (!visiblePortal(el)) continue;
|
|
686
|
-
const s = getComputedStyle(el);
|
|
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
|
-
|
|
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
|
|
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;
|
|
@@ -938,13 +946,24 @@ function setupMobileDrawer() {
|
|
|
938
946
|
cell.click();
|
|
939
947
|
} catch {
|
|
940
948
|
}
|
|
941
|
-
|
|
949
|
+
let reasonTries = 0;
|
|
950
|
+
const applyReasonPortal = () => {
|
|
942
951
|
if (disposed || token !== modelAdvanceToken) return;
|
|
943
|
-
if (
|
|
952
|
+
if (modelPortal && modelPortal.getAttribute("data-qzsy-model-step") === "reasoning") return;
|
|
953
|
+
const found = findReasoningPortal();
|
|
954
|
+
if (found) {
|
|
955
|
+
applyModelSheet(found);
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
reasonTries += 1;
|
|
959
|
+
if (reasonTries > 24) {
|
|
944
960
|
cancelAdvanceOverlay();
|
|
945
961
|
clearModelSheet();
|
|
962
|
+
return;
|
|
946
963
|
}
|
|
947
|
-
|
|
964
|
+
window.setTimeout(applyReasonPortal, 50);
|
|
965
|
+
};
|
|
966
|
+
window.setTimeout(applyReasonPortal, 30);
|
|
948
967
|
};
|
|
949
968
|
let attempts = 0;
|
|
950
969
|
const pollReasonCell = () => {
|
|
@@ -987,7 +1006,7 @@ function setupMobileDrawer() {
|
|
|
987
1006
|
return;
|
|
988
1007
|
}
|
|
989
1008
|
reopenParent();
|
|
990
|
-
},
|
|
1009
|
+
}, 300);
|
|
991
1010
|
};
|
|
992
1011
|
const syncModelSheet = () => {
|
|
993
1012
|
if (disposed) return;
|
|
@@ -996,12 +1015,11 @@ function setupMobileDrawer() {
|
|
|
996
1015
|
return;
|
|
997
1016
|
}
|
|
998
1017
|
if (modelAdvancing) {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
}
|
|
1018
|
+
const next = findReasoningPortal();
|
|
1019
|
+
if (next) {
|
|
1020
|
+
reasoningStep = true;
|
|
1021
|
+
applyModelSheet(next);
|
|
1022
|
+
return;
|
|
1005
1023
|
}
|
|
1006
1024
|
if (modelPortal && !visiblePortal(modelPortal)) modelPortal = null;
|
|
1007
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
|
|
505
|
-
for (const
|
|
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
|
-
if (!visiblePortal(el)) continue;
|
|
510
|
-
const s = getComputedStyle(el);
|
|
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
|
-
|
|
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
|
|
523
|
+
return [...roots].pop() || null;
|
|
522
524
|
};
|
|
523
525
|
|
|
524
526
|
const hostBackdropPresent = () => {
|
|
@@ -756,19 +758,36 @@ 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;
|
|
762
771
|
reasoningStep = true;
|
|
763
772
|
try { cell.click(); } catch { /* 忽略 */ }
|
|
764
|
-
|
|
773
|
+
let reasonTries = 0;
|
|
774
|
+
const applyReasonPortal = () => {
|
|
765
775
|
if (disposed || token !== modelAdvanceToken) return;
|
|
766
|
-
if (
|
|
767
|
-
|
|
776
|
+
if (modelPortal && modelPortal.getAttribute('data-qzsy-model-step') === 'reasoning') return;
|
|
777
|
+
const found = findReasoningPortal();
|
|
778
|
+
if (found) {
|
|
779
|
+
applyModelSheet(found);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
reasonTries += 1;
|
|
783
|
+
if (reasonTries > 24) {
|
|
768
784
|
cancelAdvanceOverlay();
|
|
769
785
|
clearModelSheet();
|
|
786
|
+
return;
|
|
770
787
|
}
|
|
771
|
-
|
|
788
|
+
window.setTimeout(applyReasonPortal, 50);
|
|
789
|
+
};
|
|
790
|
+
window.setTimeout(applyReasonPortal, 30);
|
|
772
791
|
};
|
|
773
792
|
let attempts = 0;
|
|
774
793
|
const pollReasonCell = () => {
|
|
@@ -810,7 +829,7 @@ export function setupMobileDrawer() {
|
|
|
810
829
|
return;
|
|
811
830
|
}
|
|
812
831
|
reopenParent();
|
|
813
|
-
},
|
|
832
|
+
}, 300);
|
|
814
833
|
};
|
|
815
834
|
|
|
816
835
|
const syncModelSheet = () => {
|
|
@@ -820,12 +839,12 @@ export function setupMobileDrawer() {
|
|
|
820
839
|
return;
|
|
821
840
|
}
|
|
822
841
|
if (modelAdvancing) {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
842
|
+
// 宿主可能在选中模型后自己就把推理菜单挂出来了;先直接接住
|
|
843
|
+
const next = findReasoningPortal();
|
|
844
|
+
if (next) {
|
|
845
|
+
reasoningStep = true;
|
|
846
|
+
applyModelSheet(next);
|
|
847
|
+
return;
|
|
829
848
|
}
|
|
830
849
|
if (modelPortal && !visiblePortal(modelPortal)) modelPortal = null;
|
|
831
850
|
return;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qzsy/dsh-bridge",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.4",
|
|
4
4
|
"description": "DSH 远程隧道云官方客户端插件:一条命令把本机 DeepSeek Harness 接入云端隧道(设备码绑定、短期令牌、本地门禁代理、HTTP/SSE/WebSocket 反向隧道)。",
|
|
5
|
-
"releaseNotes": "【v3.5.
|
|
5
|
+
"releaseNotes": "【v3.5.4】模型 → 思考级别连招\\n• 接住宿主自己弹出的推理菜单,嵌套 portal 也能 sheet 化\\n• 给宿主 300ms 自动衔接窗口,没出现才回退点「推理等级」\\n• 当前模型 / 不同模型两条路径统一,推理选完才关闭",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
8
8
|
"exports": {
|