dsh-m 0.9.68 → 0.9.70
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 +24 -0
- package/lib/client.js +301 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,18 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
8
8
|
|
|
9
9
|
## 中文
|
|
10
10
|
|
|
11
|
+
### 0.9.70 修复:拖拽观感与静止态一致——退役拖拽期实心固化(ADR-0017 v2)
|
|
12
|
+
|
|
13
|
+
- **根因(用户浅色模式双态实拍)**:0.9.69 的拖拽期「计算值固化强制实心」在按住/松手瞬间造成实心↔玻璃的观感跳变,浅色模式下尤为突兀——防透字与观感连续两个目标里,实心方案牺牲了后者。
|
|
14
|
+
- **修复(ADR-0017 决策 3 修订 v2)**:拖拽态与静止态**同玻璃同 blur,外观逐像素一致**——透字防护回归静止态既有不变量(毛玻璃模糊背景自持;透字病灶的成因是「关 blur 还留半透明」,两样都不动即无此病,openbmc 静止态可读性本就由 blur 兜住)。拖拽可供性只剩 grabbing 光标与投影加深;`solidifyColor`/`--dshm-drag-bg`/拖拽态 `!important` 全部退役(清单定长回归 4,`client-lightbox` 断言含「无残留」负向锚)。性能保底手段(pointer capture/手势期监听/rAF 帧内写/translate3d/will-change)全部保留;拖拽期恢复的每帧 backdrop 重滤镜由 GPU 合成承担,弱机若报告掉帧再评估。
|
|
15
|
+
- **验证**:1323 项测试全绿、typecheck 零错误;ADR-0017 修订 v2(Considered Options 翻案记录在案)。
|
|
16
|
+
|
|
17
|
+
### 0.9.69 功能:还原态面板头部拖拽 + 位置记忆(ADR-0017)
|
|
18
|
+
|
|
19
|
+
- **方案**:市场面板(`.dshm-panel`)还原态下按住头部(标题/tab 行)即可自由拖拽——头部全域为手柄,tab/全屏/关闭钮点击语义原样(`closest("button")` 门卫 + 多指重入守卫);双击头部非按钮区切换全屏;释放位置烘焙为视口绝对坐标存 `dshm-panel-pos`,下次打开还原,无记忆首开与旧版 flex 居中渲染逐字节一致。运动范围四周 8px;desktop profile 额外让出壳层标题拖拽带(quota-watch 契约:`data-windows-titlebar` + `--dsh-windows-titlebar-height`,web 无此变量恒 0)——胶囊时代「停进系统按钮区就再也抓不回」的坑由 clamp 机制性杜绝。挂载/全屏往返/resize 三触发源重夹紧,还原 commit 的重夹紧时序由 render 期 latest-ref 镜像保证(执行评审 E1-1 实证修复并以结构锚钉死)。
|
|
20
|
+
- **皮肤免疫(拖拽期 = 最快渲染路径)**:拖拽背景取手势开始时 `getComputedStyle` 的最终色强制 α=1 写入 `--dshm-drag-bg` 并 `!important` 自持——openbmc/uefi 玻璃皮肤(bg-base α=0.55 病类,quota-watch 胶囊拖拽变半透明的同款病例)下按住拖动面板完全实心不透字;同时拖拽期关闭 backdrop-filter,每帧只剩 compositor 平移一层:pointer capture + 手势期才挂 document 监听 + pointermove 只存点、样式帧内写 + 拖拽帧绝不走 React state。静止态玻璃外观、0.9.27 防误关守卫、灯箱/详情 modal 语义全部不动。
|
|
21
|
+
- **验证**:1326 项测试全绿、typecheck 零错误;构建产物含 `dshm-panel--dragging`/`dshm-panel-pos` 逐字锚;`!important` 清单定长 4→7(client-lightbox 断言登记)。实施计划与执行结果经独立评审 Agent 五轮对抗评审全闭环(16 条:13 条修于计划、3 条修于代码/如实申报)。
|
|
22
|
+
|
|
11
23
|
### 0.9.68 性能:合并市场物化——同身份免重付 5MB 解析 + 适配 + 合并(ADR-0016)
|
|
12
24
|
|
|
13
25
|
- **问题(性能架构审查旗舰项,三轮评审闭环)**:领域概念「合并市场(Merged Market)」在 GLOSSARY 有名无主——装配逻辑内联在 `communityOutcome`,每个市场请求(缓存全命中的快路径也在内)都重付磁盘读 5.36MB 社区目录 + JSON.parse(实测 30–55ms)→ 目录适配层重建 4,400 条(30–90ms)→ 合并排序(5–35ms);且全部是**同步 JS、阻塞宿主事件循环**——市场每翻一页/每提交一次搜索,宿主的 SSE 对话流与工具调用停摆 65–400ms。搜索评分因归一化 WeakMap 跨请求永不命中再加 ~330ms(冷)。
|
|
@@ -505,6 +517,18 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
505
517
|
|
|
506
518
|
## English
|
|
507
519
|
|
|
520
|
+
### Fix in 0.9.70 — drag appearance matches the resting state: drag-time solidification retired (ADR-0017 v2)
|
|
521
|
+
|
|
522
|
+
- **Root cause (user side-by-side capture in light mode)**: the 0.9.69 drag-time "computed-value solidification" made the panel jump between solid and glass at grab/release - jarring in light mode. Of the two goals (no see-through, visual continuity), the solid approach sacrificed continuity.
|
|
523
|
+
- **Fix (ADR-0017 decision 3 revised to v2)**: the drag state now shares the resting state's exact glass and blur - pixel-identical appearance. See-through protection returns to the resting invariant (frosted blur holds the background; the disease class was "blur off while still translucent" - leave both untouched and it cannot occur, exactly how openbmc's resting readability is already held). Drag affordance reduces to the grabbing cursor and a deeper shadow; `solidifyColor` / `--dshm-drag-bg` / drag-time `!important` are all retired (inventory back to a pinned 4, with a no-residue negative anchor in `client-lightbox`). The performance toolbox (pointer capture / gesture-scoped listeners / rAF-frame writes / translate3d / will-change) is fully retained; the restored per-frame backdrop re-filter is carried by GPU compositing - revisit only if weak-GPU jank is reported.
|
|
524
|
+
- **Verification**: 1323 tests green, typecheck clean; ADR-0017 revised to v2 (Considered Options overruling recorded).
|
|
525
|
+
|
|
526
|
+
### Feature in 0.9.69 — drag the marketplace panel by its header in restore state, with position memory (ADR-0017)
|
|
527
|
+
|
|
528
|
+
- **Design**: hold the panel header (title/tab row) in restore state to drag the marketplace panel freely - the whole header is the handle while tab / fullscreen / close buttons keep their click semantics (`closest("button")` gate + multi-pointer re-entry guard); double-click on a non-button header area toggles fullscreen; on release the position is baked into absolute viewport coordinates and stored under `dshm-panel-pos`, restored on next open - with no stored position the first render stays byte-identical to the old flex-centered layout. The motion range is clamped 8px on all sides; the desktop profile additionally yields the shell titlebar drag band (quota-watch contract: `data-windows-titlebar` + `--dsh-windows-titlebar-height`; web without the variable is always 0) - the capsule-era trap of parking inside the system button zone and never being grabbable again is eliminated by construction. Mount, fullscreen round-trips and window resize all re-clamp; the restore-commit re-clamp is ordered by render-phase latest-ref mirrors (execution review E1-1, reproduced, fixed, and pinned by structural anchors).
|
|
529
|
+
- **Skin immunity (drag = fastest render path)**: the drag background is captured at gesture start from `getComputedStyle` with alpha forced to 1 into `--dshm-drag-bg`, self-held by `!important` - on glass skins (openbmc/uefi, bg-base alpha 0.55 disease class - the same case that made the quota-watch capsule translucent while dragged) the panel stays fully opaque with zero see-through. backdrop-filter is disabled for the gesture, so each frame is a single compositor translation: pointer capture + document-level listeners only during the gesture + pointermove stores the point only (styles written inside rAF) + drag frames never touch React state. Resting glass looks, the 0.9.27 mis-close guard, lightbox and detail-modal semantics are all untouched.
|
|
530
|
+
- **Verification**: 1326 tests green, typecheck clean; bundle carries literal anchors `dshm-panel--dragging` / `dshm-panel-pos`; the `!important` inventory is length-pinned 4→7 (registered in client-lightbox assertions). Plan and execution were adversarially reviewed by an independent reviewer agent across five rounds - 16 findings, all closed (13 fixed in the plan, 3 fixed in code / honestly declared).
|
|
531
|
+
|
|
508
532
|
### Performance in 0.9.68 — materialized merged market: skip the 5MB parse + adapt + merge for repeat identities (ADR-0016)
|
|
509
533
|
|
|
510
534
|
- **Problem (flagship item of the performance architecture review, closed over three review rounds)**: the domain concept "Merged Market" existed in the GLOSSARY without an owning module - assembly logic lived inline in `communityOutcome`, so every market request (including fully cache-hit fast paths) re-paid a 5.36MB community catalog disk read + JSON.parse (measured 30-55ms) → adapter rebuild of 4,400 entries (30-90ms) → merge + sort (5-35ms). All of it is **synchronous JS blocking the host event loop** - every page turn / committed search froze the host's SSE streams and tool calls for 65-400ms. Search scoring added ~330ms cold because the normalization WeakMap never survived across requests.
|
package/lib/client.js
CHANGED
|
@@ -494,6 +494,94 @@ var init_lightbox = __esm({
|
|
|
494
494
|
}
|
|
495
495
|
});
|
|
496
496
|
|
|
497
|
+
// src/client/window-drag.js
|
|
498
|
+
var window_drag_exports = {};
|
|
499
|
+
__export(window_drag_exports, {
|
|
500
|
+
DRAG_SLOP_MOUSE: () => DRAG_SLOP_MOUSE,
|
|
501
|
+
DRAG_SLOP_TOUCH: () => DRAG_SLOP_TOUCH,
|
|
502
|
+
PANEL_MARGIN: () => PANEL_MARGIN,
|
|
503
|
+
POS_KEY: () => POS_KEY,
|
|
504
|
+
clampPoint: () => clampPoint,
|
|
505
|
+
dragSlop: () => dragSlop,
|
|
506
|
+
grabOffset: () => grabOffset,
|
|
507
|
+
isDragTarget: () => isDragTarget,
|
|
508
|
+
loadPanelPos: () => loadPanelPos,
|
|
509
|
+
savePanelPos: () => savePanelPos,
|
|
510
|
+
titlebarTopInset: () => titlebarTopInset
|
|
511
|
+
});
|
|
512
|
+
function dragSlop(pointerType) {
|
|
513
|
+
return pointerType === "touch" || pointerType === "pen" ? DRAG_SLOP_TOUCH : DRAG_SLOP_MOUSE;
|
|
514
|
+
}
|
|
515
|
+
function grabOffset(point, rect) {
|
|
516
|
+
return { dx: point.x - rect.left, dy: point.y - rect.top };
|
|
517
|
+
}
|
|
518
|
+
function titlebarTopInset({ titlebar = false, fullscreen = false, height } = {}) {
|
|
519
|
+
if (!titlebar || fullscreen) return 0;
|
|
520
|
+
const value = Number.parseFloat(height);
|
|
521
|
+
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
522
|
+
}
|
|
523
|
+
function normalizeSize(size) {
|
|
524
|
+
if (typeof size === "number") return { width: size, height: size };
|
|
525
|
+
const width = typeof size?.width === "number" ? size.width : 0;
|
|
526
|
+
const height = typeof size?.height === "number" ? size.height : 0;
|
|
527
|
+
return { width, height };
|
|
528
|
+
}
|
|
529
|
+
function finiteInset(v) {
|
|
530
|
+
return Number.isFinite(v) ? v : 0;
|
|
531
|
+
}
|
|
532
|
+
function clampPoint(point, viewport, size = 0, insets = ZERO_INSETS) {
|
|
533
|
+
const { width, height } = normalizeSize(size);
|
|
534
|
+
const insetLeft = finiteInset(insets?.left);
|
|
535
|
+
const insetRight = finiteInset(insets?.right);
|
|
536
|
+
const insetTop = finiteInset(insets?.top);
|
|
537
|
+
const insetBottom = finiteInset(insets?.bottom);
|
|
538
|
+
const minX = PANEL_MARGIN + insetLeft;
|
|
539
|
+
const minY = PANEL_MARGIN + insetTop;
|
|
540
|
+
const maxX = Math.max(minX, viewport.width - insetRight - PANEL_MARGIN - width);
|
|
541
|
+
const maxY = Math.max(minY, viewport.height - insetBottom - PANEL_MARGIN - height);
|
|
542
|
+
return {
|
|
543
|
+
x: Math.min(Math.max(point.x, minX), maxX),
|
|
544
|
+
y: Math.min(Math.max(point.y, minY), maxY)
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function loadPanelPos(storage) {
|
|
548
|
+
if (!storage || typeof storage.getItem !== "function") return null;
|
|
549
|
+
try {
|
|
550
|
+
const raw = storage.getItem(POS_KEY);
|
|
551
|
+
if (typeof raw !== "string" || raw === "") return null;
|
|
552
|
+
const parsed = JSON.parse(raw);
|
|
553
|
+
if (typeof parsed !== "object" || parsed === null || !Number.isFinite(parsed.x) || !Number.isFinite(parsed.y)) {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
return { x: parsed.x, y: parsed.y };
|
|
557
|
+
} catch {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function savePanelPos(storage, point) {
|
|
562
|
+
if (typeof point !== "object" || point === null || !Number.isFinite(point.x) || !Number.isFinite(point.y)) {
|
|
563
|
+
throw new TypeError(`invalid panel position: ${JSON.stringify(point)}`);
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
storage.setItem(POS_KEY, JSON.stringify({ x: point.x, y: point.y }));
|
|
567
|
+
} catch {
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function isDragTarget(el) {
|
|
571
|
+
return Boolean(el?.closest?.("button"));
|
|
572
|
+
}
|
|
573
|
+
var POS_KEY, PANEL_MARGIN, DRAG_SLOP_MOUSE, DRAG_SLOP_TOUCH, ZERO_INSETS;
|
|
574
|
+
var init_window_drag = __esm({
|
|
575
|
+
"src/client/window-drag.js"() {
|
|
576
|
+
"use strict";
|
|
577
|
+
POS_KEY = "dshm-panel-pos";
|
|
578
|
+
PANEL_MARGIN = 8;
|
|
579
|
+
DRAG_SLOP_MOUSE = 6;
|
|
580
|
+
DRAG_SLOP_TOUCH = 10;
|
|
581
|
+
ZERO_INSETS = { left: 0, right: 0, top: 0, bottom: 0 };
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
|
|
497
585
|
// src/client/img-chain.js
|
|
498
586
|
var img_chain_exports = {};
|
|
499
587
|
__export(img_chain_exports, {
|
|
@@ -1668,6 +1756,7 @@ var { DEFAULT_PAGE_SIZE: DEFAULT_PAGE_SIZE2, MARKET_PAGE_SIZES: MARKET_PAGE_SIZE
|
|
|
1668
1756
|
var { readMarketSnapshot: readMarketSnapshot2, writeMarketSnapshot: writeMarketSnapshot2, isDefaultFirstPageQuery: isDefaultFirstPageQuery2 } = (init_market_snapshot(), __toCommonJS(market_snapshot_exports));
|
|
1669
1757
|
var { backdropCloseHandlers: backdropCloseHandlers2 } = (init_backdrop(), __toCommonJS(backdrop_exports));
|
|
1670
1758
|
var { lbStep: lbStep2, lbNeighbors: lbNeighbors2, swipeDir: swipeDir2 } = (init_lightbox(), __toCommonJS(lightbox_exports));
|
|
1759
|
+
var { dragSlop: dragSlop2, grabOffset: grabOffset2, titlebarTopInset: titlebarTopInset2, clampPoint: clampPoint2, loadPanelPos: loadPanelPos2, savePanelPos: savePanelPos2, isDragTarget: isDragTarget2 } = (init_window_drag(), __toCommonJS(window_drag_exports));
|
|
1671
1760
|
var { WESERV_TIMEOUT_MS: WESERV_TIMEOUT_MS2, weservUrl: weservUrl2, serviceBucketOf: serviceBucketOf2, tierOrder: tierOrder2, nextTier: nextTier2, needsTimeout: needsTimeout2, preferredTier: preferredTier2, rememberSuccess: rememberSuccess2 } = (init_img_chain(), __toCommonJS(img_chain_exports));
|
|
1672
1761
|
var { createMarkdown: createMarkdown2 } = (init_markdown(), __toCommonJS(markdown_exports));
|
|
1673
1762
|
var { ExtLink, MdImg, renderMarkdown } = createMarkdown2(h);
|
|
@@ -1845,6 +1934,7 @@ var ZH = {
|
|
|
1845
1934
|
"pager.jump.ph": "\u9875\u53F7",
|
|
1846
1935
|
"panel.fullscreen": "\u5168\u5C4F",
|
|
1847
1936
|
"panel.restore": "\u8FD8\u539F",
|
|
1937
|
+
"panel.dragHint": "\u62D6\u62FD\u79FB\u52A8 \xB7 \u53CC\u51FB\u5207\u6362\u5168\u5C4F",
|
|
1848
1938
|
"lb.title": "\u63D2\u4EF6\u622A\u56FE\u9884\u89C8",
|
|
1849
1939
|
"lb.prev": "\u4E0A\u4E00\u5F20\uFF08\u2190\uFF09",
|
|
1850
1940
|
"lb.next": "\u4E0B\u4E00\u5F20\uFF08\u2192\uFF09",
|
|
@@ -2131,6 +2221,7 @@ var EN = {
|
|
|
2131
2221
|
"pager.jump.ph": "Page",
|
|
2132
2222
|
"panel.fullscreen": "Fullscreen",
|
|
2133
2223
|
"panel.restore": "Restore",
|
|
2224
|
+
"panel.dragHint": "Drag to move \xB7 Double-click to toggle fullscreen",
|
|
2134
2225
|
"lb.title": "Plugin screenshot preview",
|
|
2135
2226
|
"lb.prev": "Previous (\u2190)",
|
|
2136
2227
|
"lb.next": "Next (\u2192)",
|
|
@@ -2275,6 +2366,23 @@ function communityLabels(data) {
|
|
|
2275
2366
|
var CSS = `
|
|
2276
2367
|
.dshm-overlay{position:fixed;inset:0;z-index:2147483000;background:var(--dsw-alias-bg-mask-3,rgba(15,23,42,.48));display:flex;align-items:center;justify-content:center;padding:max(24px,var(--dsh-windows-titlebar-height,0px)) 16px 24px;box-sizing:border-box}
|
|
2277
2368
|
.dshm-panel{width:min(920px,100%);height:min(680px,86vh);display:flex;flex-direction:column;background:var(--dsw-alias-bg-elevated,var(--dsw-alias-bg-base,#fff));background:color-mix(in srgb,var(--dsw-alias-bg-base,#fff) 86%,transparent);backdrop-filter:blur(14px) saturate(1.3);-webkit-backdrop-filter:blur(14px) saturate(1.3);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(2,6,23,.25);overflow:hidden;font-family:inherit;color:var(--dsw-alias-label-primary,inherit)}
|
|
2369
|
+
/* 0.9.70 \u8FD8\u539F\u6001\u62D6\u62FD\uFF1A.abs = \u6709\u4F4D\u7F6E\u8BB0\u5FC6\u65F6\u7684\u7EDD\u5BF9\u5B9A\u4F4D\u5F62\u6001\uFF08overlay \u4E3A inset:0 fixed\uFF0C
|
|
2370
|
+
\u7EDD\u5BF9\u5B9A\u4F4D\u5750\u6807\u5373\u89C6\u53E3\u5750\u6807\uFF09\uFF1B\u65E0\u8BB0\u5F55\u65F6\u9762\u677F\u4ECD\u8D70 overlay flex \u5C45\u4E2D\uFF0C\u6E32\u67D3\u8DEF\u5F84\u4E0E\u65E7\u7248\u9010\u5B57\u8282\u4E00\u81F4\u3002
|
|
2371
|
+
\u62D6\u62FD\u89C2\u611F\u4E0D\u53D8\u91CF\uFF080.9.70 \u4FEE\u8BA2\uFF0CADR-0017 v2\uFF09\uFF1A\u62D6\u62FD\u6001\u4E0E\u9759\u6B62\u6001\u540C\u73BB\u7483\u540C blur\uFF0C\u5916\u89C2\u9010\u50CF\u7D20\u4E00\u81F4
|
|
2372
|
+
\u2014\u2014\u7528\u6237\u53CC\u6001\u5B9E\u62CD\u88C1\u51B3\uFF1A0.9.69 \u7684\u62D6\u62FD\u671F\u5B9E\u5FC3\u56FA\u5316\u5728\u6D45\u8272\u6A21\u5F0F\u4E0B\u5B9E/\u73BB\u7483\u8DF3\u53D8\u89C2\u611F\u7A81\u5140\uFF0C\u9000\u5F79\uFF1B
|
|
2373
|
+
\u900F\u5B57\u9632\u62A4\u56DE\u5F52\u9759\u6B62\u6001\u65E2\u6709\u4E0D\u53D8\u91CF\uFF08\u6BDB\u73BB\u7483\u6A21\u7CCA\u80CC\u666F\u81EA\u6301\u2014\u2014\u900F\u5B57\u75C5\u7076\u7684\u6210\u56E0\u662F\u300C\u5173 blur \u8FD8\u7559
|
|
2374
|
+
\u534A\u900F\u660E\u300D\uFF0C\u4E24\u6837\u90FD\u4E0D\u52A8\u5373\u65E0\u6B64\u75C5\uFF09\u3002\u62D6\u62FD\u53EF\u4F9B\u6027\u53EA\u5269 grabbing \u5149\u6807\u4E0E\u6295\u5F71\u52A0\u6DF1\uFF1B\u4EE3\u4EF7\u662F\u62D6\u62FD\u671F
|
|
2375
|
+
\u6062\u590D\u6BCF\u5E27 backdrop \u91CD\u6EE4\u955C\uFF08GPU \u5408\u6210\u627F\u62C5\uFF0C\u5F31\u673A\u82E5\u62A5\u544A\u6389\u5E27\u518D\u8BC4\u4F30\uFF09\uFF0C\u5E27\u5185\u5199/capture/
|
|
2376
|
+
\u624B\u52BF\u671F\u76D1\u542C\u7B49\u5176\u4F59\u6027\u80FD\u624B\u6BB5\u5168\u90E8\u4FDD\u7559\u3002 */
|
|
2377
|
+
.dshm-panel.abs{position:absolute;margin:0}
|
|
2378
|
+
.dshm-panel--dragging{cursor:grabbing;box-shadow:0 24px 64px rgba(2,6,23,.38)}
|
|
2379
|
+
/* 0.9.69 \u62D6\u62FD\u624B\u67C4\uFF1Auser-select/touch-action/tap-highlight \u65E0\u6761\u4EF6\u751F\u6548\uFF08\u5168\u5C4F\u53CC\u51FB\u5207\u6362\u65F6
|
|
2380
|
+
\u6807\u9898\u4E0D\u51FA\u73B0\u9009\u4E2D\u95EA\u73B0\uFF09\uFF1Bgrab \u5149\u6807\u4EC5\u5728\u300C\u8FD8\u539F\u6001\u4E14\u975E\u62D6\u62FD\u4E2D\u300D\u76F4\u63A5\u547D\u4E2D head\u2014\u2014\u62D6\u62FD\u4E2D head
|
|
2381
|
+
\u65E0\u81EA\u6709 cursor \u58F0\u660E\uFF0C\u81EA\u7136\u7EE7\u627F\u9762\u677F\u6839 .dshm-panel--dragging \u7684 grabbing\uFF08cursor \u6309
|
|
2382
|
+
\u300C\u5143\u7D20\u81EA\u6709\u58F0\u660E > \u7EE7\u627F\u300D\u89E3\u6790\uFF1Agrab \u82E5\u5728\u62D6\u62FD\u4E2D\u4ECD\u76F4\u63A5\u547D\u4E2D head\uFF0Cgrabbing \u6C38\u8FDC\u7EE7\u627F\u4E0D\u8FDB
|
|
2383
|
+
\u6210\u4E3A\u6B7B\u4EE3\u7801\u2014\u2014\u8BC4\u5BA1 R2-2\uFF09\u3002\u5168\u5C4F\u7981\u62D6\uFF08\u786C\u6027\u8981\u6C42\u2462\uFF09\uFF0C\u5149\u6807\u4E0D\u8BEF\u5BFC\uFF08\u8BC4\u5BA1 R1-6\uFF09\u3002 */
|
|
2384
|
+
.dshm-head{user-select:none;touch-action:none;-webkit-tap-highlight-color:transparent}
|
|
2385
|
+
.dshm-panel:not(.full):not(.dshm-panel--dragging) .dshm-head{cursor:grab}
|
|
2278
2386
|
/* 0.9.4 Windows Desktop \u5168\u5C4F\u4FEE\u590D\uFF1A\u58F3\u5728 html \u4E0A\u8BBE --dsh-windows-titlebar-height\uFF0840px\uFF0C
|
|
2279
2387
|
titleBarStyle:hidden + titleBarOverlay:42 \u7CFB\u7EDF\u7ED8\u5236 \u2014 \u25A1 \u2715\uFF09\uFF0C\u5176\u4E0A\u8FD8\u6709\u5168\u5BBD
|
|
2280
2388
|
-webkit-app-region:drag \u62D6\u62FD\u5E26\u2014\u2014\u8BE5\u5E26\u6309\u5E03\u5C40\u53C2\u4E0E\u62D6\u62FD\u5224\u5B9A\u3001\u65E0\u89C6 z-index/\u7ED8\u5236\u987A\u5E8F\uFF0C
|
|
@@ -4666,6 +4774,27 @@ function FsIcon({ full }) {
|
|
|
4666
4774
|
h("path", { d: "M9 4H4v5M15 4h5v5M9 20H4v-5M15 20h5v-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
4667
4775
|
);
|
|
4668
4776
|
}
|
|
4777
|
+
function readTitlebarInsets() {
|
|
4778
|
+
if (typeof document === "undefined" || !document.documentElement) {
|
|
4779
|
+
return { left: 0, right: 0, top: 0, bottom: 0 };
|
|
4780
|
+
}
|
|
4781
|
+
const htmlEl = document.documentElement;
|
|
4782
|
+
let height = "";
|
|
4783
|
+
try {
|
|
4784
|
+
height = window.getComputedStyle(htmlEl).getPropertyValue("--dsh-windows-titlebar-height");
|
|
4785
|
+
} catch {
|
|
4786
|
+
}
|
|
4787
|
+
return {
|
|
4788
|
+
left: 0,
|
|
4789
|
+
right: 0,
|
|
4790
|
+
top: titlebarTopInset2({
|
|
4791
|
+
titlebar: htmlEl.hasAttribute("data-windows-titlebar"),
|
|
4792
|
+
fullscreen: htmlEl.hasAttribute("data-fullscreen"),
|
|
4793
|
+
height
|
|
4794
|
+
}),
|
|
4795
|
+
bottom: 0
|
|
4796
|
+
};
|
|
4797
|
+
}
|
|
4669
4798
|
function MarketPanel({ onClose }) {
|
|
4670
4799
|
const [tab, setTab] = useState("market");
|
|
4671
4800
|
const FS_KEY = "dshm-panel-fullscreen";
|
|
@@ -4686,6 +4815,157 @@ function MarketPanel({ onClose }) {
|
|
|
4686
4815
|
return next;
|
|
4687
4816
|
});
|
|
4688
4817
|
}, []);
|
|
4818
|
+
const panelRef = useRef(null);
|
|
4819
|
+
const headRef = useRef(null);
|
|
4820
|
+
const draggingRef = useRef(false);
|
|
4821
|
+
const suppressClickRef = useRef(false);
|
|
4822
|
+
const [pos, setPos] = useState(() => {
|
|
4823
|
+
try {
|
|
4824
|
+
return loadPanelPos2(typeof window !== "undefined" && window.localStorage ? window.localStorage : null);
|
|
4825
|
+
} catch {
|
|
4826
|
+
return null;
|
|
4827
|
+
}
|
|
4828
|
+
});
|
|
4829
|
+
const fullRef = useRef(full);
|
|
4830
|
+
fullRef.current = full;
|
|
4831
|
+
const posRef = useRef(pos);
|
|
4832
|
+
posRef.current = pos;
|
|
4833
|
+
const reclamp = useCallback(() => {
|
|
4834
|
+
if (fullRef.current || draggingRef.current || !panelRef.current || typeof window === "undefined") return;
|
|
4835
|
+
const prev = posRef.current;
|
|
4836
|
+
if (!prev) return;
|
|
4837
|
+
const rect = panelRef.current.getBoundingClientRect();
|
|
4838
|
+
const next = clampPoint2(
|
|
4839
|
+
{ x: prev.x, y: prev.y },
|
|
4840
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
4841
|
+
{ width: rect.width, height: rect.height },
|
|
4842
|
+
readTitlebarInsets()
|
|
4843
|
+
);
|
|
4844
|
+
if (next.x !== prev.x || next.y !== prev.y) setPos(next);
|
|
4845
|
+
}, []);
|
|
4846
|
+
useLayoutEffect(() => {
|
|
4847
|
+
reclamp();
|
|
4848
|
+
}, [full, reclamp]);
|
|
4849
|
+
useEffect(() => {
|
|
4850
|
+
window.addEventListener("resize", reclamp);
|
|
4851
|
+
return () => window.removeEventListener("resize", reclamp);
|
|
4852
|
+
}, [reclamp]);
|
|
4853
|
+
useEffect(() => {
|
|
4854
|
+
const headEl = headRef.current;
|
|
4855
|
+
if (!headEl) return void 0;
|
|
4856
|
+
let dragging = false;
|
|
4857
|
+
let moved = false;
|
|
4858
|
+
let slop = 6;
|
|
4859
|
+
let originX = 0;
|
|
4860
|
+
let originY = 0;
|
|
4861
|
+
let grab = { dx: 0, dy: 0 };
|
|
4862
|
+
let rest = { x: 0, y: 0 };
|
|
4863
|
+
let size = { width: 0, height: 0 };
|
|
4864
|
+
let lastPoint = null;
|
|
4865
|
+
const bindGesture = () => {
|
|
4866
|
+
document.addEventListener("pointermove", onPointerMove);
|
|
4867
|
+
document.addEventListener("pointerup", onPointerUp);
|
|
4868
|
+
document.addEventListener("pointercancel", onPointerCancel);
|
|
4869
|
+
};
|
|
4870
|
+
const unbindGesture = () => {
|
|
4871
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
4872
|
+
document.removeEventListener("pointerup", onPointerUp);
|
|
4873
|
+
document.removeEventListener("pointercancel", onPointerCancel);
|
|
4874
|
+
};
|
|
4875
|
+
const frameStep = () => {
|
|
4876
|
+
if (!dragging || !lastPoint || !panelRef.current) return;
|
|
4877
|
+
const topLeft = clampPoint2(
|
|
4878
|
+
{ x: lastPoint.x - grab.dx, y: lastPoint.y - grab.dy },
|
|
4879
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
4880
|
+
size,
|
|
4881
|
+
readTitlebarInsets()
|
|
4882
|
+
);
|
|
4883
|
+
panelRef.current.style.transform = `translate3d(${Math.round(topLeft.x - rest.x)}px, ${Math.round(topLeft.y - rest.y)}px, 0)`;
|
|
4884
|
+
};
|
|
4885
|
+
const onPointerMove = (event) => {
|
|
4886
|
+
if (!dragging) return;
|
|
4887
|
+
if (!moved) {
|
|
4888
|
+
if (Math.hypot(event.clientX - originX, event.clientY - originY) < slop) return;
|
|
4889
|
+
moved = true;
|
|
4890
|
+
document.body.style.userSelect = "none";
|
|
4891
|
+
panelRef.current.classList.add("dshm-panel--dragging");
|
|
4892
|
+
panelRef.current.style.willChange = "transform";
|
|
4893
|
+
}
|
|
4894
|
+
lastPoint = { x: event.clientX, y: event.clientY };
|
|
4895
|
+
if (typeof window.requestAnimationFrame === "function") window.requestAnimationFrame(frameStep);
|
|
4896
|
+
else frameStep();
|
|
4897
|
+
};
|
|
4898
|
+
const bakeFinal = (event) => {
|
|
4899
|
+
const panel = panelRef.current;
|
|
4900
|
+
const final = clampPoint2(
|
|
4901
|
+
{ x: event.clientX - grab.dx, y: event.clientY - grab.dy },
|
|
4902
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
4903
|
+
size,
|
|
4904
|
+
readTitlebarInsets()
|
|
4905
|
+
);
|
|
4906
|
+
panel.style.left = `${final.x}px`;
|
|
4907
|
+
panel.style.top = `${final.y}px`;
|
|
4908
|
+
panel.classList.remove("dshm-panel--dragging");
|
|
4909
|
+
panel.style.willChange = "";
|
|
4910
|
+
panel.style.transform = "";
|
|
4911
|
+
try {
|
|
4912
|
+
savePanelPos2(window.localStorage, final);
|
|
4913
|
+
} catch {
|
|
4914
|
+
}
|
|
4915
|
+
suppressClickRef.current = true;
|
|
4916
|
+
setPos(final);
|
|
4917
|
+
};
|
|
4918
|
+
const onPointerUp = (event) => {
|
|
4919
|
+
if (!dragging) return;
|
|
4920
|
+
dragging = false;
|
|
4921
|
+
draggingRef.current = false;
|
|
4922
|
+
unbindGesture();
|
|
4923
|
+
document.body.style.userSelect = "";
|
|
4924
|
+
if (!moved) return;
|
|
4925
|
+
bakeFinal(event);
|
|
4926
|
+
};
|
|
4927
|
+
const onPointerCancel = () => {
|
|
4928
|
+
if (!dragging) return;
|
|
4929
|
+
dragging = false;
|
|
4930
|
+
draggingRef.current = false;
|
|
4931
|
+
unbindGesture();
|
|
4932
|
+
document.body.style.userSelect = "";
|
|
4933
|
+
const panel = panelRef.current;
|
|
4934
|
+
panel.classList.remove("dshm-panel--dragging");
|
|
4935
|
+
panel.style.willChange = "";
|
|
4936
|
+
panel.style.transform = "";
|
|
4937
|
+
};
|
|
4938
|
+
const onPointerDown = (event) => {
|
|
4939
|
+
if (dragging) return;
|
|
4940
|
+
if (fullRef.current || event.button !== 0 || isDragTarget2(event.target)) return;
|
|
4941
|
+
const panel = panelRef.current;
|
|
4942
|
+
if (!panel) return;
|
|
4943
|
+
dragging = true;
|
|
4944
|
+
draggingRef.current = true;
|
|
4945
|
+
moved = false;
|
|
4946
|
+
lastPoint = null;
|
|
4947
|
+
originX = event.clientX;
|
|
4948
|
+
originY = event.clientY;
|
|
4949
|
+
slop = dragSlop2(event.pointerType);
|
|
4950
|
+
const rect = panel.getBoundingClientRect();
|
|
4951
|
+
grab = grabOffset2({ x: event.clientX, y: event.clientY }, rect);
|
|
4952
|
+
rest = { x: rect.left, y: rect.top };
|
|
4953
|
+
size = { width: rect.width, height: rect.height };
|
|
4954
|
+
if (headEl.setPointerCapture) {
|
|
4955
|
+
try {
|
|
4956
|
+
headEl.setPointerCapture(event.pointerId);
|
|
4957
|
+
} catch {
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
bindGesture();
|
|
4961
|
+
};
|
|
4962
|
+
headEl.addEventListener("pointerdown", onPointerDown);
|
|
4963
|
+
return () => {
|
|
4964
|
+
headEl.removeEventListener("pointerdown", onPointerDown);
|
|
4965
|
+
unbindGesture();
|
|
4966
|
+
document.body.style.userSelect = "";
|
|
4967
|
+
};
|
|
4968
|
+
}, []);
|
|
4689
4969
|
const marketCommunity = useMarketData("community");
|
|
4690
4970
|
const marketPrimary = useMarketData("primary");
|
|
4691
4971
|
const markets = { community: marketCommunity, primary: marketPrimary };
|
|
@@ -4841,10 +5121,29 @@ function MarketPanel({ onClose }) {
|
|
|
4841
5121
|
{ className: full ? "dshm-overlay full" : "dshm-overlay", ...backdropCloseHandlers2(onClose) },
|
|
4842
5122
|
h(
|
|
4843
5123
|
"div",
|
|
4844
|
-
{
|
|
5124
|
+
{
|
|
5125
|
+
className: ["dshm-panel", full && "full", !full && pos && "abs"].filter(Boolean).join(" "),
|
|
5126
|
+
style: !full && pos ? { left: `${pos.x}px`, top: `${pos.y}px` } : void 0,
|
|
5127
|
+
ref: panelRef,
|
|
5128
|
+
onClick: (e) => e.stopPropagation()
|
|
5129
|
+
},
|
|
4845
5130
|
h(
|
|
4846
5131
|
"div",
|
|
4847
|
-
{
|
|
5132
|
+
{
|
|
5133
|
+
className: "dshm-head",
|
|
5134
|
+
title: full ? void 0 : lookup("panel.dragHint"),
|
|
5135
|
+
ref: headRef,
|
|
5136
|
+
onDoubleClick: (e) => {
|
|
5137
|
+
if (isDragTarget2(e.target)) return;
|
|
5138
|
+
toggleFull();
|
|
5139
|
+
},
|
|
5140
|
+
onClickCapture: (e) => {
|
|
5141
|
+
if (!suppressClickRef.current) return;
|
|
5142
|
+
suppressClickRef.current = false;
|
|
5143
|
+
e.stopPropagation();
|
|
5144
|
+
e.preventDefault();
|
|
5145
|
+
}
|
|
5146
|
+
},
|
|
4848
5147
|
h("span", { className: "dshm-title" }, lookup("title.full")),
|
|
4849
5148
|
h("span", { className: "dshm-head-divider", "aria-hidden": "true" }),
|
|
4850
5149
|
h(
|