dsh-m 0.9.81 → 0.9.83
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 +96 -18
- package/lib/core/external-journal.js +12 -0
- package/lib/core/host-api.js +10 -1
- package/package.json +1 -1
- package/registry.json +7 -7
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.83 功能:手机窄屏自适应——面板头部两行重排 + 窄视口默认全屏
|
|
12
|
+
|
|
13
|
+
- **问题(主人手机实拍)**:`.dshm-head` 是单行 no-wrap flex,最小内容宽 ~440-470px,手机面板内容宽仅 330-365px → 行尾溢出被 `.dshm-panel` overflow:hidden 裁掉:还原/关闭按钮组不可见不可点(关面板只剩遮罩点击)、版本 chip 拦腰截断、标题逐词换行、seg 键逐字竖排。移动端问题第三次以「用户手机截图反馈」方式暴露(0.9.66 触屏滑动方向、0.9.67 字号失控之后)。
|
|
14
|
+
- **方案(容器查询两行重排 + 窄视口默认全屏,grilling 共识 Q1-Q10 + 独立评审 4 轮 11 条修订 R1-R8/N1-N3)**:`.dshm-panel` 加 `container-type:inline-size`(面板宽高均公式派生不依赖内容,包含安全;灯箱 portal 挂 body、sticky 行在滚动容器内,均不受影响),`@container (max-width:600px)` 两行重排——行1 [短标题 ⋯spacer⋯ chip winctl]、行2 [seg 三键拉通 36px 触控];DOM 顺序零改动(纯 order+wrap,源锚/tab 序/读屏序稳定);标题双 span 切换(窄显「插件市场」,`title.panel` 现成 key);版本 chip 常态隐藏、warn 态缩为 ⬆ icon(tooltip/自升级语义不变),隐藏/收拢规则以专属类 `.dshm-verchip` 收窄——头部 profile 徽标(非 web profile)不在共识范围、不受影响;新规则对不支持容器查询的浏览器零生效(条件规则组等效兜底,老浏览器逐字节同旧版);窄视口(≤600px)初始挂载恒全屏(仅判定一次、不随 resize 跟随,FS_KEY 记忆窄视口下不生效——防「还原一次被永久记住挤压态」),宽视口行为逐字节同今日;overlay 窄视口 padding 8px。范围红线:ZoneChips/卡片网格/灯箱/设置页/遮罩契约/拖拽模型零触碰。
|
|
15
|
+
- **验证**:1440 项测试全绿(0.9.82 基线 1435 + 新增源锚 5 组于 client-mobile-head.test.mjs;既有 render-smoke 两处断言同步改写——「warn 类名锚 + `⬆ v…` 文本锚」,0.9.82「一处既有用例标题改写」先例同款)、typecheck 零错误(`run-command-error-digest` F1 族时序用例为既有载荷敏感假红、与本改动无关(干净 HEAD 加压复现同红)——本版随执行评审 E5 以「就绪门」根治四用例(F1/T5 abort/timeout、F1-R/A/C):3 并发加压 9/9 全绿,修复前同法 6/6 红);`node scripts/verify-panel-mobile.mjs` 真浏览器 13 断言全过(393×852 窄 + 1280×800 宽 + FS_KEY 2×2 矩阵 + chip 双态:默认全屏/winctl 可达/头部零溢出/标题切换/seg 36px 触控/chip 隐藏与 warn 收拢/还原态 8px/宽视口 FS_KEY 回归);verify 脚本照 verify-lightbox 惯例手动运行不进 CI(本地 http 源 + /dshm 桩,零外网依赖);真机复核:主人手机。
|
|
16
|
+
|
|
17
|
+
### 0.9.82 功能:外部操作记录 failed 行单条移除——红点死锁解除(ADR-0018 修订)
|
|
18
|
+
|
|
19
|
+
- **问题(红点死锁 + 操作层与展示层不对齐)**:外部记录(CLI / Agent 工具通道)失败后点亮设置 tab 红点,但外部行没有单条 ✕——0.9.79 的原裁决因服务端只有 list/clear 而不给出 ✕ 出口;唯一消除手段「清除已结束」连带清空全部终态记录(含成功历史),行为上等同永久亮灯,警报疲劳会让红点彻底失效。展示层(渠道徽标、按日分组、计数)0.9.79 起已与本地记录同构,唯独操作层掉队。
|
|
20
|
+
- **方案(服务端 per-id 删除链路 + 客户端分流接线)**:core 新增 `removeExternalOperation(id)`(整文件读-改-写原子替换,幂等——id 不存在静默 no-op,保留记录原样透写不改写历史字段);host-api 新增 `external-operations-remove` method(body.id string 校验 400,鉴权同 list/clear 零新增特例);store 新增 `removeExternal(id)`(overlay 剔除 + **墓碑** `removedIds`——拦截在途 fetch 迟到响应把已删行并回,`clearExternal` 重置);`canRemoveRow` 对外部记录放行 `failed`(done 仍不给 ✕,与面板「问题态才有 ✕」语义精确对齐);`onOpsRemove` 按渠道分流——本地路径逐字节不变(R0 红线),外部路径**先 api 成功再动 overlay**(事务顺序强于「清除已结束」的 fire-and-forget),失败行保留 + err toast(`op.remove.failed` 新键,{err} 插值带出服务端错误线索,zh/en 同步)。单条 ✕ 无二次确认(与面板一致)。ADR-0018 文末追加修订注记,原裁决原文保留。
|
|
21
|
+
- **验证**:1435 项测试全绿(0.9.80 基线 1425 + 净增 10:journal 存储 3、host-api method 4、store removeExternal+墓碑 2、canRemoveRow 改写与接线锚 2,含一处既有用例标题改写)、typecheck 零错误;实施计划经独立评审 Agent 三轮评审收敛(探针路径/链接期报错形态/墓碑兜底/断言保全/401 惯例/插值链路/回退口径 11 条意见全部闭环)。
|
|
22
|
+
|
|
11
23
|
### 0.9.80 修复:已装页开关抖动根治 + 社区开关定位恒定 + 「已实测」徽章退役(三项 UX 修复,client-only)
|
|
12
24
|
|
|
13
25
|
- **① 已装页开关抖动根治(主人实拍:点开关整页抖、开关原地失踪、鼠标不再命中)**:CDP 实测每次点击开关发生 47px×2 卡片栅格位移 + 0.6–1.3s 命中丢失窗口(原点落到上方卡片 `.dshm-links` 外链行,误触即开浏览器标签)。根因三件:`ProgressLine`(install 专用 pnpm 进度条)对 toggle 也挂载(35px 行 + body 12px gap,插入/拔出即位移);toast/重启横幅是面板 column flex 的流内子元素(出现/消失压缩 body 39px×2);开关视觉翻转要等 5 个刷新请求落地(1.5–2.3s「点了没反应」真空期,且期间开关 `locked` 灰态)。修复:新增 `progressTarget`(仅 install/upgrade/self-upgrade 挂进度条;`activeMutateTarget` 语义不动,仍管逐行禁用);toast/横幅包进 `.dshm-banner-stack`(absolute 底部浮层,pointer-events 穿透);`pendingToggles` 乐观翻转(点击 42ms 即变,`finally` 摘除=成功收敛/失败回滚);toggle 只重取已装视图(installed+market×2+updates+external-operations → installed×1,`view-refresh.js` 未动、单测原样);`.dshm-switch::before` 热区 34×19 → 46×31(视觉不变)。CDP 回测四断言全过:位移 0、命中零丢失、翻转 42ms、body 底缘 0 位移。
|
|
@@ -579,6 +591,18 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
579
591
|
|
|
580
592
|
## English
|
|
581
593
|
|
|
594
|
+
### 0.9.83 Feature — mobile adaptive layout: two-row panel head + fullscreen-by-default on narrow viewports
|
|
595
|
+
|
|
596
|
+
- **Problem (user phone screenshot)**: `.dshm-head` is a single-line no-wrap flex whose minimum content width is ~440-470px, while a phone panel offers only 330-365px — the row-end overflow gets clipped by `.dshm-panel`'s `overflow:hidden`: the restore/close button group becomes invisible and untappable (closing the panel is left to backdrop taps only), the version chip is cut mid-glyph, the title wraps word-by-word into a tower, and the seg tabs stack their CJK labels one character per line. This is the third mobile issue surfaced via "user phone screenshot" (after 0.9.66 touch-swipe direction and 0.9.67 font blow-up).
|
|
597
|
+
- **Fix (container-query two-row re-layout + fullscreen-by-default on narrow viewports, grilling consensus Q1-Q10 + 11 revisions from a 4-round independent review R1-R8/N1-N3)**: `.dshm-panel` gains `container-type:inline-size` (panel width/height are formula-derived and content-independent, so containment is safe; the lightbox portals to body and the sticky category row lives inside the scroll container — neither is affected), and `@container (max-width:600px)` re-lays the head into two rows — row 1 [short title ⋯spacer⋯ chip winctl], row 2 [three seg tabs stretched full-width at 36px touch height]; DOM order is untouched (pure order+wrap — source anchors, tab order and screen-reader order all stable); the title switches via dual spans (short 「插件市场」 on narrow, reusing the existing `title.panel` key); the version chip is hidden in its idle state and collapses to a ⬆ icon when outdated (tooltip and self-upgrade semantics intact), with hide/collapse rules narrowed by the dedicated `.dshm-verchip` class — the head's profile badge (non-web profiles) is outside the consensus and unaffected; new rules are inert on browsers without container-query support (conditional-rule-group equivalent fallback — byte-identical to the old layout); on viewports ≤600px the panel mounts fullscreen by default (checked once at mount, not followed on resize; the FS_KEY memory is ignored on narrow viewports — preventing "restore once, stay cramped forever"), while wide viewports stay byte-for-byte identical to today; overlay padding drops to 8px on narrow viewports. Scope red lines: ZoneChips, card grid, lightbox, settings tab, backdrop-close contract and the drag model are untouched.
|
|
598
|
+
- **Verification**: 1440 tests green (0.9.82 baseline 1435 + 5 new source-anchor groups in client-mobile-head.test.mjs; two existing render-smoke assertions rewritten in sync — the warn class anchor and the `⬆ v…` text anchor, same precedent as 0.9.82's single retitled case), typecheck clean (`run-command-error-digest` F1-family timing cases are a pre-existing load-sensitive false-red, unrelated to this change — reproduced red under load on a clean HEAD too; this release also cures all four of them (F1/T5 abort/timeout, F1-R/A/C) with a readiness gate per execution-review E5: 3-instance concurrent stress 9/9 green, vs 6/6 red before); `node scripts/verify-panel-mobile.mjs` passes all 13 real-browser assertions (393×852 narrow + 1280×800 wide + the FS_KEY 2×2 matrix + both chip states: default fullscreen / winctl reachable / zero head overflow / title switching / 36px seg touch targets / chip hidden and warn collapsed / 8px restored overlay / wide-viewport FS_KEY regression); the verify script follows the verify-lightbox convention — manual, not in CI (local http origin + /dshm stub, zero external network); real-device recheck: the owner's phone.
|
|
599
|
+
|
|
600
|
+
### 0.9.82 Feature — per-row dismiss for failed external operation records: the red-dot deadlock is broken (ADR-0018 revision)
|
|
601
|
+
|
|
602
|
+
- **Problem (red-dot deadlock + ops layer lagging behind the display layer)**: a failed external record (CLI / agent-tool channel) lights the settings-tab red dot but had no per-row dismiss — the 0.9.79 ruling withheld the ✕ because the server only exposed list/clear. The only way to clear the dot, "Clear ended", also wipes every finished record (successful history included) — behaviorally a permanent alert, and alert fatigue defeats the dot entirely. The display layer (channel badge, day grouping, counts) has been isomorphic with local records since 0.9.79; only the operations layer lagged.
|
|
603
|
+
- **Fix (server per-id delete chain + client channel branching)**: core gains `removeExternalOperation(id)` (whole-file read-modify-write atomic replace, idempotent — silent no-op for missing ids, surviving records written back verbatim); host-api gains the `external-operations-remove` method (body.id string-validated with 400, auth identical to list/clear — zero new special cases); the store gains `removeExternal(id)` (overlay removal + a **tombstone** set `removedIds` that intercepts in-flight fetch responses from re-adding just-deleted rows, reset by `clearExternal`); `canRemoveRow` lets external `failed` rows through (done still has no ✕, precisely matching the panel's problem-status-only rule); `onOpsRemove` branches by channel — the local path is byte-for-byte unchanged (R0 red line), the external path **awaits the server delete before touching the overlay**, and on failure keeps the row and toasts with the new `op.remove.failed` key ({err} interpolation carries the server error, zh/en). No confirmation dialog (same as the panel ✕). ADR-0018 gains a revision note; the original ruling text is preserved.
|
|
604
|
+
- **Verification**: 1435 tests green (0.9.80 baseline 1425 + 10 net new: journal storage 3, host-api method 4, store removeExternal+tombstone 2, canRemoveRow rewrite and wiring anchors 2, incl. one retitled existing case), typecheck clean; the implementation plan converged through three independent review-agent rounds (probe path / link-time error shape / tombstone backstop / assertion preservation / 401 convention / interpolation chain / rollback stance — 11 findings all closed).
|
|
605
|
+
|
|
582
606
|
### Fixes in 0.9.80 — installed-tab toggle jitter eliminated + community switch pinned to its title row + 已实测 badge retired (three UX fixes, client-only)
|
|
583
607
|
|
|
584
608
|
- **① Installed-tab toggle jitter eliminated (user report: clicking the switch shook the whole page, the switch vanished in place, and the cursor no longer hit it)**: CDP measurement showed a 47px×2 card-grid shift per click plus a 0.6–1.3s hit-test loss window (the cursor's fixed point landed on the card above's `.dshm-links` external-links row — a mis-click opens a browser tab). Three root causes: `ProgressLine` (the install-only pnpm progress bar) mounted for toggles too (35px row + 12px body gap — inserting/unmounting it is the shift itself); the toast/restart banners were in-flow flex children of the panel column (mount/dismiss compressed the body by 39px×2); the switch visually flipped only after five refresh requests landed (a 1.5–2.3s "nothing happened" dead window, with the switch greyed to `locked`). Fixes: a new `progressTarget` gates ProgressLine to install/upgrade/self-upgrade (`activeMutateTarget` semantics untouched — it still drives per-row disabling); toasts/banners are wrapped in `.dshm-banner-stack` (absolute bottom overlay, pointer-events pass-through); optimistic `pendingToggles` flips the switch in 42ms (cleared in `finally` — converges on success, reverts to server state on failure); toggle refreshes the installed view only (installed+market×2+updates+external-operations → installed×1; `view-refresh.js` untouched, its unit tests unchanged); `.dshm-switch::before` grows the hit area 34×19 → 46×31 (visuals unchanged). CDP re-probe passes all four assertions: zero drift, zero hit-loss, 42ms flip, stable body bottom edge.
|
package/lib/client.js
CHANGED
|
@@ -1404,6 +1404,7 @@ function createOperationsStore(storage) {
|
|
|
1404
1404
|
let records = safeParse(s.read());
|
|
1405
1405
|
let persistDegraded = false;
|
|
1406
1406
|
let externalOverlay = [];
|
|
1407
|
+
const removedIds = /* @__PURE__ */ new Set();
|
|
1407
1408
|
const persist = () => {
|
|
1408
1409
|
if (persistDegraded) return;
|
|
1409
1410
|
try {
|
|
@@ -1421,13 +1422,24 @@ function createOperationsStore(storage) {
|
|
|
1421
1422
|
setExternal(next) {
|
|
1422
1423
|
const incoming = (Array.isArray(next) ? next : []).filter((r) => r && typeof r === "object" && typeof r.id === "string");
|
|
1423
1424
|
const byId = new Map(externalOverlay.map((r) => [r.id, r]));
|
|
1424
|
-
for (const r of incoming)
|
|
1425
|
+
for (const r of incoming) {
|
|
1426
|
+
if (removedIds.has(r.id)) continue;
|
|
1427
|
+
byId.set(r.id, { ...byId.get(r.id) || {}, ...r });
|
|
1428
|
+
}
|
|
1425
1429
|
externalOverlay = [...byId.values()];
|
|
1426
1430
|
return this.listMerged();
|
|
1427
1431
|
},
|
|
1428
1432
|
/** 清空外部 overlay(「清除已结束」联动 external-operations-clear 成功后调用)。 */
|
|
1429
1433
|
clearExternal() {
|
|
1430
1434
|
externalOverlay = [];
|
|
1435
|
+
removedIds.clear();
|
|
1436
|
+
return this.listMerged();
|
|
1437
|
+
},
|
|
1438
|
+
/** 按 id 剔除外部 overlay(0.9.82 修订 R2-3):仅内存视图——调用方必须已在服务端
|
|
1439
|
+
* 删除成功后调用(external-operations-remove);墓碑拦住在途 fetch 迟到响应的复活。 */
|
|
1440
|
+
removeExternal(id) {
|
|
1441
|
+
removedIds.add(id);
|
|
1442
|
+
externalOverlay = externalOverlay.filter((r) => r.id !== id);
|
|
1431
1443
|
return this.listMerged();
|
|
1432
1444
|
},
|
|
1433
1445
|
/** @returns {OperationRecord[]} 外部 overlay 当前快照(原始形态,浅拷贝)。 */
|
|
@@ -1730,13 +1742,14 @@ function opRowVm(r) {
|
|
|
1730
1742
|
dir: isToggle && typeof meta.on === "boolean" ? meta.on ? "on" : "off" : null,
|
|
1731
1743
|
version,
|
|
1732
1744
|
target: typeof rec.target === "string" ? rec.target : "",
|
|
1733
|
-
// 外部记录渠道标记(0.9.79 ADR-0018):非空 = 渠道徽标渲染 +
|
|
1745
|
+
// 外部记录渠道标记(0.9.79 ADR-0018):非空 = 渠道徽标渲染 + 单条移除出口判定
|
|
1746
|
+
// (0.9.82 修订 R2-3:failed 放行,见 canRemoveRow)
|
|
1734
1747
|
channel: typeof rec.channel === "string" && rec.channel !== "" ? rec.channel : null
|
|
1735
1748
|
};
|
|
1736
1749
|
}
|
|
1737
1750
|
function canRemoveRow(r) {
|
|
1738
1751
|
if (!r || typeof r !== "object") return false;
|
|
1739
|
-
if (r.channel) return
|
|
1752
|
+
if (r.channel) return r.status === "failed";
|
|
1740
1753
|
return r.status === "input" || r.status === "failed" || r.status === "superseded";
|
|
1741
1754
|
}
|
|
1742
1755
|
function channelLabelKey(channel) {
|
|
@@ -1997,6 +2010,7 @@ var ZH = {
|
|
|
1997
2010
|
"op.superseded.note": "{target} \u5DF2\u8DF3\u8FC7\uFF08\u524D\u63D0\u5DF2\u4E0D\u6210\u7ACB\u6216\u5DF2\u624B\u52A8\u5904\u7406\uFF09",
|
|
1998
2011
|
"op.cancelled": "\u7528\u6237\u653E\u5F03\u786E\u8BA4",
|
|
1999
2012
|
"op.remove": "\u79FB\u9664\u8BB0\u5F55",
|
|
2013
|
+
"op.remove.failed": "\u8BB0\u5F55\u79FB\u9664\u5931\u8D25\uFF1A{err}",
|
|
2000
2014
|
"op.panel.jump": "\u67E5\u770B\u64CD\u4F5C\u8BB0\u5F55",
|
|
2001
2015
|
"op.kind.install": "\u5B89\u88C5",
|
|
2002
2016
|
"op.kind.upgrade": "\u5347\u7EA7",
|
|
@@ -2296,6 +2310,7 @@ var EN = {
|
|
|
2296
2310
|
"op.superseded.note": "{target} skipped (precondition gone or already handled)",
|
|
2297
2311
|
"op.cancelled": "user cancelled",
|
|
2298
2312
|
"op.remove": "Dismiss",
|
|
2313
|
+
"op.remove.failed": "Failed to dismiss record: {err}",
|
|
2299
2314
|
"op.panel.jump": "Open operations card",
|
|
2300
2315
|
"op.kind.install": "Install",
|
|
2301
2316
|
"op.kind.upgrade": "Upgrade",
|
|
@@ -2907,8 +2922,12 @@ button.dshm-badge:hover{filter:brightness(.95)}
|
|
|
2907
2922
|
.dshm-entry svg{flex:none;width:16px;height:16px}
|
|
2908
2923
|
.dshm-entry span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2909
2924
|
/* \u6536\u8D77\uFF08rail\uFF09\u6001\uFF1A\u4E0E\u5BBF\u4E3B\u8BBE\u7F6E\u5165\u53E3 / dsh-skins \u540C\u51E0\u4F55\u2014\u201436\xD736 \u5706\u5F62\u3001margin 8/10\u3001
|
|
2910
|
-
\u56FE\u6807 18\u3001\u5185\u5BB9\u6C34\u5E73\u5C45\u4E2D\u3002\u7F3A\u8FD9\u5957\u4F1A\u5BFC\u81F4\u6536\u8D77\u65F6\u56FE\u6807\u504F\u79BB\u8F74\u7EBF\u4E14\u884C\u8DDD\u4E0D\u9F50\uFF08\u5B9E\u6D4B\u5DE6\u504F ~4px\uFF09\u3002
|
|
2911
|
-
|
|
2925
|
+
\u56FE\u6807 18\u3001\u5185\u5BB9\u6C34\u5E73\u5C45\u4E2D\u3002\u7F3A\u8FD9\u5957\u4F1A\u5BFC\u81F4\u6536\u8D77\u65F6\u56FE\u6807\u504F\u79BB\u8F74\u7EBF\u4E14\u884C\u8DDD\u4E0D\u9F50\uFF08\u5B9E\u6D4B\u5DE6\u504F ~4px\uFF09\u3002
|
|
2926
|
+
2026-10-14\uFF1A\u5DE6\u53F3 margin \u6539 auto\u2014\u2014\u69FD\u4F4D\u51FA\u53E3\u662F\u5217\u5411 flex\uFF0C36px \u5B9A\u5BBD\u6309\u94AE\u9ED8\u8BA4\u8D34\u4EA4\u53C9\u8F74
|
|
2927
|
+
\u8D77\u70B9\uFF08\u5DE6\u4FA7\uFF09\uFF1B\u684C\u9762 rail \u7A84\u53EA\u6709 ~4px \u53EF\u611F\u77E5\uFF0C\u624B\u673A\u5BBD rail \u5DE6\u504F ~22px\uFF08\u624B\u673A\u622A\u56FE\u50CF\u7D20
|
|
2928
|
+
\u5B9E\u6D4B\uFF1A\u9F7F\u8F6E/\u8C03\u8272\u76D8/\u9CB8\u9C7C cx\u224899-100\uFF0C\u5E02\u573A\u56DB\u5BAB\u683C cx=77.5\uFF0Crail \u4E2D\u7EBF\u2248100\uFF09\u3002auto
|
|
2929
|
+
margin \u4E0E dsh-skins rail \u5305\u88C5\u5C42 justify-content:center \u7B49\u6548\uFF08palette cx=100.5\uFF09\u3002 */
|
|
2930
|
+
.dshm-entry.rail{width:36px;height:36px;margin:8px auto 10px;padding:0;justify-content:center;border-radius:50%;gap:0}
|
|
2912
2931
|
.dshm-entry.rail svg{width:18px;height:18px}
|
|
2913
2932
|
[data-slot="sidebar.footer.action"]{display:flex!important;flex-direction:column;width:100%;min-width:0}
|
|
2914
2933
|
[data-slot="sidebar.footer.action"]>*{flex:none;min-width:0}
|
|
@@ -2945,6 +2964,50 @@ button.dshm-dshchip:disabled{cursor:default;opacity:.75}
|
|
|
2945
2964
|
.dshm-compat-peers{margin:0 0 8px;padding-left:18px;font-size:12px}
|
|
2946
2965
|
.dshm-compat-peers li{font-family:ui-monospace,monospace}
|
|
2947
2966
|
.dshm-compat-risk{font-size:12px;color:#f87171;margin-bottom:12px}
|
|
2967
|
+
/* ---------- \u624B\u673A\u7A84\u5C4F\u81EA\u9002\u5E94\uFF080.9.83\uFF0C\u4E3B\u4EBA\u624B\u673A\u622A\u56FE\u9A71\u52A8\uFF09 ---------- */
|
|
2968
|
+
/* \u75C5\u7076\uFF080.9.82 \u5B9E\u62CD\uFF09\uFF1A.dshm-head \u5355\u884C no-wrap flex \u6700\u5C0F\u5185\u5BB9\u5BBD ~440-470px\uFF0C\u624B\u673A\u9762\u677F
|
|
2969
|
+
\u5185\u5BB9\u5BBD\u4EC5 330-365px \u2192 \u884C\u5C3E\u6EA2\u51FA\u88AB .dshm-panel overflow:hidden \u88C1\u6389\u2014\u2014winctl\uFF08\u8FD8\u539F/\u5173\u95ED\uFF09
|
|
2970
|
+
\u4E0D\u53EF\u89C1\u4E0D\u53EF\u70B9\u3001\u7248\u672C chip \u62E6\u8170\u622A\u65AD\u3001\u6807\u9898\u9010\u8BCD\u6362\u884C\u3001seg \u952E\u9010\u5B57\u7AD6\u6392\u3002
|
|
2971
|
+
\u8F7D\u4F53\u9009\u578B\uFF1A\u5BB9\u5668\u67E5\u8BE2\u800C\u975E\u89C6\u53E3\u5A92\u4F53\u67E5\u8BE2\u2014\u2014\u9762\u677F\u6709\u8FD8\u539F\u5C45\u4E2D/.abs \u62D6\u62FD\u4F4D/.full \u5168\u5C4F\u4E09\u5F62\u6001\uFF0C
|
|
2972
|
+
\u89C6\u53E3\u5BBD \u2260 \u9762\u677F\u5BBD\uFF08600px \u89C6\u53E3\u2192568px \u9762\u677F\u4ECD\u6EA2\u51FA\uFF09\uFF1B\u5BB9\u5668\u67E5\u8BE2\u8DDF\u968F\u9762\u677F\u5B9E\u9645\u5BBD\u5EA6\u3002\u9762\u677F\u5BBD\u9AD8
|
|
2973
|
+
\u5747\u4E3A min(920px,100%)/min(680px,86vh) \u516C\u5F0F\u6D3E\u751F\u3001\u4E0D\u4F9D\u8D56\u5185\u5BB9\uFF0Cinline-size \u5305\u542B\u5B89\u5168\uFF1B
|
|
2974
|
+
\u706F\u7BB1 portal \u6302 body\uFF080.9.73\uFF09\u3001\u5206\u7C7B\u884C sticky \u5728 .dshm-body \u6EDA\u52A8\u5BB9\u5668\u5185\uFF0C\u5747\u4E0D\u53D7\u7956\u5148
|
|
2975
|
+
inline-size \u5305\u542B\u5F71\u54CD\u3002\u65AD\u70B9\uFF1A\u5BB9\u5668 600px\uFF08\u4E24\u884C\u91CD\u6392\uFF09\u4E0E\u89C6\u53E3 600px\uFF08\u9ED8\u8BA4\u5168\u5C4F\u5224\u5B9A +
|
|
2976
|
+
overlay 8px\uFF09\u540C\u503C\u3002
|
|
2977
|
+
\u515C\u5E95\u8BED\u4E49\u8BF4\u660E\uFF08\u5171\u8BC6 Q3 + \u8BC4\u5BA1 R7\uFF09\uFF1A\u300C\u4E0D\u652F\u6301\u5BB9\u5668\u67E5\u8BE2\u7684\u6D4F\u89C8\u5668\u65B0\u89C4\u5219\u96F6\u751F\u6548\u300D\u6309 CSS
|
|
2978
|
+
\u6761\u4EF6\u89C4\u5219\u7EC4\u673A\u5236\u7B49\u6548\u843D\u5730\u2014\u2014.dshm-title-short \u57FA\u7840\u9690\u85CF\u4E0D\u4F9D\u8D56\u5BB9\u5668\u67E5\u8BE2\uFF08\u8001\u6D4F\u89C8\u5668\u4E5F\u8981
|
|
2979
|
+
\u751F\u6548\uFF09\uFF1B\u5176\u4F59\u65B0\u89C4\u5219\u90FD\u662F\u6574\u5757 at-rule\uFF08@supports/@container/@media \u9876\u5C42\u5144\u5F1F\uFF09\uFF0C\u4E0D\u652F\u6301
|
|
2980
|
+
\u5BB9\u5668\u67E5\u8BE2\u7684\u6D4F\u89C8\u5668\u5BF9\u672A\u77E5 at-rule \u6574\u5757\u5FFD\u7565\uFF0C\u6548\u679C\u7B49\u540C\u5168\u90E8\u5305\u8FDB @supports\u3002\u4E0D\u8981\u673A\u68B0\u518D
|
|
2981
|
+
\u5D4C\u5957\u4E00\u5C42\u3002 */
|
|
2982
|
+
.dshm-title-short{display:none}
|
|
2983
|
+
/* \u515C\u5E95\uFF1A\u4E0D\u652F\u6301\u5BB9\u5668\u67E5\u8BE2\u7684\u6D4F\u89C8\u5668\u53EA\u751F\u6548\u672C\u6761\u2014\u2014\u77ED\u6807\u9898\u9690\u85CF\uFF0C\u89C6\u89C9\u4E0E\u65E7\u7248\u9010\u5B57\u8282\u4E00\u81F4 */
|
|
2984
|
+
@supports (container-type: inline-size) {
|
|
2985
|
+
.dshm-panel{container-type:inline-size}
|
|
2986
|
+
}
|
|
2987
|
+
/* \u4E24\u884C\u91CD\u6392\uFF1A\u884C1 [\u77ED\u6807\u9898 \u22EFspacer\u22EF chip winctl]\uFF0C\u884C2 [seg \u62C9\u901A]\u3002DOM \u987A\u5E8F\u4E0D\u52A8
|
|
2988
|
+
\uFF08title\u2192divider\u2192seg\u2192spacer\u2192chip\u2192winctl\uFF09\uFF0C\u5168\u9760 order+wrap\u2014\u2014\u6E90\u951A/tab \u5E8F/\u8BFB\u5C4F\u5E8F
|
|
2989
|
+
\u96F6\u6270\u52A8\uFF1B\u89C6\u89C9\u5E8F\u4E0E DOM \u5E8F\u4E0D\u4E00\u81F4\uFF08seg \u5728 DOM \u524D\u3001\u89C6\u89C9\u5728\u540E\uFF09\uFF0C\u9762\u677F\u5185\u5143\u7D20\u5C11\u53EF\u63A5\u53D7 */
|
|
2990
|
+
@container (max-width:600px){
|
|
2991
|
+
.dshm-head{flex-wrap:wrap;row-gap:6px}
|
|
2992
|
+
.dshm-head-divider{display:none}
|
|
2993
|
+
.dshm-title-full{display:none}
|
|
2994
|
+
.dshm-title-short{display:inline}
|
|
2995
|
+
.dshm-spacer{order:1}
|
|
2996
|
+
.dshm-head>.dshm-dshchip{order:2}
|
|
2997
|
+
.dshm-winctl{order:3}
|
|
2998
|
+
.dshm-winctl button{height:36px}
|
|
2999
|
+
.dshm-seg{order:4;flex:1 1 100%}
|
|
3000
|
+
.dshm-seg button{flex:1;justify-content:center;white-space:nowrap;padding:0 10px;height:36px}
|
|
3001
|
+
/* \u7248\u672C chip\uFF08\u5171\u8BC6 Q7 + \u8BC4\u5BA1 R5 \u6536\u7A84\uFF09\uFF1A\u5E38\u6001\u9690\u85CF\uFF08\u8BBE\u7F6E\u9875\u968F\u65F6\u53EF\u89C1\uFF09\uFF1Bwarn \u6001\u7F29\u4E3A \u2B06
|
|
3002
|
+
icon\u2014\u2014\u6587\u5B57 span \u5168\u9690\u85CF\uFF0Ctooltip \u4E0E\u70B9\u51FB\u81EA\u5347\u7EA7\u8BED\u4E49\u4E0D\u53D8\u3002\u89C4\u5219\u4EE5\u7248\u672C chip \u4E13\u5C5E\u7C7B
|
|
3003
|
+
.dshm-verchip \u6536\u7A84\uFF1A\u5934\u90E8\u53E6\u6709 profile \u5FBD\u6807\uFF08span.dshm-dshchip\uFF0C\u4EC5\u975E web profile
|
|
3004
|
+
\u6E32\u67D3\uFF09\uFF0C\u4E0D\u5728\u5171\u8BC6\u8303\u56F4\u5185\uFF0C\u4E0D\u5F97\u8FDE\u5E26\u9690\u85CF */
|
|
3005
|
+
.dshm-head>.dshm-verchip:not(.warn){display:none}
|
|
3006
|
+
.dshm-verchip.warn .dshm-dshchip-name,.dshm-verchip.warn .dshm-dshchip-v,.dshm-verchip.warn .dshm-up-v{display:none}
|
|
3007
|
+
}
|
|
3008
|
+
/* overlay \u7A84\u89C6\u53E3\u515C\u5E95\uFF1A\u8FD8\u539F\u6001 8px\uFF08.full \u7684 padding:0 \u7279\u5F02\u6027 (0,2,0)>(0,1,0) \u81EA\u7136\u4FDD\u6301\uFF09\uFF1B
|
|
3009
|
+
\u7A84\u89C6\u53E3\u9ED8\u8BA4\u5168\u5C4F\u7684 JS \u5224\u5B9A\u5728 MarketPanel full \u521D\u59CB\u5316 */
|
|
3010
|
+
@media (max-width:600px){.dshm-overlay{padding:8px}}
|
|
2948
3011
|
`;
|
|
2949
3012
|
function cssVersionOf(s) {
|
|
2950
3013
|
let h2 = 5381;
|
|
@@ -4963,10 +5026,10 @@ function OperationsCard({ records, onClearFinished, onRemove }) {
|
|
|
4963
5026
|
r.status === "running" ? Spin() : null,
|
|
4964
5027
|
r.error ? h("span", { className: "dsvm-opnote" }, r.error) : null,
|
|
4965
5028
|
r.warning ? h("span", { className: "dsvm-opnote" }, r.warning) : null,
|
|
4966
|
-
// 单条移除(终审·新伤2 → 0.9.79 R3-1/R2-3):出口判定下沉 canRemoveRow——
|
|
4967
|
-
// 本地 input/failed/superseded 保留
|
|
4968
|
-
//
|
|
4969
|
-
canRemoveRow2(r) && onRemove ? h("button", { className: "dshm-xbtn", title: lookup("op.remove"), onClick: () => onRemove(r
|
|
5029
|
+
// 单条移除(终审·新伤2 → 0.9.79 R3-1/R2-3 → 0.9.82 修订 R2-3):出口判定下沉 canRemoveRow——
|
|
5030
|
+
// 本地 input/failed/superseded 保留 ✕;外部记录 failed 放行(服务端 per-id delete 已建),
|
|
5031
|
+
// done 仍不给 ✕。onRemove 收整行 record,onOpsRemove 按 channel 分流(本地 remove / 外部 api)。
|
|
5032
|
+
canRemoveRow2(r) && onRemove ? h("button", { className: "dshm-xbtn", title: lookup("op.remove"), onClick: () => onRemove(r) }, h(XIcon)) : null
|
|
4970
5033
|
);
|
|
4971
5034
|
};
|
|
4972
5035
|
return h(
|
|
@@ -5062,21 +5125,21 @@ function DshmVersionChip({ version, notify, initialCheck }) {
|
|
|
5062
5125
|
"button",
|
|
5063
5126
|
{
|
|
5064
5127
|
type: "button",
|
|
5065
|
-
className: "dshm-dshchip warn",
|
|
5128
|
+
className: "dshm-dshchip dshm-verchip warn",
|
|
5066
5129
|
title: lookup("selfupdate.available", { v: state.latest }),
|
|
5067
5130
|
onClick: doUpgrade,
|
|
5068
5131
|
disabled: busy
|
|
5069
5132
|
},
|
|
5070
|
-
"dsh-m ",
|
|
5133
|
+
h("span", { className: "dshm-dshchip-name" }, "dsh-m "),
|
|
5071
5134
|
h("span", { className: "dshm-dshchip-v" }, `v${version}`),
|
|
5072
|
-
busy ? h(Spin) : h("span", { className: "dshm-up" },
|
|
5135
|
+
busy ? h(Spin) : h("span", { className: "dshm-up" }, "\u2B06", h("span", { className: "dshm-up-v" }, ` v${state.latest}`))
|
|
5073
5136
|
);
|
|
5074
5137
|
}
|
|
5075
5138
|
const title = state.kind === "ahead" && state.latest ? lookup("selfupdate.ahead", { v: state.latest }) : `dsh-m v${version}`;
|
|
5076
5139
|
return h(
|
|
5077
5140
|
"span",
|
|
5078
|
-
{ className: "dshm-dshchip", title },
|
|
5079
|
-
"dsh-m ",
|
|
5141
|
+
{ className: "dshm-dshchip dshm-verchip", title },
|
|
5142
|
+
h("span", { className: "dshm-dshchip-name" }, "dsh-m "),
|
|
5080
5143
|
h("span", { className: "dshm-dshchip-v" }, `v${version}`)
|
|
5081
5144
|
);
|
|
5082
5145
|
}
|
|
@@ -5125,6 +5188,7 @@ function MarketPanel({ onClose }) {
|
|
|
5125
5188
|
const FS_KEY = "dshm-panel-fullscreen";
|
|
5126
5189
|
const [full, setFull] = useState(() => {
|
|
5127
5190
|
try {
|
|
5191
|
+
if (typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(max-width: 600px)").matches) return true;
|
|
5128
5192
|
return typeof window !== "undefined" && window.localStorage ? window.localStorage.getItem(FS_KEY) === "1" : false;
|
|
5129
5193
|
} catch {
|
|
5130
5194
|
return false;
|
|
@@ -5490,7 +5554,12 @@ function MarketPanel({ onClose }) {
|
|
|
5490
5554
|
e.preventDefault();
|
|
5491
5555
|
}
|
|
5492
5556
|
},
|
|
5493
|
-
h(
|
|
5557
|
+
h(
|
|
5558
|
+
"span",
|
|
5559
|
+
{ className: "dshm-title" },
|
|
5560
|
+
h("span", { className: "dshm-title-full" }, lookup("title.full")),
|
|
5561
|
+
h("span", { className: "dshm-title-short" }, lookup("title.panel"))
|
|
5562
|
+
),
|
|
5494
5563
|
h("span", { className: "dshm-head-divider", "aria-hidden": "true" }),
|
|
5495
5564
|
h(
|
|
5496
5565
|
"div",
|
|
@@ -5553,9 +5622,18 @@ function MarketPanel({ onClose }) {
|
|
|
5553
5622
|
void api("external-operations-clear").catch(() => {
|
|
5554
5623
|
});
|
|
5555
5624
|
},
|
|
5556
|
-
onOpsRemove: (
|
|
5557
|
-
|
|
5558
|
-
|
|
5625
|
+
onOpsRemove: (rec) => {
|
|
5626
|
+
if (rec && rec.channel) {
|
|
5627
|
+
api("external-operations-remove", { id: rec.id }).then(() => {
|
|
5628
|
+
opsStore.removeExternal(rec.id);
|
|
5629
|
+
syncOps();
|
|
5630
|
+
}).catch((err) => {
|
|
5631
|
+
notify({ kind: "err", text: lookup("op.remove.failed", { err: err && err.message || "unknown" }) });
|
|
5632
|
+
});
|
|
5633
|
+
} else {
|
|
5634
|
+
opsStore.remove(rec.id);
|
|
5635
|
+
syncOps();
|
|
5636
|
+
}
|
|
5559
5637
|
}
|
|
5560
5638
|
}) : null
|
|
5561
5639
|
// 操作记录已整体搬迁至设置 tab 末位通宽卡(0.9.x 布局重构·第二批):底部不再常驻
|
|
@@ -105,6 +105,18 @@ export async function clearExternalOperations() {
|
|
|
105
105
|
await mkdir(dirname(file), { recursive: true });
|
|
106
106
|
await atomicWrite(file, '[]\n');
|
|
107
107
|
}
|
|
108
|
+
/** 按 id 删除单条外部记录(0.9.82 修订 R2-3):幂等——id 不存在静默 no-op;
|
|
109
|
+
* 整文件读-改-写与 append 同款并发纪律(ADR-0018 Consequences 既有明示局限);
|
|
110
|
+
* 保留记录原样透写,不做 normalize 重写(删除不改写历史字段)。 */
|
|
111
|
+
export async function removeExternalOperation(id) {
|
|
112
|
+
const file = journalFile();
|
|
113
|
+
const records = await readAll(file);
|
|
114
|
+
const kept = records.filter((r) => r.id !== id);
|
|
115
|
+
if (kept.length === records.length)
|
|
116
|
+
return;
|
|
117
|
+
await mkdir(dirname(file), { recursive: true });
|
|
118
|
+
await atomicWrite(file, `${JSON.stringify(kept, null, 2)}\n`);
|
|
119
|
+
}
|
|
108
120
|
/**
|
|
109
121
|
* 九个公共变更入口共用的终态直落钩子(0.9.79,ADR-0018):
|
|
110
122
|
* - channel 缺省 'external'(审计上漏报更糟,未知调用方落兜底而非丢弃);
|
package/lib/core/host-api.js
CHANGED
|
@@ -12,7 +12,7 @@ import { resolveDshVersion, readLauncherPackageVersion } from './dsh-version.js'
|
|
|
12
12
|
import { runDoctor } from './doctor.js';
|
|
13
13
|
import { listInstalledWithMeta, listMarket, installFromRegistry, selfUpgrade, uninstallPlugin, upgradePlugin, InstallGuardError, } from './market.js';
|
|
14
14
|
import { runProfileTransaction, TransactionError } from './profile-transaction.js';
|
|
15
|
-
import { listExternalOperations, clearExternalOperations } from './external-journal.js';
|
|
15
|
+
import { listExternalOperations, clearExternalOperations, removeExternalOperation } from './external-journal.js';
|
|
16
16
|
import { readInstalledPluginReadme } from './installed.js';
|
|
17
17
|
import { loadPrimaryScreenshots } from './primary-screenshots.js';
|
|
18
18
|
import { isNewerVersion, npmLatest, npmPackumentReadme } from './versions.js';
|
|
@@ -234,6 +234,7 @@ export function createApiDispatcher(ctx) {
|
|
|
234
234
|
ensureService: ctx.deps?.ensureService,
|
|
235
235
|
listExternalOperations: ctx.deps?.listExternalOperations ?? listExternalOperations,
|
|
236
236
|
clearExternalOperations: ctx.deps?.clearExternalOperations ?? clearExternalOperations,
|
|
237
|
+
removeExternalOperation: ctx.deps?.removeExternalOperation ?? removeExternalOperation,
|
|
237
238
|
};
|
|
238
239
|
const cfg = () => ctx.controller.config;
|
|
239
240
|
// 0.9.0:宿主信任检查委派(含 ping 与未知 method,fail-closed);
|
|
@@ -589,6 +590,14 @@ export function createApiDispatcher(ctx) {
|
|
|
589
590
|
payload = {};
|
|
590
591
|
break;
|
|
591
592
|
}
|
|
593
|
+
case 'external-operations-remove': {
|
|
594
|
+
// 0.9.82 修订 R2-3:外部 failed 行单条 ✕ 的服务端出口;鉴权同 list/clear(零新增特例)
|
|
595
|
+
if (typeof body.id !== 'string' || body.id === '')
|
|
596
|
+
throw new ApiProtocolError(400, '缺少 id');
|
|
597
|
+
await d.removeExternalOperation(body.id);
|
|
598
|
+
payload = {};
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
592
601
|
case 'restart': {
|
|
593
602
|
// 0.9.0:Desktop 不触发旧 helper(Electron 生命周期归官方;杀子进程=红线)
|
|
594
603
|
assertWriteAllowed(profile, 'restart');
|
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -205,15 +205,15 @@
|
|
|
205
205
|
"homepage": "https://github.com/meyaomiao/dsh-server-deck"
|
|
206
206
|
},
|
|
207
207
|
{
|
|
208
|
-
"id": "
|
|
209
|
-
"name": "
|
|
210
|
-
"description": "
|
|
208
|
+
"id": "ds-bot",
|
|
209
|
+
"name": "DS Bot",
|
|
210
|
+
"description": "多 Bot 协作:把 DSH 变成常驻 Bot 团队,支持主 Bot、Bot 间消息与群聊。",
|
|
211
211
|
"category": "watchlist",
|
|
212
|
-
"tags": ["
|
|
212
|
+
"tags": ["Bot", "群聊", "多 Agent", "协作"],
|
|
213
213
|
"source": "npm",
|
|
214
|
-
"npm": "
|
|
215
|
-
"github": "
|
|
216
|
-
"homepage": "https://github.com/
|
|
214
|
+
"npm": "ds-bot",
|
|
215
|
+
"github": "Fei2411/DeepSeek-Bot",
|
|
216
|
+
"homepage": "https://github.com/Fei2411/DeepSeek-Bot"
|
|
217
217
|
},
|
|
218
218
|
{
|
|
219
219
|
"id": "dsh-chat-manager",
|