dsh-m 0.9.68 → 0.9.69
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 +12 -0
- package/lib/client.js +332 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,12 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
8
8
|
|
|
9
9
|
## 中文
|
|
10
10
|
|
|
11
|
+
### 0.9.69 功能:还原态面板头部拖拽 + 位置记忆(ADR-0017)
|
|
12
|
+
|
|
13
|
+
- **方案**:市场面板(`.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 实证修复并以结构锚钉死)。
|
|
14
|
+
- **皮肤免疫(拖拽期 = 最快渲染路径)**:拖拽背景取手势开始时 `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 语义全部不动。
|
|
15
|
+
- **验证**:1326 项测试全绿、typecheck 零错误;构建产物含 `dshm-panel--dragging`/`dshm-panel-pos` 逐字锚;`!important` 清单定长 4→7(client-lightbox 断言登记)。实施计划与执行结果经独立评审 Agent 五轮对抗评审全闭环(16 条:13 条修于计划、3 条修于代码/如实申报)。
|
|
16
|
+
|
|
11
17
|
### 0.9.68 性能:合并市场物化——同身份免重付 5MB 解析 + 适配 + 合并(ADR-0016)
|
|
12
18
|
|
|
13
19
|
- **问题(性能架构审查旗舰项,三轮评审闭环)**:领域概念「合并市场(Merged Market)」在 GLOSSARY 有名无主——装配逻辑内联在 `communityOutcome`,每个市场请求(缓存全命中的快路径也在内)都重付磁盘读 5.36MB 社区目录 + JSON.parse(实测 30–55ms)→ 目录适配层重建 4,400 条(30–90ms)→ 合并排序(5–35ms);且全部是**同步 JS、阻塞宿主事件循环**——市场每翻一页/每提交一次搜索,宿主的 SSE 对话流与工具调用停摆 65–400ms。搜索评分因归一化 WeakMap 跨请求永不命中再加 ~330ms(冷)。
|
|
@@ -505,6 +511,12 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
505
511
|
|
|
506
512
|
## English
|
|
507
513
|
|
|
514
|
+
### Feature in 0.9.69 — drag the marketplace panel by its header in restore state, with position memory (ADR-0017)
|
|
515
|
+
|
|
516
|
+
- **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).
|
|
517
|
+
- **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.
|
|
518
|
+
- **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).
|
|
519
|
+
|
|
508
520
|
### Performance in 0.9.68 — materialized merged market: skip the 5MB parse + adapt + merge for repeat identities (ADR-0016)
|
|
509
521
|
|
|
510
522
|
- **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,120 @@ 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
|
+
solidifyColor: () => solidifyColor,
|
|
511
|
+
titlebarTopInset: () => titlebarTopInset
|
|
512
|
+
});
|
|
513
|
+
function dragSlop(pointerType) {
|
|
514
|
+
return pointerType === "touch" || pointerType === "pen" ? DRAG_SLOP_TOUCH : DRAG_SLOP_MOUSE;
|
|
515
|
+
}
|
|
516
|
+
function grabOffset(point, rect) {
|
|
517
|
+
return { dx: point.x - rect.left, dy: point.y - rect.top };
|
|
518
|
+
}
|
|
519
|
+
function titlebarTopInset({ titlebar = false, fullscreen = false, height } = {}) {
|
|
520
|
+
if (!titlebar || fullscreen) return 0;
|
|
521
|
+
const value = Number.parseFloat(height);
|
|
522
|
+
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
523
|
+
}
|
|
524
|
+
function normalizeSize(size) {
|
|
525
|
+
if (typeof size === "number") return { width: size, height: size };
|
|
526
|
+
const width = typeof size?.width === "number" ? size.width : 0;
|
|
527
|
+
const height = typeof size?.height === "number" ? size.height : 0;
|
|
528
|
+
return { width, height };
|
|
529
|
+
}
|
|
530
|
+
function finiteInset(v) {
|
|
531
|
+
return Number.isFinite(v) ? v : 0;
|
|
532
|
+
}
|
|
533
|
+
function clampPoint(point, viewport, size = 0, insets = ZERO_INSETS) {
|
|
534
|
+
const { width, height } = normalizeSize(size);
|
|
535
|
+
const insetLeft = finiteInset(insets?.left);
|
|
536
|
+
const insetRight = finiteInset(insets?.right);
|
|
537
|
+
const insetTop = finiteInset(insets?.top);
|
|
538
|
+
const insetBottom = finiteInset(insets?.bottom);
|
|
539
|
+
const minX = PANEL_MARGIN + insetLeft;
|
|
540
|
+
const minY = PANEL_MARGIN + insetTop;
|
|
541
|
+
const maxX = Math.max(minX, viewport.width - insetRight - PANEL_MARGIN - width);
|
|
542
|
+
const maxY = Math.max(minY, viewport.height - insetBottom - PANEL_MARGIN - height);
|
|
543
|
+
return {
|
|
544
|
+
x: Math.min(Math.max(point.x, minX), maxX),
|
|
545
|
+
y: Math.min(Math.max(point.y, minY), maxY)
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function solidifyColor(computedColor) {
|
|
549
|
+
if (typeof computedColor !== "string") return null;
|
|
550
|
+
const srgb = COLOR_SRGB_RE.exec(computedColor.trim());
|
|
551
|
+
if (srgb) {
|
|
552
|
+
const alpha = srgb[4] !== void 0 ? Number.parseFloat(srgb[4]) : 1;
|
|
553
|
+
if (!Number.isFinite(alpha) || alpha < 0.05) return null;
|
|
554
|
+
const to255 = (v) => Math.round(Number.parseFloat(v) * 255);
|
|
555
|
+
return `rgb(${to255(srgb[1])}, ${to255(srgb[2])}, ${to255(srgb[3])})`;
|
|
556
|
+
}
|
|
557
|
+
const rgb = RGB_RE.exec(computedColor.trim());
|
|
558
|
+
if (rgb) {
|
|
559
|
+
const parts = rgb[1].split(",").map((p) => p.trim());
|
|
560
|
+
if (parts.length !== 3 && parts.length !== 4) return null;
|
|
561
|
+
const r = Number.parseFloat(parts[0]);
|
|
562
|
+
const g = Number.parseFloat(parts[1]);
|
|
563
|
+
const b = Number.parseFloat(parts[2]);
|
|
564
|
+
const a = parts.length === 4 ? Number.parseFloat(parts[3]) : 1;
|
|
565
|
+
if (![r, g, b, a].every(Number.isFinite)) return null;
|
|
566
|
+
if (a < 0.05) return null;
|
|
567
|
+
return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
|
|
568
|
+
}
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
function loadPanelPos(storage) {
|
|
572
|
+
if (!storage || typeof storage.getItem !== "function") return null;
|
|
573
|
+
try {
|
|
574
|
+
const raw = storage.getItem(POS_KEY);
|
|
575
|
+
if (typeof raw !== "string" || raw === "") return null;
|
|
576
|
+
const parsed = JSON.parse(raw);
|
|
577
|
+
if (typeof parsed !== "object" || parsed === null || !Number.isFinite(parsed.x) || !Number.isFinite(parsed.y)) {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
return { x: parsed.x, y: parsed.y };
|
|
581
|
+
} catch {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function savePanelPos(storage, point) {
|
|
586
|
+
if (typeof point !== "object" || point === null || !Number.isFinite(point.x) || !Number.isFinite(point.y)) {
|
|
587
|
+
throw new TypeError(`invalid panel position: ${JSON.stringify(point)}`);
|
|
588
|
+
}
|
|
589
|
+
try {
|
|
590
|
+
storage.setItem(POS_KEY, JSON.stringify({ x: point.x, y: point.y }));
|
|
591
|
+
} catch {
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
function isDragTarget(el) {
|
|
595
|
+
return Boolean(el?.closest?.("button"));
|
|
596
|
+
}
|
|
597
|
+
var POS_KEY, PANEL_MARGIN, DRAG_SLOP_MOUSE, DRAG_SLOP_TOUCH, ZERO_INSETS, RGB_RE, COLOR_SRGB_RE;
|
|
598
|
+
var init_window_drag = __esm({
|
|
599
|
+
"src/client/window-drag.js"() {
|
|
600
|
+
"use strict";
|
|
601
|
+
POS_KEY = "dshm-panel-pos";
|
|
602
|
+
PANEL_MARGIN = 8;
|
|
603
|
+
DRAG_SLOP_MOUSE = 6;
|
|
604
|
+
DRAG_SLOP_TOUCH = 10;
|
|
605
|
+
ZERO_INSETS = { left: 0, right: 0, top: 0, bottom: 0 };
|
|
606
|
+
RGB_RE = /^rgba?\(([^)]+)\)$/i;
|
|
607
|
+
COLOR_SRGB_RE = /^color\(srgb\s+([0-9.]+)\s+([0-9.]+)\s+([0-9.]+)(?:\s*\/\s*([0-9.]+))?\)$/i;
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
|
|
497
611
|
// src/client/img-chain.js
|
|
498
612
|
var img_chain_exports = {};
|
|
499
613
|
__export(img_chain_exports, {
|
|
@@ -1668,6 +1782,7 @@ var { DEFAULT_PAGE_SIZE: DEFAULT_PAGE_SIZE2, MARKET_PAGE_SIZES: MARKET_PAGE_SIZE
|
|
|
1668
1782
|
var { readMarketSnapshot: readMarketSnapshot2, writeMarketSnapshot: writeMarketSnapshot2, isDefaultFirstPageQuery: isDefaultFirstPageQuery2 } = (init_market_snapshot(), __toCommonJS(market_snapshot_exports));
|
|
1669
1783
|
var { backdropCloseHandlers: backdropCloseHandlers2 } = (init_backdrop(), __toCommonJS(backdrop_exports));
|
|
1670
1784
|
var { lbStep: lbStep2, lbNeighbors: lbNeighbors2, swipeDir: swipeDir2 } = (init_lightbox(), __toCommonJS(lightbox_exports));
|
|
1785
|
+
var { dragSlop: dragSlop2, grabOffset: grabOffset2, titlebarTopInset: titlebarTopInset2, clampPoint: clampPoint2, solidifyColor: solidifyColor2, loadPanelPos: loadPanelPos2, savePanelPos: savePanelPos2, isDragTarget: isDragTarget2 } = (init_window_drag(), __toCommonJS(window_drag_exports));
|
|
1671
1786
|
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
1787
|
var { createMarkdown: createMarkdown2 } = (init_markdown(), __toCommonJS(markdown_exports));
|
|
1673
1788
|
var { ExtLink, MdImg, renderMarkdown } = createMarkdown2(h);
|
|
@@ -1845,6 +1960,7 @@ var ZH = {
|
|
|
1845
1960
|
"pager.jump.ph": "\u9875\u53F7",
|
|
1846
1961
|
"panel.fullscreen": "\u5168\u5C4F",
|
|
1847
1962
|
"panel.restore": "\u8FD8\u539F",
|
|
1963
|
+
"panel.dragHint": "\u62D6\u62FD\u79FB\u52A8 \xB7 \u53CC\u51FB\u5207\u6362\u5168\u5C4F",
|
|
1848
1964
|
"lb.title": "\u63D2\u4EF6\u622A\u56FE\u9884\u89C8",
|
|
1849
1965
|
"lb.prev": "\u4E0A\u4E00\u5F20\uFF08\u2190\uFF09",
|
|
1850
1966
|
"lb.next": "\u4E0B\u4E00\u5F20\uFF08\u2192\uFF09",
|
|
@@ -2131,6 +2247,7 @@ var EN = {
|
|
|
2131
2247
|
"pager.jump.ph": "Page",
|
|
2132
2248
|
"panel.fullscreen": "Fullscreen",
|
|
2133
2249
|
"panel.restore": "Restore",
|
|
2250
|
+
"panel.dragHint": "Drag to move \xB7 Double-click to toggle fullscreen",
|
|
2134
2251
|
"lb.title": "Plugin screenshot preview",
|
|
2135
2252
|
"lb.prev": "Previous (\u2190)",
|
|
2136
2253
|
"lb.next": "Next (\u2192)",
|
|
@@ -2275,6 +2392,23 @@ function communityLabels(data) {
|
|
|
2275
2392
|
var CSS = `
|
|
2276
2393
|
.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
2394
|
.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)}
|
|
2395
|
+
/* 0.9.69 \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
|
|
2396
|
+
\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
|
|
2397
|
+
\u62D6\u62FD\u6001\u81EA\u6301\uFF080.9.62 \u5F71\u9662\u540C\u6B3E\u7EAA\u5F8B\uFF09\uFF1Abackground \u53D6\u8C03\u7528\u65B9\u624B\u52BF\u5F00\u59CB\u65F6\u5199\u5165\u7684 --dshm-drag-bg
|
|
2398
|
+
\uFF08getComputedStyle \u89E3\u6790\u540E\u7684\u6700\u7EC8\u8272\u5F3A\u5236 \u03B1=1\uFF0C\u89C1 window-drag.solidifyColor\uFF09\uFF0C\u76AE\u80A4\u5BF9
|
|
2399
|
+
--dsw-alias-* \u7684\u534A\u900F\u660E\u5B9A\u4E49\uFF08openbmc/uefi bg-base \u03B1=0.55 \u75C5\u7C7B\uFF09\u65E0\u6CD5\u518D\u8BA9\u62D6\u62FD\u4E2D\u9762\u677F\u900F\u5149\uFF1B
|
|
2400
|
+
blur \u5173\u95ED\u662F\u62D6\u62FD\u671F\u6700\u5927\u6027\u80FD\u6536\u76CA\uFF08\u6BCF\u5E27\u514D\u5927\u9762\u79EF\u91CD\u6EE4\u955C\uFF09\uFF0Ctransform \u2260 none \u7EF4\u6301\u5305\u542B\u5757\uFF0C
|
|
2401
|
+
fixed \u540E\u4EE3\u4E0D\u9003\u9038\u3002\u81EA\u6301\u4E09\u5904\u5DF2\u767B\u8BB0\u4E8E client-lightbox.test.mjs \u5B9A\u957F\u65AD\u8A00\uFF084\u21927\uFF09\uFF1B
|
|
2402
|
+
\u672C\u6CE8\u91CA\u523B\u610F\u4E0D\u7528\u6E05\u5355\u5173\u952E\u5B57\u5B57\u9762\u91CF\u2014\u2014\u8BA1\u6570\u5BF9\u6CE8\u91CA\u63D0\u53CA\u540C\u6837\u751F\u6548\uFF08\u8BC4\u5BA1 R1-2\uFF09\u3002 */
|
|
2403
|
+
.dshm-panel.abs{position:absolute;margin:0}
|
|
2404
|
+
.dshm-panel--dragging{cursor:grabbing;background:var(--dshm-drag-bg,Canvas)!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important;box-shadow:0 24px 64px rgba(2,6,23,.38)}
|
|
2405
|
+
/* 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
|
|
2406
|
+
\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
|
|
2407
|
+
\u65E0\u81EA\u6709 cursor \u58F0\u660E\uFF0C\u81EA\u7136\u7EE7\u627F\u9762\u677F\u6839 .dshm-panel--dragging \u7684 grabbing\uFF08cursor \u6309
|
|
2408
|
+
\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
|
|
2409
|
+
\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 */
|
|
2410
|
+
.dshm-head{user-select:none;touch-action:none;-webkit-tap-highlight-color:transparent}
|
|
2411
|
+
.dshm-panel:not(.full):not(.dshm-panel--dragging) .dshm-head{cursor:grab}
|
|
2278
2412
|
/* 0.9.4 Windows Desktop \u5168\u5C4F\u4FEE\u590D\uFF1A\u58F3\u5728 html \u4E0A\u8BBE --dsh-windows-titlebar-height\uFF0840px\uFF0C
|
|
2279
2413
|
titleBarStyle:hidden + titleBarOverlay:42 \u7CFB\u7EDF\u7ED8\u5236 \u2014 \u25A1 \u2715\uFF09\uFF0C\u5176\u4E0A\u8FD8\u6709\u5168\u5BBD
|
|
2280
2414
|
-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 +4800,27 @@ function FsIcon({ full }) {
|
|
|
4666
4800
|
h("path", { d: "M9 4H4v5M15 4h5v5M9 20H4v-5M15 20h5v-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
4667
4801
|
);
|
|
4668
4802
|
}
|
|
4803
|
+
function readTitlebarInsets() {
|
|
4804
|
+
if (typeof document === "undefined" || !document.documentElement) {
|
|
4805
|
+
return { left: 0, right: 0, top: 0, bottom: 0 };
|
|
4806
|
+
}
|
|
4807
|
+
const htmlEl = document.documentElement;
|
|
4808
|
+
let height = "";
|
|
4809
|
+
try {
|
|
4810
|
+
height = window.getComputedStyle(htmlEl).getPropertyValue("--dsh-windows-titlebar-height");
|
|
4811
|
+
} catch {
|
|
4812
|
+
}
|
|
4813
|
+
return {
|
|
4814
|
+
left: 0,
|
|
4815
|
+
right: 0,
|
|
4816
|
+
top: titlebarTopInset2({
|
|
4817
|
+
titlebar: htmlEl.hasAttribute("data-windows-titlebar"),
|
|
4818
|
+
fullscreen: htmlEl.hasAttribute("data-fullscreen"),
|
|
4819
|
+
height
|
|
4820
|
+
}),
|
|
4821
|
+
bottom: 0
|
|
4822
|
+
};
|
|
4823
|
+
}
|
|
4669
4824
|
function MarketPanel({ onClose }) {
|
|
4670
4825
|
const [tab, setTab] = useState("market");
|
|
4671
4826
|
const FS_KEY = "dshm-panel-fullscreen";
|
|
@@ -4686,6 +4841,162 @@ function MarketPanel({ onClose }) {
|
|
|
4686
4841
|
return next;
|
|
4687
4842
|
});
|
|
4688
4843
|
}, []);
|
|
4844
|
+
const panelRef = useRef(null);
|
|
4845
|
+
const headRef = useRef(null);
|
|
4846
|
+
const draggingRef = useRef(false);
|
|
4847
|
+
const suppressClickRef = useRef(false);
|
|
4848
|
+
const [pos, setPos] = useState(() => {
|
|
4849
|
+
try {
|
|
4850
|
+
return loadPanelPos2(typeof window !== "undefined" && window.localStorage ? window.localStorage : null);
|
|
4851
|
+
} catch {
|
|
4852
|
+
return null;
|
|
4853
|
+
}
|
|
4854
|
+
});
|
|
4855
|
+
const fullRef = useRef(full);
|
|
4856
|
+
fullRef.current = full;
|
|
4857
|
+
const posRef = useRef(pos);
|
|
4858
|
+
posRef.current = pos;
|
|
4859
|
+
const reclamp = useCallback(() => {
|
|
4860
|
+
if (fullRef.current || draggingRef.current || !panelRef.current || typeof window === "undefined") return;
|
|
4861
|
+
const prev = posRef.current;
|
|
4862
|
+
if (!prev) return;
|
|
4863
|
+
const rect = panelRef.current.getBoundingClientRect();
|
|
4864
|
+
const next = clampPoint2(
|
|
4865
|
+
{ x: prev.x, y: prev.y },
|
|
4866
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
4867
|
+
{ width: rect.width, height: rect.height },
|
|
4868
|
+
readTitlebarInsets()
|
|
4869
|
+
);
|
|
4870
|
+
if (next.x !== prev.x || next.y !== prev.y) setPos(next);
|
|
4871
|
+
}, []);
|
|
4872
|
+
useLayoutEffect(() => {
|
|
4873
|
+
reclamp();
|
|
4874
|
+
}, [full, reclamp]);
|
|
4875
|
+
useEffect(() => {
|
|
4876
|
+
window.addEventListener("resize", reclamp);
|
|
4877
|
+
return () => window.removeEventListener("resize", reclamp);
|
|
4878
|
+
}, [reclamp]);
|
|
4879
|
+
useEffect(() => {
|
|
4880
|
+
const headEl = headRef.current;
|
|
4881
|
+
if (!headEl) return void 0;
|
|
4882
|
+
let dragging = false;
|
|
4883
|
+
let moved = false;
|
|
4884
|
+
let slop = 6;
|
|
4885
|
+
let originX = 0;
|
|
4886
|
+
let originY = 0;
|
|
4887
|
+
let grab = { dx: 0, dy: 0 };
|
|
4888
|
+
let rest = { x: 0, y: 0 };
|
|
4889
|
+
let size = { width: 0, height: 0 };
|
|
4890
|
+
let lastPoint = null;
|
|
4891
|
+
const bindGesture = () => {
|
|
4892
|
+
document.addEventListener("pointermove", onPointerMove);
|
|
4893
|
+
document.addEventListener("pointerup", onPointerUp);
|
|
4894
|
+
document.addEventListener("pointercancel", onPointerCancel);
|
|
4895
|
+
};
|
|
4896
|
+
const unbindGesture = () => {
|
|
4897
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
4898
|
+
document.removeEventListener("pointerup", onPointerUp);
|
|
4899
|
+
document.removeEventListener("pointercancel", onPointerCancel);
|
|
4900
|
+
};
|
|
4901
|
+
const frameStep = () => {
|
|
4902
|
+
if (!dragging || !lastPoint || !panelRef.current) return;
|
|
4903
|
+
const topLeft = clampPoint2(
|
|
4904
|
+
{ x: lastPoint.x - grab.dx, y: lastPoint.y - grab.dy },
|
|
4905
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
4906
|
+
size,
|
|
4907
|
+
readTitlebarInsets()
|
|
4908
|
+
);
|
|
4909
|
+
panelRef.current.style.transform = `translate3d(${Math.round(topLeft.x - rest.x)}px, ${Math.round(topLeft.y - rest.y)}px, 0)`;
|
|
4910
|
+
};
|
|
4911
|
+
const onPointerMove = (event) => {
|
|
4912
|
+
if (!dragging) return;
|
|
4913
|
+
if (!moved) {
|
|
4914
|
+
if (Math.hypot(event.clientX - originX, event.clientY - originY) < slop) return;
|
|
4915
|
+
moved = true;
|
|
4916
|
+
document.body.style.userSelect = "none";
|
|
4917
|
+
panelRef.current.classList.add("dshm-panel--dragging");
|
|
4918
|
+
panelRef.current.style.willChange = "transform";
|
|
4919
|
+
}
|
|
4920
|
+
lastPoint = { x: event.clientX, y: event.clientY };
|
|
4921
|
+
if (typeof window.requestAnimationFrame === "function") window.requestAnimationFrame(frameStep);
|
|
4922
|
+
else frameStep();
|
|
4923
|
+
};
|
|
4924
|
+
const bakeFinal = (event) => {
|
|
4925
|
+
const panel = panelRef.current;
|
|
4926
|
+
const final = clampPoint2(
|
|
4927
|
+
{ x: event.clientX - grab.dx, y: event.clientY - grab.dy },
|
|
4928
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
4929
|
+
size,
|
|
4930
|
+
readTitlebarInsets()
|
|
4931
|
+
);
|
|
4932
|
+
panel.style.left = `${final.x}px`;
|
|
4933
|
+
panel.style.top = `${final.y}px`;
|
|
4934
|
+
panel.classList.remove("dshm-panel--dragging");
|
|
4935
|
+
panel.style.willChange = "";
|
|
4936
|
+
panel.style.transform = "";
|
|
4937
|
+
panel.style.removeProperty("--dshm-drag-bg");
|
|
4938
|
+
try {
|
|
4939
|
+
savePanelPos2(window.localStorage, final);
|
|
4940
|
+
} catch {
|
|
4941
|
+
}
|
|
4942
|
+
suppressClickRef.current = true;
|
|
4943
|
+
setPos(final);
|
|
4944
|
+
};
|
|
4945
|
+
const onPointerUp = (event) => {
|
|
4946
|
+
if (!dragging) return;
|
|
4947
|
+
dragging = false;
|
|
4948
|
+
draggingRef.current = false;
|
|
4949
|
+
unbindGesture();
|
|
4950
|
+
document.body.style.userSelect = "";
|
|
4951
|
+
if (!moved) return;
|
|
4952
|
+
bakeFinal(event);
|
|
4953
|
+
};
|
|
4954
|
+
const onPointerCancel = () => {
|
|
4955
|
+
if (!dragging) return;
|
|
4956
|
+
dragging = false;
|
|
4957
|
+
draggingRef.current = false;
|
|
4958
|
+
unbindGesture();
|
|
4959
|
+
document.body.style.userSelect = "";
|
|
4960
|
+
const panel = panelRef.current;
|
|
4961
|
+
panel.classList.remove("dshm-panel--dragging");
|
|
4962
|
+
panel.style.willChange = "";
|
|
4963
|
+
panel.style.transform = "";
|
|
4964
|
+
panel.style.removeProperty("--dshm-drag-bg");
|
|
4965
|
+
};
|
|
4966
|
+
const onPointerDown = (event) => {
|
|
4967
|
+
if (dragging) return;
|
|
4968
|
+
if (fullRef.current || event.button !== 0 || isDragTarget2(event.target)) return;
|
|
4969
|
+
const panel = panelRef.current;
|
|
4970
|
+
if (!panel) return;
|
|
4971
|
+
dragging = true;
|
|
4972
|
+
draggingRef.current = true;
|
|
4973
|
+
moved = false;
|
|
4974
|
+
lastPoint = null;
|
|
4975
|
+
originX = event.clientX;
|
|
4976
|
+
originY = event.clientY;
|
|
4977
|
+
slop = dragSlop2(event.pointerType);
|
|
4978
|
+
const rect = panel.getBoundingClientRect();
|
|
4979
|
+
grab = grabOffset2({ x: event.clientX, y: event.clientY }, rect);
|
|
4980
|
+
rest = { x: rect.left, y: rect.top };
|
|
4981
|
+
size = { width: rect.width, height: rect.height };
|
|
4982
|
+
const solid = solidifyColor2(window.getComputedStyle(panel).backgroundColor);
|
|
4983
|
+
if (solid) panel.style.setProperty("--dshm-drag-bg", solid);
|
|
4984
|
+
else panel.style.removeProperty("--dshm-drag-bg");
|
|
4985
|
+
if (headEl.setPointerCapture) {
|
|
4986
|
+
try {
|
|
4987
|
+
headEl.setPointerCapture(event.pointerId);
|
|
4988
|
+
} catch {
|
|
4989
|
+
}
|
|
4990
|
+
}
|
|
4991
|
+
bindGesture();
|
|
4992
|
+
};
|
|
4993
|
+
headEl.addEventListener("pointerdown", onPointerDown);
|
|
4994
|
+
return () => {
|
|
4995
|
+
headEl.removeEventListener("pointerdown", onPointerDown);
|
|
4996
|
+
unbindGesture();
|
|
4997
|
+
document.body.style.userSelect = "";
|
|
4998
|
+
};
|
|
4999
|
+
}, []);
|
|
4689
5000
|
const marketCommunity = useMarketData("community");
|
|
4690
5001
|
const marketPrimary = useMarketData("primary");
|
|
4691
5002
|
const markets = { community: marketCommunity, primary: marketPrimary };
|
|
@@ -4841,10 +5152,29 @@ function MarketPanel({ onClose }) {
|
|
|
4841
5152
|
{ className: full ? "dshm-overlay full" : "dshm-overlay", ...backdropCloseHandlers2(onClose) },
|
|
4842
5153
|
h(
|
|
4843
5154
|
"div",
|
|
4844
|
-
{
|
|
5155
|
+
{
|
|
5156
|
+
className: ["dshm-panel", full && "full", !full && pos && "abs"].filter(Boolean).join(" "),
|
|
5157
|
+
style: !full && pos ? { left: `${pos.x}px`, top: `${pos.y}px` } : void 0,
|
|
5158
|
+
ref: panelRef,
|
|
5159
|
+
onClick: (e) => e.stopPropagation()
|
|
5160
|
+
},
|
|
4845
5161
|
h(
|
|
4846
5162
|
"div",
|
|
4847
|
-
{
|
|
5163
|
+
{
|
|
5164
|
+
className: "dshm-head",
|
|
5165
|
+
title: full ? void 0 : lookup("panel.dragHint"),
|
|
5166
|
+
ref: headRef,
|
|
5167
|
+
onDoubleClick: (e) => {
|
|
5168
|
+
if (isDragTarget2(e.target)) return;
|
|
5169
|
+
toggleFull();
|
|
5170
|
+
},
|
|
5171
|
+
onClickCapture: (e) => {
|
|
5172
|
+
if (!suppressClickRef.current) return;
|
|
5173
|
+
suppressClickRef.current = false;
|
|
5174
|
+
e.stopPropagation();
|
|
5175
|
+
e.preventDefault();
|
|
5176
|
+
}
|
|
5177
|
+
},
|
|
4848
5178
|
h("span", { className: "dshm-title" }, lookup("title.full")),
|
|
4849
5179
|
h("span", { className: "dshm-head-divider", "aria-hidden": "true" }),
|
|
4850
5180
|
h(
|