@qzsy/dsh-bridge 3.4.0 → 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 +27 -0
- package/client/client.js +44 -12
- package/client/mobile.js +54 -17
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
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
|
+
|
|
18
|
+
## [v3.4.1] - 2026-11-02
|
|
19
|
+
|
|
20
|
+
### 🐛 修复
|
|
21
|
+
|
|
22
|
+
- **模型 sheet 识别收紧**:模型菜单被改造成 sheet 后立刻清空触发链,并优先用 `data-model-id` 等模型标记定位;关闭模型面板后紧接着打开账号等其它菜单,不会误套用底部 sheet。
|
|
23
|
+
- **无搜索框宿主兜底**:触发链窗口从 6 秒收紧到 2.5 秒,只对「模型触发后出现、且行数 > 2」的菜单生效。
|
|
24
|
+
|
|
25
|
+
### 🧪 验证
|
|
26
|
+
|
|
27
|
+
- 实测:第一层「模型 / 推理等级」popover 不改造;二级模型菜单 sheet 化;关闭后再开账号菜单,`data-qzsy-model-sheet` 数量为 0,无残留遮罩。
|
|
28
|
+
|
|
29
|
+
|
|
3
30
|
## [v3.4.0] - 2026-11-02
|
|
4
31
|
|
|
5
32
|
### ✨ 新增
|
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);
|
|
@@ -624,7 +638,6 @@ function setupMobileDrawer() {
|
|
|
624
638
|
if ((s.position === "fixed" || s.position === "absolute") && portal.getBoundingClientRect().width > 120) return portal;
|
|
625
639
|
}
|
|
626
640
|
}
|
|
627
|
-
if (Date.now() > modelChainUntil) return null;
|
|
628
641
|
const candidates = [];
|
|
629
642
|
for (const el of document.body.children) {
|
|
630
643
|
if (!(el instanceof HTMLElement)) continue;
|
|
@@ -637,13 +650,15 @@ function setupMobileDrawer() {
|
|
|
637
650
|
const text = menu.innerText || "";
|
|
638
651
|
const rows = menu.querySelectorAll('[role="menuitem"], [role="option"], button, a, [tabindex]').length;
|
|
639
652
|
const hasSearch = Boolean(el.querySelector('input[placeholder*="\u641C\u7D22\u6A21\u578B"], input[placeholder*="Search model"]'));
|
|
653
|
+
const hasModelMarker = Boolean(el.querySelector("[data-model-id], [data-provider-id], [data-model], [data-model-name]"));
|
|
640
654
|
if (/推理等级|Reasoning effort|推理强度/i.test(text) && rows <= 2) continue;
|
|
641
655
|
const rect = el.getBoundingClientRect();
|
|
642
656
|
const alreadySheet = rect.width >= window.innerWidth - 2 && Math.abs(rect.bottom - window.innerHeight) < 2 && rect.height > 120;
|
|
643
657
|
if (alreadySheet && !hasSearch) continue;
|
|
644
|
-
|
|
658
|
+
const chained = Date.now() < modelChainUntil && rows > 2;
|
|
659
|
+
if (hasSearch || hasModelMarker || chained) candidates.push({ el, rows, hasSearch, hasModelMarker });
|
|
645
660
|
}
|
|
646
|
-
candidates.sort((a, b) => Number(b.hasSearch) - Number(a.hasSearch) || b.rows - a.rows);
|
|
661
|
+
candidates.sort((a, b) => Number(b.hasSearch) - Number(a.hasSearch) || Number(b.hasModelMarker) - Number(a.hasModelMarker) || b.rows - a.rows);
|
|
647
662
|
return candidates[0]?.el || null;
|
|
648
663
|
};
|
|
649
664
|
const hostBackdropPresent = () => {
|
|
@@ -783,6 +798,7 @@ function setupMobileDrawer() {
|
|
|
783
798
|
if (modelPortal === portal) return;
|
|
784
799
|
clearModelSheet();
|
|
785
800
|
modelPortal = portal;
|
|
801
|
+
modelChainUntil = 0;
|
|
786
802
|
portal.setAttribute("data-qzsy-model-sheet", "");
|
|
787
803
|
portal.classList.add("qzsy-model-sheet");
|
|
788
804
|
attachModelDrag(portal);
|
|
@@ -821,7 +837,7 @@ function setupMobileDrawer() {
|
|
|
821
837
|
return;
|
|
822
838
|
}
|
|
823
839
|
if (isModelTrigger(target)) {
|
|
824
|
-
modelChainUntil = Date.now() +
|
|
840
|
+
modelChainUntil = Date.now() + 2500;
|
|
825
841
|
window.setTimeout(syncModelSheet, 30);
|
|
826
842
|
window.setTimeout(syncModelSheet, 180);
|
|
827
843
|
}
|
|
@@ -886,6 +902,7 @@ function setupMobileDrawer() {
|
|
|
886
902
|
active: false,
|
|
887
903
|
dx: 0
|
|
888
904
|
};
|
|
905
|
+
window.addEventListener("touchmove", preventDrawerScroll, { passive: false });
|
|
889
906
|
return;
|
|
890
907
|
}
|
|
891
908
|
if (!sidebar.contains(event.target)) return;
|
|
@@ -899,6 +916,7 @@ function setupMobileDrawer() {
|
|
|
899
916
|
active: false,
|
|
900
917
|
dx: 0
|
|
901
918
|
};
|
|
919
|
+
window.addEventListener("touchmove", preventDrawerScroll, { passive: false });
|
|
902
920
|
};
|
|
903
921
|
const onTouchMove = (event) => {
|
|
904
922
|
if (modelPortal || !gesture || disposed) return;
|
|
@@ -910,14 +928,17 @@ function setupMobileDrawer() {
|
|
|
910
928
|
if (Math.abs(dx) < DRAG_THRESHOLD) return;
|
|
911
929
|
if (Math.abs(dx) < Math.abs(dy) * 1.2) {
|
|
912
930
|
gesture = null;
|
|
931
|
+
removeDrawerPrevent();
|
|
913
932
|
return;
|
|
914
933
|
}
|
|
915
934
|
if (gesture.mode === "open" && dx <= 0) {
|
|
916
935
|
gesture = null;
|
|
936
|
+
removeDrawerPrevent();
|
|
917
937
|
return;
|
|
918
938
|
}
|
|
919
939
|
if (gesture.mode === "close" && dx >= 0) {
|
|
920
940
|
gesture = null;
|
|
941
|
+
removeDrawerPrevent();
|
|
921
942
|
return;
|
|
922
943
|
}
|
|
923
944
|
gesture.active = true;
|
|
@@ -926,8 +947,12 @@ function setupMobileDrawer() {
|
|
|
926
947
|
}
|
|
927
948
|
const width = Math.max(1, gesture.width);
|
|
928
949
|
let x;
|
|
929
|
-
if (gesture.mode === "open")
|
|
930
|
-
|
|
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
|
+
}
|
|
931
956
|
gesture.dx = dx;
|
|
932
957
|
setDragProgress(x, width);
|
|
933
958
|
};
|
|
@@ -935,15 +960,15 @@ function setupMobileDrawer() {
|
|
|
935
960
|
if (!gesture) return;
|
|
936
961
|
const current = gesture;
|
|
937
962
|
gesture = null;
|
|
963
|
+
removeDrawerPrevent();
|
|
938
964
|
if (!current.active) return;
|
|
939
965
|
const endX = touch ? touch.clientX : current.startX + current.dx;
|
|
940
966
|
const dx = endX - current.startX;
|
|
941
967
|
const elapsed = Math.max(1, Date.now() - current.startT);
|
|
942
968
|
const velocity = dx / elapsed;
|
|
943
|
-
const threshold = Math.min(
|
|
944
|
-
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);
|
|
945
971
|
document.body.classList.toggle(OPEN_CLASS, openNow);
|
|
946
|
-
if (openNow) ensureSidebarExpanded();
|
|
947
972
|
syncAria();
|
|
948
973
|
suppressClickUntil = Date.now() + 250;
|
|
949
974
|
window.requestAnimationFrame(clearDrag);
|
|
@@ -969,6 +994,12 @@ function setupMobileDrawer() {
|
|
|
969
994
|
window.requestAnimationFrame(() => {
|
|
970
995
|
if (!disposed && document.body) document.body.classList.add("qzsy-drawer-ready");
|
|
971
996
|
});
|
|
997
|
+
const preExpandSidebar = () => {
|
|
998
|
+
if (disposed || !isMobileViewport()) return;
|
|
999
|
+
ensureSidebarExpanded();
|
|
1000
|
+
};
|
|
1001
|
+
window.setTimeout(preExpandSidebar, 600);
|
|
1002
|
+
window.setTimeout(preExpandSidebar, 1600);
|
|
972
1003
|
if (typeof MutationObserver === "function" && document.body) {
|
|
973
1004
|
observer = new MutationObserver(() => {
|
|
974
1005
|
if (!document.querySelector(`.${HEADER_CLASS}`) || !document.querySelector(`.${MENU_CLASS}`) || !document.querySelector(`.${BACKDROP_CLASS}`) || !document.getElementById(MOBILE_DRAWER_CSS_ID)) {
|
|
@@ -995,6 +1026,7 @@ function setupMobileDrawer() {
|
|
|
995
1026
|
window.removeEventListener("touchmove", onTouchMove);
|
|
996
1027
|
window.removeEventListener("touchend", onTouchEnd);
|
|
997
1028
|
window.removeEventListener("touchcancel", onTouchCancel);
|
|
1029
|
+
removeDrawerPrevent();
|
|
998
1030
|
clearModelSheet();
|
|
999
1031
|
clearDrag();
|
|
1000
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 = () => {
|
|
@@ -440,8 +456,7 @@ export function setupMobileDrawer() {
|
|
|
440
456
|
if ((s.position === 'fixed' || s.position === 'absolute') && portal.getBoundingClientRect().width > 120) return portal;
|
|
441
457
|
}
|
|
442
458
|
}
|
|
443
|
-
//
|
|
444
|
-
if (Date.now() > modelChainUntil) return null;
|
|
459
|
+
// 无搜索框的宿主(如移动端版本)用「模型标记 / 模型触发链 + 菜单行数」兜底
|
|
445
460
|
const candidates = [];
|
|
446
461
|
for (const el of document.body.children) {
|
|
447
462
|
if (!(el instanceof HTMLElement)) continue;
|
|
@@ -454,15 +469,19 @@ export function setupMobileDrawer() {
|
|
|
454
469
|
const text = menu.innerText || '';
|
|
455
470
|
const rows = menu.querySelectorAll('[role="menuitem"], [role="option"], button, a, [tabindex]').length;
|
|
456
471
|
const hasSearch = Boolean(el.querySelector('input[placeholder*="搜索模型"], input[placeholder*="Search model"]'));
|
|
472
|
+
const hasModelMarker = Boolean(el.querySelector('[data-model-id], [data-provider-id], [data-model], [data-model-name]'));
|
|
457
473
|
// 排除「模型 / 推理等级」两行的第一层 popover
|
|
458
474
|
if (/推理等级|Reasoning effort|推理强度/i.test(text) && rows <= 2) continue;
|
|
459
475
|
// 已经是全宽底部 sheet 的宿主菜单不再重复改造
|
|
460
476
|
const rect = el.getBoundingClientRect();
|
|
461
477
|
const alreadySheet = rect.width >= window.innerWidth - 2 && Math.abs(rect.bottom - window.innerHeight) < 2 && rect.height > 120;
|
|
462
478
|
if (alreadySheet && !hasSearch) continue;
|
|
463
|
-
|
|
479
|
+
const chained = Date.now() < modelChainUntil && rows > 2;
|
|
480
|
+
if (hasSearch || hasModelMarker || chained) candidates.push({ el, rows, hasSearch, hasModelMarker });
|
|
464
481
|
}
|
|
465
|
-
candidates.sort((a, b) => Number(b.hasSearch) - Number(a.hasSearch)
|
|
482
|
+
candidates.sort((a, b) => Number(b.hasSearch) - Number(a.hasSearch)
|
|
483
|
+
|| Number(b.hasModelMarker) - Number(a.hasModelMarker)
|
|
484
|
+
|| b.rows - a.rows);
|
|
466
485
|
return candidates[0]?.el || null;
|
|
467
486
|
};
|
|
468
487
|
|
|
@@ -602,6 +621,7 @@ export function setupMobileDrawer() {
|
|
|
602
621
|
if (modelPortal === portal) return;
|
|
603
622
|
clearModelSheet();
|
|
604
623
|
modelPortal = portal;
|
|
624
|
+
modelChainUntil = 0;
|
|
605
625
|
portal.setAttribute('data-qzsy-model-sheet', '');
|
|
606
626
|
portal.classList.add('qzsy-model-sheet');
|
|
607
627
|
attachModelDrag(portal);
|
|
@@ -645,7 +665,7 @@ export function setupMobileDrawer() {
|
|
|
645
665
|
|
|
646
666
|
// 模型切换器:记录触发链,等宿主把二级菜单 portal 挂到 body 后再改造成 sheet
|
|
647
667
|
if (isModelTrigger(target)) {
|
|
648
|
-
modelChainUntil = Date.now() +
|
|
668
|
+
modelChainUntil = Date.now() + 2500;
|
|
649
669
|
window.setTimeout(syncModelSheet, 30);
|
|
650
670
|
window.setTimeout(syncModelSheet, 180);
|
|
651
671
|
}
|
|
@@ -725,6 +745,7 @@ export function setupMobileDrawer() {
|
|
|
725
745
|
active: false,
|
|
726
746
|
dx: 0,
|
|
727
747
|
};
|
|
748
|
+
window.addEventListener('touchmove', preventDrawerScroll, { passive: false });
|
|
728
749
|
return;
|
|
729
750
|
}
|
|
730
751
|
|
|
@@ -739,6 +760,7 @@ export function setupMobileDrawer() {
|
|
|
739
760
|
active: false,
|
|
740
761
|
dx: 0,
|
|
741
762
|
};
|
|
763
|
+
window.addEventListener('touchmove', preventDrawerScroll, { passive: false });
|
|
742
764
|
};
|
|
743
765
|
|
|
744
766
|
const onTouchMove = (event) => {
|
|
@@ -751,9 +773,9 @@ export function setupMobileDrawer() {
|
|
|
751
773
|
if (!gesture.active) {
|
|
752
774
|
if (Math.abs(dx) < DRAG_THRESHOLD) return;
|
|
753
775
|
// 纵向为主:交还给宿主滚动,本次不拦截
|
|
754
|
-
if (Math.abs(dx) < Math.abs(dy) * 1.2) { gesture = null; return; }
|
|
755
|
-
if (gesture.mode === 'open' && dx <= 0) { gesture = null; return; }
|
|
756
|
-
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; }
|
|
757
779
|
gesture.active = true;
|
|
758
780
|
suppressClickUntil = Date.now() + 450;
|
|
759
781
|
document.body.classList.add(DRAGGING_CLASS);
|
|
@@ -761,8 +783,13 @@ export function setupMobileDrawer() {
|
|
|
761
783
|
|
|
762
784
|
const width = Math.max(1, gesture.width);
|
|
763
785
|
let x;
|
|
764
|
-
if (gesture.mode === 'open')
|
|
765
|
-
|
|
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
|
+
}
|
|
766
793
|
gesture.dx = dx;
|
|
767
794
|
setDragProgress(x, width);
|
|
768
795
|
};
|
|
@@ -771,19 +798,19 @@ export function setupMobileDrawer() {
|
|
|
771
798
|
if (!gesture) return;
|
|
772
799
|
const current = gesture;
|
|
773
800
|
gesture = null;
|
|
801
|
+
removeDrawerPrevent();
|
|
774
802
|
if (!current.active) return;
|
|
775
803
|
|
|
776
804
|
const endX = touch ? touch.clientX : current.startX + current.dx;
|
|
777
805
|
const dx = endX - current.startX;
|
|
778
806
|
const elapsed = Math.max(1, Date.now() - current.startT);
|
|
779
807
|
const velocity = dx / elapsed;
|
|
780
|
-
const threshold = Math.min(
|
|
808
|
+
const threshold = Math.min(80, Math.max(50, current.width * 0.22));
|
|
781
809
|
const openNow = current.mode === 'open'
|
|
782
|
-
? dx > threshold || velocity > 0.
|
|
783
|
-
: !(dx < -Math.max(
|
|
810
|
+
? dx > threshold || velocity > 0.35
|
|
811
|
+
: !(dx < -Math.max(40, threshold * 0.6) || velocity < -0.3);
|
|
784
812
|
|
|
785
813
|
document.body.classList.toggle(OPEN_CLASS, openNow);
|
|
786
|
-
if (openNow) ensureSidebarExpanded();
|
|
787
814
|
syncAria();
|
|
788
815
|
suppressClickUntil = Date.now() + 250;
|
|
789
816
|
// 等一帧让浏览器接住当前拖动位置,再撤掉拖动样式做吸附动画
|
|
@@ -816,6 +843,15 @@ export function setupMobileDrawer() {
|
|
|
816
843
|
if (!disposed && document.body) document.body.classList.add('qzsy-drawer-ready');
|
|
817
844
|
});
|
|
818
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
|
+
|
|
819
855
|
if (typeof MutationObserver === 'function' && document.body) {
|
|
820
856
|
observer = new MutationObserver(() => {
|
|
821
857
|
if (!document.querySelector(`.${HEADER_CLASS}`) || !document.querySelector(`.${MENU_CLASS}`) || !document.querySelector(`.${BACKDROP_CLASS}`) || !document.getElementById(MOBILE_DRAWER_CSS_ID)) {
|
|
@@ -841,6 +877,7 @@ export function setupMobileDrawer() {
|
|
|
841
877
|
window.removeEventListener('touchmove', onTouchMove);
|
|
842
878
|
window.removeEventListener('touchend', onTouchEnd);
|
|
843
879
|
window.removeEventListener('touchcancel', onTouchCancel);
|
|
880
|
+
removeDrawerPrevent();
|
|
844
881
|
clearModelSheet();
|
|
845
882
|
clearDrag();
|
|
846
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.
|
|
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": {
|