dsh-m 0.9.74 → 0.9.75
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 +154 -20
- 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.75 功能:操作记录信息增强——行内时刻/按日分组/开关方向/耗时/版本变迁/来源标记(数据零迁移)
|
|
12
|
+
|
|
13
|
+
- **背景(主人设置页实拍:开关记录读不出「开还是关」)**:操作记录行只有「状态 + 类型 + 包名」三要素——开关看不出方向(截图里连续两条 `开关 @iasiv5/dsh-quota-watch` 无法分辨先后开/关)、无任何时间、看不出耗时、看不出对话区发起还是界面点击、升级不知道版本从哪到哪。而这些信息**全部已在每条记录里**(`createdAt`/`updatedAt` 是 store 打点常态、`meta.on` 0.4.0 起就随开关入账、`meta.session` 是泵拾取标记)——纯展示层缺口,非数据缺失;浏览器 localStorage(`dshm-operations`)里的存量旧记录升级后立即可读,零迁移。
|
|
14
|
+
- **方案(展示派生全部收敛纯函数模块)**:新增 `src/client/operations-view.js`(`fmtOpClock`/`fmtOpFull`/`opDayKey`/`fmtOpDuration`/`opUpgradeMeta`/`groupOpsByDay`/`opRowVm`,Node tests 直测,惯例同 toggle-view):① 行内 `HH:mm`(tabular-nums 对齐,title 完整到秒);② 已结束**按日分组**,组头 今天/昨天/YYYY-MM-DD,**新组在上、组内新→旧**——最新一条贴「已结束」标题可见,不再沉底滚动找;③ 开关/社区开关按 `meta.on` 渲染 `开`(绿)/`关`(灰)徽章——原「社区开关专属方向渲染」(0.9.71 评审 M1 `targetText`)退役为泛化实现;④ 终态耗时 `<1s/42s/1m02s/3h05m`(免翻译);⑤ 升级行版本变迁 `v0.9.71→v0.9.72`(github 源回退 sha 前 7 位,与 notify.upgraded 口径一致);⑥ `meta.session` 行加「对话」来源徽章;⑦ 「已结束(N)」计数(无计数旧键 `settings.ops.group.done` 退役)。字段缺席逐段省略,旧行形态优雅回退;字号守 0.9.67 十二 px 家族,徽章 `currentColor` 描边自适应主题。
|
|
15
|
+
- **数据管道(唯一一处非展示层改动)**:执行泵成功路径支持执行器回传 `value.opMeta` 并入记录 meta——底座取**落账时实读**的当前 meta(入队快照会丢拾取阶段追加的 session 标记,破「陈旧快照不回卷」保证,评审自纠);失败路径不入账(结果不可信)。恢复路径(`dispatchRestored`)成功值统一接进成功值通道(`value = r`)——生产实现今日返回 `{ ok: true }`,对现有流零行为变化;两处 `doUpgrade` 调用点把 `fromVersion/version/sha` 管道为 `opMeta`。
|
|
16
|
+
- **验证**:1375 项测试全绿(0.9.74 基线 1339 + 新增 36:`client-operations-view` 27 用例含格式化 TZ 无关派生/分组序/行 VM 回退/新病灶负向锚(`targetText` 与无计数旧键不得残留)+ 泵 opMeta 4 用例(done/warned 入账、失败与脏输入不入、恢复路径入账、session 保留))、typecheck 零错误;构建产物含新规则逐字锚。
|
|
17
|
+
|
|
11
18
|
### 0.9.74 功能:详情截图条滚轮横滚——裸滚轮 deltaY 安全映射 scrollLeft(钢人三红线)
|
|
12
19
|
|
|
13
20
|
- **背景(主人验收 0.9.73 实拍反馈)**:截图条是横滚容器,鼠标滚轮在横滚容器上默认纵滚祖先(弹窗)——图片多时纯鼠标用户只能拖 ~700px 细滚动条(离散目标上的连续输入,精度成本极高);触控板双指横滑(deltaX)与 Shift+滚轮虽有原生路径,但属隐藏知识不可达。
|
|
@@ -545,6 +552,13 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
545
552
|
|
|
546
553
|
## English
|
|
547
554
|
|
|
555
|
+
### Feature in 0.9.75 — operations log enrichment: inline clock/day groups/toggle direction/duration/upgrade versions/chat source (zero data migration)
|
|
556
|
+
|
|
557
|
+
- **Background (owner screenshot: toggle records unreadable as on/off)**: each operations row carried only "status + kind + package" — a toggle showed no direction (two consecutive `toggle @iasiv5/dsh-quota-watch` rows in the screenshot were indistinguishable), no timestamp, no duration, no chat-vs-UI source, no upgrade from→to versions. Yet **all of this already exists per record** (`createdAt`/`updatedAt` are store bookkeeping, `meta.on` has been recorded with every toggle since 0.4.0, `meta.session` is the pump pickup marker) — a pure presentation gap, not missing data. Existing records in browser localStorage (`dshm-operations`) become readable right after upgrade, zero migration.
|
|
558
|
+
- **Approach (all display derivation in one pure module)**: new `src/client/operations-view.js` (`fmtOpClock`/`fmtOpFull`/`opDayKey`/`fmtOpDuration`/`opUpgradeMeta`/`groupOpsByDay`/`opRowVm`, directly unit-tested, same convention as toggle-view): ① inline `HH:mm` (tabular-nums, full-to-second time in title); ② finished records **grouped by local day**, headers Today/Yesterday/YYYY-MM-DD, **newest group on top, newest-first within** — the latest row sits right under the "Ended" caption instead of sinking to the scroll bottom; ③ toggles render an `On` (green) / `Off` (grey) badge from `meta.on` — the 0.9.71 community-toggle-only direction rendering (`targetText`) retires into this generalized path; ④ terminal duration `<1s/42s/1m02s/3h05m` (translation-free); ⑤ upgrade rows show `v0.9.71→v0.9.72` (github source falls back to 7-char sha, consistent with notify.upgraded); ⑥ `meta.session` rows get a "Chat" source badge; ⑦ "Ended (N)" count (the count-less key `settings.ops.group.done` retires). Missing fields omit their segment — old records degrade to the previous row shape; 12px type-family discipline (0.9.67) kept, badges use `currentColor` borders to follow the theme.
|
|
559
|
+
- **Data plumbing (the single non-presentation change)**: the pump's success path now merges an executor-returned `value.opMeta` into the record meta — the merge base is the **freshly read** meta at finalize time (the enqueue snapshot lacks the pickup-time session marker and would break the stale-snapshot no-rollback guarantee; caught in self-review); failures never merge (result untrustworthy). The restored path (`dispatchRestored`) now routes its success value through the same channel (`value = r`) — the production implementation returns `{ ok: true }` today, so existing flows are behavior-unchanged; both `doUpgrade` call sites pipe `fromVersion/version/sha` into `opMeta`.
|
|
560
|
+
- **Verification**: 1375 tests all green (0.9.74 baseline 1339 + 36 new: `client-operations-view` 27 cases covering TZ-agnostic time formatting, group ordering, row-VM fallbacks, and negative anchors against the retired `targetText`/count-less key; 4 pump opMeta cases covering done/warned merge, failure & dirty-input rejection, restored-path merge, session preservation), typecheck clean; build artifact carries the new rules verbatim.
|
|
561
|
+
|
|
548
562
|
### Feature in 0.9.74 — screenshot-strip wheel hscroll: bare-wheel deltaY safely mapped to scrollLeft (steelman's three red lines)
|
|
549
563
|
|
|
550
564
|
- **Background (owner feedback while verifying 0.9.73)**: the strip is a horizontal scroll container, and a mouse wheel over it scrolls the ancestor (the modal) by default — with many screenshots, mouse-only users must drag a ~700px thin scrollbar (continuous input on a discrete target); trackpad two-finger hscroll (deltaX) and Shift+wheel exist natively but are hidden knowledge.
|
package/lib/client.js
CHANGED
|
@@ -1511,6 +1511,7 @@ function createOpsPump(store, getCtx) {
|
|
|
1511
1511
|
} else if (ctx && ctx.dispatchRestored) {
|
|
1512
1512
|
const r = await ctx.dispatchRestored(queued);
|
|
1513
1513
|
if (!r || !r.ok) err = Object.assign(new Error(r && r.error || "unknown"), r && r.issue ? { issue: r.issue } : {});
|
|
1514
|
+
else value = r;
|
|
1514
1515
|
} else {
|
|
1515
1516
|
err = new Error("\u65E0\u6267\u884C\u4E0A\u4E0B\u6587\uFF08\u9762\u677F\u672A\u6302\u8F7D\uFF09");
|
|
1516
1517
|
}
|
|
@@ -1518,7 +1519,13 @@ function createOpsPump(store, getCtx) {
|
|
|
1518
1519
|
err = e;
|
|
1519
1520
|
}
|
|
1520
1521
|
if (!err) {
|
|
1521
|
-
|
|
1522
|
+
const opMeta = value && typeof value === "object" && value.opMeta && typeof value.opMeta === "object" ? value.opMeta : null;
|
|
1523
|
+
const cur = opMeta ? store.list().find((x) => x.id === queued.id) : null;
|
|
1524
|
+
finalize({
|
|
1525
|
+
status: value && value.opWarning ? "warned" : "done",
|
|
1526
|
+
warning: value && value.opWarning,
|
|
1527
|
+
...opMeta ? { meta: { ...cur && cur.meta || {}, ...opMeta } } : {}
|
|
1528
|
+
});
|
|
1522
1529
|
if (waitersEntry) waitersEntry.resolve(value);
|
|
1523
1530
|
} else {
|
|
1524
1531
|
const isInput = Boolean(err.issue);
|
|
@@ -1586,6 +1593,90 @@ var init_operations = __esm({
|
|
|
1586
1593
|
}
|
|
1587
1594
|
});
|
|
1588
1595
|
|
|
1596
|
+
// src/client/operations-view.js
|
|
1597
|
+
var operations_view_exports = {};
|
|
1598
|
+
__export(operations_view_exports, {
|
|
1599
|
+
fmtOpClock: () => fmtOpClock,
|
|
1600
|
+
fmtOpDuration: () => fmtOpDuration,
|
|
1601
|
+
fmtOpFull: () => fmtOpFull,
|
|
1602
|
+
groupOpsByDay: () => groupOpsByDay,
|
|
1603
|
+
opDayKey: () => opDayKey,
|
|
1604
|
+
opRowVm: () => opRowVm,
|
|
1605
|
+
opUpgradeMeta: () => opUpgradeMeta,
|
|
1606
|
+
pad2: () => pad2
|
|
1607
|
+
});
|
|
1608
|
+
function pad2(n) {
|
|
1609
|
+
return String(n).padStart(2, "0");
|
|
1610
|
+
}
|
|
1611
|
+
function fmtOpClock(ts) {
|
|
1612
|
+
const d = new Date(ts);
|
|
1613
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
1614
|
+
}
|
|
1615
|
+
function fmtOpFull(ts) {
|
|
1616
|
+
const d = new Date(ts);
|
|
1617
|
+
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${fmtOpClock(ts)}:${pad2(d.getSeconds())}`;
|
|
1618
|
+
}
|
|
1619
|
+
function opDayKey(ts) {
|
|
1620
|
+
const d = new Date(ts);
|
|
1621
|
+
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
1622
|
+
}
|
|
1623
|
+
function fmtOpDuration(ms) {
|
|
1624
|
+
if (typeof ms !== "number" || !Number.isFinite(ms) || ms < 0) return "";
|
|
1625
|
+
if (ms < 1e3) return "<1s";
|
|
1626
|
+
const s = Math.floor(ms / 1e3);
|
|
1627
|
+
if (s < 60) return `${s}s`;
|
|
1628
|
+
const m = Math.floor(s / 60);
|
|
1629
|
+
if (m < 60) return `${m}m${pad2(s % 60)}s`;
|
|
1630
|
+
const hr = Math.floor(m / 60);
|
|
1631
|
+
return `${hr}h${pad2(m % 60)}m`;
|
|
1632
|
+
}
|
|
1633
|
+
function opUpgradeMeta(res) {
|
|
1634
|
+
if (!res || typeof res !== "object") return null;
|
|
1635
|
+
const from = res.fromVersion ? String(res.fromVersion) : "";
|
|
1636
|
+
const to = res.version ? String(res.version) : res.sha ? String(res.sha).slice(0, 7) : "";
|
|
1637
|
+
return from || to ? { from, to } : null;
|
|
1638
|
+
}
|
|
1639
|
+
function groupOpsByDay(records, now = Date.now()) {
|
|
1640
|
+
const list = Array.isArray(records) ? records.filter(Boolean) : [];
|
|
1641
|
+
const todayKey = opDayKey(now);
|
|
1642
|
+
const yesterdayKey = opDayKey(now - 864e5);
|
|
1643
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
1644
|
+
for (const r of list) {
|
|
1645
|
+
const key = r && Number.isFinite(r.createdAt) ? opDayKey(r.createdAt) : "";
|
|
1646
|
+
if (!byDay.has(key)) byDay.set(key, []);
|
|
1647
|
+
byDay.get(key).push(r);
|
|
1648
|
+
}
|
|
1649
|
+
const kindOf = (key) => key === todayKey ? "today" : key === yesterdayKey ? "yesterday" : "date";
|
|
1650
|
+
return [...byDay.entries()].sort((a, b) => a[0] < b[0] ? 1 : a[0] > b[0] ? -1 : 0).map(([key, rs]) => ({ key, kind: kindOf(key), records: rs.slice().reverse() }));
|
|
1651
|
+
}
|
|
1652
|
+
function opRowVm(r) {
|
|
1653
|
+
const rec = r || {};
|
|
1654
|
+
const meta = rec.meta && typeof rec.meta === "object" ? rec.meta : {};
|
|
1655
|
+
const ts = Number.isFinite(rec.createdAt) ? rec.createdAt : null;
|
|
1656
|
+
const durMs = rec.status && TERMINAL_CLEARABLE.has(rec.status) && Number.isFinite(rec.createdAt) && Number.isFinite(rec.updatedAt) ? Math.max(0, rec.updatedAt - rec.createdAt) : null;
|
|
1657
|
+
const isToggle = rec.kind === "toggle" || rec.kind === "community-toggle";
|
|
1658
|
+
let version = "";
|
|
1659
|
+
if (rec.kind === "upgrade") {
|
|
1660
|
+
const m = opUpgradeMeta({ fromVersion: meta.from, version: meta.to });
|
|
1661
|
+
if (m) version = `${m.from ? `v${m.from}` : ""}\u2192${m.to ? `v${m.to}` : ""}`;
|
|
1662
|
+
}
|
|
1663
|
+
return {
|
|
1664
|
+
clock: ts === null ? "" : fmtOpClock(ts),
|
|
1665
|
+
full: ts === null ? "" : fmtOpFull(ts),
|
|
1666
|
+
durText: durMs === null ? "" : fmtOpDuration(durMs),
|
|
1667
|
+
dir: isToggle && typeof meta.on === "boolean" ? meta.on ? "on" : "off" : null,
|
|
1668
|
+
session: meta.session === true,
|
|
1669
|
+
version,
|
|
1670
|
+
target: typeof rec.target === "string" ? rec.target : ""
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
var init_operations_view = __esm({
|
|
1674
|
+
"src/client/operations-view.js"() {
|
|
1675
|
+
"use strict";
|
|
1676
|
+
init_operations();
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
|
|
1589
1680
|
// src/client/favorites.js
|
|
1590
1681
|
var favorites_exports = {};
|
|
1591
1682
|
__export(favorites_exports, {
|
|
@@ -1792,6 +1883,7 @@ var { RESTART_POLL_MS: RESTART_POLL_MS2, RESTART_DEADLINE_MS: RESTART_DEADLINE_M
|
|
|
1792
1883
|
var { refreshAfterMutation: refreshAfterMutation2 } = (init_view_refresh(), __toCommonJS(view_refresh_exports));
|
|
1793
1884
|
var { applyInstalledUpdates: applyInstalledUpdates2, installedUpdateStats: installedUpdateStats2 } = (init_installed_updates(), __toCommonJS(installed_updates_exports));
|
|
1794
1885
|
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));
|
|
1886
|
+
var { groupOpsByDay: groupOpsByDay2, opRowVm: opRowVm2, opUpgradeMeta: opUpgradeMeta2 } = (init_operations_view(), __toCommonJS(operations_view_exports));
|
|
1795
1887
|
var { createFavoritesStore: createFavoritesStore2, partitionStale: partitionStale2 } = (init_favorites(), __toCommonJS(favorites_exports));
|
|
1796
1888
|
var { readSelfCheckCache: readSelfCheckCache2, writeSelfCheckCache: writeSelfCheckCache2, clearSelfCheckCache: clearSelfCheckCache2, deriveChipState: deriveChipState2 } = (init_self_check(), __toCommonJS(self_check_exports));
|
|
1797
1889
|
var { shouldShowInstallCmd: shouldShowInstallCmd2 } = (init_install_cmd(), __toCommonJS(install_cmd_exports));
|
|
@@ -1851,6 +1943,11 @@ var ZH = {
|
|
|
1851
1943
|
"op.status.warned": "\u5E26\u8B66\u544A",
|
|
1852
1944
|
"op.status.failed": "\u5931\u8D25",
|
|
1853
1945
|
"op.status.superseded": "\u5DF2\u8DF3\u8FC7",
|
|
1946
|
+
"op.day.today": "\u4ECA\u5929",
|
|
1947
|
+
"op.day.yesterday": "\u6628\u5929",
|
|
1948
|
+
"op.session": "\u5BF9\u8BDD",
|
|
1949
|
+
"op.session.tip": "\u5BF9\u8BDD\u533A\u53D1\u8D77\uFF08\u975E\u754C\u9762\u70B9\u51FB\uFF09",
|
|
1950
|
+
"settings.ops.group.done.n": "\u5DF2\u7ED3\u675F\uFF08{n}\uFF09",
|
|
1854
1951
|
"favorites.hint": "\u8FD8\u6CA1\u6709\u6536\u85CF\u2014\u2014\u53BB\u793E\u533A/\u7CBE\u9009\u9875\u70B9\u63D2\u4EF6\u5361\u7247\u53F3\u4E0A\u89D2\u7684 \u2606 \u6536\u85CF",
|
|
1855
1952
|
"favorites.stale": "{n} \u6761\u6536\u85CF\u5DF2\u4ECE\u76EE\u5F55\u4E0B\u67B6",
|
|
1856
1953
|
"favorites.clean": "\u6E05\u7406\u5931\u6548\u6536\u85CF",
|
|
@@ -2015,7 +2112,6 @@ var ZH = {
|
|
|
2015
2112
|
"settings.ops": "\u64CD\u4F5C\u8BB0\u5F55",
|
|
2016
2113
|
"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",
|
|
2017
2114
|
"settings.ops.group.active": "\u8FDB\u884C\u4E2D",
|
|
2018
|
-
"settings.ops.group.done": "\u5DF2\u7ED3\u675F",
|
|
2019
2115
|
"settings.ops.reddot": "\u5B58\u5728\u5931\u8D25\u6216\u5E26\u8B66\u544A\u7684\u64CD\u4F5C\u8BB0\u5F55",
|
|
2020
2116
|
"registry.refreshed": "\u6536\u5F55\u6E05\u5355\u5DF2\u5F3A\u5236\u5237\u65B0",
|
|
2021
2117
|
"notify.installed": "\u5DF2\u5B89\u88C5 {pkg}{version}",
|
|
@@ -2142,6 +2238,11 @@ var EN = {
|
|
|
2142
2238
|
"op.status.warned": "Warned",
|
|
2143
2239
|
"op.status.failed": "Failed",
|
|
2144
2240
|
"op.status.superseded": "Skipped",
|
|
2241
|
+
"op.day.today": "Today",
|
|
2242
|
+
"op.day.yesterday": "Yesterday",
|
|
2243
|
+
"op.session": "Chat",
|
|
2244
|
+
"op.session.tip": "Initiated from chat (not a UI click)",
|
|
2245
|
+
"settings.ops.group.done.n": "Ended ({n})",
|
|
2145
2246
|
"favorites.hint": "No favorites yet \u2014 tap \u2606 on a plugin card in Community/Curated to bookmark it",
|
|
2146
2247
|
"favorites.stale": "{n} favorites no longer in the catalog",
|
|
2147
2248
|
"favorites.clean": "Clean up stale favorites",
|
|
@@ -2306,7 +2407,6 @@ var EN = {
|
|
|
2306
2407
|
"settings.ops": "Operations",
|
|
2307
2408
|
"settings.ops.empty": "No operations yet. History of installs, upgrades, uninstalls, toggles and registry / community catalog settings will appear here.",
|
|
2308
2409
|
"settings.ops.group.active": "Active",
|
|
2309
|
-
"settings.ops.group.done": "Finished",
|
|
2310
2410
|
"settings.ops.reddot": "Failed or warned operations exist",
|
|
2311
2411
|
"registry.refreshed": "Registry force-refreshed",
|
|
2312
2412
|
"notify.installed": "Installed {pkg}{version}",
|
|
@@ -2605,6 +2705,16 @@ var CSS = `
|
|
|
2605
2705
|
.dsvm-opkind{color:var(--dsw-alias-label-secondary,#4b5563)}
|
|
2606
2706
|
.dsvm-optarget{font-weight:500;overflow-wrap:anywhere}
|
|
2607
2707
|
.dsvm-opnote{color:var(--dsw-alias-label-caption,#6b7280);font-size:11px;overflow-wrap:anywhere}
|
|
2708
|
+
/* 0.9.75 \u64CD\u4F5C\u8BB0\u5F55\u4FE1\u606F\u589E\u5F3A\uFF1A\u884C\u5185\u65F6\u95F4/\u8017\u65F6\uFF08tabular-nums \u5BF9\u9F50\uFF09\u3001\u6309\u65E5\u5206\u7EC4\u5934\u3001\u5F00\u5173\u65B9\u5411\u4E0E
|
|
2709
|
+
\u5BF9\u8BDD\u6765\u6E90\u5FBD\u7AE0\u3001\u5347\u7EA7\u7248\u672C\u53D8\u8FC1\u3002\u5B57\u53F7\u5B88 0.9.67 \u7684 12px \u5BB6\u65CF\uFF08\u6B21\u7EA7 11px\uFF09\uFF0C\u989C\u8272\u6CBF\u7528\u65E2\u6709\u8BED\u4E49\uFF1A
|
|
2710
|
+
\u65B9\u5411\u300C\u5F00\u300D\u7528 ok \u540C\u6B3E\u7EFF\uFF0C\u300C\u5173\u300D\u7528\u6B21\u7EA7\u7070\uFF1B\u5FBD\u7AE0 currentColor \u63CF\u8FB9\u81EA\u9002\u5E94\u6DF1\u6D45\u4E3B\u9898\u3002 */
|
|
2711
|
+
.dsvm-optime,.dsvm-opdur{font-size:11px;color:var(--dsw-alias-label-caption,#9ca3af);font-variant-numeric:tabular-nums}
|
|
2712
|
+
.dsvm-opday{font-size:11px;font-weight:600;color:var(--dsw-alias-label-caption,#9ca3af);margin:6px 0 0}
|
|
2713
|
+
.dsvm-opgroup.done .dsvm-opday:first-child{margin-top:0}
|
|
2714
|
+
.dsvm-opdir,.dsvm-opsession{font-size:11px;line-height:16px;padding:0 6px;border-radius:8px;border:1px solid currentColor}
|
|
2715
|
+
.dsvm-opdir.on{color:#15803d}
|
|
2716
|
+
.dsvm-opdir.off,.dsvm-opsession{color:var(--dsw-alias-label-secondary,#4b5563)}
|
|
2717
|
+
.dsvm-opver{font-size:11px;color:var(--dsw-alias-label-secondary,#4b5563);font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
|
|
2608
2718
|
.dsvm-favbtn{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-caption,#9ca3af);font-size:15px;line-height:1;cursor:pointer;padding:0 2px;margin-left:auto}
|
|
2609
2719
|
.dsvm-favbtn:hover{color:var(--dsw-alias-state-business-primary,#4d6bfe)}
|
|
2610
2720
|
.dsvm-favbtn.on{color:#e0a33c}
|
|
@@ -3760,7 +3870,11 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind, o
|
|
|
3760
3870
|
};
|
|
3761
3871
|
const doUpgrade = async (it) => {
|
|
3762
3872
|
try {
|
|
3763
|
-
const res = await ops.runOp("upgrade", it.installedPkg, () =>
|
|
3873
|
+
const res = await ops.runOp("upgrade", it.installedPkg, async () => {
|
|
3874
|
+
const r = await api("upgrade", { pkg: it.installedPkg });
|
|
3875
|
+
const m = opUpgradeMeta2(r);
|
|
3876
|
+
return m ? { ...r, opMeta: m } : r;
|
|
3877
|
+
});
|
|
3764
3878
|
const note = upgradeNotify2(res.activation);
|
|
3765
3879
|
const text = lookup("notify.upgraded", { pkg: res.pkg, from: res.fromVersion ? `v${res.fromVersion}` : "\u2014", to: res.version ? `v${res.version}` : res.sha ? res.sha.slice(0, 7) : "latest" }) + (res.buildApprovals && res.buildApprovals.length ? lookup("notify.builds", { names: res.buildApprovals.join(", ") }) : res.fallbackAllBuilds ? lookup("notify.builds.fallback") : "") + (note.suffixKey ? lookup(note.suffixKey) : "");
|
|
3766
3880
|
notify({ kind: "ok", needsRestart: note.needsRestart, text });
|
|
@@ -4165,7 +4279,11 @@ function InstalledTab({ notify, installed, updates, onMutation, ops }) {
|
|
|
4165
4279
|
};
|
|
4166
4280
|
const doUpgrade = async (it) => {
|
|
4167
4281
|
try {
|
|
4168
|
-
const res = await ops.runOp("upgrade", it.pkg, () =>
|
|
4282
|
+
const res = await ops.runOp("upgrade", it.pkg, async () => {
|
|
4283
|
+
const r = await api("upgrade", { pkg: it.pkg });
|
|
4284
|
+
const m = opUpgradeMeta2(r);
|
|
4285
|
+
return m ? { ...r, opMeta: m } : r;
|
|
4286
|
+
});
|
|
4169
4287
|
const note = upgradeNotify2(res.activation);
|
|
4170
4288
|
notify({
|
|
4171
4289
|
kind: "ok",
|
|
@@ -4727,19 +4845,28 @@ function OperationsCard({ records, onClearFinished, onRemove }) {
|
|
|
4727
4845
|
const finished = records.filter((r) => active.indexOf(r) < 0).slice(-50);
|
|
4728
4846
|
const clearable = records.some((r) => TERMINAL_CLEARABLE2.has(r.status));
|
|
4729
4847
|
const [scrollRef, scrollCls] = useScrollableContain(finished.length);
|
|
4730
|
-
const
|
|
4731
|
-
const row = (r) =>
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
h(
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4848
|
+
const dayGroups = useMemo(() => groupOpsByDay2(finished), [records]);
|
|
4849
|
+
const row = (r) => {
|
|
4850
|
+
const vm = opRowVm2(r);
|
|
4851
|
+
const tip = [vm.full, vm.durText, r.error || r.warning || ""].filter(Boolean).join(" \xB7 ");
|
|
4852
|
+
return h(
|
|
4853
|
+
"div",
|
|
4854
|
+
{ key: r.id, className: `dsvm-oprow ${OP_STATUS_CLS[r.status] || ""}` },
|
|
4855
|
+
vm.clock ? h("span", { className: "dsvm-optime", title: vm.full }, vm.clock) : null,
|
|
4856
|
+
h("span", { className: "dsvm-opstatus" }, lookup("op.status." + r.status)),
|
|
4857
|
+
h("span", { className: "dsvm-opkind" }, lookup("op.kind." + r.kind)),
|
|
4858
|
+
vm.dir ? h("span", { className: `dsvm-opdir ${vm.dir}` }, lookup(vm.dir === "on" ? "op.state.on" : "op.state.off")) : null,
|
|
4859
|
+
vm.session ? h("span", { className: "dsvm-opsession", title: lookup("op.session.tip") }, lookup("op.session")) : null,
|
|
4860
|
+
h("span", { className: "dsvm-optarget", title: tip || void 0 }, vm.target),
|
|
4861
|
+
vm.version ? h("span", { className: "dsvm-opver", title: tip || void 0 }, vm.version) : null,
|
|
4862
|
+
vm.durText ? h("span", { className: "dsvm-opdur" }, vm.durText) : null,
|
|
4863
|
+
r.status === "running" ? Spin() : null,
|
|
4864
|
+
r.error ? h("span", { className: "dsvm-opnote" }, r.error) : null,
|
|
4865
|
+
r.warning ? h("span", { className: "dsvm-opnote" }, r.warning) : null,
|
|
4866
|
+
// 单条移除(终审·新伤2):input/failed/superseded 行给出口(对话区来源的 input 不再永挂)
|
|
4867
|
+
(r.status === "input" || r.status === "failed" || r.status === "superseded") && onRemove ? h("button", { className: "dshm-xbtn", title: lookup("op.remove"), onClick: () => onRemove(r.id) }, h(XIcon)) : null
|
|
4868
|
+
);
|
|
4869
|
+
};
|
|
4743
4870
|
return h(
|
|
4744
4871
|
"div",
|
|
4745
4872
|
{ className: "dshm-section dshm-ops-card" },
|
|
@@ -4749,7 +4876,7 @@ function OperationsCard({ records, onClearFinished, onRemove }) {
|
|
|
4749
4876
|
finished.length ? h(
|
|
4750
4877
|
"div",
|
|
4751
4878
|
{ className: "dsvm-ops-caption", style: { marginTop: active.length ? "7px" : "0" } },
|
|
4752
|
-
lookup("settings.ops.group.done")
|
|
4879
|
+
lookup("settings.ops.group.done.n", { n: finished.length })
|
|
4753
4880
|
) : null,
|
|
4754
4881
|
finished.length ? h(
|
|
4755
4882
|
"div",
|
|
@@ -4762,7 +4889,14 @@ function OperationsCard({ records, onClearFinished, onRemove }) {
|
|
|
4762
4889
|
h(
|
|
4763
4890
|
"div",
|
|
4764
4891
|
{ className: "dsvm-opgroup done" },
|
|
4765
|
-
...
|
|
4892
|
+
...dayGroups.map(
|
|
4893
|
+
(g) => h(
|
|
4894
|
+
React.Fragment,
|
|
4895
|
+
{ key: g.key || "op-day-unknown" },
|
|
4896
|
+
g.key ? h("div", { className: "dsvm-opday" }, g.kind === "today" ? lookup("op.day.today") : g.kind === "yesterday" ? lookup("op.day.yesterday") : g.key) : null,
|
|
4897
|
+
...g.records.map(row)
|
|
4898
|
+
)
|
|
4899
|
+
)
|
|
4766
4900
|
)
|
|
4767
4901
|
) : null,
|
|
4768
4902
|
finished.length ? h(
|