@qzsy/dsh-bridge 3.4.1 → 3.4.2
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 +15 -0
- package/client/client.js +38 -8
- package/client/mobile.js +45 -12
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## [v3.4.2] - 2026-11-02
|
|
4
|
+
|
|
5
|
+
### 🐛 修复
|
|
6
|
+
|
|
7
|
+
- **侧滑跟手**:水平拖动期间动态注册非 passive `touchmove` 阻止页面横滑/边缘返回,抽屉真正 1:1 跟手;纵向滚动不拦截。
|
|
8
|
+
- **首滑不卡**:手机首屏后在屏幕外预热展开侧栏,第一次侧滑不再在手指下触发宿主重排/挂载完整会话列表。
|
|
9
|
+
- **起手不跳**:拖动起点与 CSS `-102%` 关闭位对齐,修掉第一帧约 6px 跳变。
|
|
10
|
+
- **内容不弹**:拖动期间也强制侧栏内容 `100%` 宽,松手时不再出现 `280px → 300px` 宽度跳变。
|
|
11
|
+
- **松手更轻**:打开距离阈值从 90px 降到约 66px、速度阈值降低;移除松手时重复的展开点击;`box-shadow` 改为过渡。
|
|
12
|
+
|
|
13
|
+
### 🧪 验证
|
|
14
|
+
|
|
15
|
+
- 真实触摸事件模拟:预热展开、dx=20 首帧 -286、水平移动 `defaultPrevented=true`、纵向不拦截、短滑 110px 吸附打开、阴影过渡,7/7 通过。
|
|
16
|
+
|
|
17
|
+
|
|
3
18
|
## [v3.4.1] - 2026-11-02
|
|
4
19
|
|
|
5
20
|
### 🐛 修复
|
package/client/client.js
CHANGED
|
@@ -300,17 +300,19 @@ var MOBILE_DRAWER_CSS = `
|
|
|
300
300
|
transform: translateX(-102%) !important;
|
|
301
301
|
overflow: hidden !important;
|
|
302
302
|
overscroll-behavior: contain;
|
|
303
|
+
touch-action: pan-y;
|
|
303
304
|
will-change: transform;
|
|
304
305
|
}
|
|
305
306
|
/* \u9996\u5E27\u5148\u5C31\u4F4D\u3001\u4E0D\u64AD\u653E\u5165\u573A\u52A8\u753B\uFF0C\u907F\u514D\u9875\u9762\u52A0\u8F7D\u65F6\u4FA7\u680F\u4ECE 0 \u6ED1\u51FA */
|
|
306
307
|
body.qzsy-drawer-ready div[class*="_sidebarCol"] {
|
|
307
|
-
transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1) !important;
|
|
308
|
+
transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 280ms ease !important;
|
|
308
309
|
}
|
|
309
310
|
body.qzsy-drawer-open div[class*="_sidebarCol"] {
|
|
310
311
|
transform: translateX(0) !important;
|
|
311
312
|
box-shadow: 8px 0 32px rgba(0, 0, 0, 0.18) !important;
|
|
312
313
|
}
|
|
313
|
-
body.qzsy-drawer-open div[class*="_sidebarCol"] > div > div[class*="_root"]
|
|
314
|
+
body.qzsy-drawer-open div[class*="_sidebarCol"] > div > div[class*="_root"],
|
|
315
|
+
body.qzsy-drawer-dragging div[class*="_sidebarCol"] > div > div[class*="_root"] {
|
|
314
316
|
width: 100% !important;
|
|
315
317
|
}
|
|
316
318
|
|
|
@@ -514,6 +516,17 @@ function setupMobileDrawer() {
|
|
|
514
516
|
let modelChainUntil = 0;
|
|
515
517
|
let modelDrag = null;
|
|
516
518
|
let modelDragHandlers = null;
|
|
519
|
+
const preventDrawerScroll = (event) => {
|
|
520
|
+
if (!gesture || !event.cancelable) return;
|
|
521
|
+
const touch = event.touches?.[0];
|
|
522
|
+
if (!touch) return;
|
|
523
|
+
const dx = touch.clientX - gesture.startX;
|
|
524
|
+
const dy = touch.clientY - gesture.startY;
|
|
525
|
+
if (Math.abs(dx) > 4 && Math.abs(dx) > Math.abs(dy) * 1.2) event.preventDefault();
|
|
526
|
+
};
|
|
527
|
+
const removeDrawerPrevent = () => {
|
|
528
|
+
window.removeEventListener("touchmove", preventDrawerScroll);
|
|
529
|
+
};
|
|
517
530
|
const ensureElements = () => {
|
|
518
531
|
if (disposed || !document.body) return;
|
|
519
532
|
injectStyles();
|
|
@@ -561,8 +574,9 @@ function setupMobileDrawer() {
|
|
|
561
574
|
}
|
|
562
575
|
};
|
|
563
576
|
const setDragProgress = (x, width) => {
|
|
577
|
+
const span = Math.max(1, width * 1.02);
|
|
564
578
|
document.body.style.setProperty("--qzsy-drawer-x", `${x}px`);
|
|
565
|
-
document.body.style.setProperty("--qzsy-drawer-progress", String(Math.max(0, Math.min(1, 1 - Math.abs(x) /
|
|
579
|
+
document.body.style.setProperty("--qzsy-drawer-progress", String(Math.max(0, Math.min(1, 1 - Math.abs(x) / span))));
|
|
566
580
|
};
|
|
567
581
|
const clearDrag = () => {
|
|
568
582
|
document.body.classList.remove(DRAGGING_CLASS);
|
|
@@ -888,6 +902,7 @@ function setupMobileDrawer() {
|
|
|
888
902
|
active: false,
|
|
889
903
|
dx: 0
|
|
890
904
|
};
|
|
905
|
+
window.addEventListener("touchmove", preventDrawerScroll, { passive: false });
|
|
891
906
|
return;
|
|
892
907
|
}
|
|
893
908
|
if (!sidebar.contains(event.target)) return;
|
|
@@ -901,6 +916,7 @@ function setupMobileDrawer() {
|
|
|
901
916
|
active: false,
|
|
902
917
|
dx: 0
|
|
903
918
|
};
|
|
919
|
+
window.addEventListener("touchmove", preventDrawerScroll, { passive: false });
|
|
904
920
|
};
|
|
905
921
|
const onTouchMove = (event) => {
|
|
906
922
|
if (modelPortal || !gesture || disposed) return;
|
|
@@ -912,14 +928,17 @@ function setupMobileDrawer() {
|
|
|
912
928
|
if (Math.abs(dx) < DRAG_THRESHOLD) return;
|
|
913
929
|
if (Math.abs(dx) < Math.abs(dy) * 1.2) {
|
|
914
930
|
gesture = null;
|
|
931
|
+
removeDrawerPrevent();
|
|
915
932
|
return;
|
|
916
933
|
}
|
|
917
934
|
if (gesture.mode === "open" && dx <= 0) {
|
|
918
935
|
gesture = null;
|
|
936
|
+
removeDrawerPrevent();
|
|
919
937
|
return;
|
|
920
938
|
}
|
|
921
939
|
if (gesture.mode === "close" && dx >= 0) {
|
|
922
940
|
gesture = null;
|
|
941
|
+
removeDrawerPrevent();
|
|
923
942
|
return;
|
|
924
943
|
}
|
|
925
944
|
gesture.active = true;
|
|
@@ -928,8 +947,12 @@ function setupMobileDrawer() {
|
|
|
928
947
|
}
|
|
929
948
|
const width = Math.max(1, gesture.width);
|
|
930
949
|
let x;
|
|
931
|
-
if (gesture.mode === "open")
|
|
932
|
-
|
|
950
|
+
if (gesture.mode === "open") {
|
|
951
|
+
const startOffset = -width * 1.02;
|
|
952
|
+
x = Math.max(startOffset, Math.min(0, startOffset + dx));
|
|
953
|
+
} else {
|
|
954
|
+
x = Math.max(-width, Math.min(0, dx));
|
|
955
|
+
}
|
|
933
956
|
gesture.dx = dx;
|
|
934
957
|
setDragProgress(x, width);
|
|
935
958
|
};
|
|
@@ -937,15 +960,15 @@ function setupMobileDrawer() {
|
|
|
937
960
|
if (!gesture) return;
|
|
938
961
|
const current = gesture;
|
|
939
962
|
gesture = null;
|
|
963
|
+
removeDrawerPrevent();
|
|
940
964
|
if (!current.active) return;
|
|
941
965
|
const endX = touch ? touch.clientX : current.startX + current.dx;
|
|
942
966
|
const dx = endX - current.startX;
|
|
943
967
|
const elapsed = Math.max(1, Date.now() - current.startT);
|
|
944
968
|
const velocity = dx / elapsed;
|
|
945
|
-
const threshold = Math.min(
|
|
946
|
-
const openNow = current.mode === "open" ? dx > threshold || velocity > 0.
|
|
969
|
+
const threshold = Math.min(80, Math.max(50, current.width * 0.22));
|
|
970
|
+
const openNow = current.mode === "open" ? dx > threshold || velocity > 0.35 : !(dx < -Math.max(40, threshold * 0.6) || velocity < -0.3);
|
|
947
971
|
document.body.classList.toggle(OPEN_CLASS, openNow);
|
|
948
|
-
if (openNow) ensureSidebarExpanded();
|
|
949
972
|
syncAria();
|
|
950
973
|
suppressClickUntil = Date.now() + 250;
|
|
951
974
|
window.requestAnimationFrame(clearDrag);
|
|
@@ -971,6 +994,12 @@ function setupMobileDrawer() {
|
|
|
971
994
|
window.requestAnimationFrame(() => {
|
|
972
995
|
if (!disposed && document.body) document.body.classList.add("qzsy-drawer-ready");
|
|
973
996
|
});
|
|
997
|
+
const preExpandSidebar = () => {
|
|
998
|
+
if (disposed || !isMobileViewport()) return;
|
|
999
|
+
ensureSidebarExpanded();
|
|
1000
|
+
};
|
|
1001
|
+
window.setTimeout(preExpandSidebar, 600);
|
|
1002
|
+
window.setTimeout(preExpandSidebar, 1600);
|
|
974
1003
|
if (typeof MutationObserver === "function" && document.body) {
|
|
975
1004
|
observer = new MutationObserver(() => {
|
|
976
1005
|
if (!document.querySelector(`.${HEADER_CLASS}`) || !document.querySelector(`.${MENU_CLASS}`) || !document.querySelector(`.${BACKDROP_CLASS}`) || !document.getElementById(MOBILE_DRAWER_CSS_ID)) {
|
|
@@ -997,6 +1026,7 @@ function setupMobileDrawer() {
|
|
|
997
1026
|
window.removeEventListener("touchmove", onTouchMove);
|
|
998
1027
|
window.removeEventListener("touchend", onTouchEnd);
|
|
999
1028
|
window.removeEventListener("touchcancel", onTouchCancel);
|
|
1029
|
+
removeDrawerPrevent();
|
|
1000
1030
|
clearModelSheet();
|
|
1001
1031
|
clearDrag();
|
|
1002
1032
|
document.body?.classList.remove(OPEN_CLASS);
|
package/client/mobile.js
CHANGED
|
@@ -97,17 +97,19 @@ export const MOBILE_DRAWER_CSS = `
|
|
|
97
97
|
transform: translateX(-102%) !important;
|
|
98
98
|
overflow: hidden !important;
|
|
99
99
|
overscroll-behavior: contain;
|
|
100
|
+
touch-action: pan-y;
|
|
100
101
|
will-change: transform;
|
|
101
102
|
}
|
|
102
103
|
/* 首帧先就位、不播放入场动画,避免页面加载时侧栏从 0 滑出 */
|
|
103
104
|
body.qzsy-drawer-ready div[class*="_sidebarCol"] {
|
|
104
|
-
transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1) !important;
|
|
105
|
+
transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 280ms ease !important;
|
|
105
106
|
}
|
|
106
107
|
body.qzsy-drawer-open div[class*="_sidebarCol"] {
|
|
107
108
|
transform: translateX(0) !important;
|
|
108
109
|
box-shadow: 8px 0 32px rgba(0, 0, 0, 0.18) !important;
|
|
109
110
|
}
|
|
110
|
-
body.qzsy-drawer-open div[class*="_sidebarCol"] > div > div[class*="_root"]
|
|
111
|
+
body.qzsy-drawer-open div[class*="_sidebarCol"] > div > div[class*="_root"],
|
|
112
|
+
body.qzsy-drawer-dragging div[class*="_sidebarCol"] > div > div[class*="_root"] {
|
|
111
113
|
width: 100% !important;
|
|
112
114
|
}
|
|
113
115
|
|
|
@@ -321,6 +323,19 @@ export function setupMobileDrawer() {
|
|
|
321
323
|
let modelDrag = null;
|
|
322
324
|
let modelDragHandlers = null;
|
|
323
325
|
|
|
326
|
+
const preventDrawerScroll = (event) => {
|
|
327
|
+
if (!gesture || !event.cancelable) return;
|
|
328
|
+
const touch = event.touches?.[0];
|
|
329
|
+
if (!touch) return;
|
|
330
|
+
const dx = touch.clientX - gesture.startX;
|
|
331
|
+
const dy = touch.clientY - gesture.startY;
|
|
332
|
+
if (Math.abs(dx) > 4 && Math.abs(dx) > Math.abs(dy) * 1.2) event.preventDefault();
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const removeDrawerPrevent = () => {
|
|
336
|
+
window.removeEventListener('touchmove', preventDrawerScroll);
|
|
337
|
+
};
|
|
338
|
+
|
|
324
339
|
const ensureElements = () => {
|
|
325
340
|
if (disposed || !document.body) return;
|
|
326
341
|
injectStyles();
|
|
@@ -371,8 +386,9 @@ export function setupMobileDrawer() {
|
|
|
371
386
|
};
|
|
372
387
|
|
|
373
388
|
const setDragProgress = (x, width) => {
|
|
389
|
+
const span = Math.max(1, width * 1.02);
|
|
374
390
|
document.body.style.setProperty('--qzsy-drawer-x', `${x}px`);
|
|
375
|
-
document.body.style.setProperty('--qzsy-drawer-progress', String(Math.max(0, Math.min(1, 1 - Math.abs(x) /
|
|
391
|
+
document.body.style.setProperty('--qzsy-drawer-progress', String(Math.max(0, Math.min(1, 1 - Math.abs(x) / span))));
|
|
376
392
|
};
|
|
377
393
|
|
|
378
394
|
const clearDrag = () => {
|
|
@@ -729,6 +745,7 @@ export function setupMobileDrawer() {
|
|
|
729
745
|
active: false,
|
|
730
746
|
dx: 0,
|
|
731
747
|
};
|
|
748
|
+
window.addEventListener('touchmove', preventDrawerScroll, { passive: false });
|
|
732
749
|
return;
|
|
733
750
|
}
|
|
734
751
|
|
|
@@ -743,6 +760,7 @@ export function setupMobileDrawer() {
|
|
|
743
760
|
active: false,
|
|
744
761
|
dx: 0,
|
|
745
762
|
};
|
|
763
|
+
window.addEventListener('touchmove', preventDrawerScroll, { passive: false });
|
|
746
764
|
};
|
|
747
765
|
|
|
748
766
|
const onTouchMove = (event) => {
|
|
@@ -755,9 +773,9 @@ export function setupMobileDrawer() {
|
|
|
755
773
|
if (!gesture.active) {
|
|
756
774
|
if (Math.abs(dx) < DRAG_THRESHOLD) return;
|
|
757
775
|
// 纵向为主:交还给宿主滚动,本次不拦截
|
|
758
|
-
if (Math.abs(dx) < Math.abs(dy) * 1.2) { gesture = null; return; }
|
|
759
|
-
if (gesture.mode === 'open' && dx <= 0) { gesture = null; return; }
|
|
760
|
-
if (gesture.mode === 'close' && dx >= 0) { gesture = null; return; }
|
|
776
|
+
if (Math.abs(dx) < Math.abs(dy) * 1.2) { gesture = null; removeDrawerPrevent(); return; }
|
|
777
|
+
if (gesture.mode === 'open' && dx <= 0) { gesture = null; removeDrawerPrevent(); return; }
|
|
778
|
+
if (gesture.mode === 'close' && dx >= 0) { gesture = null; removeDrawerPrevent(); return; }
|
|
761
779
|
gesture.active = true;
|
|
762
780
|
suppressClickUntil = Date.now() + 450;
|
|
763
781
|
document.body.classList.add(DRAGGING_CLASS);
|
|
@@ -765,8 +783,13 @@ export function setupMobileDrawer() {
|
|
|
765
783
|
|
|
766
784
|
const width = Math.max(1, gesture.width);
|
|
767
785
|
let x;
|
|
768
|
-
if (gesture.mode === 'open')
|
|
769
|
-
|
|
786
|
+
if (gesture.mode === 'open') {
|
|
787
|
+
// 与 CSS 的 -102% 起点对齐,避免起手第一帧跳 6px
|
|
788
|
+
const startOffset = -width * 1.02;
|
|
789
|
+
x = Math.max(startOffset, Math.min(0, startOffset + dx));
|
|
790
|
+
} else {
|
|
791
|
+
x = Math.max(-width, Math.min(0, dx));
|
|
792
|
+
}
|
|
770
793
|
gesture.dx = dx;
|
|
771
794
|
setDragProgress(x, width);
|
|
772
795
|
};
|
|
@@ -775,19 +798,19 @@ export function setupMobileDrawer() {
|
|
|
775
798
|
if (!gesture) return;
|
|
776
799
|
const current = gesture;
|
|
777
800
|
gesture = null;
|
|
801
|
+
removeDrawerPrevent();
|
|
778
802
|
if (!current.active) return;
|
|
779
803
|
|
|
780
804
|
const endX = touch ? touch.clientX : current.startX + current.dx;
|
|
781
805
|
const dx = endX - current.startX;
|
|
782
806
|
const elapsed = Math.max(1, Date.now() - current.startT);
|
|
783
807
|
const velocity = dx / elapsed;
|
|
784
|
-
const threshold = Math.min(
|
|
808
|
+
const threshold = Math.min(80, Math.max(50, current.width * 0.22));
|
|
785
809
|
const openNow = current.mode === 'open'
|
|
786
|
-
? dx > threshold || velocity > 0.
|
|
787
|
-
: !(dx < -Math.max(
|
|
810
|
+
? dx > threshold || velocity > 0.35
|
|
811
|
+
: !(dx < -Math.max(40, threshold * 0.6) || velocity < -0.3);
|
|
788
812
|
|
|
789
813
|
document.body.classList.toggle(OPEN_CLASS, openNow);
|
|
790
|
-
if (openNow) ensureSidebarExpanded();
|
|
791
814
|
syncAria();
|
|
792
815
|
suppressClickUntil = Date.now() + 250;
|
|
793
816
|
// 等一帧让浏览器接住当前拖动位置,再撤掉拖动样式做吸附动画
|
|
@@ -820,6 +843,15 @@ export function setupMobileDrawer() {
|
|
|
820
843
|
if (!disposed && document.body) document.body.classList.add('qzsy-drawer-ready');
|
|
821
844
|
});
|
|
822
845
|
|
|
846
|
+
// 预热:手机端首屏后把宿主侧栏展开好(它在屏幕外),这样第一次侧滑不会在
|
|
847
|
+
// 手指下触发宿主重排/挂载完整会话列表,起手更稳。
|
|
848
|
+
const preExpandSidebar = () => {
|
|
849
|
+
if (disposed || !isMobileViewport()) return;
|
|
850
|
+
ensureSidebarExpanded();
|
|
851
|
+
};
|
|
852
|
+
window.setTimeout(preExpandSidebar, 600);
|
|
853
|
+
window.setTimeout(preExpandSidebar, 1600);
|
|
854
|
+
|
|
823
855
|
if (typeof MutationObserver === 'function' && document.body) {
|
|
824
856
|
observer = new MutationObserver(() => {
|
|
825
857
|
if (!document.querySelector(`.${HEADER_CLASS}`) || !document.querySelector(`.${MENU_CLASS}`) || !document.querySelector(`.${BACKDROP_CLASS}`) || !document.getElementById(MOBILE_DRAWER_CSS_ID)) {
|
|
@@ -845,6 +877,7 @@ export function setupMobileDrawer() {
|
|
|
845
877
|
window.removeEventListener('touchmove', onTouchMove);
|
|
846
878
|
window.removeEventListener('touchend', onTouchEnd);
|
|
847
879
|
window.removeEventListener('touchcancel', onTouchCancel);
|
|
880
|
+
removeDrawerPrevent();
|
|
848
881
|
clearModelSheet();
|
|
849
882
|
clearDrag();
|
|
850
883
|
document.body?.classList.remove(OPEN_CLASS);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qzsy/dsh-bridge",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.2",
|
|
4
4
|
"description": "DSH 远程隧道云官方客户端插件:一条命令把本机 DeepSeek Harness 接入云端隧道(设备码绑定、短期令牌、本地门禁代理、HTTP/SSE/WebSocket 反向隧道)。",
|
|
5
|
-
"releaseNotes": "【v3.4.1
|
|
5
|
+
"releaseNotes": "【v3.4.2】移动端侧滑跟手优化\\n• 水平拖动阻止页面横滑/边缘返回,抽屉 1:1 跟手;纵向滚动不受影响\\n• 首屏后在屏幕外预热展开侧栏,第一次侧滑不再卡顿\\n• 起手不再跳 6px,松手时内容宽度不再 280→300 跳变\\n• 打开阈值降到约 66px,速度阈值降低,松手吸附更轻快",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
8
8
|
"exports": {
|