dsh-m 0.9.71 → 0.9.72
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 +14 -0
- package/lib/client.js +34 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,13 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
8
8
|
|
|
9
9
|
## 中文
|
|
10
10
|
|
|
11
|
+
### 0.9.72 修复:设置页滚轮黑洞——操作记录滚动区/横幅的 contain 改为「可滚才挂」(Chrome 144 规范对齐暴露)
|
|
12
|
+
|
|
13
|
+
- **根因(主人设置页实拍:操作记录条数少、滚动条尚未出现时,鼠标悬停该区域滚轮无法翻页)**:`.dshm-ops-scroll` 恒挂 `overscroll-behavior:contain`(0.9.64 随 220px 滚动区引入)。`overflow-y:auto` 使元素无论内容多少都是滚动容器,而规范口径「没有可滚溢出的滚动容器恒在滚动边界」+ contain「到边界禁止向祖先链滚」→ 内容不满时整块区域吞掉滚轮。Chrome 144 起「Respect overscroll-behavior on non-scrollable scroll containers」落地(此前引擎仅真可滚才执行 contain,病灶被宽容掩盖),潜伏缺陷被引擎升级激活;错误/提示横幅 `.dshm-banner-text`(限高滚动时代引入的同款恒挂)同病。
|
|
14
|
+
- **修复(意图精确化:有得滚才断链)**:新增 `useScrollableContain(dep)`——实测 `scrollHeight > clientHeight`(+1 容亚像素取整)才挂 `.is-scrollable`(= contain),容器与首子元素入 ResizeObserver,改宽折行/注记增删引起的可滚性翻转自动复检,SSR/Node(ResizeObserver 缺席)安全跳过;操作记录滚动区、重启横幅、toast 三处接线。钢人结论:0.9.64 每个局部决策(限高滚动区/50 条上限/清除钉底/contain 自身的嵌套滚动卫生)都成立,错的只是「静态 CSS 事实表达了动态适用前提」——日志不满限时滚轮还给页面,日志实滚时边界断链照旧。
|
|
15
|
+
- **顺带**:滚动区补 `tabIndex=0` + aria-label(0.9.45 U8 键盘可达纪律延续,50 条记录此前键盘用户无法滚动);布局契约不动(通栏/220px/钉底照旧);布局 mock 同步(`.opscroll` 双规则 + 样本标注实滚类)。
|
|
16
|
+
- **验证**:1330 项测试全绿(新增 `client-scroll-contain` 结构锚 4 用例:恒挂形态退役/条件规则在/hook 守卫/三处接线)、typecheck 零错误。
|
|
17
|
+
|
|
11
18
|
### 0.9.71 功能:设置类操作记录——社区目录开关与清单源变更入账(DESIGN §2.6 修订)
|
|
12
19
|
|
|
13
20
|
- **背景**:设置页「社区目录开关」与「校验并应用 / 恢复默认」(清单源)的变更此前不入操作记录,事后无法回看「何时改的、从哪改到哪」——而 agent 供给面(`dshm_search` 可见目录规模 20 余精选 ↔ 4,000+ 社区)与信任根(自定义清单源)恰恰挂在这两个开关上。
|
|
@@ -525,6 +532,13 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
525
532
|
|
|
526
533
|
## English
|
|
527
534
|
|
|
535
|
+
### Fix in 0.9.72 — settings-page wheel black hole: ops scroll region and banner now apply contain only when actually scrollable (exposed by Chrome 144 spec alignment)
|
|
536
|
+
|
|
537
|
+
- **Root cause (owner screenshot: with few operation records and no scrollbar yet, wheeling over the scroll region does nothing)**: `.dshm-ops-scroll` carried a permanent `overscroll-behavior:contain` (introduced with the 220px region in 0.9.64). `overflow-y:auto` makes the element a scroll container no matter how little content it has, and per spec a scroll container without scrollable overflow is always at its scroll boundary — with contain forbidding chaining, the whole area swallows the wheel. Chrome 144's "Respect overscroll-behavior on non-scrollable scroll containers" made engines honor this unconditionally (previously contain only applied when actually scrollable), activating the latent defect; the banner text (`.dshm-banner-text`, same permanent contain from the capped-banner era) shares the disease.
|
|
538
|
+
- **Fix (intent made precise: block chaining only when scrollable)**: new `useScrollableContain(dep)` — attaches `.is-scrollable` (= contain) only when `scrollHeight > clientHeight` (+1 for sub-pixel rounding), with the container and first child in a ResizeObserver so wrap/note changes re-check, and a safe skip when ResizeObserver is absent (SSR/Node); wired into the ops scroll region, restart banner, and toast. Steelman verdict: every 0.9.64 decision stands (capped region / 50-record cap / pinned clear button / contain's own nested-scroll hygiene); the only flaw was a static CSS fact expressing a dynamic precondition — short logs now hand the wheel back to the page, long logs keep boundary containment.
|
|
539
|
+
- **Also**: the scroll region gains `tabIndex=0` + aria-label (keyboard-reachability discipline from 0.9.45 U8); the layout contract is untouched; the layout mock is synced.
|
|
540
|
+
- **Verification**: 1330 tests green (new client-scroll-contain structural anchors, 4 cases), typecheck clean.
|
|
541
|
+
|
|
528
542
|
### Feature in 0.9.71 — settings-class operation records: community catalog toggle and registry source changes are now logged (DESIGN §2.6 revision)
|
|
529
543
|
|
|
530
544
|
- **Background**: changes made via the settings page's community catalog toggle and the registry source actions (apply / restore default) never appeared in the operations panel, leaving no answer to "when was it changed, and from what" — even though the agent-facing supply surface (`dshm_search` scope: ~20 curated vs 4,000+ community entries) and the trust root (custom registry source) hang exactly on these two switches.
|
package/lib/client.js
CHANGED
|
@@ -2550,7 +2550,10 @@ var CSS = `
|
|
|
2550
2550
|
.dsvm-btn{border:1px solid rgba(255,255,255,.35);background:rgba(255,255,255,.14);color:#fff;border-radius:8px;padding:5px 14px;font:inherit;font-size:13px;cursor:pointer}
|
|
2551
2551
|
.dsvm-btn:hover{background:rgba(255,255,255,.24)}
|
|
2552
2552
|
.dsvm-ops-caption{font-size:11px;color:var(--dsw-alias-label-caption,#9ca3af);margin:2px 0 -3px}
|
|
2553
|
-
.
|
|
2553
|
+
/* 0.9.72 \u6EDA\u8F6E\u6CBB\u7406\uFF08useScrollableContain\uFF09\uFF1Acontain \u53EA\u5728\u771F\u7684\u53EF\u6EDA\u65F6\u6302\u2014\u2014Chrome 144+ \u5BF9
|
|
2554
|
+
\u300C\u65E0\u53EF\u6EDA\u6EA2\u51FA\u7684\u6EDA\u52A8\u5BB9\u5668\u300D\u4E5F\u6267\u884C contain\uFF0C\u6052\u6302\u4F1A\u628A\u4E0D\u6EE1\u9AD8\u7684\u6EDA\u52A8\u533A\u53D8\u6210\u6EDA\u8F6E\u9ED1\u6D1E */
|
|
2555
|
+
.dshm-ops-scroll{max-height:220px;overflow-y:auto}
|
|
2556
|
+
.dshm-ops-scroll.is-scrollable{overscroll-behavior:contain}
|
|
2554
2557
|
.dshm-ops-scroll::-webkit-scrollbar{width:8px}
|
|
2555
2558
|
.dshm-ops-scroll::-webkit-scrollbar-thumb{background:rgba(127,127,127,.28);border-radius:4px}
|
|
2556
2559
|
.dshm-ops-scroll::-webkit-scrollbar-thumb:hover{background:rgba(127,127,127,.45)}
|
|
@@ -2598,7 +2601,9 @@ button.dshm-badge:hover{filter:brightness(.95)}
|
|
|
2598
2601
|
.dshm-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:4px}
|
|
2599
2602
|
.dshm-banner{display:flex;align-items:center;gap:10px;padding:10px 14px;border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-state-warn-tertiary,#fffbeb);color:var(--dsw-alias-state-warn-primary,#b45309);font-size:12px}
|
|
2600
2603
|
.dshm-banner.err{background:rgba(239,68,68,.13);color:var(--dsw-alias-state-error-primary,#b91c1c)}
|
|
2601
|
-
.
|
|
2604
|
+
/* 0.9.72 \u540C\u6B3E\u6EDA\u8F6E\u6CBB\u7406\uFF08\u89C1 dshm-ops-scroll \u6CE8\uFF09\uFF1A\u6A2A\u5E45\u6587\u672C\u4E0D\u6EE1 140px \u65F6\u4E0D\u5F97\u541E\u6EDA\u8F6E */
|
|
2605
|
+
.dshm-banner .dshm-banner-text{flex:1;max-height:140px;overflow:auto;white-space:pre-wrap;word-break:break-word;line-height:18px}
|
|
2606
|
+
.dshm-banner .dshm-banner-text.is-scrollable{overscroll-behavior:contain}
|
|
2602
2607
|
.dshm-row{display:flex;align-items:center;gap:8px}
|
|
2603
2608
|
.dshm-kv{display:grid;grid-template-columns:96px 1fr;gap:6px 10px;font-size:12px;align-items:baseline}
|
|
2604
2609
|
.dshm-kv .k{color:var(--dsw-alias-label-caption,#6b7280);font-size:11px}
|
|
@@ -2967,14 +2972,12 @@ function RestartBanner({ note, onDone, desktop, onRestarted }) {
|
|
|
2967
2972
|
setErr(String(e && e.message || e));
|
|
2968
2973
|
}
|
|
2969
2974
|
}, [onDone, onRestarted]);
|
|
2975
|
+
const text = phase === "restarting" ? lookup("restart.doing") : phase === "waiting" ? lookup("restart.waiting") : err ? lookup("restart.failed", { err }) : note || lookup("banner.done");
|
|
2976
|
+
const [textRef, textScrollCls] = useScrollableContain(text);
|
|
2970
2977
|
return h(
|
|
2971
2978
|
"div",
|
|
2972
2979
|
{ className: "dshm-banner" },
|
|
2973
|
-
h(
|
|
2974
|
-
"span",
|
|
2975
|
-
{ className: "dshm-banner-text" },
|
|
2976
|
-
phase === "restarting" ? lookup("restart.doing") : phase === "waiting" ? lookup("restart.waiting") : err ? lookup("restart.failed", { err }) : note || lookup("banner.done")
|
|
2977
|
-
),
|
|
2980
|
+
h("span", { className: `dshm-banner-text${textScrollCls}`, ref: textRef }, text),
|
|
2978
2981
|
phase === "idle" && !err && !desktop ? h("button", { className: "dshm-btn primary sm", onClick: restart }, lookup("restart.now")) : null,
|
|
2979
2982
|
phase === "restarting" || phase === "waiting" ? Spin() : null,
|
|
2980
2983
|
phase === "idle" && err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, lookup("common.ok")) : null,
|
|
@@ -4659,10 +4662,26 @@ var OP_STATUS_CLS = {
|
|
|
4659
4662
|
failed: "err",
|
|
4660
4663
|
superseded: "sup"
|
|
4661
4664
|
};
|
|
4665
|
+
function useScrollableContain(dep) {
|
|
4666
|
+
const ref = useRef(null);
|
|
4667
|
+
const [scrollable, setScrollable] = useState(false);
|
|
4668
|
+
useEffect(() => {
|
|
4669
|
+
const el = ref.current;
|
|
4670
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
4671
|
+
const check = () => setScrollable(el.scrollHeight > el.clientHeight + 1);
|
|
4672
|
+
check();
|
|
4673
|
+
const ro = new ResizeObserver(check);
|
|
4674
|
+
ro.observe(el);
|
|
4675
|
+
if (el.firstElementChild) ro.observe(el.firstElementChild);
|
|
4676
|
+
return () => ro.disconnect();
|
|
4677
|
+
}, [dep]);
|
|
4678
|
+
return [ref, scrollable ? " is-scrollable" : ""];
|
|
4679
|
+
}
|
|
4662
4680
|
function OperationsCard({ records, onClearFinished, onRemove }) {
|
|
4663
4681
|
const active = records.filter((r) => r.status === "queued" || r.status === "running" || r.status === "input");
|
|
4664
4682
|
const finished = records.filter((r) => active.indexOf(r) < 0).slice(-50);
|
|
4665
4683
|
const clearable = records.some((r) => TERMINAL_CLEARABLE2.has(r.status));
|
|
4684
|
+
const [scrollRef, scrollCls] = useScrollableContain(finished.length);
|
|
4666
4685
|
const targetText = (r) => r.kind === "community-toggle" && r.meta && typeof r.meta.on === "boolean" ? lookup(r.meta.on ? "op.state.on" : "op.state.off") : r.target;
|
|
4667
4686
|
const row = (r) => h(
|
|
4668
4687
|
"div",
|
|
@@ -4689,7 +4708,12 @@ function OperationsCard({ records, onClearFinished, onRemove }) {
|
|
|
4689
4708
|
) : null,
|
|
4690
4709
|
finished.length ? h(
|
|
4691
4710
|
"div",
|
|
4692
|
-
{
|
|
4711
|
+
{
|
|
4712
|
+
className: `dshm-ops-scroll${scrollCls}`,
|
|
4713
|
+
ref: scrollRef,
|
|
4714
|
+
tabIndex: 0,
|
|
4715
|
+
"aria-label": lookup("settings.ops")
|
|
4716
|
+
},
|
|
4693
4717
|
h(
|
|
4694
4718
|
"div",
|
|
4695
4719
|
{ className: "dsvm-opgroup done" },
|
|
@@ -5115,6 +5139,7 @@ function MarketPanel({ onClose }) {
|
|
|
5115
5139
|
const outdatedCount = installedUpdateStats2(mergedItems || []).outdatedCount;
|
|
5116
5140
|
const [banner, setBanner] = useState(null);
|
|
5117
5141
|
const [toast, setToast] = useState(null);
|
|
5142
|
+
const [toastTextRef, toastScrollCls] = useScrollableContain(toast ? toast.text : "");
|
|
5118
5143
|
useEffect(() => {
|
|
5119
5144
|
const onKey = (e) => {
|
|
5120
5145
|
if (e.key === "Escape" && dsvmModalDepth === 0 && onClose) onClose();
|
|
@@ -5234,7 +5259,7 @@ function MarketPanel({ onClose }) {
|
|
|
5234
5259
|
toast ? h(
|
|
5235
5260
|
"div",
|
|
5236
5261
|
{ className: toast.kind === "err" ? "dshm-banner err" : "dshm-banner" },
|
|
5237
|
-
h("span", { className:
|
|
5262
|
+
h("span", { className: `dshm-banner-text${toastScrollCls}`, ref: toastTextRef }, toast.text)
|
|
5238
5263
|
) : null,
|
|
5239
5264
|
banner ? h(RestartBanner, { note: banner.text, onDone: () => setBanner(null), desktop: banner.desktop === true, onRestarted: reloadPing }) : null
|
|
5240
5265
|
)
|