@qzsy/dsh-bridge 3.3.0 → 3.4.0
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 +31 -0
- package/README.md +3 -0
- package/client/client.js +540 -19
- package/client/mobile.js +570 -23
- package/package.json +2 -2
package/client/client.js
CHANGED
|
@@ -220,27 +220,63 @@ var MOBILE_MAX_WIDTH = 767;
|
|
|
220
220
|
var MOBILE_DRAWER_CSS_ID = "qzsy-mobile-drawer-styles";
|
|
221
221
|
var MOBILE_UI_FLAG = "data-qzsy-mobile-drawer";
|
|
222
222
|
var OPEN_CLASS = "qzsy-drawer-open";
|
|
223
|
+
var DRAGGING_CLASS = "qzsy-drawer-dragging";
|
|
224
|
+
var HEADER_CLASS = "qzsy-mobile-header";
|
|
223
225
|
var MENU_CLASS = "qzsy-mobile-menu-btn";
|
|
224
226
|
var BACKDROP_CLASS = "qzsy-mobile-backdrop";
|
|
225
227
|
var STYLE_PLUGIN = "@qzsy/dsh-bridge";
|
|
228
|
+
var EDGE_START_ZONE = 28;
|
|
229
|
+
var DRAG_THRESHOLD = 8;
|
|
226
230
|
var MOBILE_DRAWER_CSS = `
|
|
227
|
-
|
|
228
|
-
|
|
231
|
+
:root {
|
|
232
|
+
--qzsy-safe-top: env(safe-area-inset-top, 0px);
|
|
233
|
+
--qzsy-safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
234
|
+
--qzsy-safe-left: env(safe-area-inset-left, 0px);
|
|
235
|
+
--qzsy-safe-right: env(safe-area-inset-right, 0px);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.qzsy-mobile-header,
|
|
239
|
+
.qzsy-mobile-backdrop,
|
|
240
|
+
.qzsy-model-sheet-backdrop {
|
|
229
241
|
display: none;
|
|
230
242
|
}
|
|
231
243
|
|
|
244
|
+
/* \u83DC\u5355\u6309\u94AE\u5BB9\u5668\uFF1A\u59CB\u7EC8\u5728\u5BBF\u4E3B\u4E4B\u4E0A\uFF1B\u5BB9\u5668\u81EA\u8EAB\u4E0D\u62E6\u622A\u70B9\u51FB */
|
|
245
|
+
.qzsy-mobile-header {
|
|
246
|
+
position: fixed;
|
|
247
|
+
top: 0;
|
|
248
|
+
left: 0;
|
|
249
|
+
right: 0;
|
|
250
|
+
z-index: 65;
|
|
251
|
+
box-sizing: border-box;
|
|
252
|
+
align-items: flex-end;
|
|
253
|
+
padding-top: 10px;
|
|
254
|
+
padding-top: calc(10px + var(--qzsy-safe-top, 0px));
|
|
255
|
+
padding-right: 10px;
|
|
256
|
+
padding-right: calc(10px + var(--qzsy-safe-right, 0px));
|
|
257
|
+
padding-bottom: 2px;
|
|
258
|
+
padding-left: 10px;
|
|
259
|
+
padding-left: calc(10px + var(--qzsy-safe-left, 0px));
|
|
260
|
+
pointer-events: none;
|
|
261
|
+
}
|
|
262
|
+
|
|
232
263
|
@media (max-width: 767px) {
|
|
233
|
-
|
|
264
|
+
.qzsy-mobile-header { display: flex; }
|
|
265
|
+
|
|
266
|
+
/* 1. \u5DE6\u4FA7\u680F\u8131\u79BB grid\uFF0C\u5185\u5BB9\u5360\u6EE1\uFF1B\u9876\u90E8\u7ED9\u83DC\u5355\u6309\u94AE\u7559\u51FA\u5B89\u5168\u533A\u9AD8\u5EA6 */
|
|
234
267
|
[data-slot="root"] > div[class*="_frame"] {
|
|
235
268
|
grid-template-columns: 0px minmax(0px, 1fr) minmax(0px, 0px) !important;
|
|
236
|
-
padding-top: 52px !important;
|
|
237
269
|
box-sizing: border-box !important;
|
|
270
|
+
padding-top: 52px !important;
|
|
271
|
+
padding-top: calc(52px + var(--qzsy-safe-top, 0px)) !important;
|
|
272
|
+
padding-bottom: 0 !important;
|
|
273
|
+
padding-bottom: var(--qzsy-safe-bottom, 0px) !important;
|
|
238
274
|
}
|
|
239
275
|
div[class*="_centerCol"] { grid-column: 2 !important; }
|
|
240
276
|
div[class*="_rightbarCol"] { grid-column: 3 !important; }
|
|
241
277
|
div[class*="_overlayLayer"] { grid-column: 1 / -1 !important; }
|
|
242
278
|
|
|
243
|
-
/* 2. \u539F\u751F\u4FA7\u680F\u6539\u4E3A fixed \u62BD\u5C49\uFF08\u9ED8\u8BA4\u85CF\u5728\u5C4F\u5E55\u5DE6\u4FA7\uFF09 */
|
|
279
|
+
/* 2. \u539F\u751F\u4FA7\u680F\u6539\u4E3A fixed \u62BD\u5C49\uFF08\u9ED8\u8BA4\u85CF\u5728\u5C4F\u5E55\u5DE6\u4FA7\uFF1B\u5185\u90E8\u907F\u5F00\u5B89\u5168\u533A\uFF09 */
|
|
244
280
|
div[class*="_sidebarCol"] {
|
|
245
281
|
position: fixed !important;
|
|
246
282
|
top: 0 !important;
|
|
@@ -250,16 +286,26 @@ var MOBILE_DRAWER_CSS = `
|
|
|
250
286
|
max-width: 84vw !important;
|
|
251
287
|
height: 100vh !important;
|
|
252
288
|
height: 100dvh !important;
|
|
289
|
+
box-sizing: border-box !important;
|
|
290
|
+
padding-top: 0 !important;
|
|
291
|
+
padding-top: var(--qzsy-safe-top, 0px) !important;
|
|
292
|
+
padding-bottom: 0 !important;
|
|
293
|
+
padding-bottom: var(--qzsy-safe-bottom, 0px) !important;
|
|
294
|
+
padding-left: 0 !important;
|
|
295
|
+
padding-left: var(--qzsy-safe-left, 0px) !important;
|
|
253
296
|
z-index: 60 !important;
|
|
254
297
|
background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
|
|
255
298
|
border-right: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.08)) !important;
|
|
256
299
|
box-shadow: none !important;
|
|
257
300
|
transform: translateX(-102%) !important;
|
|
258
|
-
transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1) !important;
|
|
259
301
|
overflow: hidden !important;
|
|
260
302
|
overscroll-behavior: contain;
|
|
261
303
|
will-change: transform;
|
|
262
304
|
}
|
|
305
|
+
/* \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
|
+
body.qzsy-drawer-ready div[class*="_sidebarCol"] {
|
|
307
|
+
transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1) !important;
|
|
308
|
+
}
|
|
263
309
|
body.qzsy-drawer-open div[class*="_sidebarCol"] {
|
|
264
310
|
transform: translateX(0) !important;
|
|
265
311
|
box-shadow: 8px 0 32px rgba(0, 0, 0, 0.18) !important;
|
|
@@ -268,6 +314,17 @@ var MOBILE_DRAWER_CSS = `
|
|
|
268
314
|
width: 100% !important;
|
|
269
315
|
}
|
|
270
316
|
|
|
317
|
+
/* \u62D6\u52A8\u4E2D\uFF1A\u5173\u95ED\u8FC7\u6E21\uFF0Ctransform/\u906E\u7F69\u76F4\u63A5\u8DDF\u624B */
|
|
318
|
+
body.qzsy-drawer-dragging div[class*="_sidebarCol"] {
|
|
319
|
+
transition: none !important;
|
|
320
|
+
transform: translateX(var(--qzsy-drawer-x, -102%)) !important;
|
|
321
|
+
}
|
|
322
|
+
body.qzsy-drawer-dragging .qzsy-mobile-backdrop {
|
|
323
|
+
transition: none !important;
|
|
324
|
+
opacity: var(--qzsy-drawer-progress, 0) !important;
|
|
325
|
+
pointer-events: auto !important;
|
|
326
|
+
}
|
|
327
|
+
|
|
271
328
|
/* 3. \u534A\u900F\u660E\u906E\u7F69\uFF1A\u70B9\u51FB\u6536\u8D77 */
|
|
272
329
|
.qzsy-mobile-backdrop {
|
|
273
330
|
display: block;
|
|
@@ -286,17 +343,13 @@ var MOBILE_DRAWER_CSS = `
|
|
|
286
343
|
pointer-events: auto;
|
|
287
344
|
}
|
|
288
345
|
|
|
289
|
-
/* 4. \
|
|
346
|
+
/* 4. \u83DC\u5355\u6309\u94AE\uFF1A\u4F4D\u4E8E\u5B89\u5168\u533A\u4E0B\u65B9\u7684 52px \u7559\u767D\u6761\u5185 */
|
|
290
347
|
.qzsy-mobile-menu-btn {
|
|
291
348
|
display: inline-flex;
|
|
292
|
-
position: fixed;
|
|
293
|
-
top: 10px;
|
|
294
|
-
left: 10px;
|
|
295
|
-
width: 40px;
|
|
296
|
-
height: 40px;
|
|
297
|
-
z-index: 65;
|
|
298
349
|
align-items: center;
|
|
299
350
|
justify-content: center;
|
|
351
|
+
width: 40px;
|
|
352
|
+
height: 40px;
|
|
300
353
|
padding: 0;
|
|
301
354
|
border: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.1));
|
|
302
355
|
border-radius: 12px;
|
|
@@ -304,6 +357,7 @@ var MOBILE_DRAWER_CSS = `
|
|
|
304
357
|
color: var(--dsw-alias-label-primary, #111827);
|
|
305
358
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
|
306
359
|
cursor: pointer;
|
|
360
|
+
pointer-events: auto;
|
|
307
361
|
-webkit-tap-highlight-color: transparent;
|
|
308
362
|
transition: opacity 180ms ease, transform 180ms ease;
|
|
309
363
|
}
|
|
@@ -313,6 +367,109 @@ var MOBILE_DRAWER_CSS = `
|
|
|
313
367
|
opacity: 0;
|
|
314
368
|
pointer-events: none;
|
|
315
369
|
}
|
|
370
|
+
|
|
371
|
+
/* 5. \u6A21\u578B\u5207\u6362\u5668\uFF1A\u628A\u5BBF\u4E3B\u684C\u9762 popover \u6539\u6210\u79FB\u52A8\u7AEF\u5E95\u90E8 sheet */
|
|
372
|
+
@keyframes qzsy-model-sheet-in {
|
|
373
|
+
from { transform: translateY(100%); opacity: 0.6; }
|
|
374
|
+
to { transform: translateY(0); opacity: 1; }
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.qzsy-model-sheet-backdrop {
|
|
378
|
+
display: block;
|
|
379
|
+
position: fixed;
|
|
380
|
+
inset: 0;
|
|
381
|
+
z-index: 1190;
|
|
382
|
+
background: rgba(0, 0, 0, 0.4);
|
|
383
|
+
opacity: 0;
|
|
384
|
+
pointer-events: none;
|
|
385
|
+
transition: opacity 240ms ease;
|
|
386
|
+
touch-action: none;
|
|
387
|
+
-webkit-tap-highlight-color: transparent;
|
|
388
|
+
}
|
|
389
|
+
body.qzsy-model-sheet-open .qzsy-model-sheet-backdrop {
|
|
390
|
+
opacity: 1;
|
|
391
|
+
pointer-events: auto;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
[data-qzsy-model-sheet] {
|
|
395
|
+
position: fixed !important;
|
|
396
|
+
top: auto !important;
|
|
397
|
+
left: 0 !important;
|
|
398
|
+
right: 0 !important;
|
|
399
|
+
bottom: 0 !important;
|
|
400
|
+
width: 100vw !important;
|
|
401
|
+
max-width: 100vw !important;
|
|
402
|
+
height: auto !important;
|
|
403
|
+
max-height: 82vh !important;
|
|
404
|
+
max-height: 82dvh !important;
|
|
405
|
+
margin: 0 !important;
|
|
406
|
+
padding: 18px 0 0 !important;
|
|
407
|
+
border: 0 !important;
|
|
408
|
+
border-radius: 18px 18px 0 0 !important;
|
|
409
|
+
background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
|
|
410
|
+
box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.18) !important;
|
|
411
|
+
box-sizing: border-box !important;
|
|
412
|
+
display: flex !important;
|
|
413
|
+
flex-direction: column !important;
|
|
414
|
+
overflow: hidden !important;
|
|
415
|
+
z-index: 1200 !important;
|
|
416
|
+
transform-origin: bottom center !important;
|
|
417
|
+
animation: qzsy-model-sheet-in 260ms cubic-bezier(0.16, 1, 0.3, 1) !important;
|
|
418
|
+
will-change: transform;
|
|
419
|
+
}
|
|
420
|
+
[data-qzsy-model-sheet]::before {
|
|
421
|
+
content: '';
|
|
422
|
+
position: absolute;
|
|
423
|
+
top: 7px;
|
|
424
|
+
left: 50%;
|
|
425
|
+
width: 36px;
|
|
426
|
+
height: 4px;
|
|
427
|
+
margin-left: -18px;
|
|
428
|
+
border-radius: 999px;
|
|
429
|
+
background: rgba(0, 0, 0, 0.18);
|
|
430
|
+
pointer-events: none;
|
|
431
|
+
}
|
|
432
|
+
[data-qzsy-model-sheet] > div,
|
|
433
|
+
[data-qzsy-model-sheet] div[role="menu"] {
|
|
434
|
+
width: 100% !important;
|
|
435
|
+
max-width: 100% !important;
|
|
436
|
+
border: 0 !important;
|
|
437
|
+
border-radius: 0 !important;
|
|
438
|
+
background: transparent !important;
|
|
439
|
+
box-shadow: none !important;
|
|
440
|
+
box-sizing: border-box !important;
|
|
441
|
+
}
|
|
442
|
+
[data-qzsy-model-sheet] div[role="menu"] {
|
|
443
|
+
max-height: calc(82vh - 40px) !important;
|
|
444
|
+
max-height: calc(82dvh - 40px) !important;
|
|
445
|
+
overflow-y: auto !important;
|
|
446
|
+
overscroll-behavior: contain !important;
|
|
447
|
+
padding: 8px 12px calc(14px + var(--qzsy-safe-bottom, 0px)) !important;
|
|
448
|
+
}
|
|
449
|
+
[data-qzsy-model-sheet] input {
|
|
450
|
+
width: 100% !important;
|
|
451
|
+
box-sizing: border-box !important;
|
|
452
|
+
}
|
|
453
|
+
[data-qzsy-model-sheet] button[role="menuitem"],
|
|
454
|
+
[data-qzsy-model-sheet] [role="option"] {
|
|
455
|
+
width: 100% !important;
|
|
456
|
+
box-sizing: border-box !important;
|
|
457
|
+
}
|
|
458
|
+
[data-qzsy-model-sheet].qzsy-model-sheet-closing {
|
|
459
|
+
transform: translateY(100%) !important;
|
|
460
|
+
opacity: 0 !important;
|
|
461
|
+
transition: transform 220ms ease, opacity 220ms ease !important;
|
|
462
|
+
animation: none !important;
|
|
463
|
+
}
|
|
464
|
+
body.qzsy-model-sheet-dragging [data-qzsy-model-sheet] {
|
|
465
|
+
transform: translateY(var(--qzsy-model-sheet-y, 0px)) !important;
|
|
466
|
+
transition: none !important;
|
|
467
|
+
animation: none !important;
|
|
468
|
+
}
|
|
469
|
+
body.qzsy-model-sheet-dragging .qzsy-model-sheet-backdrop {
|
|
470
|
+
opacity: var(--qzsy-model-sheet-progress, 1) !important;
|
|
471
|
+
transition: none !important;
|
|
472
|
+
}
|
|
316
473
|
}
|
|
317
474
|
`;
|
|
318
475
|
function isElement(value) {
|
|
@@ -336,6 +493,10 @@ function injectStyles() {
|
|
|
336
493
|
}
|
|
337
494
|
if (style.textContent !== MOBILE_DRAWER_CSS) style.textContent = MOBILE_DRAWER_CSS;
|
|
338
495
|
}
|
|
496
|
+
function drawerWidth() {
|
|
497
|
+
if (typeof window === "undefined") return 300;
|
|
498
|
+
return Math.min(300, Math.round(window.innerWidth * 0.84));
|
|
499
|
+
}
|
|
339
500
|
function setupMobileDrawer() {
|
|
340
501
|
if (typeof document === "undefined" || typeof window === "undefined") return () => {
|
|
341
502
|
};
|
|
@@ -346,16 +507,29 @@ function setupMobileDrawer() {
|
|
|
346
507
|
let menuBtn = null;
|
|
347
508
|
let observer = null;
|
|
348
509
|
let disposed = false;
|
|
510
|
+
let gesture = null;
|
|
511
|
+
let suppressClickUntil = 0;
|
|
512
|
+
let modelPortal = null;
|
|
513
|
+
let modelBackdrop = null;
|
|
514
|
+
let modelChainUntil = 0;
|
|
515
|
+
let modelDrag = null;
|
|
516
|
+
let modelDragHandlers = null;
|
|
349
517
|
const ensureElements = () => {
|
|
350
518
|
if (disposed || !document.body) return;
|
|
351
519
|
injectStyles();
|
|
352
|
-
let
|
|
520
|
+
let header = document.querySelector(`.${HEADER_CLASS}`);
|
|
521
|
+
if (!header) {
|
|
522
|
+
header = document.createElement("div");
|
|
523
|
+
header.className = HEADER_CLASS;
|
|
524
|
+
document.body.appendChild(header);
|
|
525
|
+
}
|
|
526
|
+
let btn = header.querySelector(`.${MENU_CLASS}`);
|
|
353
527
|
if (!btn) {
|
|
354
528
|
btn = document.createElement("button");
|
|
355
529
|
btn.type = "button";
|
|
356
530
|
btn.className = MENU_CLASS;
|
|
357
531
|
btn.innerHTML = '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><line x1="3" y1="8" x2="21" y2="8"></line><line x1="3" y1="15" x2="15" y2="15"></line></svg>';
|
|
358
|
-
|
|
532
|
+
header.appendChild(btn);
|
|
359
533
|
}
|
|
360
534
|
let mask = document.querySelector(`.${BACKDROP_CLASS}`);
|
|
361
535
|
if (!mask) {
|
|
@@ -386,6 +560,15 @@ function setupMobileDrawer() {
|
|
|
386
560
|
}
|
|
387
561
|
}
|
|
388
562
|
};
|
|
563
|
+
const setDragProgress = (x, width) => {
|
|
564
|
+
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))));
|
|
566
|
+
};
|
|
567
|
+
const clearDrag = () => {
|
|
568
|
+
document.body.classList.remove(DRAGGING_CLASS);
|
|
569
|
+
document.body.style.removeProperty("--qzsy-drawer-x");
|
|
570
|
+
document.body.style.removeProperty("--qzsy-drawer-progress");
|
|
571
|
+
};
|
|
389
572
|
const openDrawer = () => {
|
|
390
573
|
if (disposed || !isMobileViewport() || !document.body) return;
|
|
391
574
|
ensureElements();
|
|
@@ -410,7 +593,225 @@ function setupMobileDrawer() {
|
|
|
410
593
|
if (!disposed && isOpen()) closeDrawer({ restoreFocus: false });
|
|
411
594
|
}, 60);
|
|
412
595
|
};
|
|
596
|
+
const bodyPortalOf = (node) => {
|
|
597
|
+
let el = node;
|
|
598
|
+
while (el && el.parentElement && el.parentElement !== document.body) el = el.parentElement;
|
|
599
|
+
return el && el.parentElement === document.body ? el : null;
|
|
600
|
+
};
|
|
601
|
+
const visiblePortal = (el) => {
|
|
602
|
+
if (!el || !el.isConnected) return false;
|
|
603
|
+
const s = getComputedStyle(el);
|
|
604
|
+
if (s.display === "none" || s.visibility === "hidden") return false;
|
|
605
|
+
const r = el.getBoundingClientRect();
|
|
606
|
+
return r.width > 0 && r.height > 0;
|
|
607
|
+
};
|
|
608
|
+
const isModelTrigger = (target) => {
|
|
609
|
+
const el = target?.closest?.('button, [role="menuitem"], [role="button"], a');
|
|
610
|
+
if (!el) return false;
|
|
611
|
+
const label = `${el.getAttribute("aria-label") || ""} ${el.getAttribute("title") || ""} ${el.innerText || ""}`;
|
|
612
|
+
return /模型|Model|model/.test(label);
|
|
613
|
+
};
|
|
614
|
+
const findModelPortal = () => {
|
|
615
|
+
if (!isMobileViewport()) return null;
|
|
616
|
+
const search = document.querySelector(
|
|
617
|
+
'input[placeholder*="\u641C\u7D22\u6A21\u578B"], input[placeholder*="Search model"], input[aria-label*="\u641C\u7D22\u6A21\u578B"], input[aria-label*="Search model"]'
|
|
618
|
+
);
|
|
619
|
+
if (search) {
|
|
620
|
+
const menu = search.closest('[role="menu"]') || search.closest('[class*="_menu"]');
|
|
621
|
+
const portal = bodyPortalOf(menu || search);
|
|
622
|
+
if (portal && visiblePortal(portal)) {
|
|
623
|
+
const s = getComputedStyle(portal);
|
|
624
|
+
if ((s.position === "fixed" || s.position === "absolute") && portal.getBoundingClientRect().width > 120) return portal;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (Date.now() > modelChainUntil) return null;
|
|
628
|
+
const candidates = [];
|
|
629
|
+
for (const el of document.body.children) {
|
|
630
|
+
if (!(el instanceof HTMLElement)) continue;
|
|
631
|
+
if (el === modelBackdrop || el.classList.contains(HEADER_CLASS) || el.classList.contains(BACKDROP_CLASS)) continue;
|
|
632
|
+
if (!visiblePortal(el)) continue;
|
|
633
|
+
const s = getComputedStyle(el);
|
|
634
|
+
if (s.position !== "fixed" && s.position !== "absolute") continue;
|
|
635
|
+
const menu = el.querySelector('[role="menu"], [role="listbox"]');
|
|
636
|
+
if (!menu) continue;
|
|
637
|
+
const text = menu.innerText || "";
|
|
638
|
+
const rows = menu.querySelectorAll('[role="menuitem"], [role="option"], button, a, [tabindex]').length;
|
|
639
|
+
const hasSearch = Boolean(el.querySelector('input[placeholder*="\u641C\u7D22\u6A21\u578B"], input[placeholder*="Search model"]'));
|
|
640
|
+
if (/推理等级|Reasoning effort|推理强度/i.test(text) && rows <= 2) continue;
|
|
641
|
+
const rect = el.getBoundingClientRect();
|
|
642
|
+
const alreadySheet = rect.width >= window.innerWidth - 2 && Math.abs(rect.bottom - window.innerHeight) < 2 && rect.height > 120;
|
|
643
|
+
if (alreadySheet && !hasSearch) continue;
|
|
644
|
+
if (hasSearch || rows > 2) candidates.push({ el, rows, hasSearch });
|
|
645
|
+
}
|
|
646
|
+
candidates.sort((a, b) => Number(b.hasSearch) - Number(a.hasSearch) || b.rows - a.rows);
|
|
647
|
+
return candidates[0]?.el || null;
|
|
648
|
+
};
|
|
649
|
+
const hostBackdropPresent = () => {
|
|
650
|
+
for (const el of document.body.children) {
|
|
651
|
+
if (!(el instanceof HTMLElement)) continue;
|
|
652
|
+
if (el === modelBackdrop || el.classList.contains(HEADER_CLASS) || el.classList.contains(BACKDROP_CLASS)) continue;
|
|
653
|
+
const s = getComputedStyle(el);
|
|
654
|
+
if (s.position !== "fixed") continue;
|
|
655
|
+
const r = el.getBoundingClientRect();
|
|
656
|
+
if (r.width < window.innerWidth - 4 || r.height < window.innerHeight - 4) continue;
|
|
657
|
+
if (Number(s.opacity) < 0.1) continue;
|
|
658
|
+
const bg = s.backgroundColor || "";
|
|
659
|
+
if (!/^rgba?\(/.test(bg) || /rgba\(0,\s*0,\s*0,\s*0\)/.test(bg)) continue;
|
|
660
|
+
if (el.querySelector('[role="menu"]')) continue;
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
return false;
|
|
664
|
+
};
|
|
665
|
+
const requestHostModelClose = (portal) => {
|
|
666
|
+
if (disposed || !portal || !portal.isConnected) return;
|
|
667
|
+
const PointerCtor = typeof window.PointerEvent === "function" ? window.PointerEvent : window.MouseEvent;
|
|
668
|
+
try {
|
|
669
|
+
document.body.dispatchEvent(new PointerCtor("pointerdown", { bubbles: true, cancelable: true }));
|
|
670
|
+
} catch {
|
|
671
|
+
}
|
|
672
|
+
try {
|
|
673
|
+
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true, cancelable: true }));
|
|
674
|
+
} catch {
|
|
675
|
+
}
|
|
676
|
+
window.setTimeout(() => {
|
|
677
|
+
if (disposed || !portal.isConnected) return;
|
|
678
|
+
const trigger = document.querySelector('button[aria-haspopup="menu"][aria-expanded="true"][aria-label*="\u9009\u62E9\u6A21\u578B"], button[aria-haspopup="menu"][aria-expanded="true"][aria-label*="Select model"]');
|
|
679
|
+
try {
|
|
680
|
+
trigger?.click();
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
window.setTimeout(() => {
|
|
684
|
+
if (!disposed && portal.isConnected && modelPortal === portal) clearModelSheet();
|
|
685
|
+
}, 450);
|
|
686
|
+
}, 120);
|
|
687
|
+
};
|
|
688
|
+
const closeModelSheetNow = () => {
|
|
689
|
+
const portal = modelPortal;
|
|
690
|
+
if (!portal) return;
|
|
691
|
+
document.body.classList.remove("qzsy-model-sheet-open");
|
|
692
|
+
document.body.style.removeProperty("--qzsy-model-sheet-y");
|
|
693
|
+
document.body.style.removeProperty("--qzsy-model-sheet-progress");
|
|
694
|
+
requestHostModelClose(portal);
|
|
695
|
+
};
|
|
696
|
+
const closeModelSheetAnimated = () => {
|
|
697
|
+
const portal = modelPortal;
|
|
698
|
+
if (!portal) return;
|
|
699
|
+
portal.classList.add("qzsy-model-sheet-closing");
|
|
700
|
+
document.body.classList.remove("qzsy-model-sheet-open");
|
|
701
|
+
window.setTimeout(() => requestHostModelClose(portal), 180);
|
|
702
|
+
};
|
|
703
|
+
const ensureModelBackdrop = () => {
|
|
704
|
+
if (modelBackdrop || hostBackdropPresent()) return;
|
|
705
|
+
modelBackdrop = document.createElement("div");
|
|
706
|
+
modelBackdrop.className = "qzsy-model-sheet-backdrop";
|
|
707
|
+
modelBackdrop.setAttribute("aria-hidden", "true");
|
|
708
|
+
modelBackdrop.addEventListener("pointerdown", (event) => event.stopPropagation(), true);
|
|
709
|
+
modelBackdrop.addEventListener("click", (event) => {
|
|
710
|
+
event.preventDefault();
|
|
711
|
+
event.stopPropagation();
|
|
712
|
+
closeModelSheetNow();
|
|
713
|
+
});
|
|
714
|
+
document.body.appendChild(modelBackdrop);
|
|
715
|
+
};
|
|
716
|
+
const detachModelDrag = () => {
|
|
717
|
+
if (!modelPortal || !modelDragHandlers) return;
|
|
718
|
+
modelPortal.removeEventListener("touchstart", modelDragHandlers.onStart);
|
|
719
|
+
modelPortal.removeEventListener("touchmove", modelDragHandlers.onMove);
|
|
720
|
+
modelPortal.removeEventListener("touchend", modelDragHandlers.onEnd);
|
|
721
|
+
modelPortal.removeEventListener("touchcancel", modelDragHandlers.onEnd);
|
|
722
|
+
modelDragHandlers = null;
|
|
723
|
+
};
|
|
724
|
+
const attachModelDrag = (portal) => {
|
|
725
|
+
if (!portal || modelDragHandlers) return;
|
|
726
|
+
const onStart = (event) => {
|
|
727
|
+
if (event.touches?.length !== 1) return;
|
|
728
|
+
const touch = event.touches[0];
|
|
729
|
+
const rect = portal.getBoundingClientRect();
|
|
730
|
+
if (touch.clientY - rect.top > 30) return;
|
|
731
|
+
if (event.target.closest?.('input, textarea, select, button, a, [contenteditable="true"]')) return;
|
|
732
|
+
modelDrag = { startY: touch.clientY, startT: Date.now(), dy: 0, active: false, height: rect.height };
|
|
733
|
+
};
|
|
734
|
+
const onMove = (event) => {
|
|
735
|
+
if (!modelDrag) return;
|
|
736
|
+
const touch = event.touches?.[0];
|
|
737
|
+
if (!touch) return;
|
|
738
|
+
const dy = touch.clientY - modelDrag.startY;
|
|
739
|
+
if (!modelDrag.active) {
|
|
740
|
+
if (dy < 6) return;
|
|
741
|
+
modelDrag.active = true;
|
|
742
|
+
document.body.classList.add("qzsy-model-sheet-dragging");
|
|
743
|
+
}
|
|
744
|
+
modelDrag.dy = Math.max(0, dy);
|
|
745
|
+
document.body.style.setProperty("--qzsy-model-sheet-y", `${modelDrag.dy}px`);
|
|
746
|
+
document.body.style.setProperty("--qzsy-model-sheet-progress", String(Math.max(0, 1 - modelDrag.dy / Math.max(1, modelDrag.height))));
|
|
747
|
+
if (event.cancelable) event.preventDefault();
|
|
748
|
+
};
|
|
749
|
+
const onEnd = () => {
|
|
750
|
+
if (!modelDrag) return;
|
|
751
|
+
const drag = modelDrag;
|
|
752
|
+
modelDrag = null;
|
|
753
|
+
if (!drag.active) return;
|
|
754
|
+
document.body.classList.remove("qzsy-model-sheet-dragging");
|
|
755
|
+
document.body.style.removeProperty("--qzsy-model-sheet-y");
|
|
756
|
+
document.body.style.removeProperty("--qzsy-model-sheet-progress");
|
|
757
|
+
const velocity = drag.dy / Math.max(1, Date.now() - drag.startT);
|
|
758
|
+
if (drag.dy > 110 || velocity > 0.5) closeModelSheetAnimated();
|
|
759
|
+
};
|
|
760
|
+
portal.addEventListener("touchstart", onStart, { passive: true });
|
|
761
|
+
portal.addEventListener("touchmove", onMove, { passive: false });
|
|
762
|
+
portal.addEventListener("touchend", onEnd, { passive: true });
|
|
763
|
+
portal.addEventListener("touchcancel", onEnd, { passive: true });
|
|
764
|
+
modelDragHandlers = { onStart, onMove, onEnd };
|
|
765
|
+
};
|
|
766
|
+
const clearModelSheet = () => {
|
|
767
|
+
if (modelPortal) {
|
|
768
|
+
detachModelDrag();
|
|
769
|
+
modelPortal.removeAttribute("data-qzsy-model-sheet");
|
|
770
|
+
modelPortal.classList.remove("qzsy-model-sheet", "qzsy-model-sheet-closing");
|
|
771
|
+
}
|
|
772
|
+
modelPortal = null;
|
|
773
|
+
modelDrag = null;
|
|
774
|
+
document.body?.classList.remove("qzsy-model-sheet-open", "qzsy-model-sheet-dragging");
|
|
775
|
+
document.body?.style.removeProperty("--qzsy-model-sheet-y");
|
|
776
|
+
document.body?.style.removeProperty("--qzsy-model-sheet-progress");
|
|
777
|
+
if (modelBackdrop) {
|
|
778
|
+
modelBackdrop.remove();
|
|
779
|
+
modelBackdrop = null;
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
const applyModelSheet = (portal) => {
|
|
783
|
+
if (modelPortal === portal) return;
|
|
784
|
+
clearModelSheet();
|
|
785
|
+
modelPortal = portal;
|
|
786
|
+
portal.setAttribute("data-qzsy-model-sheet", "");
|
|
787
|
+
portal.classList.add("qzsy-model-sheet");
|
|
788
|
+
attachModelDrag(portal);
|
|
789
|
+
ensureModelBackdrop();
|
|
790
|
+
window.requestAnimationFrame(() => {
|
|
791
|
+
if (!disposed && modelPortal === portal) document.body.classList.add("qzsy-model-sheet-open");
|
|
792
|
+
});
|
|
793
|
+
};
|
|
794
|
+
const syncModelSheet = () => {
|
|
795
|
+
if (disposed) return;
|
|
796
|
+
if (!isMobileViewport()) {
|
|
797
|
+
clearModelSheet();
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (modelPortal && !visiblePortal(modelPortal)) clearModelSheet();
|
|
801
|
+
const found = findModelPortal();
|
|
802
|
+
if (!found) {
|
|
803
|
+
if (modelPortal?.isConnected) return;
|
|
804
|
+
if (modelPortal) clearModelSheet();
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (found !== modelPortal) applyModelSheet(found);
|
|
808
|
+
};
|
|
413
809
|
const onClick = (event) => {
|
|
810
|
+
if (Date.now() < suppressClickUntil) {
|
|
811
|
+
event.preventDefault();
|
|
812
|
+
event.stopPropagation();
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
414
815
|
const target = event.target;
|
|
415
816
|
if (!isElement(target)) return;
|
|
416
817
|
if (target.closest(`.${MENU_CLASS}`)) {
|
|
@@ -419,6 +820,11 @@ function setupMobileDrawer() {
|
|
|
419
820
|
else openDrawer();
|
|
420
821
|
return;
|
|
421
822
|
}
|
|
823
|
+
if (isModelTrigger(target)) {
|
|
824
|
+
modelChainUntil = Date.now() + 6e3;
|
|
825
|
+
window.setTimeout(syncModelSheet, 30);
|
|
826
|
+
window.setTimeout(syncModelSheet, 180);
|
|
827
|
+
}
|
|
422
828
|
if (!isOpen()) return;
|
|
423
829
|
if (target.closest(`.${BACKDROP_CLASS}`)) {
|
|
424
830
|
closeDrawer();
|
|
@@ -447,10 +853,108 @@ function setupMobileDrawer() {
|
|
|
447
853
|
if (event.key === "Escape" && isOpen()) closeDrawer();
|
|
448
854
|
};
|
|
449
855
|
const onResize = () => {
|
|
450
|
-
if (!isMobileViewport() && isOpen())
|
|
856
|
+
if (!isMobileViewport() && isOpen()) {
|
|
857
|
+
gesture = null;
|
|
858
|
+
clearDrag();
|
|
859
|
+
closeDrawer({ restoreFocus: false });
|
|
860
|
+
}
|
|
861
|
+
syncModelSheet();
|
|
451
862
|
};
|
|
452
863
|
const onVisibilityChange = () => {
|
|
453
|
-
if (document.visibilityState === "hidden" && isOpen())
|
|
864
|
+
if (document.visibilityState === "hidden" && isOpen()) {
|
|
865
|
+
gesture = null;
|
|
866
|
+
clearDrag();
|
|
867
|
+
closeDrawer({ restoreFocus: false });
|
|
868
|
+
}
|
|
869
|
+
if (document.visibilityState === "visible") syncModelSheet();
|
|
870
|
+
};
|
|
871
|
+
const onTouchStart = (event) => {
|
|
872
|
+
if (modelPortal || disposed || !isMobileViewport() || event.touches?.length !== 1) return;
|
|
873
|
+
const touch = event.touches[0];
|
|
874
|
+
const sidebar = document.querySelector('div[class*="_sidebarCol"]');
|
|
875
|
+
if (!sidebar) return;
|
|
876
|
+
const open = isOpen();
|
|
877
|
+
if (!open) {
|
|
878
|
+
if (touch.clientX > EDGE_START_ZONE) return;
|
|
879
|
+
ensureSidebarExpanded();
|
|
880
|
+
gesture = {
|
|
881
|
+
mode: "open",
|
|
882
|
+
startX: touch.clientX,
|
|
883
|
+
startY: touch.clientY,
|
|
884
|
+
startT: Date.now(),
|
|
885
|
+
width: drawerWidth(),
|
|
886
|
+
active: false,
|
|
887
|
+
dx: 0
|
|
888
|
+
};
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
if (!sidebar.contains(event.target)) return;
|
|
892
|
+
if (event.target.closest?.('input, textarea, select, [contenteditable="true"]')) return;
|
|
893
|
+
gesture = {
|
|
894
|
+
mode: "close",
|
|
895
|
+
startX: touch.clientX,
|
|
896
|
+
startY: touch.clientY,
|
|
897
|
+
startT: Date.now(),
|
|
898
|
+
width: drawerWidth(),
|
|
899
|
+
active: false,
|
|
900
|
+
dx: 0
|
|
901
|
+
};
|
|
902
|
+
};
|
|
903
|
+
const onTouchMove = (event) => {
|
|
904
|
+
if (modelPortal || !gesture || disposed) return;
|
|
905
|
+
const touch = event.touches?.[0];
|
|
906
|
+
if (!touch) return;
|
|
907
|
+
const dx = touch.clientX - gesture.startX;
|
|
908
|
+
const dy = touch.clientY - gesture.startY;
|
|
909
|
+
if (!gesture.active) {
|
|
910
|
+
if (Math.abs(dx) < DRAG_THRESHOLD) return;
|
|
911
|
+
if (Math.abs(dx) < Math.abs(dy) * 1.2) {
|
|
912
|
+
gesture = null;
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
if (gesture.mode === "open" && dx <= 0) {
|
|
916
|
+
gesture = null;
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (gesture.mode === "close" && dx >= 0) {
|
|
920
|
+
gesture = null;
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
gesture.active = true;
|
|
924
|
+
suppressClickUntil = Date.now() + 450;
|
|
925
|
+
document.body.classList.add(DRAGGING_CLASS);
|
|
926
|
+
}
|
|
927
|
+
const width = Math.max(1, gesture.width);
|
|
928
|
+
let x;
|
|
929
|
+
if (gesture.mode === "open") x = Math.max(-width, Math.min(0, -width + dx));
|
|
930
|
+
else x = Math.max(-width, Math.min(0, dx));
|
|
931
|
+
gesture.dx = dx;
|
|
932
|
+
setDragProgress(x, width);
|
|
933
|
+
};
|
|
934
|
+
const finishGesture = (touch) => {
|
|
935
|
+
if (!gesture) return;
|
|
936
|
+
const current = gesture;
|
|
937
|
+
gesture = null;
|
|
938
|
+
if (!current.active) return;
|
|
939
|
+
const endX = touch ? touch.clientX : current.startX + current.dx;
|
|
940
|
+
const dx = endX - current.startX;
|
|
941
|
+
const elapsed = Math.max(1, Date.now() - current.startT);
|
|
942
|
+
const velocity = dx / elapsed;
|
|
943
|
+
const threshold = Math.min(90, Math.max(60, current.width * 0.3));
|
|
944
|
+
const openNow = current.mode === "open" ? dx > threshold || velocity > 0.45 : !(dx < -Math.max(50, threshold * 0.7) || velocity < -0.35);
|
|
945
|
+
document.body.classList.toggle(OPEN_CLASS, openNow);
|
|
946
|
+
if (openNow) ensureSidebarExpanded();
|
|
947
|
+
syncAria();
|
|
948
|
+
suppressClickUntil = Date.now() + 250;
|
|
949
|
+
window.requestAnimationFrame(clearDrag);
|
|
950
|
+
};
|
|
951
|
+
const onTouchEnd = (event) => {
|
|
952
|
+
if (modelPortal || !gesture) return;
|
|
953
|
+
finishGesture(event.changedTouches?.[0]);
|
|
954
|
+
};
|
|
955
|
+
const onTouchCancel = () => {
|
|
956
|
+
if (modelPortal || !gesture) return;
|
|
957
|
+
finishGesture(null);
|
|
454
958
|
};
|
|
455
959
|
injectStyles();
|
|
456
960
|
ensureElements();
|
|
@@ -458,17 +962,27 @@ function setupMobileDrawer() {
|
|
|
458
962
|
document.addEventListener("keydown", onKeyDown, true);
|
|
459
963
|
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
460
964
|
window.addEventListener("resize", onResize);
|
|
965
|
+
window.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
966
|
+
window.addEventListener("touchmove", onTouchMove, { passive: true });
|
|
967
|
+
window.addEventListener("touchend", onTouchEnd, { passive: true });
|
|
968
|
+
window.addEventListener("touchcancel", onTouchCancel, { passive: true });
|
|
969
|
+
window.requestAnimationFrame(() => {
|
|
970
|
+
if (!disposed && document.body) document.body.classList.add("qzsy-drawer-ready");
|
|
971
|
+
});
|
|
461
972
|
if (typeof MutationObserver === "function" && document.body) {
|
|
462
973
|
observer = new MutationObserver(() => {
|
|
463
|
-
if (!document.querySelector(`.${MENU_CLASS}`) || !document.querySelector(`.${BACKDROP_CLASS}`) || !document.getElementById(MOBILE_DRAWER_CSS_ID)) {
|
|
974
|
+
if (!document.querySelector(`.${HEADER_CLASS}`) || !document.querySelector(`.${MENU_CLASS}`) || !document.querySelector(`.${BACKDROP_CLASS}`) || !document.getElementById(MOBILE_DRAWER_CSS_ID)) {
|
|
464
975
|
ensureElements();
|
|
465
976
|
}
|
|
977
|
+
syncModelSheet();
|
|
466
978
|
});
|
|
467
979
|
observer.observe(document.body, { childList: true });
|
|
468
980
|
}
|
|
981
|
+
syncModelSheet();
|
|
469
982
|
syncAria();
|
|
470
983
|
return () => {
|
|
471
984
|
disposed = true;
|
|
985
|
+
gesture = null;
|
|
472
986
|
try {
|
|
473
987
|
observer?.disconnect();
|
|
474
988
|
} catch {
|
|
@@ -477,8 +991,15 @@ function setupMobileDrawer() {
|
|
|
477
991
|
document.removeEventListener("keydown", onKeyDown, true);
|
|
478
992
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
479
993
|
window.removeEventListener("resize", onResize);
|
|
994
|
+
window.removeEventListener("touchstart", onTouchStart);
|
|
995
|
+
window.removeEventListener("touchmove", onTouchMove);
|
|
996
|
+
window.removeEventListener("touchend", onTouchEnd);
|
|
997
|
+
window.removeEventListener("touchcancel", onTouchCancel);
|
|
998
|
+
clearModelSheet();
|
|
999
|
+
clearDrag();
|
|
480
1000
|
document.body?.classList.remove(OPEN_CLASS);
|
|
481
|
-
document.
|
|
1001
|
+
document.body?.classList.remove("qzsy-drawer-ready");
|
|
1002
|
+
document.querySelector(`.${HEADER_CLASS}`)?.remove();
|
|
482
1003
|
document.querySelector(`.${BACKDROP_CLASS}`)?.remove();
|
|
483
1004
|
document.getElementById(MOBILE_DRAWER_CSS_ID)?.remove();
|
|
484
1005
|
root.removeAttribute(MOBILE_UI_FLAG);
|