dsh-m 0.9.70 → 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 +30 -0
- package/docs/DESIGN.md +1 -0
- package/lib/client.js +59 -14
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,21 @@ 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
|
+
|
|
18
|
+
### 0.9.71 功能:设置类操作记录——社区目录开关与清单源变更入账(DESIGN §2.6 修订)
|
|
19
|
+
|
|
20
|
+
- **背景**:设置页「社区目录开关」与「校验并应用 / 恢复默认」(清单源)的变更此前不入操作记录,事后无法回看「何时改的、从哪改到哪」——而 agent 供给面(`dshm_search` 可见目录规模 20 余精选 ↔ 4,000+ 社区)与信任根(自定义清单源)恰恰挂在这两个开关上。
|
|
21
|
+
- **方案(DESIGN §2.6 修订)**:操作记录边界从包级事务扩至**供给面/信任边界设置变更**,新增两类**终态直落**记录(不入泵、创建即终态、不参与恢复重放;恢复原样保留 / 清除 / 红点语义天然兼容):`community-toggle` 成败皆记(失败 = 已接受的变更尝试中途回滚,有「以为关了其实没关」的误信风险;meta.on),`registry-url` 仅记成功(校验拒绝零状态变更且卡片红字就地呈现,高频试错入记录只会制造红点噪音;target = 新地址、恢复默认记 `(default)`,meta.from = 原地址)。记录行 target 展示派生:开关按 meta.on 显「开/关」,清单源保持 URL。
|
|
22
|
+
- **口径**:面板发起的变更(CLI、`dsh plugin add`、DSH 设置表单直改不留痕,与包操作既有口径一致)。
|
|
23
|
+
- **评审**:独立 subagent 盲评 0 blocker / 0 major,4 minor + 3 nit 全部随本版修掉(done 打点紧贴 api 成功消除刷新链吞错隐式依赖;测试计数桩钉死「终态不咨询恢复谓词」;`(default)` 收敛共享常量等)。
|
|
24
|
+
- **验证**:1326 项测试全绿(新增设置类终态直落 describe 块 3 用例)、typecheck 零错误。
|
|
25
|
+
|
|
11
26
|
### 0.9.70 修复:拖拽观感与静止态一致——退役拖拽期实心固化(ADR-0017 v2)
|
|
12
27
|
|
|
13
28
|
- **根因(用户浅色模式双态实拍)**:0.9.69 的拖拽期「计算值固化强制实心」在按住/松手瞬间造成实心↔玻璃的观感跳变,浅色模式下尤为突兀——防透字与观感连续两个目标里,实心方案牺牲了后者。
|
|
@@ -517,6 +532,21 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
517
532
|
|
|
518
533
|
## English
|
|
519
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
|
+
|
|
542
|
+
### Feature in 0.9.71 — settings-class operation records: community catalog toggle and registry source changes are now logged (DESIGN §2.6 revision)
|
|
543
|
+
|
|
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.
|
|
545
|
+
- **Design (DESIGN §2.6 revision)**: the operations-log boundary expands from package-level transactions to **supply-scope / trust-boundary settings changes**, adding two **terminal-at-creation** record kinds (never enqueued, no queued lifecycle, no restore replay; restore-passthrough / clear / red-dot semantics work unchanged): `community-toggle` records both success and failure (failure = an accepted mutation attempt rolled back mid-flight — the "thought it was off but it wasn't" risk; meta.on), `registry-url` records success only (validation rejections change nothing and surface inline in the card; logging every typo would only manufacture red-dot noise; target = new address, restore default logged as `(default)`, meta.from = previous address). Row display derivation: the toggle shows On/Off from meta.on; the registry source keeps the URL.
|
|
546
|
+
- **Scope**: panel-initiated changes only (CLI, `dsh plugin add`, and DSH settings-form direct writes leave no record — consistent with the existing package-operations scope).
|
|
547
|
+
- **Review**: independent subagent blind review found 0 blockers / 0 majors; all 4 minors + 3 nits fixed in this release (done-record placement now hugs api success, removing an implicit reliance on the refresh chain's error swallowing; a counting stub pins "terminal records never consult the restore predicate"; `(default)` converged into a shared constant).
|
|
548
|
+
- **Verification**: 1326 tests green (new terminal-at-creation describe block with 3 cases), typecheck clean.
|
|
549
|
+
|
|
520
550
|
### Fix in 0.9.70 — drag appearance matches the resting state: drag-time solidification retired (ADR-0017 v2)
|
|
521
551
|
|
|
522
552
|
- **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.
|
package/docs/DESIGN.md
CHANGED
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
- **详情 = Modal 且为卡片超集**(「detail 显示少于摘要就是倒退」):byline / 分类 / 收录日期 / 下载量窗口三要素(计数 + 窗口 + 核对时间)/ 描述全文 / 截图灯箱(←→/Esc、**禁自动轮播**)/ 能力披露 + 红线(**默认收起**)/ 安装命令折叠行(0.9.14 起按上下文显隐:desktop 上下文与已安装条目整行隐藏——命令两来源恒为 `--profile web` 语义,desktop 装机走弹窗内安装按钮)/ deprecated 替代链接;精选条目超集另加 verified 与 tags。**安装确认走 Modal**。安装信息就地显示(0.9.15):进行中进度行与终态结果行挂 Modal 内、底层同源进度行让位——展示派生自全局操作记录,§2.6「状态不挂卡片」所有权模型不变。卡片瘦身:能力披露、截图、安装命令全部迁出卡片(Q44 精神「只在详情层、默认收起」不变,载体升级)。
|
|
95
95
|
- **详情截图来源(2026-10-09,[ADR-0015](./adr/0015-primary-screenshot-manifest.md))**:Community live 条目沿用 `CommunityEntry.screenshots`(来自版本化 `dsh-plugin-catalog`);Primary DetailModal 调 GUI Host method `primary-screenshots({ id })`,Host 按 active Primary Registry 精确查 id 与 `github`,仅当 `github` 存在时懒读仓库根 `screenshots.json`(HEAD)。清单为顶层最多 8 个仓库相对路径;缺 github、缺文件、无效内容或网络失败均返回空列表,不回退 Community Catalog、README 或 npm repository。清单只在 Modal state/Host 内存缓存(成功结果 10 分钟、空/失败结果负缓存 1 分钟),不进入 `MarketItem`、市场 localStorage 快照、收藏快照、Agent 工具或 CLI;最终图片仍经现有 `safeScreenshots` 与 [ADR-0014](./adr/0014-image-chain-weserv-first.md) 的 `useImgChain`。`source='primary'` 是 market 分区参数,与 `primary-screenshots` Host method 分离;现有 Community Zone 可独立加载社区数据。
|
|
96
96
|
- **全局操作记录**:每个变更操作一条 record(`queued / running / input=冲突待决 / done / warned / failed`),**状态不挂卡片**——翻页、搜索、切 tab 不丢;**localStorage 持久化队列**,宿主重载恢复时逐条校验「此刻仍成立才执行,否则报告」;覆盖安装、升级、卸载、开关。已装侧跟随一致化:「全部更新 (N)」批量入口 + tab 更新红点 + 卡片视觉与发现侧同体系;组管理、个人备注列 backlog。
|
|
97
|
+
- **修订(2026-10-10):设置类终态直落记录**——边界从包级事务扩至**供给面/信任边界设置变更**,新增两类**不入泵**的记录(创建即终态,无 queued 生命周期、无恢复重放;恢复分支原样保留、清除/红点语义照常):`community-toggle`(社区目录开关,**成败皆记**——失败 = 已接受的变更尝试中途回滚,有「以为关了其实没关」的误信风险,红点值得点亮;meta.on)与 `registry-url`(清单源变更 = 信任根变更,**仅记成功**——校验拒绝无任何状态变更且卡片红字就地呈现,高频试错入记录只会制造红点噪音;target = 新地址、恢复默认记 `"(default)"`,meta.from = 原地址;记录行的 target 展示派生:community-toggle 按 meta.on 显「开/关」,registry-url 保持 URL 原样)。选型依据:低频 × 高影响 × 事后回看是操作记录的靶面,瞬时原子配置写没有可跟踪的后台过程,入泵属语义错配。**口径 = 面板发起**的变更:CLI、`dsh plugin add`、DSH 设置表单(settings-compat forms store)与配置文件直改不留痕,与包操作口径一致。
|
|
97
98
|
- **收藏**:浏览器 localStorage 本地收藏(不进 profile、不进服务端);收藏区 stale 条目(目录中已下架)单独提示 + 一键清理。
|
|
98
99
|
- **agent/CLI 契约(允许破坏性变更)**:`dshm_search` 参数改为 `query / category / source('primary'|'community'|'all',默认 all,与 GUI 分区对齐) / limit(默认 10,clamp 1–80) / offset(真翻页)`;`primary_only` 删除。输出补 `community` 标记与 `downloads/stars`;社区分类直出中文标签(不再回退英文 slug);工具描述整体重写(真实规模 + 翻页语义,删「registry is curated & small」)。CLI 对齐 `--source / --offset / --limit`,默认 10 条。
|
|
99
100
|
- **技术默认件**:图片三层懒加载(IntersectionObserver + `loading=lazy` + `fetchPriority=low`;缩略图本机直连原图,不引第三方代理服务);移除客户端重复排序(服务端单一排序源);`total>200` 性能提示随分区退役;host-api limit 上限对齐新页大小;空状态逐分区定制;错误态带具体原因 + 重试。
|
package/lib/client.js
CHANGED
|
@@ -1297,6 +1297,7 @@ var init_installed_updates = __esm({
|
|
|
1297
1297
|
var operations_exports = {};
|
|
1298
1298
|
__export(operations_exports, {
|
|
1299
1299
|
OP_STORAGE_KEY: () => OP_STORAGE_KEY,
|
|
1300
|
+
REGISTRY_DEFAULT_TARGET: () => REGISTRY_DEFAULT_TARGET,
|
|
1300
1301
|
TERMINAL_CLEARABLE: () => TERMINAL_CLEARABLE,
|
|
1301
1302
|
createOperationsStore: () => createOperationsStore,
|
|
1302
1303
|
createOpsPump: () => createOpsPump,
|
|
@@ -1553,11 +1554,12 @@ async function drainRestored(store, dispatch, stillApplies) {
|
|
|
1553
1554
|
}
|
|
1554
1555
|
}
|
|
1555
1556
|
}
|
|
1556
|
-
var OP_STORAGE_KEY, TERMINAL_CLEARABLE;
|
|
1557
|
+
var OP_STORAGE_KEY, REGISTRY_DEFAULT_TARGET, TERMINAL_CLEARABLE;
|
|
1557
1558
|
var init_operations = __esm({
|
|
1558
1559
|
"src/client/operations.js"() {
|
|
1559
1560
|
"use strict";
|
|
1560
1561
|
OP_STORAGE_KEY = "dshm-operations";
|
|
1562
|
+
REGISTRY_DEFAULT_TARGET = "(default)";
|
|
1561
1563
|
TERMINAL_CLEARABLE = /* @__PURE__ */ new Set(["done", "warned", "failed", "superseded"]);
|
|
1562
1564
|
}
|
|
1563
1565
|
});
|
|
@@ -1766,7 +1768,7 @@ var { pickPayload: pickPayload2, parseToolArgs: parseToolArgs2 } = (init_tool_vi
|
|
|
1766
1768
|
var { RESTART_POLL_MS: RESTART_POLL_MS2, RESTART_DEADLINE_MS: RESTART_DEADLINE_MS2, nextRestartWait: nextRestartWait2, isAmbiguousRestartRequestError: isAmbiguousRestartRequestError2 } = (init_restart_wait(), __toCommonJS(restart_wait_exports));
|
|
1767
1769
|
var { refreshAfterMutation: refreshAfterMutation2 } = (init_view_refresh(), __toCommonJS(view_refresh_exports));
|
|
1768
1770
|
var { applyInstalledUpdates: applyInstalledUpdates2, installedUpdateStats: installedUpdateStats2 } = (init_installed_updates(), __toCommonJS(installed_updates_exports));
|
|
1769
|
-
var { createOperationsStore: createOperationsStore2, restoreRecords: restoreRecords2, createOpsPump: createOpsPump2, opAppliesTo: opAppliesTo2, TERMINAL_CLEARABLE: TERMINAL_CLEARABLE2, upgradeNotify: upgradeNotify2 } = (init_operations(), __toCommonJS(operations_exports));
|
|
1771
|
+
var { createOperationsStore: createOperationsStore2, restoreRecords: restoreRecords2, createOpsPump: createOpsPump2, opAppliesTo: opAppliesTo2, TERMINAL_CLEARABLE: TERMINAL_CLEARABLE2, upgradeNotify: upgradeNotify2, REGISTRY_DEFAULT_TARGET: REGISTRY_DEFAULT_TARGET2 } = (init_operations(), __toCommonJS(operations_exports));
|
|
1770
1772
|
var { createFavoritesStore: createFavoritesStore2, partitionStale: partitionStale2 } = (init_favorites(), __toCommonJS(favorites_exports));
|
|
1771
1773
|
var { readSelfCheckCache: readSelfCheckCache2, writeSelfCheckCache: writeSelfCheckCache2, clearSelfCheckCache: clearSelfCheckCache2, deriveChipState: deriveChipState2 } = (init_self_check(), __toCommonJS(self_check_exports));
|
|
1772
1774
|
var { shouldShowInstallCmd: shouldShowInstallCmd2 } = (init_install_cmd(), __toCommonJS(install_cmd_exports));
|
|
@@ -1815,6 +1817,10 @@ var ZH = {
|
|
|
1815
1817
|
"op.kind.upgrade": "\u5347\u7EA7",
|
|
1816
1818
|
"op.kind.uninstall": "\u5378\u8F7D",
|
|
1817
1819
|
"op.kind.toggle": "\u5F00\u5173",
|
|
1820
|
+
"op.kind.community-toggle": "\u793E\u533A\u76EE\u5F55\u5F00\u5173",
|
|
1821
|
+
"op.kind.registry-url": "\u6E05\u5355\u6E90\u53D8\u66F4",
|
|
1822
|
+
"op.state.on": "\u5F00",
|
|
1823
|
+
"op.state.off": "\u5173",
|
|
1818
1824
|
"op.status.queued": "\u6392\u961F\u4E2D",
|
|
1819
1825
|
"op.status.running": "\u8FDB\u884C\u4E2D",
|
|
1820
1826
|
"op.status.input": "\u5F85\u51B3",
|
|
@@ -1984,7 +1990,7 @@ var ZH = {
|
|
|
1984
1990
|
"settings.community.displaced": "\u4E0E\u7CBE\u9009\u91CD\u590D",
|
|
1985
1991
|
"settings.community.displaced.v": "{n} \u6761",
|
|
1986
1992
|
"settings.ops": "\u64CD\u4F5C\u8BB0\u5F55",
|
|
1987
|
-
"settings.ops.empty": "\u6682\u65E0\u64CD\u4F5C\u8BB0\u5F55\u3002\u5B89\u88C5\u3001\u5347\u7EA7\u3001\u5378\u8F7D\
|
|
1993
|
+
"settings.ops.empty": "\u6682\u65E0\u64CD\u4F5C\u8BB0\u5F55\u3002\u5B89\u88C5\u3001\u5347\u7EA7\u3001\u5378\u8F7D\u3001\u5F00\u5173\u4E0E\u4F9B\u7ED9\u9762\u8BBE\u7F6E\uFF08\u6E05\u5355\u6E90\u3001\u793E\u533A\u76EE\u5F55\uFF09\u7684\u6267\u884C\u5386\u53F2\u5C06\u5728\u8FD9\u91CC\u663E\u793A\u3002",
|
|
1988
1994
|
"settings.ops.group.active": "\u8FDB\u884C\u4E2D",
|
|
1989
1995
|
"settings.ops.group.done": "\u5DF2\u7ED3\u675F",
|
|
1990
1996
|
"settings.ops.reddot": "\u5B58\u5728\u5931\u8D25\u6216\u5E26\u8B66\u544A\u7684\u64CD\u4F5C\u8BB0\u5F55",
|
|
@@ -2102,6 +2108,10 @@ var EN = {
|
|
|
2102
2108
|
"op.kind.upgrade": "Upgrade",
|
|
2103
2109
|
"op.kind.uninstall": "Uninstall",
|
|
2104
2110
|
"op.kind.toggle": "Toggle",
|
|
2111
|
+
"op.kind.community-toggle": "Community catalog toggle",
|
|
2112
|
+
"op.kind.registry-url": "Registry source change",
|
|
2113
|
+
"op.state.on": "On",
|
|
2114
|
+
"op.state.off": "Off",
|
|
2105
2115
|
"op.status.queued": "Queued",
|
|
2106
2116
|
"op.status.running": "Running",
|
|
2107
2117
|
"op.status.input": "Pending",
|
|
@@ -2271,7 +2281,7 @@ var EN = {
|
|
|
2271
2281
|
"settings.community.displaced": "Displaced (duplicates)",
|
|
2272
2282
|
"settings.community.displaced.v": "{n}",
|
|
2273
2283
|
"settings.ops": "Operations",
|
|
2274
|
-
"settings.ops.empty": "No operations yet.
|
|
2284
|
+
"settings.ops.empty": "No operations yet. History of installs, upgrades, uninstalls, toggles and registry / community catalog settings will appear here.",
|
|
2275
2285
|
"settings.ops.group.active": "Active",
|
|
2276
2286
|
"settings.ops.group.done": "Finished",
|
|
2277
2287
|
"settings.ops.reddot": "Failed or warned operations exist",
|
|
@@ -2540,7 +2550,10 @@ var CSS = `
|
|
|
2540
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}
|
|
2541
2551
|
.dsvm-btn:hover{background:rgba(255,255,255,.24)}
|
|
2542
2552
|
.dsvm-ops-caption{font-size:11px;color:var(--dsw-alias-label-caption,#9ca3af);margin:2px 0 -3px}
|
|
2543
|
-
.
|
|
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}
|
|
2544
2557
|
.dshm-ops-scroll::-webkit-scrollbar{width:8px}
|
|
2545
2558
|
.dshm-ops-scroll::-webkit-scrollbar-thumb{background:rgba(127,127,127,.28);border-radius:4px}
|
|
2546
2559
|
.dshm-ops-scroll::-webkit-scrollbar-thumb:hover{background:rgba(127,127,127,.45)}
|
|
@@ -2588,7 +2601,9 @@ button.dshm-badge:hover{filter:brightness(.95)}
|
|
|
2588
2601
|
.dshm-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:4px}
|
|
2589
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}
|
|
2590
2603
|
.dshm-banner.err{background:rgba(239,68,68,.13);color:var(--dsw-alias-state-error-primary,#b91c1c)}
|
|
2591
|
-
.
|
|
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}
|
|
2592
2607
|
.dshm-row{display:flex;align-items:center;gap:8px}
|
|
2593
2608
|
.dshm-kv{display:grid;grid-template-columns:96px 1fr;gap:6px 10px;font-size:12px;align-items:baseline}
|
|
2594
2609
|
.dshm-kv .k{color:var(--dsw-alias-label-caption,#6b7280);font-size:11px}
|
|
@@ -2957,14 +2972,12 @@ function RestartBanner({ note, onDone, desktop, onRestarted }) {
|
|
|
2957
2972
|
setErr(String(e && e.message || e));
|
|
2958
2973
|
}
|
|
2959
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);
|
|
2960
2977
|
return h(
|
|
2961
2978
|
"div",
|
|
2962
2979
|
{ className: "dshm-banner" },
|
|
2963
|
-
h(
|
|
2964
|
-
"span",
|
|
2965
|
-
{ className: "dshm-banner-text" },
|
|
2966
|
-
phase === "restarting" ? lookup("restart.doing") : phase === "waiting" ? lookup("restart.waiting") : err ? lookup("restart.failed", { err }) : note || lookup("banner.done")
|
|
2967
|
-
),
|
|
2980
|
+
h("span", { className: `dshm-banner-text${textScrollCls}`, ref: textRef }, text),
|
|
2968
2981
|
phase === "idle" && !err && !desktop ? h("button", { className: "dshm-btn primary sm", onClick: restart }, lookup("restart.now")) : null,
|
|
2969
2982
|
phase === "restarting" || phase === "waiting" ? Spin() : null,
|
|
2970
2983
|
phase === "idle" && err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, lookup("common.ok")) : null,
|
|
@@ -4282,8 +4295,11 @@ function SettingsTab({ notify, onRegistryChanged, onForceMarket, opsRecords, onO
|
|
|
4282
4295
|
const applyAddress = async (raw) => {
|
|
4283
4296
|
setApplying(true);
|
|
4284
4297
|
setApplyError(null);
|
|
4298
|
+
const prevAddress = typeof (cfgData && cfgData.registryUrl) === "string" ? cfgData.registryUrl.trim() : "";
|
|
4285
4299
|
try {
|
|
4286
4300
|
await api("registry-config-apply", { registryUrl: raw });
|
|
4301
|
+
const to = typeof raw === "string" ? raw.trim() : "";
|
|
4302
|
+
recordSettingsOp("registry-url", to === "" ? REGISTRY_DEFAULT_TARGET2 : to, "done", { from: prevAddress === "" ? REGISTRY_DEFAULT_TARGET2 : prevAddress });
|
|
4287
4303
|
setDraftAddress(typeof raw === "string" ? raw.trim() : "");
|
|
4288
4304
|
notify({ kind: "ok", text: raw.trim() === "" ? lookup("settings.reset.ok") : lookup("settings.apply.ok"), needsRestart: false });
|
|
4289
4305
|
await reloadRegistryState();
|
|
@@ -4326,11 +4342,13 @@ function SettingsTab({ notify, onRegistryChanged, onForceMarket, opsRecords, onO
|
|
|
4326
4342
|
setCommunityBusy(true);
|
|
4327
4343
|
try {
|
|
4328
4344
|
await api("set-community", { enabled });
|
|
4345
|
+
recordSettingsOp("community-toggle", "community-catalog", "done", { on: enabled });
|
|
4329
4346
|
await reloadRegistryState();
|
|
4330
4347
|
await (onRegistryChanged ? onRegistryChanged() : reg.reload(false));
|
|
4331
4348
|
notify({ kind: "ok", text: lookup(enabled ? "settings.community.on.ok" : "settings.community.off.ok"), needsRestart: false });
|
|
4332
4349
|
} catch (e) {
|
|
4333
4350
|
setCommunityOn(prev);
|
|
4351
|
+
recordSettingsOp("community-toggle", "community-catalog", "failed", { on: enabled }, String(e && e.message || e));
|
|
4334
4352
|
notify({ kind: "err", text: lookup("settings.community.toggle.failed", { err: e && e.message || e }) });
|
|
4335
4353
|
} finally {
|
|
4336
4354
|
setCommunityBusy(false);
|
|
@@ -4631,6 +4649,10 @@ function FavoriteZone({ favorites, onOpen }) {
|
|
|
4631
4649
|
var opsStore = createOperationsStore2(typeof window !== "undefined" && window.localStorage ? window.localStorage : null);
|
|
4632
4650
|
var opsPumpCtx = null;
|
|
4633
4651
|
var opsPump = createOpsPump2(opsStore, () => opsPumpCtx);
|
|
4652
|
+
function recordSettingsOp(kind, target, status, meta, error) {
|
|
4653
|
+
opsStore.upsert({ kind, target, status, ...meta ? { meta } : {}, ...error ? { error } : {} });
|
|
4654
|
+
if (opsPumpCtx && opsPumpCtx.syncOps) opsPumpCtx.syncOps();
|
|
4655
|
+
}
|
|
4634
4656
|
var OP_STATUS_CLS = {
|
|
4635
4657
|
queued: "",
|
|
4636
4658
|
running: "run",
|
|
@@ -4640,16 +4662,33 @@ var OP_STATUS_CLS = {
|
|
|
4640
4662
|
failed: "err",
|
|
4641
4663
|
superseded: "sup"
|
|
4642
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
|
+
}
|
|
4643
4680
|
function OperationsCard({ records, onClearFinished, onRemove }) {
|
|
4644
4681
|
const active = records.filter((r) => r.status === "queued" || r.status === "running" || r.status === "input");
|
|
4645
4682
|
const finished = records.filter((r) => active.indexOf(r) < 0).slice(-50);
|
|
4646
4683
|
const clearable = records.some((r) => TERMINAL_CLEARABLE2.has(r.status));
|
|
4684
|
+
const [scrollRef, scrollCls] = useScrollableContain(finished.length);
|
|
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;
|
|
4647
4686
|
const row = (r) => h(
|
|
4648
4687
|
"div",
|
|
4649
4688
|
{ key: r.id, className: `dsvm-oprow ${OP_STATUS_CLS[r.status] || ""}` },
|
|
4650
4689
|
h("span", { className: "dsvm-opstatus" }, lookup("op.status." + r.status)),
|
|
4651
4690
|
h("span", { className: "dsvm-opkind" }, lookup("op.kind." + r.kind)),
|
|
4652
|
-
h("span", { className: "dsvm-optarget", title: r.error || r.warning || void 0 }, r
|
|
4691
|
+
h("span", { className: "dsvm-optarget", title: r.error || r.warning || void 0 }, targetText(r)),
|
|
4653
4692
|
r.status === "running" ? Spin() : null,
|
|
4654
4693
|
r.error ? h("span", { className: "dsvm-opnote" }, r.error) : null,
|
|
4655
4694
|
r.warning ? h("span", { className: "dsvm-opnote" }, r.warning) : null,
|
|
@@ -4669,7 +4708,12 @@ function OperationsCard({ records, onClearFinished, onRemove }) {
|
|
|
4669
4708
|
) : null,
|
|
4670
4709
|
finished.length ? h(
|
|
4671
4710
|
"div",
|
|
4672
|
-
{
|
|
4711
|
+
{
|
|
4712
|
+
className: `dshm-ops-scroll${scrollCls}`,
|
|
4713
|
+
ref: scrollRef,
|
|
4714
|
+
tabIndex: 0,
|
|
4715
|
+
"aria-label": lookup("settings.ops")
|
|
4716
|
+
},
|
|
4673
4717
|
h(
|
|
4674
4718
|
"div",
|
|
4675
4719
|
{ className: "dsvm-opgroup done" },
|
|
@@ -5095,6 +5139,7 @@ function MarketPanel({ onClose }) {
|
|
|
5095
5139
|
const outdatedCount = installedUpdateStats2(mergedItems || []).outdatedCount;
|
|
5096
5140
|
const [banner, setBanner] = useState(null);
|
|
5097
5141
|
const [toast, setToast] = useState(null);
|
|
5142
|
+
const [toastTextRef, toastScrollCls] = useScrollableContain(toast ? toast.text : "");
|
|
5098
5143
|
useEffect(() => {
|
|
5099
5144
|
const onKey = (e) => {
|
|
5100
5145
|
if (e.key === "Escape" && dsvmModalDepth === 0 && onClose) onClose();
|
|
@@ -5214,7 +5259,7 @@ function MarketPanel({ onClose }) {
|
|
|
5214
5259
|
toast ? h(
|
|
5215
5260
|
"div",
|
|
5216
5261
|
{ className: toast.kind === "err" ? "dshm-banner err" : "dshm-banner" },
|
|
5217
|
-
h("span", { className:
|
|
5262
|
+
h("span", { className: `dshm-banner-text${toastScrollCls}`, ref: toastTextRef }, toast.text)
|
|
5218
5263
|
) : null,
|
|
5219
5264
|
banner ? h(RestartBanner, { note: banner.text, onDone: () => setBanner(null), desktop: banner.desktop === true, onRestarted: reloadPing }) : null
|
|
5220
5265
|
)
|