@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 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) / width))));
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") x = Math.max(-width, Math.min(0, -width + dx));
932
- else x = Math.max(-width, Math.min(0, dx));
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(90, Math.max(60, current.width * 0.3));
946
- const openNow = current.mode === "open" ? dx > threshold || velocity > 0.45 : !(dx < -Math.max(50, threshold * 0.7) || velocity < -0.35);
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) / width))));
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') x = Math.max(-width, Math.min(0, -width + dx));
769
- else x = Math.max(-width, Math.min(0, dx));
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(90, Math.max(60, current.width * 0.3));
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.45
787
- : !(dx < -Math.max(50, threshold * 0.7) || velocity < -0.35);
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.1",
3
+ "version": "3.4.2",
4
4
  "description": "DSH 远程隧道云官方客户端插件:一条命令把本机 DeepSeek Harness 接入云端隧道(设备码绑定、短期令牌、本地门禁代理、HTTP/SSE/WebSocket 反向隧道)。",
5
- "releaseNotes": "【v3.4.1】模型 sheet 识别收紧\\n• 模型菜单改造后立即清空触发链,关闭后再开账号菜单不会误套底部 sheet\\n• 优先按 data-model-id 等模型标记定位;无搜索框宿主的兜底窗口收紧到 2.5 秒\\n• 第一层「模型 / 推理等级」popover 保持不变",
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": {