dsh-m 0.1.0 → 0.2.0
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/README.en.md +104 -0
- package/README.md +61 -23
- package/docs/DESIGN.md +172 -0
- package/lib/cli.js +134 -63
- package/lib/client.js +1254 -217
- package/lib/core/dsh-cli.js +118 -10
- package/lib/core/host-api.js +285 -0
- package/lib/core/httpx.js +93 -36
- package/lib/core/installed.js +69 -4
- package/lib/core/market.js +481 -104
- package/lib/core/npm-integrity.js +141 -0
- package/lib/core/progress.js +113 -0
- package/lib/core/registry-check.js +111 -0
- package/lib/core/registry-controller.js +321 -0
- package/lib/core/registry.js +634 -98
- package/lib/core/versions.js +80 -9
- package/lib/host.js +22 -161
- package/lib/tools.js +56 -41
- package/package.json +4 -2
- package/registry.json +95 -7
- package/DESIGN.md +0 -140
package/lib/client.js
CHANGED
|
@@ -2,46 +2,501 @@ window.__ModuleLoader__.load({
|
|
|
2
2
|
id: "dsh-m",
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
"use strict";
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
|
+
|
|
26
|
+
// src/client/market-state.js
|
|
27
|
+
var market_state_exports = {};
|
|
28
|
+
__export(market_state_exports, {
|
|
29
|
+
MARKET_PAGE_SIZE: () => MARKET_PAGE_SIZE,
|
|
30
|
+
normalizeMarketQuery: () => normalizeMarketQuery,
|
|
31
|
+
normalizeMarketResponse: () => normalizeMarketResponse,
|
|
32
|
+
registryNotice: () => registryNotice,
|
|
33
|
+
resetPageOnFilterChange: () => resetPageOnFilterChange
|
|
34
|
+
});
|
|
35
|
+
function toSafeInt(value, fallback, min, max) {
|
|
36
|
+
const n = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
37
|
+
if (min !== void 0 && n < min) return min;
|
|
38
|
+
if (max !== void 0 && n > max) return max;
|
|
39
|
+
return n;
|
|
40
|
+
}
|
|
41
|
+
function normalizeMarketQuery(input) {
|
|
42
|
+
const raw = input && typeof input === "object" ? input : {};
|
|
43
|
+
const query = typeof raw.query === "string" ? raw.query.trim() : "";
|
|
44
|
+
const category = typeof raw.category === "string" && CATEGORIES.includes(raw.category) ? raw.category : null;
|
|
45
|
+
const offset = toSafeInt(raw.offset, 0, 0);
|
|
46
|
+
const limit = toSafeInt(raw.limit, MARKET_PAGE_SIZE, 1, MARKET_PAGE_SIZE);
|
|
47
|
+
return { query, category, offset, limit };
|
|
48
|
+
}
|
|
49
|
+
function resetPageOnFilterChange(previous, next) {
|
|
50
|
+
const prev = previous && typeof previous === "object" ? previous : {};
|
|
51
|
+
const merged = { ...next };
|
|
52
|
+
if (prev.query !== next.query || prev.category !== next.category) {
|
|
53
|
+
merged.offset = 0;
|
|
54
|
+
}
|
|
55
|
+
return merged;
|
|
56
|
+
}
|
|
57
|
+
function normalizeMarketResponse(raw) {
|
|
58
|
+
const body = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
59
|
+
const items = Array.isArray(body.items) ? body.items.filter((it) => it && typeof it === "object") : [];
|
|
60
|
+
const total = toSafeInt(body.total, items.length, 0);
|
|
61
|
+
const offset = toSafeInt(body.offset, 0, 0);
|
|
62
|
+
const limit = toSafeInt(body.limit, MARKET_PAGE_SIZE, 1);
|
|
63
|
+
let categoryCounts = {};
|
|
64
|
+
if (body.categoryCounts && typeof body.categoryCounts === "object" && !Array.isArray(body.categoryCounts)) {
|
|
65
|
+
for (const [key, value] of Object.entries(body.categoryCounts)) {
|
|
66
|
+
if (typeof value === "number" && Number.isFinite(value)) categoryCounts[key] = value;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const rs = body.registryState && typeof body.registryState === "object" && !Array.isArray(body.registryState) ? body.registryState : {};
|
|
70
|
+
const registryState = {
|
|
71
|
+
...FALLBACK_REGISTRY_STATE,
|
|
72
|
+
...rs,
|
|
73
|
+
errors: Array.isArray(rs.errors) ? rs.errors.map(String) : []
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
items,
|
|
77
|
+
total,
|
|
78
|
+
offset,
|
|
79
|
+
limit,
|
|
80
|
+
categoryCounts,
|
|
81
|
+
registryState,
|
|
82
|
+
installedComplete: body.installedComplete === true,
|
|
83
|
+
latestComplete: body.latestComplete === true,
|
|
84
|
+
latestTimedOut: body.latestTimedOut === true
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function registryNotice(summary, total) {
|
|
88
|
+
const s = summary && typeof summary === "object" ? summary : {};
|
|
89
|
+
let key = "notice.default";
|
|
90
|
+
if (s.status === "unavailable") key = "notice.unavailable";
|
|
91
|
+
else if (s.stale || s.status === "stale") key = "notice.stale";
|
|
92
|
+
else if (!s.isDefault) key = "notice.custom";
|
|
93
|
+
return { key, count: typeof total === "number" && Number.isFinite(total) ? total : 0 };
|
|
94
|
+
}
|
|
95
|
+
var MARKET_PAGE_SIZE, CATEGORIES, FALLBACK_REGISTRY_STATE;
|
|
96
|
+
var init_market_state = __esm({
|
|
97
|
+
"src/client/market-state.js"() {
|
|
98
|
+
"use strict";
|
|
99
|
+
MARKET_PAGE_SIZE = 50;
|
|
100
|
+
CATEGORIES = ["market", "tools", "ui", "search", "media", "other"];
|
|
101
|
+
FALLBACK_REGISTRY_STATE = {
|
|
102
|
+
configuredAddress: "",
|
|
103
|
+
activeAddress: null,
|
|
104
|
+
source: "bundled",
|
|
105
|
+
status: "unavailable",
|
|
106
|
+
isDefault: true,
|
|
107
|
+
stale: false,
|
|
108
|
+
fetchedAt: null,
|
|
109
|
+
errors: [],
|
|
110
|
+
count: 0
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
});
|
|
5
114
|
|
|
6
115
|
// src/client/main.jsx
|
|
7
116
|
var React = require("react");
|
|
8
117
|
var rd = require("react-dom");
|
|
9
118
|
var h = React.createElement;
|
|
10
|
-
var { useState, useEffect, useCallback, useMemo } = React;
|
|
119
|
+
var { useState, useEffect, useCallback, useMemo, useRef } = React;
|
|
11
120
|
var PLUGIN_ID = "dsh-m";
|
|
12
121
|
var API = "/dshm";
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
122
|
+
var { MARKET_PAGE_SIZE: MARKET_PAGE_SIZE2, normalizeMarketQuery: normalizeMarketQuery2, resetPageOnFilterChange: resetPageOnFilterChange2, normalizeMarketResponse: normalizeMarketResponse2, registryNotice: registryNotice2 } = (init_market_state(), __toCommonJS(market_state_exports));
|
|
123
|
+
var ZH = {
|
|
124
|
+
"market.title": "\u63D2\u4EF6\u5E02\u573A",
|
|
125
|
+
"tab.market": "\u5E02\u573A",
|
|
126
|
+
"tab.installed": "\u5DF2\u88C5",
|
|
127
|
+
"tab.settings": "\u8BBE\u7F6E",
|
|
128
|
+
"cat.all": "\u5168\u90E8",
|
|
129
|
+
"cat.market": "\u5E02\u573A",
|
|
130
|
+
"cat.tools": "\u5DE5\u5177",
|
|
131
|
+
"cat.ui": "\u754C\u9762",
|
|
132
|
+
"cat.search": "\u641C\u7D22",
|
|
133
|
+
"cat.media": "\u591A\u5A92\u4F53",
|
|
134
|
+
"cat.other": "\u5176\u4ED6",
|
|
135
|
+
"search.ph": "\u641C\u7D22\u540D\u79F0 / \u63CF\u8FF0 / \u6807\u7B7E\u2026",
|
|
136
|
+
"common.refresh": "\u5237\u65B0",
|
|
137
|
+
"common.close": "\u5173\u95ED",
|
|
138
|
+
"common.later": "\u7A0D\u540E",
|
|
139
|
+
"common.ok": "\u77E5\u9053\u4E86",
|
|
140
|
+
"common.none": "\u2014",
|
|
141
|
+
"market.loading": "\u52A0\u8F7D\u6536\u5F55\u6E05\u5355\u4E2D\u2026 ",
|
|
142
|
+
"market.empty": "\u6CA1\u6709\u5339\u914D\u7684\u6536\u5F55\u6761\u76EE",
|
|
143
|
+
"installed.loading": "\u8BFB\u53D6 web profile \u4E2D\u2026 ",
|
|
144
|
+
"installed.empty": "web profile \u5C1A\u672A\u5B89\u88C5\u4EFB\u4F55 dsh \u63D2\u4EF6",
|
|
145
|
+
"installed.none": "\u672A\u5B89\u88C5",
|
|
146
|
+
"installed.others": "\u53E6\u6709 {n} \u4E2A\u975E dsh \u4F9D\u8D56\uFF08\u672A\u8BC6\u522B\u4E3A\u63D2\u4EF6\uFF09\uFF0C\u5DF2\u9ED8\u8BA4\u6298\u53E0\u3002",
|
|
147
|
+
"badge.installed": "\u5DF2\u5B89\u88C5",
|
|
148
|
+
"badge.update": "\u53EF\u5347\u7EA7",
|
|
149
|
+
"badge.market": "\u5E02\u573A\u5B89\u88C5",
|
|
150
|
+
"badge.nonmarket": "\u975E\u5E02\u573A\u5B89\u88C5",
|
|
151
|
+
"badge.custom": "\u81EA\u5B9A\u4E49",
|
|
152
|
+
"action.install": "\u5B89\u88C5",
|
|
153
|
+
"action.upgrade": "\u5347\u7EA7",
|
|
154
|
+
"action.uninstall": "\u5378\u8F7D",
|
|
155
|
+
"confirm.uninstall": "\u786E\u8BA4\u5378\u8F7D\uFF1F",
|
|
156
|
+
"confirm.unlink": "\u786E\u8BA4\u79FB\u9664\u672C\u5730\u5F15\u7528\uFF1F",
|
|
157
|
+
"confirm.core": "\u26A0\uFE0F \u786E\u8BA4\u5378\u8F7D\u6838\u5FC3\u5305\uFF1F",
|
|
158
|
+
"detail.id": "\u6536\u5F55 id",
|
|
159
|
+
"detail.source": "\u6765\u6E90",
|
|
160
|
+
"detail.latest": "\u6700\u65B0",
|
|
161
|
+
"detail.installed": "\u5DF2\u88C5",
|
|
162
|
+
"detail.tags": "\u6807\u7B7E",
|
|
163
|
+
"detail.pkg": "\u5305\u540D",
|
|
164
|
+
"detail.spec": "\u5B89\u88C5 spec",
|
|
165
|
+
"detail.listed": "\u6536\u5F55",
|
|
166
|
+
"detail.listed.no": "\u4E0D\u5728\u6536\u5F55\u6E05\u5355\u4E2D",
|
|
167
|
+
"detail.path": "\u8DEF\u5F84",
|
|
168
|
+
"detail.note": "\u6CE8\u610F",
|
|
169
|
+
"detail.links": "\u8BE6\u60C5",
|
|
170
|
+
"link.home": "\u5B98\u7F51",
|
|
171
|
+
"manage.hint": "\u5DF2\u5B89\u88C5\uFF0C\u53EF\u5728\u300C\u5DF2\u88C5\u300D\u9875\u7BA1\u7406",
|
|
172
|
+
"version.failed": "\u7248\u672C\u67E5\u8BE2\u5931\u8D25",
|
|
173
|
+
"src.npm": "npm",
|
|
174
|
+
"src.github": "github",
|
|
175
|
+
"src.link": "\u672C\u5730 link",
|
|
176
|
+
"src.file": "\u672C\u5730 file",
|
|
177
|
+
"src.unknown": "\u672A\u77E5",
|
|
178
|
+
"sub.latest": "\u6700\u65B0 v{v}",
|
|
179
|
+
"sub.head": "HEAD {sha}",
|
|
180
|
+
"sub.installed": "\u5DF2\u88C5 v{v}",
|
|
181
|
+
"settings.registry": "\u6536\u5F55\u6E05\u5355\uFF08registry\uFF09",
|
|
182
|
+
"settings.source": "\u5F53\u524D\u6765\u6E90",
|
|
183
|
+
"settings.updated": "\u66F4\u65B0\u65F6\u95F4",
|
|
184
|
+
"settings.count": "\u6761\u76EE\u6570",
|
|
185
|
+
"settings.count.v": "{n} \u6761",
|
|
186
|
+
"settings.policy": "\u7F13\u5B58\u7B56\u7565",
|
|
187
|
+
"settings.policy.v": "TTL 60 \u5206\u949F\uFF1B\u8BBE\u7F6E registryUrl \u53EF\u8986\u76D6\u6E90",
|
|
188
|
+
"settings.remotehint": "\u8FDC\u7AEF\u63D0\u793A",
|
|
189
|
+
"settings.force": "\u5F3A\u5236\u5237\u65B0",
|
|
190
|
+
"settings.self": "dsh-m \u81EA\u8EAB",
|
|
191
|
+
"settings.current": "\u5F53\u524D\u7248\u672C",
|
|
192
|
+
"settings.npmlatest": "npm \u6700\u65B0",
|
|
193
|
+
"settings.lookupfailed": "\u67E5\u8BE2\u5931\u8D25\uFF1A{err}",
|
|
194
|
+
"settings.upgradeself": "\u5347\u7EA7 dsh-m",
|
|
195
|
+
"settings.upgradehint": "\u5347\u7EA7\u540E\u540C\u6837\u9700\u8981\u91CD\u542F\u751F\u6548",
|
|
196
|
+
"settings.about": "\u5173\u4E8E",
|
|
197
|
+
"settings.about.text": "\u4E2A\u4EBA DSH \u63D2\u4EF6\u5E02\u573A\uFF1A\u6536\u5F55\u3001\u5B89\u88C5\u3001\u5378\u8F7D\u3001\u5347\u7EA7\u5168\u90E8\u672C\u673A\u5B8C\u6210\uFF1B\u6536\u5F55\u6E05\u5355\u652F\u6301\u81EA\u5B9A\u4E49\u8986\u76D6\uFF0C\u5E94\u7528\u5373\u65F6\u751F\u6548\u3002",
|
|
198
|
+
"src.override": "\u81EA\u5B9A\u4E49\u6E90",
|
|
199
|
+
"src.jsdelivr": "jsDelivr\uFF08@main\uFF09",
|
|
200
|
+
"src.raw": "raw.githubusercontent\uFF08@main\uFF09",
|
|
201
|
+
"src.cache": "\u672C\u5730\u7F13\u5B58",
|
|
202
|
+
"src.bundled": "\u5305\u5185\u5FEB\u7167\uFF08\u515C\u5E95\uFF09",
|
|
203
|
+
"src.default.raw": "raw.githubusercontent\uFF08@main\uFF09",
|
|
204
|
+
"src.default.jsdelivr": "jsDelivr\uFF08@main\uFF09",
|
|
205
|
+
"src.default.cache": "\u9ED8\u8BA4\u6E05\u5355\u7F13\u5B58",
|
|
206
|
+
"src.custom.url": "\u81EA\u5B9A\u4E49 URL \u6E90",
|
|
207
|
+
"src.custom.file": "\u672C\u5730\u6587\u4EF6\u6E90",
|
|
208
|
+
"src.custom.cache": "\u81EA\u5B9A\u4E49\u6E90\uFF08\u7F13\u5B58\uFF09",
|
|
209
|
+
"src.custom.unavailable": "\u81EA\u5B9A\u4E49\u6E90\uFF08\u4E0D\u53EF\u7528\uFF09",
|
|
210
|
+
"settings.address": "Registry \u5730\u5740",
|
|
211
|
+
"settings.address.hint": "\u7A7A = \u5B98\u65B9\u9ED8\u8BA4\u6E05\u5355\uFF1B\u652F\u6301 HTTPS URL \u6216\u672C\u673A\u7EDD\u5BF9\u8DEF\u5F84 / file://\u3002\u6574\u4F53\u8986\u76D6\u9ED8\u8BA4\u6E05\u5355\uFF0C\u4E0D\u505A\u5408\u5E76\u3002",
|
|
212
|
+
"settings.address.ph": "https://example.com/registry.json \u6216 /path/to/registry.json",
|
|
213
|
+
"settings.configured": "\u914D\u7F6E\u5730\u5740",
|
|
214
|
+
"settings.activecfg": "\u5F53\u524D\u751F\u6548\u914D\u7F6E",
|
|
215
|
+
"settings.effective": "\u751F\u6548\u6765\u6E90",
|
|
216
|
+
"settings.status.label": "\u914D\u7F6E\u72B6\u6001",
|
|
217
|
+
"settings.status.loading": "\u52A0\u8F7D\u4E2D",
|
|
218
|
+
"settings.status.ready": "\u5DF2\u751F\u6548",
|
|
219
|
+
"settings.status.pending": "\u5F85\u5199\u5165\uFF08\u6821\u9A8C\u5DF2\u901A\u8FC7\uFF09",
|
|
220
|
+
"settings.status.rejected": "\u5DF2\u62D2\u7EDD\uFF08\u4FDD\u6301\u65E7\u914D\u7F6E\uFF09",
|
|
221
|
+
"settings.status.unavailable": "\u4E0D\u53EF\u7528",
|
|
222
|
+
"settings.apply": "\u6821\u9A8C\u5E76\u5E94\u7528",
|
|
223
|
+
"settings.apply.applying": "\u6821\u9A8C\u4E2D\u2026",
|
|
224
|
+
"settings.apply.ok": "Registry \u5730\u5740\u5DF2\u751F\u6548\uFF08\u65E0\u9700\u91CD\u542F\uFF09",
|
|
225
|
+
"settings.apply.failed": "\u5E94\u7528\u5931\u8D25\uFF1A{err}",
|
|
226
|
+
"settings.reset": "\u6062\u590D\u9ED8\u8BA4",
|
|
227
|
+
"settings.reset.ok": "\u5DF2\u6062\u590D\u9ED8\u8BA4\u6536\u5F55\u6E05\u5355",
|
|
228
|
+
"settings.download": "\u4E0B\u8F7D\u9ED8\u8BA4 registry.json",
|
|
229
|
+
"settings.download.downloading": "\u4E0B\u8F7D\u4E2D\u2026",
|
|
230
|
+
"settings.download.ok": "\u9ED8\u8BA4\u6E05\u5355\u5DF2\u4E0B\u8F7D\uFF08\u5F53\u524D\u914D\u7F6E\u4E0D\u53D8\uFF09",
|
|
231
|
+
"settings.download.failed": "\u4E0B\u8F7D\u5931\u8D25\uFF1A{err}",
|
|
232
|
+
"settings.diagnose": "\u68C0\u67E5\u6761\u76EE\u53EF\u8FBE\u6027",
|
|
233
|
+
"settings.diagnose.running": "\u8BCA\u65AD\u4E2D\u2026",
|
|
234
|
+
"settings.diagnose.failed": "\u8BCA\u65AD\u5931\u8D25\uFF1A{err}",
|
|
235
|
+
"settings.diagnose.result": "\u63A2\u6D4B {checked} \u9879 \xB7 \u901A\u8FC7 {passed} \xB7 \u5931\u8D25 {failed}{trunc}",
|
|
236
|
+
"settings.diagnose.truncated": "\uFF08\u4EC5\u663E\u793A\u524D 100 \u6761\u95EE\u9898\uFF09",
|
|
237
|
+
"settings.diagnose.none": "\u672A\u53D1\u73B0\u95EE\u9898",
|
|
238
|
+
"settings.trust.hint": "\u26A0\uFE0F \u81EA\u5B9A\u4E49\u6536\u5F55\u6E05\u5355\u672A\u7ECF\u5B98\u65B9 CI \u6821\u9A8C\uFF0C\u6761\u76EE\u6765\u6E90\u8BF7\u786E\u8BA4\u53EF\u4FE1\u540E\u518D\u5B89\u88C5\u3002",
|
|
239
|
+
"settings.cache.hint": "\u5207\u6362\u540E\u65E7\u81EA\u5B9A\u4E49\u6E90\u7F13\u5B58\u5C06\u88AB\u6E05\u7406\uFF08\u9ED8\u8BA4\u7F13\u5B58\u4FDD\u7559\uFF09\uFF1B\u81EA\u5B9A\u4E49\u6E90\u5931\u8D25\u65F6\u4FDD\u7559\u5176\u6700\u8FD1\u4E00\u6B21\u6210\u529F\u7F13\u5B58\u3002",
|
|
240
|
+
"settings.warnings": "\u7EF4\u62A4\u63D0\u793A",
|
|
241
|
+
"notice.default": "\u5B98\u65B9\u9ED8\u8BA4\u6536\u5F55\u6E05\u5355 \xB7 \u5171 {count} \u6761",
|
|
242
|
+
"notice.custom": "\u81EA\u5B9A\u4E49\u6536\u5F55\u6E05\u5355 \xB7 \u5171 {count} \u6761",
|
|
243
|
+
"notice.stale": "\u6765\u6E90\u4E3A\u672C\u5730\u7F13\u5B58\uFF08\u5171 {count} \u6761\uFF09\uFF0C\u53EF\u7528\u300C\u5F3A\u5236\u5237\u65B0\u300D\u66F4\u65B0",
|
|
244
|
+
"notice.unavailable": "\u6536\u5F55\u6E05\u5355\u4E0D\u53EF\u7528 \xB7 \u8BF7\u5230\u8BBE\u7F6E\u9875\u68C0\u67E5\u5730\u5740",
|
|
245
|
+
"market.page.prev": "\u4E0A\u4E00\u9875",
|
|
246
|
+
"market.page.next": "\u4E0B\u4E00\u9875",
|
|
247
|
+
"market.page.info": "\u7B2C {page} / {pages} \u9875 \xB7 \u5171 {total} \u6761",
|
|
248
|
+
"market.perf": "\u6536\u5F55\u8D85\u8FC7 200 \u6761\uFF1A\u4EC5\u67E5\u8BE2\u5F53\u524D\u9875\u7684\u6700\u65B0\u7248\u672C\uFF08\u6BCF\u9875 50 \u6761\uFF09\uFF0C\u5982\u9700\u5168\u90E8\u8BF7\u7528\u641C\u7D22/\u5206\u7C7B\u8FC7\u6EE4",
|
|
249
|
+
"notice.toolview.err": "\u6536\u5F55\u6E05\u5355\u6682\u4E0D\u53EF\u7528",
|
|
250
|
+
"self.upgraded": "dsh-m \u5DF2\u66F4\u65B0\u5230 v{v}\uFF0C\u91CD\u542F\u540E\u751F\u6548",
|
|
251
|
+
"self.failed": "\u81EA\u66F4\u65B0\u5931\u8D25\uFF1A{err}",
|
|
252
|
+
"registry.refreshed": "\u6536\u5F55\u6E05\u5355\u5DF2\u5F3A\u5236\u5237\u65B0",
|
|
253
|
+
"notify.installed": "\u5DF2\u5B89\u88C5 {pkg}{version}",
|
|
254
|
+
"notify.allowbuilds": "\uFF08\u6CE8\u610F\uFF1A\u8BE5\u63D2\u4EF6\u6267\u884C\u4E86\u6784\u5EFA\u811A\u672C\uFF0C\u5DF2\u6309\u7B56\u7565\u653E\u884C\uFF09",
|
|
255
|
+
"notify.uninstalled": "\u5DF2\u5378\u8F7D {pkg}",
|
|
256
|
+
"notify.livedisabled": "\uFF08\u5DF2\u5148\u4E0B\u7EBF\u8FD0\u884C\u4E2D\u7684\u754C\u9762\uFF09",
|
|
257
|
+
"notify.leftovers": "\uFF1B\u68C0\u6D4B\u5230\u7591\u4F3C\u6B8B\u7559\u6570\u636E\uFF1A{paths}",
|
|
258
|
+
"notify.upgraded": "\u5DF2\u5347\u7EA7 {pkg}\uFF08{from} \u2192 {to}\uFF09",
|
|
259
|
+
"notify.upgradehint": "\uFF08\u6CE8\u610F\uFF1A\u8BE5\u63D2\u4EF6\u6267\u884C\u4E86\u6784\u5EFA\u811A\u672C\uFF09",
|
|
260
|
+
"failed.install": "\u5B89\u88C5\u5931\u8D25\uFF1A{err}",
|
|
261
|
+
"failed.uninstall": "\u5378\u8F7D\u5931\u8D25\uFF1A{err}",
|
|
262
|
+
"failed.upgrade": "\u5347\u7EA7\u5931\u8D25\uFF1A{err}",
|
|
263
|
+
"failed.selfupdate": "\u81EA\u66F4\u65B0\u5931\u8D25\uFF1A{err}",
|
|
264
|
+
"failed.load": "\u52A0\u8F7D\u5931\u8D25\uFF1A{err}",
|
|
265
|
+
"failed.read": "\u8BFB\u53D6\u5931\u8D25\uFF1A{err}",
|
|
266
|
+
"failed.open": "\u6253\u5F00\u5E02\u573A\u9762\u677F\u5931\u8D25:",
|
|
267
|
+
"banner.done": "\u53D8\u66F4\u5B8C\u6210\uFF0C\u9700\u8981\u91CD\u542F DSH Web \u540E\u751F\u6548\u3002",
|
|
268
|
+
"restart.doing": "\u6B63\u5728\u8BF7\u6C42\u91CD\u542F\u2026",
|
|
269
|
+
"restart.waiting": "\u5DF2\u8BF7\u6C42\u91CD\u542F\uFF0C\u7B49\u5F85 DSH Web \u6062\u590D\u2026",
|
|
270
|
+
"restart.now": "\u26A1 \u4E00\u952E\u91CD\u542F",
|
|
271
|
+
"restart.failed": "\u91CD\u542F\u5931\u8D25\uFF1A{err}",
|
|
272
|
+
"restart.timeout": "\u91CD\u542F\u8D85\u65F6\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5 dsh web \u670D\u52A1\u72B6\u6001",
|
|
273
|
+
"restart.hint.done": "\u5DF2\u8BF7\u6C42\u91CD\u542F DSH web\uFF08via {via}\uFF09\u3002\u670D\u52A1\u51E0\u79D2\u5185\u6062\u590D\uFF0C\u4E4B\u540E\u8BA9\u7528\u6237\u5237\u65B0\u9875\u9762\u5373\u53EF\u3002",
|
|
274
|
+
"phase.resolving": "\u89E3\u6790\u4F9D\u8D56",
|
|
275
|
+
"phase.downloading": "\u4E0B\u8F7D",
|
|
276
|
+
"phase.linking": "\u94FE\u63A5\u5B89\u88C5",
|
|
277
|
+
"phase.building": "\u6784\u5EFA\u811A\u672C",
|
|
278
|
+
"phase.ready": "\u51C6\u5907\u4E2D",
|
|
279
|
+
"readme.show": "\u{1F4D6} README",
|
|
280
|
+
"readme.hide": "\u6536\u8D77 README",
|
|
281
|
+
"readme.loading": "\u52A0\u8F7D README\u2026 ",
|
|
282
|
+
"readme.none": "\uFF08\u8BE5\u63D2\u4EF6\u6CA1\u6709 README\uFF09",
|
|
283
|
+
"readme.truncated": "\u2026\uFF08\u8D85\u8FC7 64KB \u5DF2\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9\u89C1\u63D2\u4EF6\u76EE\u5F55\uFF09",
|
|
284
|
+
"warn.unlink": "\u5378\u8F7D\u53EA\u79FB\u9664 profile \u5BF9\u672C\u5730\u76EE\u5F55\u7684\u5F15\u7528\uFF08{path}\uFF09\uFF0C\u4E0D\u4F1A\u5220\u9664\u76EE\u5F55\u672C\u8EAB\u3002",
|
|
285
|
+
"warn.core": "\u8FD9\u662F file: \u5B89\u88C5\u7684\u6838\u5FC3/\u5F52\u6863\u5305\uFF0C\u5378\u8F7D\u53EF\u80FD\u5F71\u54CD DSH \u529F\u80FD\uFF0C\u4E14\u9700\u8981\u624B\u52A8\u6062\u590D\u3002",
|
|
286
|
+
"profile.hint": "web profile\uFF1A{path}",
|
|
287
|
+
"title.panel": "\u63D2\u4EF6\u5E02\u573A",
|
|
288
|
+
"title.full": "DeepSeek Harness \u63D2\u4EF6\u5E02\u573A"
|
|
289
|
+
};
|
|
290
|
+
var EN = {
|
|
291
|
+
"market.title": "Plugin Marketplace",
|
|
292
|
+
"title.panel": "Plugin Marketplace",
|
|
293
|
+
"title.full": "DeepSeek Harness Plugin Marketplace",
|
|
294
|
+
"tab.market": "Market",
|
|
295
|
+
"tab.installed": "Installed",
|
|
296
|
+
"tab.settings": "Settings",
|
|
297
|
+
"cat.all": "All",
|
|
298
|
+
"cat.market": "Market",
|
|
299
|
+
"cat.tools": "Tools",
|
|
300
|
+
"cat.ui": "UI",
|
|
301
|
+
"cat.search": "Search",
|
|
302
|
+
"cat.media": "Media",
|
|
303
|
+
"cat.other": "Other",
|
|
304
|
+
"search.ph": "Search name, description, tags\u2026",
|
|
305
|
+
"common.refresh": "Refresh",
|
|
306
|
+
"common.close": "Close",
|
|
307
|
+
"common.later": "Later",
|
|
308
|
+
"common.ok": "OK",
|
|
309
|
+
"common.none": "\u2014",
|
|
310
|
+
"market.loading": "Loading listings\u2026 ",
|
|
311
|
+
"market.empty": "No matching listings",
|
|
312
|
+
"installed.loading": "Reading web profile\u2026 ",
|
|
313
|
+
"installed.empty": "No DSH plugins installed in this web profile",
|
|
314
|
+
"installed.none": "Not installed",
|
|
315
|
+
"installed.others": "{n} non-DSH dependencies (not recognized as plugins) are collapsed.",
|
|
316
|
+
"badge.installed": "Installed",
|
|
317
|
+
"badge.update": "Update",
|
|
318
|
+
"badge.market": "Via market",
|
|
319
|
+
"badge.nonmarket": "Non-market",
|
|
320
|
+
"badge.custom": "Custom",
|
|
321
|
+
"action.install": "Install",
|
|
322
|
+
"action.upgrade": "Upgrade",
|
|
323
|
+
"action.uninstall": "Uninstall",
|
|
324
|
+
"confirm.uninstall": "Confirm uninstall?",
|
|
325
|
+
"confirm.unlink": "Confirm remove link?",
|
|
326
|
+
"confirm.core": "\u26A0\uFE0F Remove core package?",
|
|
327
|
+
"detail.id": "Listing id",
|
|
328
|
+
"detail.source": "Source",
|
|
329
|
+
"detail.latest": "Latest",
|
|
330
|
+
"detail.installed": "Installed",
|
|
331
|
+
"detail.tags": "Tags",
|
|
332
|
+
"detail.pkg": "Package",
|
|
333
|
+
"detail.spec": "Spec",
|
|
334
|
+
"detail.listed": "Listed",
|
|
335
|
+
"detail.listed.no": "Not in the registry",
|
|
336
|
+
"detail.path": "Path",
|
|
337
|
+
"detail.note": "Note",
|
|
338
|
+
"detail.links": "Details",
|
|
339
|
+
"link.home": "Homepage",
|
|
340
|
+
"manage.hint": "Installed \u2014 manage it on the Installed tab",
|
|
341
|
+
"version.failed": "version lookup failed",
|
|
342
|
+
"src.npm": "npm",
|
|
343
|
+
"src.github": "github",
|
|
344
|
+
"src.link": "local link",
|
|
345
|
+
"src.file": "local file",
|
|
346
|
+
"src.unknown": "unknown",
|
|
347
|
+
"sub.latest": "Latest v{v}",
|
|
348
|
+
"sub.head": "HEAD {sha}",
|
|
349
|
+
"sub.installed": "Installed v{v}",
|
|
350
|
+
"settings.registry": "Registry",
|
|
351
|
+
"settings.source": "Source",
|
|
352
|
+
"settings.updated": "Updated",
|
|
353
|
+
"settings.count": "Listings",
|
|
354
|
+
"settings.count.v": "{n} listings",
|
|
355
|
+
"settings.policy": "Caching",
|
|
356
|
+
"settings.policy.v": "60 min TTL; override via registryUrl",
|
|
357
|
+
"settings.remotehint": "Remote notice",
|
|
358
|
+
"settings.force": "Force refresh",
|
|
359
|
+
"settings.self": "dsh-m itself",
|
|
360
|
+
"settings.current": "Current version",
|
|
361
|
+
"settings.npmlatest": "npm latest",
|
|
362
|
+
"settings.lookupfailed": "lookup failed: {err}",
|
|
363
|
+
"settings.upgradeself": "Upgrade dsh-m",
|
|
364
|
+
"settings.upgradehint": "A restart is required after upgrading",
|
|
365
|
+
"settings.about": "About",
|
|
366
|
+
"settings.about.text": "A personal DSH plugin marketplace \u2014 browse, install, uninstall and upgrade, all local; registry overrides apply live.",
|
|
367
|
+
"src.override": "Custom source",
|
|
368
|
+
"src.jsdelivr": "jsDelivr (@main)",
|
|
369
|
+
"src.raw": "raw.githubusercontent (@main)",
|
|
370
|
+
"src.cache": "Local cache",
|
|
371
|
+
"src.bundled": "Bundled snapshot (fallback)",
|
|
372
|
+
"src.default.raw": "raw.githubusercontent (@main)",
|
|
373
|
+
"src.default.jsdelivr": "jsDelivr (@main)",
|
|
374
|
+
"src.default.cache": "Default registry cache",
|
|
375
|
+
"src.custom.url": "Custom URL source",
|
|
376
|
+
"src.custom.file": "Local file source",
|
|
377
|
+
"src.custom.cache": "Custom source (cache)",
|
|
378
|
+
"src.custom.unavailable": "Custom source (unavailable)",
|
|
379
|
+
"settings.address": "Registry address",
|
|
380
|
+
"settings.address.hint": "Empty = official default registry; accepts an HTTPS URL or a local absolute path / file://. Replaces (not merges) the default registry. Live effect.",
|
|
381
|
+
"settings.address.ph": "https://example.com/registry.json or /path/to/registry.json",
|
|
382
|
+
"settings.configured": "Configured address",
|
|
383
|
+
"settings.activecfg": "Active config",
|
|
384
|
+
"settings.effective": "Effective source",
|
|
385
|
+
"settings.status.label": "Config status",
|
|
386
|
+
"settings.status.loading": "Loading",
|
|
387
|
+
"settings.status.ready": "Applied",
|
|
388
|
+
"settings.status.pending": "Pending write (validated)",
|
|
389
|
+
"settings.status.rejected": "Rejected (previous config kept)",
|
|
390
|
+
"settings.status.unavailable": "Unavailable",
|
|
391
|
+
"settings.apply": "Validate & apply",
|
|
392
|
+
"settings.apply.applying": "Validating\u2026",
|
|
393
|
+
"settings.apply.ok": "Registry address applied (no restart needed)",
|
|
394
|
+
"settings.apply.failed": "Apply failed: {err}",
|
|
395
|
+
"settings.reset": "Restore default",
|
|
396
|
+
"settings.reset.ok": "Restored to the default registry",
|
|
397
|
+
"settings.download": "Download default registry.json",
|
|
398
|
+
"settings.download.downloading": "Downloading\u2026",
|
|
399
|
+
"settings.download.ok": "Default registry downloaded (current config unchanged)",
|
|
400
|
+
"settings.download.failed": "Download failed: {err}",
|
|
401
|
+
"settings.diagnose": "Check entries reachability",
|
|
402
|
+
"settings.diagnose.running": "Checking\u2026",
|
|
403
|
+
"settings.diagnose.failed": "Diagnose failed: {err}",
|
|
404
|
+
"settings.diagnose.result": "Probes {checked} \xB7 passed {passed} \xB7 failed {failed}{trunc}",
|
|
405
|
+
"settings.diagnose.truncated": " (showing first 100 issues)",
|
|
406
|
+
"settings.diagnose.none": "No issues found",
|
|
407
|
+
"settings.trust.hint": "\u26A0\uFE0F Custom registries are not validated by official CI. Only install entries from sources you trust.",
|
|
408
|
+
"settings.cache.hint": "Old custom-source caches are cleaned after switching (the default cache is kept); a failed custom source keeps its last good cache.",
|
|
409
|
+
"settings.warnings": "Maintenance notice",
|
|
410
|
+
"notice.default": "Official default registry \xB7 {count} listings",
|
|
411
|
+
"notice.custom": "Custom registry \xB7 {count} listings",
|
|
412
|
+
"notice.stale": "Served from local cache ({count} listings) \u2014 force refresh to update",
|
|
413
|
+
"notice.unavailable": "Registry unavailable \xB7 check the address in Settings",
|
|
414
|
+
"market.page.prev": "Previous",
|
|
415
|
+
"market.page.next": "Next",
|
|
416
|
+
"market.page.info": "Page {page} / {pages} \xB7 {total} listings",
|
|
417
|
+
"market.perf": "200+ listings: latest versions are queried for the current page only (50 per page); use search/category filters",
|
|
418
|
+
"notice.toolview.err": "Registry temporarily unavailable",
|
|
419
|
+
"self.upgraded": "dsh-m updated to v{v} \u2014 restart to take effect",
|
|
420
|
+
"self.failed": "Self-update failed: {err}",
|
|
421
|
+
"registry.refreshed": "Registry force-refreshed",
|
|
422
|
+
"notify.installed": "Installed {pkg}{version}",
|
|
423
|
+
"notify.allowbuilds": " (note: this plugin ran build scripts, allowed by policy)",
|
|
424
|
+
"notify.uninstalled": "Uninstalled {pkg}",
|
|
425
|
+
"notify.livedisabled": " (live UI disabled first)",
|
|
426
|
+
"notify.leftovers": "; possible leftover data: {paths}",
|
|
427
|
+
"notify.upgraded": "Upgraded {pkg} ({from} \u2192 {to})",
|
|
428
|
+
"notify.upgradehint": " (note: this plugin ran build scripts)",
|
|
429
|
+
"failed.install": "Install failed: {err}",
|
|
430
|
+
"failed.uninstall": "Uninstall failed: {err}",
|
|
431
|
+
"failed.upgrade": "Upgrade failed: {err}",
|
|
432
|
+
"failed.selfupdate": "Self-update failed: {err}",
|
|
433
|
+
"failed.load": "Load failed: {err}",
|
|
434
|
+
"failed.read": "Read failed: {err}",
|
|
435
|
+
"failed.open": "Failed to open the marketplace panel:",
|
|
436
|
+
"banner.done": "Changes applied. Restart DSH Web to take effect.",
|
|
437
|
+
"restart.doing": "Requesting restart\u2026",
|
|
438
|
+
"restart.waiting": "Restart requested, waiting for DSH Web\u2026",
|
|
439
|
+
"restart.now": "\u26A1 Restart",
|
|
440
|
+
"restart.failed": "Restart failed: {err}",
|
|
441
|
+
"restart.timeout": "Restart timed out \u2014 check the dsh web service manually",
|
|
442
|
+
"restart.hint.done": "Restart requested (via {via}). The service will be back in seconds; ask the user to refresh afterwards.",
|
|
443
|
+
"phase.resolving": "Resolving",
|
|
444
|
+
"phase.downloading": "Downloading",
|
|
445
|
+
"phase.linking": "Linking",
|
|
446
|
+
"phase.building": "Building",
|
|
447
|
+
"phase.ready": "Preparing",
|
|
448
|
+
"readme.show": "\u{1F4D6} README",
|
|
449
|
+
"readme.hide": "Hide README",
|
|
450
|
+
"readme.loading": "Loading README\u2026 ",
|
|
451
|
+
"readme.none": "(No README)",
|
|
452
|
+
"readme.truncated": "\u2026(truncated at 64KB \u2014 see the plugin directory for full content)",
|
|
453
|
+
"warn.unlink": "Uninstalling only removes the profile's reference to the local directory ({path}); the directory itself is kept.",
|
|
454
|
+
"warn.core": "This is a core/archive package installed via file:. Uninstalling may affect DSH features and requires manual restore.",
|
|
455
|
+
"profile.hint": "web profile: {path}",
|
|
456
|
+
"title.panel": "Plugin Marketplace",
|
|
457
|
+
"title.full": "DeepSeek Harness Plugin Marketplace"
|
|
458
|
+
};
|
|
459
|
+
function browserLang() {
|
|
460
|
+
const lang = typeof document !== "undefined" && document.documentElement.lang || typeof navigator !== "undefined" && navigator.language || "zh";
|
|
461
|
+
return /^en\b/i.test(String(lang)) ? "en" : "zh";
|
|
462
|
+
}
|
|
463
|
+
function interpolate(tpl, params) {
|
|
464
|
+
if (!params) return String(tpl);
|
|
465
|
+
return String(tpl).replace(/\{(\w+)\}/g, (_, k) => params[k] != null ? String(params[k]) : `{${k}}`);
|
|
466
|
+
}
|
|
467
|
+
function lookup(key, params) {
|
|
468
|
+
const dict = browserLang() === "en" ? EN : ZH;
|
|
469
|
+
return interpolate(dict[key] ?? ZH[key] ?? key, params);
|
|
470
|
+
}
|
|
471
|
+
var CATEGORIES2 = ["market", "tools", "ui", "search", "media", "other"];
|
|
21
472
|
var CSS = `
|
|
22
473
|
.dshm-overlay{position:fixed;inset:0;z-index:2147483000;background:var(--dsw-alias-bg-mask-3,rgba(15,23,42,.48));display:flex;align-items:center;justify-content:center;padding:24px 16px;box-sizing:border-box}
|
|
23
|
-
.dshm-panel{width:min(920px,100%);height:min(680px,86vh);display:flex;flex-direction:column;background:var(--dsw-alias-bg-layer-3,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(2,6,23,.25);overflow:hidden;font-family:inherit;color:var(--dsw-alias-label-primary,inherit)}
|
|
474
|
+
.dshm-panel{width:min(920px,100%);height:min(680px,86vh);display:flex;flex-direction:column;background:var(--dsw-alias-bg-overlay,var(--dsw-alias-bg-layer-3,#fff));border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(2,6,23,.25);overflow:hidden;font-family:inherit;color:var(--dsw-alias-label-primary,inherit)}
|
|
24
475
|
.dshm-head{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
|
|
25
476
|
.dshm-title{font-weight:700;font-size:15px;margin-right:6px}
|
|
26
|
-
.dshm-
|
|
27
|
-
.dshm-
|
|
28
|
-
.dshm-
|
|
477
|
+
.dshm-seg{display:inline-flex;align-items:center;gap:2px;padding:2px;border:1px solid var(--dsw-alias-border-l2,#e2e4e8);border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f5f6f8)}
|
|
478
|
+
.dshm-seg button{appearance:none;border:0;background:transparent;height:28px;padding:0 14px;border-radius:7px;font:inherit;font-size:12px;color:var(--dsw-alias-label-tertiary,#7b8088);cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:background .15s,color .15s,box-shadow .15s}
|
|
479
|
+
.dshm-seg button:hover{color:var(--dsw-alias-label-secondary,#4b5058)}
|
|
480
|
+
.dshm-seg button.on{background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-primary,#17191c);font-weight:600;box-shadow:var(--dsw-shadow-lv1,0 2px 8px rgb(20 24 32 / 8%))}
|
|
481
|
+
.dshm-seg .dshm-count{font-size:11px;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-caption,#9ca3af);margin:0}
|
|
482
|
+
.dshm-seg button.on .dshm-count{color:var(--dsw-alias-state-business-primary,#4d6bfe)}
|
|
29
483
|
.dshm-spacer{flex:1}
|
|
30
484
|
.dshm-body{flex:1;overflow:auto;padding:14px;display:flex;flex-direction:column;gap:12px}
|
|
31
485
|
.dshm-hint{color:var(--dsw-alias-label-caption,#6b7280);font-size:12px;line-height:18px;margin:0}
|
|
32
|
-
.dshm-err{color:var(--dsw-alias-state-
|
|
486
|
+
.dshm-err{color:var(--dsw-alias-state-error-primary,#b91c1c);font-size:12px;line-height:18px}
|
|
33
487
|
.dshm-btn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-primary,inherit);border-radius:8px;padding:5px 12px;font:inherit;font-size:12px;cursor:pointer;white-space:nowrap}
|
|
34
488
|
.dshm-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
|
|
35
489
|
.dshm-btn:disabled{opacity:.5;cursor:default}
|
|
36
490
|
.dshm-btn.primary{background:var(--dsw-alias-interactive-bg-selected,#4f46e5);border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5);color:#fff}
|
|
37
491
|
.dshm-btn.primary:hover{filter:brightness(1.08)}
|
|
38
|
-
.dshm-btn.danger{color:var(--dsw-alias-state-
|
|
492
|
+
.dshm-btn.danger{color:var(--dsw-alias-state-error-primary,#b91c1c);border-color:var(--dsw-alias-state-error-primary,#b91c1c)}
|
|
39
493
|
.dshm-btn.sm{padding:3px 9px;font-size:11px}
|
|
40
|
-
.dshm-input{flex:1;min-width:120px;border:1px solid var(--dsw-alias-border-l2,#c7d2fe);background:var(--dsw-alias-bg-layer-2,transparent);color:var(--dsw-alias-label-primary,inherit);border-radius:8px;padding:
|
|
494
|
+
.dshm-input{flex:1;min-width:120px;border:1px solid var(--dsw-alias-border-l2,#c7d2fe);background:var(--dsw-alias-bg-layer-2,transparent);color:var(--dsw-alias-label-primary,inherit);border-radius:8px;padding:5px 10px;font:inherit;font-size:12px;outline:none}
|
|
41
495
|
.dshm-input:focus{border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5)}
|
|
42
496
|
.dshm-chips{display:flex;flex-wrap:wrap;gap:6px}
|
|
43
497
|
.dshm-chip{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);border-radius:999px;padding:2px 10px;font:inherit;font-size:11px;cursor:pointer}
|
|
44
|
-
.dshm-chip
|
|
498
|
+
.dshm-chip:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
|
|
499
|
+
.dshm-chip.on{background:var(--dsw-specific-sidebar-nav-item-active,rgba(38,49,72,.08));border-color:transparent;color:var(--dsw-alias-label-primary,inherit);font-weight:500}
|
|
45
500
|
.dshm-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
|
|
46
501
|
@media (max-width:680px){.dshm-cards{grid-template-columns:1fr}}
|
|
47
502
|
.dshm-card{display:flex;gap:12px;align-items:flex-start;background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.04));border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;padding:12px;cursor:pointer;text-align:left;width:100%;box-sizing:border-box;min-width:0;font:inherit;color:var(--dsw-alias-label-primary,inherit);transition:border-color .16s,background .16s}
|
|
@@ -51,20 +506,62 @@ var CSS = `
|
|
|
51
506
|
.dshm-top{display:flex;align-items:center;gap:8px;min-width:0}
|
|
52
507
|
.dshm-name{flex:1;min-width:0;font-weight:600;font-size:14px;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
53
508
|
.dshm-badge{flex:none;font-size:11px;line-height:16px;padding:0 6px;border-radius:999px;background:var(--dsw-alias-state-success-tertiary,#ecfdf5);color:var(--dsw-alias-state-success-primary,#047857)}
|
|
54
|
-
.dshm-badge.warn{background:var(--dsw-alias-state-
|
|
55
|
-
.dshm-badge.info{background:var(--dsw-alias-
|
|
56
|
-
.dshm-badge.err{background:var(--dsw-alias-state-
|
|
509
|
+
.dshm-badge.warn{background:var(--dsw-alias-state-warn-tertiary,#fffbeb);color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
510
|
+
.dshm-badge.info{background:var(--dsw-alias-state-business-tertiary,#eef2ff);color:var(--dsw-alias-state-business-primary,#4338ca)}
|
|
511
|
+
.dshm-badge.err{background:var(--dsw-alias-state-error-secondary,#fee2e2);color:var(--dsw-alias-state-error-primary,#b91c1c)}
|
|
57
512
|
.dshm-desc{color:var(--dsw-alias-label-tertiary,#6b7280);font-size:12px;line-height:18px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
58
513
|
.dshm-sub{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--dsw-alias-label-caption,#6b7280)}
|
|
59
514
|
.dshm-detail{margin-top:8px;border-top:1px dashed var(--dsw-alias-border-l2,#e5e7eb);padding-top:8px;display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#4b5563)}
|
|
60
515
|
.dshm-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:4px}
|
|
61
|
-
.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-
|
|
516
|
+
.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}
|
|
62
517
|
.dshm-banner .dshm-banner-text{flex:1}
|
|
63
518
|
.dshm-row{display:flex;align-items:center;gap:8px}
|
|
64
|
-
.dshm-kv{display:grid;grid-template-columns:
|
|
65
|
-
.dshm-kv .k{color:var(--dsw-alias-label-caption,#6b7280)}
|
|
519
|
+
.dshm-kv{display:grid;grid-template-columns:96px 1fr;gap:6px 10px;font-size:12px;align-items:baseline}
|
|
520
|
+
.dshm-kv .k{color:var(--dsw-alias-label-caption,#6b7280);font-size:11px}
|
|
521
|
+
.dshm-section{background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.04));border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:9px;margin:0 0 10px}
|
|
522
|
+
.dshm-section:last-child{margin-bottom:2px}
|
|
523
|
+
.dshm-section-title{display:flex;align-items:center;gap:7px;font-weight:700;font-size:12px;letter-spacing:.2px;color:var(--dsw-alias-label-primary,inherit)}
|
|
524
|
+
.dshm-section-title::before{content:"";width:3px;height:11px;border-radius:2px;background:var(--dsw-alias-interactive-bg-selected,#4f46e5)}
|
|
525
|
+
.dshm-section-sub{margin-left:auto;font-weight:400;font-size:11px;color:var(--dsw-alias-label-caption,#9ca3af)}
|
|
526
|
+
.dshm-note{border-left:3px solid var(--dsw-alias-border-l2,#cbd5e1);padding:3px 10px;color:var(--dsw-alias-label-caption,#9ca3af);font-size:11px;line-height:17px;word-break:break-word}
|
|
527
|
+
.dshm-note.warn{border-left-color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
528
|
+
.dshm-code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.12));padding:1px 7px;border-radius:5px;word-break:break-all}
|
|
66
529
|
.dshm-spin{display:inline-block;width:12px;height:12px;border:2px solid var(--dsw-alias-border-l2,#c7d2fe);border-top-color:var(--dsw-alias-interactive-bg-selected,#4f46e5);border-radius:50%;animation:dshm-rot .8s linear infinite;vertical-align:-2px}
|
|
67
530
|
@keyframes dshm-rot{to{transform:rotate(360deg)}}
|
|
531
|
+
.dshm-others{display:flex;align-items:center;gap:8px;padding:10px 12px;border:1px dashed var(--dsw-alias-border-l2,#e2e4e8);border-radius:10px;color:var(--dsw-alias-label-caption,#9ca3af);font-size:12px;line-height:18px}
|
|
532
|
+
.dshm-readme{max-height:280px;overflow:auto;background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.04));border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;padding:10px 12px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#4b5563);margin:0}
|
|
533
|
+
.dshm-readme.md{font-family:inherit;white-space:normal;word-break:break-word}
|
|
534
|
+
.dshm-readme.md h1,.dshm-readme.md h2,.dshm-readme.md h3,.dshm-readme.md h4,.dshm-readme.md h5,.dshm-readme.md h6{margin:8px 0 4px;font-weight:700;line-height:1.4;color:var(--dsw-alias-label-primary,inherit)}
|
|
535
|
+
.dshm-readme.md h1{font-size:15px}.dshm-readme.md h2{font-size:14px}.dshm-readme.md h3{font-size:13px}.dshm-readme.md h4,.dshm-readme.md h5,.dshm-readme.md h6{font-size:12px}
|
|
536
|
+
.dshm-readme.md>:first-child{margin-top:0}
|
|
537
|
+
.dshm-readme.md p{margin:4px 0}
|
|
538
|
+
.dshm-readme.md a{color:var(--dsw-alias-state-business-primary,#4d6bfe);text-decoration:none}
|
|
539
|
+
.dshm-readme.md a:hover{text-decoration:underline}
|
|
540
|
+
.dshm-readme.md code{background:rgba(127,127,127,.16);border-radius:4px;padding:1px 4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}
|
|
541
|
+
.dshm-readme.md pre{background:rgba(127,127,127,.12);border:1px solid var(--dsw-alias-border-l2,transparent);border-radius:8px;padding:8px 10px;overflow:auto;margin:6px 0}
|
|
542
|
+
.dshm-readme.md pre code{background:transparent;padding:0;font-size:11px;line-height:16px}
|
|
543
|
+
.dshm-readme.md img{max-height:20px;max-width:100%;vertical-align:middle}
|
|
544
|
+
.dshm-readme.md ul,.dshm-readme.md ol{margin:4px 0;padding-left:20px}
|
|
545
|
+
.dshm-readme.md li{margin:2px 0}
|
|
546
|
+
.dshm-readme.md blockquote{margin:6px 0;padding:2px 10px;border-left:3px solid var(--dsw-alias-border-l2,#cbd5e1);color:var(--dsw-alias-label-caption,#6b7280)}
|
|
547
|
+
.dshm-readme.md table{border-collapse:collapse;margin:6px 0;font-size:11px}
|
|
548
|
+
.dshm-readme.md th,.dshm-readme.md td{border:1px solid var(--dsw-alias-border-l2,#cbd5e1);padding:3px 8px;text-align:left}
|
|
549
|
+
.dshm-readme.md th{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.1))}
|
|
550
|
+
.dshm-readme.md hr{border:0;border-top:1px solid var(--dsw-alias-border-l2,#cbd5e1);margin:8px 0}
|
|
551
|
+
.dshm-readme.md .dshm-md-note{margin-top:8px;padding-top:6px;border-top:1px dashed var(--dsw-alias-border-l2,#cbd5e1);color:var(--dsw-alias-label-caption,#9ca3af);font-size:11px}
|
|
552
|
+
.dshm-links{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:7px;padding-top:6px;border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);font-size:11px;color:var(--dsw-alias-label-caption,#6b7280)}
|
|
553
|
+
.dshm-links-k,.dshm-links-sep{color:var(--dsw-alias-label-caption,#9ca3af)}
|
|
554
|
+
.dshm-links a{color:var(--dsw-alias-state-business-primary,#4d6bfe);text-decoration:none;font-weight:500}
|
|
555
|
+
.dshm-links a:hover{text-decoration:underline}
|
|
556
|
+
.dshm-prog{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#4b5563)}
|
|
557
|
+
.dshm-prog .bar{flex:1;height:6px;border-radius:3px;background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.08));overflow:hidden;min-width:80px}
|
|
558
|
+
.dshm-prog .bar i{display:block;height:100%;background:var(--dsw-alias-interactive-bg-selected,#4f46e5);transition:width .3s}
|
|
559
|
+
.dshm-entry{box-sizing:border-box;display:flex;align-items:center;gap:8px;width:calc(100% + 4px);height:42px;margin:4px -2px;padding:0 10px 0 8px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,inherit);font:inherit;font-size:14px;line-height:22px;cursor:pointer;overflow:hidden}
|
|
560
|
+
.dshm-entry:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
|
|
561
|
+
.dshm-entry svg{flex:none;width:16px;height:16px}
|
|
562
|
+
.dshm-entry span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
563
|
+
[data-slot="sidebar.footer.action"]{display:flex!important;flex-direction:column;width:100%;min-width:0}
|
|
564
|
+
[data-slot="sidebar.footer.action"]>*{flex:none;min-width:0}
|
|
68
565
|
.dshm-empty{text-align:center;color:var(--dsw-alias-label-caption,#6b7280);font-size:13px;padding:32px 0}
|
|
69
566
|
`;
|
|
70
567
|
function ensureCss() {
|
|
@@ -74,11 +571,12 @@ function ensureCss() {
|
|
|
74
571
|
el.textContent = CSS;
|
|
75
572
|
document.head.appendChild(el);
|
|
76
573
|
}
|
|
77
|
-
async function api(method, params) {
|
|
574
|
+
async function api(method, params, signal) {
|
|
78
575
|
const res = await fetch(API, {
|
|
79
576
|
method: "POST",
|
|
80
577
|
headers: { "content-type": "application/json" },
|
|
81
|
-
body: JSON.stringify({ method, ...params || {} })
|
|
578
|
+
body: JSON.stringify({ method, ...params || {} }),
|
|
579
|
+
...signal ? { signal } : {}
|
|
82
580
|
});
|
|
83
581
|
const data = await res.json().catch(() => ({}));
|
|
84
582
|
if (!res.ok || data.ok === false) throw new Error(data.error || `API ${res.status}`);
|
|
@@ -100,6 +598,51 @@ function useAsync(fn, deps) {
|
|
|
100
598
|
}, [run]);
|
|
101
599
|
return { ...state, reload: run };
|
|
102
600
|
}
|
|
601
|
+
function useMarketData() {
|
|
602
|
+
const [query, setQuery] = useState(() => normalizeMarketQuery2({}));
|
|
603
|
+
const [data, setData] = useState(null);
|
|
604
|
+
const [loading, setLoading] = useState(true);
|
|
605
|
+
const [error, setError] = useState(null);
|
|
606
|
+
const genRef = useRef(0);
|
|
607
|
+
const abortRef = useRef(null);
|
|
608
|
+
const queryRef = useRef(query);
|
|
609
|
+
const fetchPage = useCallback((nextQuery, force) => {
|
|
610
|
+
const gen = ++genRef.current;
|
|
611
|
+
abortRef.current?.abort();
|
|
612
|
+
const ac = new AbortController();
|
|
613
|
+
abortRef.current = ac;
|
|
614
|
+
setLoading(true);
|
|
615
|
+
setError(null);
|
|
616
|
+
const params = {
|
|
617
|
+
query: nextQuery.query || void 0,
|
|
618
|
+
category: nextQuery.category || void 0,
|
|
619
|
+
offset: nextQuery.offset,
|
|
620
|
+
limit: nextQuery.limit,
|
|
621
|
+
...force ? { force: true } : {}
|
|
622
|
+
};
|
|
623
|
+
return api("market", params, ac.signal).then((raw) => {
|
|
624
|
+
if (genRef.current !== gen || ac.signal.aborted) return;
|
|
625
|
+
setData(normalizeMarketResponse2(raw));
|
|
626
|
+
setLoading(false);
|
|
627
|
+
}).catch((e) => {
|
|
628
|
+
if (genRef.current !== gen || ac.signal.aborted) return;
|
|
629
|
+
setError(String(e && e.message || e));
|
|
630
|
+
setLoading(false);
|
|
631
|
+
});
|
|
632
|
+
}, []);
|
|
633
|
+
const updateQuery = useCallback((patch, opts = {}) => {
|
|
634
|
+
const next = resetPageOnFilterChange2(queryRef.current, normalizeMarketQuery2({ ...queryRef.current, ...patch }));
|
|
635
|
+
queryRef.current = next;
|
|
636
|
+
setQuery(next);
|
|
637
|
+
if (opts.fetch !== false) fetchPage(next, opts.force);
|
|
638
|
+
}, [fetchPage]);
|
|
639
|
+
const reload = useCallback((force) => fetchPage(queryRef.current, force), [fetchPage]);
|
|
640
|
+
useEffect(() => {
|
|
641
|
+
fetchPage(queryRef.current, false);
|
|
642
|
+
return () => abortRef.current?.abort();
|
|
643
|
+
}, [fetchPage]);
|
|
644
|
+
return { query, data, loading, error, reload, updateQuery };
|
|
645
|
+
}
|
|
103
646
|
function Icon({ entry }) {
|
|
104
647
|
const [broken, setBroken] = useState(false);
|
|
105
648
|
const letter = String(entry.name || entry.id || "?").charAt(0).toUpperCase();
|
|
@@ -118,6 +661,181 @@ function Icon({ entry }) {
|
|
|
118
661
|
function Spin() {
|
|
119
662
|
return h("span", { className: "dshm-spin" });
|
|
120
663
|
}
|
|
664
|
+
function safeUrl(u) {
|
|
665
|
+
const t = String(u || "").trim();
|
|
666
|
+
if (/^(https?:\/\/|mailto:)/i.test(t)) return t;
|
|
667
|
+
if (/^[/#]/.test(t)) return t;
|
|
668
|
+
return "#";
|
|
669
|
+
}
|
|
670
|
+
function ExtLink({ href, className, children }) {
|
|
671
|
+
return h(
|
|
672
|
+
"a",
|
|
673
|
+
{
|
|
674
|
+
className: className || "dshm-md-a",
|
|
675
|
+
href: safeUrl(href),
|
|
676
|
+
target: "_blank",
|
|
677
|
+
rel: "noopener noreferrer",
|
|
678
|
+
onClick: (e) => e.stopPropagation()
|
|
679
|
+
},
|
|
680
|
+
children
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
function MdImg({ src, alt }) {
|
|
684
|
+
return h("img", {
|
|
685
|
+
className: "dshm-md-img",
|
|
686
|
+
src: safeUrl(src),
|
|
687
|
+
alt: alt || "",
|
|
688
|
+
referrerPolicy: "no-referrer",
|
|
689
|
+
onError: (e) => {
|
|
690
|
+
e.currentTarget.style.display = "none";
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
function mdInline(text, kb) {
|
|
695
|
+
const re = /(\[!\[[^\]]*\]\([^)]*\)\]\([^)]*\))|(!\[[^\]]*\]\([^)]*\))|(\[[^\]]*\]\([^)]*\))|(`[^`]+`)|(\*\*[^*]+\*\*)|(~~[^~]+~~)|(\*[^*\s][^*]*\*)|(<https?:\/\/[^>\s]+>)|(https?:\/\/[^\s<>()\[\]{}"'「」【】]+[^\s<>()\[\]{}"'「」【】.,;:!?…,。;:!?)】」"')])/g;
|
|
696
|
+
const src = String(text);
|
|
697
|
+
const nodes = [];
|
|
698
|
+
let last = 0;
|
|
699
|
+
let m;
|
|
700
|
+
let i = 0;
|
|
701
|
+
while (m = re.exec(src)) {
|
|
702
|
+
if (m.index > last) nodes.push(src.slice(last, m.index));
|
|
703
|
+
const tok = m[0];
|
|
704
|
+
const k = `${kb}-${i++}`;
|
|
705
|
+
if (tok.startsWith("[![")) {
|
|
706
|
+
const im = /^!\[([^\]]*)\]\(([^)]*)\)/.exec(tok.slice(1));
|
|
707
|
+
const lm = /\]\(([^)]*)\)\s*$/.exec(tok);
|
|
708
|
+
const img = h(MdImg, { src: im && im[2], alt: im && im[1] });
|
|
709
|
+
const href = lm && lm[1];
|
|
710
|
+
nodes.push(href && safeUrl(href) !== "#" ? h(ExtLink, { key: k, href }, img) : h("span", { key: k }, img));
|
|
711
|
+
} else if (tok.startsWith("![") || tok.startsWith("<![")) {
|
|
712
|
+
const im = /^!\[([^\]]*)\]\(([^)]*)\)$/.exec(tok);
|
|
713
|
+
nodes.push(h(MdImg, { key: k, src: im && im[2], alt: im && im[1] }));
|
|
714
|
+
} else if (tok.startsWith("[")) {
|
|
715
|
+
const lm = /^\[([^\]]*)\]\(([^)]*)\)$/.exec(tok);
|
|
716
|
+
nodes.push(h(ExtLink, { key: k, href: lm && lm[2] }, mdInline(lm ? lm[1] : tok, k)));
|
|
717
|
+
} else if (tok.startsWith("`")) {
|
|
718
|
+
nodes.push(h("code", { key: k }, tok.slice(1, -1)));
|
|
719
|
+
} else if (tok.startsWith("**")) {
|
|
720
|
+
nodes.push(h("strong", { key: k }, mdInline(tok.slice(2, -2), k)));
|
|
721
|
+
} else if (tok.startsWith("~~")) {
|
|
722
|
+
nodes.push(h("del", { key: k }, mdInline(tok.slice(2, -2), k)));
|
|
723
|
+
} else if (tok.startsWith("*")) {
|
|
724
|
+
nodes.push(h("em", { key: k }, mdInline(tok.slice(1, -1), k)));
|
|
725
|
+
} else if (tok.startsWith("<")) {
|
|
726
|
+
const u = tok.slice(1, -1);
|
|
727
|
+
nodes.push(h(ExtLink, { key: k, href: u }, u));
|
|
728
|
+
} else {
|
|
729
|
+
nodes.push(h(ExtLink, { key: k, href: tok }, tok.length > 72 ? `${tok.slice(0, 69)}\u2026` : tok));
|
|
730
|
+
}
|
|
731
|
+
last = m.index + tok.length;
|
|
732
|
+
}
|
|
733
|
+
if (last < src.length) nodes.push(src.slice(last));
|
|
734
|
+
return nodes;
|
|
735
|
+
}
|
|
736
|
+
function mdBlocks(lines, kb) {
|
|
737
|
+
const out = [];
|
|
738
|
+
let i = 0;
|
|
739
|
+
let n = 0;
|
|
740
|
+
const isFence = (s) => /^\s*```/.test(s);
|
|
741
|
+
const isHeading = (s) => /^#{1,6}\s+/.test(s);
|
|
742
|
+
const isHr = (s) => /^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(s);
|
|
743
|
+
const isQuote = (s) => /^\s*>/.test(s);
|
|
744
|
+
const isUl = (s) => /^\s*[-*+]\s+/.test(s);
|
|
745
|
+
const isOl = (s) => /^\s*\d+[.)]\s+/.test(s);
|
|
746
|
+
const isTableRow = (s) => s.includes("|") && /^\s*\|/.test(s);
|
|
747
|
+
while (i < lines.length) {
|
|
748
|
+
const line = lines[i];
|
|
749
|
+
if (!line.trim()) {
|
|
750
|
+
i++;
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
const k = `${kb}-b${n++}`;
|
|
754
|
+
if (isFence(line)) {
|
|
755
|
+
const buf2 = [];
|
|
756
|
+
i++;
|
|
757
|
+
while (i < lines.length && !/^\s*```\s*$/.test(lines[i])) buf2.push(lines[i++]);
|
|
758
|
+
i++;
|
|
759
|
+
out.push(h("pre", { key: k }, h("code", null, buf2.join("\n"))));
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (isHeading(line)) {
|
|
763
|
+
const hm = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
764
|
+
out.push(h(`h${hm[1].length}`, { key: k }, mdInline(hm[2], k)));
|
|
765
|
+
i++;
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (isHr(line)) {
|
|
769
|
+
out.push(h("hr", { key: k }));
|
|
770
|
+
i++;
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
if (isQuote(line)) {
|
|
774
|
+
const buf2 = [];
|
|
775
|
+
while (i < lines.length && isQuote(lines[i])) buf2.push(lines[i++].replace(/^\s*>\s?/, ""));
|
|
776
|
+
out.push(h("blockquote", { key: k }, mdBlocks(buf2, k)));
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (isUl(line) || isOl(line)) {
|
|
780
|
+
const ordered = isOl(line);
|
|
781
|
+
const re = ordered ? /^\s*\d+[.)]\s+(.*)$/ : /^\s*[-*+]\s+(.*)$/;
|
|
782
|
+
const items = [];
|
|
783
|
+
while (i < lines.length && (ordered ? isOl(lines[i]) : isUl(lines[i]))) {
|
|
784
|
+
items.push(h("li", { key: `li${items.length}` }, mdInline(re.exec(lines[i])[1], `${k}-${items.length}`)));
|
|
785
|
+
i++;
|
|
786
|
+
}
|
|
787
|
+
out.push(h(ordered ? "ol" : "ul", { key: k }, items));
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
if (isTableRow(line) && i + 1 < lines.length && lines[i + 1].includes("-") && /^\s*\|?[\s:|-]+\|?\s*$/.test(lines[i + 1])) {
|
|
791
|
+
const cells = (s) => s.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
|
|
792
|
+
const head = cells(lines[i]);
|
|
793
|
+
i += 2;
|
|
794
|
+
const rows = [];
|
|
795
|
+
while (i < lines.length && isTableRow(lines[i])) {
|
|
796
|
+
rows.push(cells(lines[i]));
|
|
797
|
+
i++;
|
|
798
|
+
}
|
|
799
|
+
out.push(
|
|
800
|
+
h(
|
|
801
|
+
"table",
|
|
802
|
+
{ key: k },
|
|
803
|
+
h("thead", null, h("tr", null, head.map((c, x) => h("th", { key: x }, mdInline(c, `${k}h${x}`))))),
|
|
804
|
+
h("tbody", null, rows.map((r, y) => h("tr", { key: y }, r.map((c, x) => h("td", { key: x }, mdInline(c, `${k}${y}x${x}`))))))
|
|
805
|
+
)
|
|
806
|
+
);
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
const buf = [line];
|
|
810
|
+
i++;
|
|
811
|
+
while (i < lines.length && lines[i].trim() && !isFence(lines[i]) && !isHeading(lines[i]) && !isHr(lines[i]) && !isQuote(lines[i]) && !isUl(lines[i]) && !isOl(lines[i])) {
|
|
812
|
+
buf.push(lines[i]);
|
|
813
|
+
i++;
|
|
814
|
+
}
|
|
815
|
+
out.push(h("p", { key: k }, mdInline(buf.join(" "), k)));
|
|
816
|
+
}
|
|
817
|
+
return out;
|
|
818
|
+
}
|
|
819
|
+
function renderMarkdown(src) {
|
|
820
|
+
return mdBlocks(String(src || "").replace(/\r\n?/g, "\n").split("\n"), "md");
|
|
821
|
+
}
|
|
822
|
+
function officialLinks({ npm, github, homepage }) {
|
|
823
|
+
const links = [];
|
|
824
|
+
if (github) links.push(["GitHub", `https://github.com/${github}`]);
|
|
825
|
+
if (npm) links.push(["npm", `https://www.npmjs.com/package/${npm}`]);
|
|
826
|
+
if (!links.length && homepage) links.push([lookup("link.home"), homepage]);
|
|
827
|
+
return links;
|
|
828
|
+
}
|
|
829
|
+
function LinksRow(props) {
|
|
830
|
+
const links = officialLinks(props);
|
|
831
|
+
if (!links.length) return null;
|
|
832
|
+
const kids = [];
|
|
833
|
+
links.forEach(([label, href], idx) => {
|
|
834
|
+
if (idx) kids.push(h("span", { key: `sep${idx}`, className: "dshm-links-sep" }, "\xB7"));
|
|
835
|
+
kids.push(h(ExtLink, { key: label, href }, label));
|
|
836
|
+
});
|
|
837
|
+
return h("div", { className: "dshm-links" }, h("span", { className: "dshm-links-k" }, `${lookup("detail.links")}\uFF1A`), ...kids);
|
|
838
|
+
}
|
|
121
839
|
function TwoStepButton({ label, confirmLabel, className, onConfirm, disabled }) {
|
|
122
840
|
const [arm, setArm] = useState(false);
|
|
123
841
|
useEffect(() => {
|
|
@@ -156,7 +874,7 @@ function RestartBanner({ note, onDone }) {
|
|
|
156
874
|
const deadline = Date.now() + 9e4;
|
|
157
875
|
for (; ; ) {
|
|
158
876
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
159
|
-
if (Date.now() > deadline) throw new Error("
|
|
877
|
+
if (Date.now() > deadline) throw new Error(lookup("restart.timeout"));
|
|
160
878
|
try {
|
|
161
879
|
const ping = await api("ping");
|
|
162
880
|
if (ping.boot !== ping0.boot) break;
|
|
@@ -176,41 +894,45 @@ function RestartBanner({ note, onDone }) {
|
|
|
176
894
|
h(
|
|
177
895
|
"span",
|
|
178
896
|
{ className: "dshm-banner-text" },
|
|
179
|
-
phase === "restarting" ? "
|
|
897
|
+
phase === "restarting" ? lookup("restart.doing") : phase === "waiting" ? lookup("restart.waiting") : err ? lookup("restart.failed", { err }) : note || lookup("banner.done")
|
|
180
898
|
),
|
|
181
|
-
phase === "idle" && !err ? h("button", { className: "dshm-btn primary sm", onClick: restart }, "
|
|
899
|
+
phase === "idle" && !err ? h("button", { className: "dshm-btn primary sm", onClick: restart }, lookup("restart.now")) : null,
|
|
182
900
|
phase === "restarting" || phase === "waiting" ? Spin() : null,
|
|
183
|
-
phase === "idle" && err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, "
|
|
184
|
-
phase === "idle" && !err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, "
|
|
901
|
+
phase === "idle" && err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, lookup("common.ok")) : null,
|
|
902
|
+
phase === "idle" && !err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, lookup("common.later")) : null
|
|
185
903
|
);
|
|
186
904
|
}
|
|
187
|
-
function MarketTab({ notify }) {
|
|
188
|
-
const {
|
|
189
|
-
const [q, setQ] = useState("");
|
|
190
|
-
const [cat, setCat] = useState(null);
|
|
905
|
+
function MarketTab({ notify, market }) {
|
|
906
|
+
const { data, loading, error, reload, query, updateQuery } = market;
|
|
191
907
|
const [openId, setOpenId] = useState(null);
|
|
192
908
|
const [busyId, setBusyId] = useState(null);
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
909
|
+
const [qInput, setQInput] = useState(query.query);
|
|
910
|
+
const debounceRef = useRef(null);
|
|
911
|
+
const items = data && data.items || [];
|
|
912
|
+
const total = data && data.total || 0;
|
|
913
|
+
const limit = data && data.limit || MARKET_PAGE_SIZE2;
|
|
914
|
+
const offset = data && data.offset || 0;
|
|
915
|
+
const page = total > 0 ? Math.floor(offset / limit) + 1 : 1;
|
|
916
|
+
const pages = total > 0 ? Math.max(1, Math.ceil(total / limit)) : 1;
|
|
917
|
+
const counts = data && data.categoryCounts || {};
|
|
918
|
+
const notice = data ? registryNotice2(data.registryState, total) : null;
|
|
919
|
+
const onSearchInput = (value) => {
|
|
920
|
+
setQInput(value);
|
|
921
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
922
|
+
debounceRef.current = setTimeout(() => updateQuery({ query: value }), 300);
|
|
923
|
+
};
|
|
203
924
|
const doInstall = async (it, version) => {
|
|
204
925
|
setBusyId(it.id);
|
|
205
926
|
try {
|
|
206
927
|
const res = await api("install", { id: it.id, ...version ? { version } : {} });
|
|
207
928
|
notify({
|
|
208
929
|
kind: "ok",
|
|
209
|
-
|
|
930
|
+
needsRestart: true,
|
|
931
|
+
text: lookup("notify.installed", { pkg: res.pkg, version: res.version ? ` v${res.version}` : "" }) + (res.usedAllowAllBuilds ? lookup("notify.allowbuilds") : "")
|
|
210
932
|
});
|
|
211
933
|
await reload(false);
|
|
212
934
|
} catch (e) {
|
|
213
|
-
notify({ kind: "err", text:
|
|
935
|
+
notify({ kind: "err", text: lookup("failed.install", { err: e && e.message || e }) });
|
|
214
936
|
} finally {
|
|
215
937
|
setBusyId(null);
|
|
216
938
|
}
|
|
@@ -218,80 +940,170 @@ function MarketTab({ notify }) {
|
|
|
218
940
|
return h(
|
|
219
941
|
React.Fragment,
|
|
220
942
|
null,
|
|
943
|
+
notice ? h(
|
|
944
|
+
"div",
|
|
945
|
+
{ className: notice.key === "notice.unavailable" ? "dshm-err" : "dshm-hint" },
|
|
946
|
+
lookup(notice.key, { count: notice.count })
|
|
947
|
+
) : null,
|
|
948
|
+
total > 200 ? h("div", { className: "dshm-hint" }, lookup("market.perf")) : null,
|
|
221
949
|
h(
|
|
222
950
|
"div",
|
|
223
951
|
{ className: "dshm-row" },
|
|
224
952
|
h("input", {
|
|
225
953
|
className: "dshm-input",
|
|
226
|
-
placeholder: "
|
|
227
|
-
value:
|
|
228
|
-
onChange: (e) =>
|
|
954
|
+
placeholder: lookup("search.ph"),
|
|
955
|
+
value: qInput,
|
|
956
|
+
onChange: (e) => onSearchInput(e.target.value)
|
|
229
957
|
}),
|
|
230
|
-
h("button", { className: "dshm-btn", onClick: () => reload(true), title: "
|
|
958
|
+
h("button", { className: "dshm-btn", onClick: () => reload(true), title: lookup("settings.policy.v") }, loading ? Spin() : `\u21BB ${lookup("common.refresh")}`)
|
|
231
959
|
),
|
|
232
960
|
h(
|
|
233
961
|
"div",
|
|
234
962
|
{ className: "dshm-chips" },
|
|
235
|
-
h("button", { className: `dshm-chip${
|
|
236
|
-
|
|
237
|
-
const n =
|
|
963
|
+
h("button", { className: `dshm-chip${query.category === null ? " on" : ""}`, onClick: () => updateQuery({ category: null, offset: 0 }) }, lookup("cat.all")),
|
|
964
|
+
CATEGORIES2.map((key) => {
|
|
965
|
+
const n = typeof counts[key] === "number" ? counts[key] : 0;
|
|
238
966
|
return h(
|
|
239
967
|
"button",
|
|
240
|
-
{ key, className: `dshm-chip${
|
|
241
|
-
`${
|
|
968
|
+
{ key, className: `dshm-chip${query.category === key ? " on" : ""}`, onClick: () => updateQuery({ category: query.category === key ? null : key, offset: 0 }) },
|
|
969
|
+
`${lookup("cat." + key)}${n ? ` ${n}` : ""}`
|
|
242
970
|
);
|
|
243
971
|
})
|
|
244
972
|
),
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
it.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
973
|
+
busyId ? h(ProgressLine, { key: "prog" }) : null,
|
|
974
|
+
loading && !data ? h("div", { className: "dshm-empty" }, lookup("market.loading"), Spin()) : error ? h("div", { className: "dshm-err" }, lookup("failed.load", { err: error })) : items.length === 0 ? h("div", { className: "dshm-empty" }, lookup("market.empty")) : h(
|
|
975
|
+
React.Fragment,
|
|
976
|
+
null,
|
|
977
|
+
h(
|
|
978
|
+
"div",
|
|
979
|
+
{ className: "dshm-cards" },
|
|
980
|
+
items.map((it) => Card({
|
|
981
|
+
key: it.id,
|
|
982
|
+
icon: h(Icon, { entry: it }),
|
|
983
|
+
name: it.name,
|
|
984
|
+
badges: [
|
|
985
|
+
it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, lookup("badge.update")) : null,
|
|
986
|
+
it.installed ? h("span", { className: "dshm-badge", key: "i" }, lookup("badge.installed")) : null,
|
|
987
|
+
h("span", { className: "dshm-badge info", key: "s" }, it.source === "npm" ? "npm" : "github")
|
|
988
|
+
],
|
|
989
|
+
desc: it.description,
|
|
990
|
+
sub: [
|
|
991
|
+
it.latestVersion ? lookup("sub.latest", { v: it.latestVersion }) : it.latestTag ? it.latestTag : it.latestSha ? lookup("sub.head", { sha: it.latestSha.slice(0, 7) }) : null,
|
|
992
|
+
it.installedVersion ? lookup("sub.installed", { v: it.installedVersion }) : null,
|
|
993
|
+
it.latestError ? lookup("version.failed") : null
|
|
994
|
+
].filter(Boolean).join(" \xB7 "),
|
|
995
|
+
links: h(LinksRow, { npm: it.npm, github: it.github, homepage: it.homepage }),
|
|
996
|
+
open: openId === it.id,
|
|
997
|
+
onToggle: () => setOpenId(openId === it.id ? null : it.id),
|
|
998
|
+
detail: DetailRows([
|
|
999
|
+
[lookup("detail.id"), it.id],
|
|
1000
|
+
[lookup("detail.source"), it.source === "npm" ? h(ExtLink, { href: `https://www.npmjs.com/package/${it.npm}` }, `npm \xB7 ${it.npm}`) : h(ExtLink, { href: `https://github.com/${it.github}` }, `GitHub \xB7 ${it.github}`)],
|
|
1001
|
+
[lookup("detail.latest"), it.latestVersion ? `v${it.latestVersion}` : it.latestTag ? it.latestTag : it.latestSha ? it.latestSha : it.latestError || "\u2014"],
|
|
1002
|
+
[lookup("detail.installed"), it.installedPkg ? `${it.installedPkg} v${it.installedVersion || "?"}` : lookup("installed.none")],
|
|
1003
|
+
[lookup("detail.tags"), (it.tags || []).join(", ") || "\u2014"],
|
|
1004
|
+
it.latestError ? [lookup("version.failed"), it.latestError] : null
|
|
1005
|
+
]),
|
|
1006
|
+
actions: [
|
|
1007
|
+
it.installed ? h("span", { className: "dshm-hint", key: "hint" }, lookup("manage.hint")) : h(
|
|
1008
|
+
"button",
|
|
1009
|
+
{
|
|
1010
|
+
key: "install",
|
|
1011
|
+
className: "dshm-btn primary sm",
|
|
1012
|
+
disabled: busyId === it.id,
|
|
1013
|
+
onClick: (e) => {
|
|
1014
|
+
e.stopPropagation();
|
|
1015
|
+
doInstall(it);
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
1018
|
+
busyId === it.id ? h(Spin) : lookup("action.install")
|
|
1019
|
+
)
|
|
1020
|
+
]
|
|
1021
|
+
}))
|
|
1022
|
+
),
|
|
1023
|
+
pages > 1 ? h(
|
|
1024
|
+
"div",
|
|
1025
|
+
{ className: "dshm-row", style: { justifyContent: "center", marginTop: "4px" } },
|
|
1026
|
+
h("button", {
|
|
1027
|
+
className: "dshm-btn sm",
|
|
1028
|
+
disabled: page <= 1 || loading,
|
|
1029
|
+
onClick: () => updateQuery({ offset: Math.max(0, offset - limit) })
|
|
1030
|
+
}, lookup("market.page.prev")),
|
|
1031
|
+
h("span", { className: "dshm-hint" }, lookup("market.page.info", { page, pages, total })),
|
|
1032
|
+
h("button", {
|
|
1033
|
+
className: "dshm-btn sm",
|
|
1034
|
+
disabled: page >= pages || loading,
|
|
1035
|
+
onClick: () => updateQuery({ offset: Math.min(total - 1, offset + limit) })
|
|
1036
|
+
}, lookup("market.page.next"))
|
|
1037
|
+
) : null
|
|
289
1038
|
)
|
|
290
1039
|
);
|
|
291
1040
|
}
|
|
292
|
-
|
|
293
|
-
|
|
1041
|
+
var PHASE_LABEL = { resolving: "phase.resolving", downloading: "phase.downloading", linking: "phase.linking", building: "phase.building" };
|
|
1042
|
+
function ProgressLine() {
|
|
1043
|
+
const [st, setSt] = useState(null);
|
|
1044
|
+
useEffect(() => {
|
|
1045
|
+
let live = true;
|
|
1046
|
+
const tick = async () => {
|
|
1047
|
+
try {
|
|
1048
|
+
const d = await api("status");
|
|
1049
|
+
if (live) setSt(d);
|
|
1050
|
+
} catch {
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
tick();
|
|
1054
|
+
const iv = setInterval(tick, 1e3);
|
|
1055
|
+
return () => {
|
|
1056
|
+
live = false;
|
|
1057
|
+
clearInterval(iv);
|
|
1058
|
+
};
|
|
1059
|
+
}, []);
|
|
1060
|
+
if (!st) return null;
|
|
1061
|
+
const pct = st.total ? Math.min(100, Math.round(st.done / st.total * 100)) : null;
|
|
1062
|
+
const phaseLabel = lookup(PHASE_LABEL[st.phase] || "phase.ready");
|
|
1063
|
+
return h(
|
|
1064
|
+
"div",
|
|
1065
|
+
{ className: "dshm-prog" },
|
|
1066
|
+
Spin(),
|
|
1067
|
+
h("span", null, `${st.target} \xB7 ${phaseLabel}${st.done ? ` ${st.done}${st.total ? "/" + st.total : ""}` : ""}`),
|
|
1068
|
+
pct !== null ? h("span", { className: "bar" }, h("i", { style: { width: pct + "%" } })) : null,
|
|
1069
|
+
st.currentPackage ? h("span", { className: "dshm-hint" }, String(st.currentPackage).slice(0, 44)) : null
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
function ReadmeBlock({ pkg }) {
|
|
1073
|
+
const [state, setState] = useState({ loading: true, text: "", err: "", truncated: false });
|
|
1074
|
+
useEffect(() => {
|
|
1075
|
+
let live = true;
|
|
1076
|
+
api("readme", { pkg }).then((d) => live && setState({ loading: false, text: d.readme, err: "", truncated: d.truncated })).catch((e) => live && setState({ loading: false, text: "", err: String(e && e.message || e) }));
|
|
1077
|
+
return () => {
|
|
1078
|
+
live = false;
|
|
1079
|
+
};
|
|
1080
|
+
}, [pkg]);
|
|
1081
|
+
if (state.loading) return h("div", { className: "dshm-hint" }, lookup("readme.loading"), Spin());
|
|
1082
|
+
if (state.err) return h("div", { className: "dshm-err" }, state.err);
|
|
1083
|
+
if (!state.text) return h("div", { className: "dshm-hint" }, lookup("readme.none"));
|
|
1084
|
+
return h(
|
|
1085
|
+
"div",
|
|
1086
|
+
{ className: "dshm-readme md", onClick: (e) => e.stopPropagation() },
|
|
1087
|
+
renderMarkdown(state.text),
|
|
1088
|
+
state.truncated ? h("div", { className: "dshm-md-note" }, lookup("readme.truncated")) : null
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
function uninstallGuard(it) {
|
|
1092
|
+
if (it.source === "link") {
|
|
1093
|
+
return {
|
|
1094
|
+
confirm: lookup("confirm.unlink"),
|
|
1095
|
+
warn: lookup("warn.unlink", { path: it.path })
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
if (it.source === "file") {
|
|
1099
|
+
return { confirm: lookup("confirm.core"), warn: lookup("warn.core") };
|
|
1100
|
+
}
|
|
1101
|
+
return { confirm: lookup("confirm.uninstall"), warn: null };
|
|
1102
|
+
}
|
|
1103
|
+
function InstalledTab({ notify, installed }) {
|
|
1104
|
+
const { loading, data, error, reload } = installed;
|
|
294
1105
|
const [openPkg, setOpenPkg] = useState(null);
|
|
1106
|
+
const [readmePkg, setReadmePkg] = useState(null);
|
|
295
1107
|
const [busyPkg, setBusyPkg] = useState(null);
|
|
296
1108
|
const doUninstall = async (it) => {
|
|
297
1109
|
setBusyPkg(it.pkg);
|
|
@@ -299,11 +1111,12 @@ function InstalledTab({ notify }) {
|
|
|
299
1111
|
const res = await api("uninstall", { pkg: it.pkg });
|
|
300
1112
|
notify({
|
|
301
1113
|
kind: "ok",
|
|
302
|
-
|
|
1114
|
+
needsRestart: true,
|
|
1115
|
+
text: lookup("notify.uninstalled", { pkg: res.pkg }) + (res.liveDisabled ? lookup("notify.livedisabled") : "") + (res.leftovers && res.leftovers.length ? lookup("notify.leftovers", { paths: res.leftovers.join(", ") }) : "")
|
|
303
1116
|
});
|
|
304
1117
|
await reload();
|
|
305
1118
|
} catch (e) {
|
|
306
|
-
notify({ kind: "err", text:
|
|
1119
|
+
notify({ kind: "err", text: lookup("failed.uninstall", { err: e && e.message || e }) });
|
|
307
1120
|
} finally {
|
|
308
1121
|
setBusyPkg(null);
|
|
309
1122
|
}
|
|
@@ -314,162 +1127,336 @@ function InstalledTab({ notify }) {
|
|
|
314
1127
|
const res = await api("upgrade", { pkg: it.pkg });
|
|
315
1128
|
notify({
|
|
316
1129
|
kind: "ok",
|
|
317
|
-
|
|
1130
|
+
needsRestart: true,
|
|
1131
|
+
text: lookup("notify.upgraded", {
|
|
1132
|
+
pkg: res.pkg,
|
|
1133
|
+
from: res.fromVersion ? `v${res.fromVersion}` : "\u2014",
|
|
1134
|
+
to: res.version ? `v${res.version}` : res.sha ? res.sha.slice(0, 7) : "latest"
|
|
1135
|
+
}) + (res.usedAllowAllBuilds ? lookup("notify.upgradehint") : "")
|
|
318
1136
|
});
|
|
319
1137
|
await reload();
|
|
320
1138
|
} catch (e) {
|
|
321
|
-
notify({ kind: "err", text:
|
|
1139
|
+
notify({ kind: "err", text: lookup("failed.upgrade", { err: e && e.message || e }) });
|
|
322
1140
|
} finally {
|
|
323
1141
|
setBusyPkg(null);
|
|
324
1142
|
}
|
|
325
1143
|
};
|
|
326
|
-
if (loading && !data) return h("div", { className: "dshm-empty" }, "
|
|
327
|
-
if (error) return h("div", { className: "dshm-err" },
|
|
1144
|
+
if (loading && !data) return h("div", { className: "dshm-empty" }, lookup("installed.loading"), Spin());
|
|
1145
|
+
if (error) return h("div", { className: "dshm-err" }, lookup("failed.read", { err: error }));
|
|
328
1146
|
const items = data && data.items || [];
|
|
329
|
-
if (!items.length) return h("div", { className: "dshm-empty" },
|
|
1147
|
+
if (!items.length) return h("div", { className: "dshm-empty" }, `${lookup("installed.empty")} (${data.profileDir})`);
|
|
330
1148
|
return h(
|
|
331
1149
|
React.Fragment,
|
|
332
1150
|
null,
|
|
333
|
-
h("div", { className: "dshm-hint" },
|
|
1151
|
+
h("div", { className: "dshm-hint" }, lookup("profile.hint", { path: data.profileDir })),
|
|
1152
|
+
data.others > 0 ? h("div", { className: "dshm-others" }, lookup("installed.others", { n: data.others })) : null,
|
|
1153
|
+
busyPkg ? h(ProgressLine, { key: "prog" }) : null,
|
|
334
1154
|
h(
|
|
335
1155
|
"div",
|
|
336
1156
|
{ className: "dshm-cards" },
|
|
337
|
-
items.map((it) =>
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
"
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
1157
|
+
items.map((it) => {
|
|
1158
|
+
const guard = uninstallGuard(it);
|
|
1159
|
+
return Card({
|
|
1160
|
+
key: it.pkg,
|
|
1161
|
+
icon: h(Icon, { entry: { name: it.name, github: it.registryGithub || it.githubRepo || (it.spec.startsWith("github:") ? it.spec.slice(7).split("#")[0] : null), icon: null } }),
|
|
1162
|
+
name: it.name,
|
|
1163
|
+
badges: [
|
|
1164
|
+
it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, `\u2B06 ${it.latestTag || (it.latestVersion ? `v${it.latestVersion}` : "")}`.trim()) : null,
|
|
1165
|
+
it.registryId ? h("span", { className: "dshm-badge", key: "r" }, lookup("badge.market")) : h("span", { className: "dshm-badge info", key: "r" }, lookup("badge.nonmarket"))
|
|
1166
|
+
],
|
|
1167
|
+
desc: it.description || "\uFF08\u65E0\u63CF\u8FF0\uFF09",
|
|
1168
|
+
sub: [
|
|
1169
|
+
`v${it.version || "?"}`,
|
|
1170
|
+
{ npm: lookup("src.npm"), github: lookup("src.github"), link: lookup("src.link"), file: lookup("src.file"), unknown: lookup("src.unknown") }[it.source] || it.source
|
|
1171
|
+
].join(" \xB7 "),
|
|
1172
|
+
links: h(LinksRow, {
|
|
1173
|
+
npm: it.source === "npm" ? it.pkg : null,
|
|
1174
|
+
github: it.registryGithub || it.githubRepo || (it.spec.startsWith("github:") ? it.spec.slice(7).split("#")[0] : null)
|
|
1175
|
+
}),
|
|
1176
|
+
open: openPkg === it.pkg,
|
|
1177
|
+
onToggle: () => setOpenPkg(openPkg === it.pkg ? null : it.pkg),
|
|
1178
|
+
detail: readmePkg === it.pkg ? h(ReadmeBlock, { pkg: it.pkg }) : DetailRows([
|
|
1179
|
+
[lookup("detail.pkg"), it.pkg],
|
|
1180
|
+
[lookup("detail.spec"), it.spec],
|
|
1181
|
+
[lookup("detail.latest"), it.latestTag || (it.latestVersion ? `v${it.latestVersion}` : "\u2014")],
|
|
1182
|
+
[lookup("detail.listed"), it.registryId || lookup("detail.listed.no")],
|
|
1183
|
+
[lookup("detail.path"), it.path],
|
|
1184
|
+
guard.warn ? [lookup("detail.note"), guard.warn] : null
|
|
1185
|
+
]),
|
|
1186
|
+
actions: [
|
|
1187
|
+
h("button", {
|
|
1188
|
+
key: "rd",
|
|
1189
|
+
className: "dshm-btn sm",
|
|
366
1190
|
onClick: (e) => {
|
|
367
1191
|
e.stopPropagation();
|
|
368
|
-
|
|
1192
|
+
setReadmePkg(readmePkg === it.pkg ? null : it.pkg);
|
|
369
1193
|
}
|
|
370
|
-
},
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
1194
|
+
}, readmePkg === it.pkg ? lookup("readme.hide") : lookup("readme.show")),
|
|
1195
|
+
it.outdated ? h(
|
|
1196
|
+
"button",
|
|
1197
|
+
{
|
|
1198
|
+
key: "up",
|
|
1199
|
+
className: "dshm-btn primary sm",
|
|
1200
|
+
disabled: busyPkg === it.pkg,
|
|
1201
|
+
onClick: (e) => {
|
|
1202
|
+
e.stopPropagation();
|
|
1203
|
+
doUpgrade(it);
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
busyPkg === it.pkg ? h(Spin) : lookup("action.upgrade")
|
|
1207
|
+
) : null,
|
|
1208
|
+
h(TwoStepButton, {
|
|
1209
|
+
key: "un",
|
|
1210
|
+
label: lookup("action.uninstall"),
|
|
1211
|
+
confirmLabel: guard.confirm,
|
|
1212
|
+
className: "dshm-btn sm",
|
|
1213
|
+
disabled: busyPkg === it.pkg,
|
|
1214
|
+
onConfirm: () => doUninstall(it)
|
|
1215
|
+
})
|
|
1216
|
+
]
|
|
1217
|
+
});
|
|
1218
|
+
})
|
|
383
1219
|
)
|
|
384
1220
|
);
|
|
385
1221
|
}
|
|
386
|
-
function
|
|
1222
|
+
function regSourceLabel(data) {
|
|
1223
|
+
if (!data) return "\u2014";
|
|
1224
|
+
const map = {
|
|
1225
|
+
"default-raw": "src.default.raw",
|
|
1226
|
+
"default-jsdelivr": "src.default.jsdelivr",
|
|
1227
|
+
"default-cache": "src.default.cache",
|
|
1228
|
+
bundled: "src.bundled",
|
|
1229
|
+
"custom-url": "src.custom.url",
|
|
1230
|
+
"custom-file": "src.custom.file",
|
|
1231
|
+
"custom-cache": "src.custom.cache",
|
|
1232
|
+
"custom-unavailable": "src.custom.unavailable",
|
|
1233
|
+
// 旧字段兼容
|
|
1234
|
+
override: "src.override",
|
|
1235
|
+
jsdelivr: "src.jsdelivr",
|
|
1236
|
+
raw: "src.raw",
|
|
1237
|
+
cache: "src.cache"
|
|
1238
|
+
};
|
|
1239
|
+
return lookup(map[data.source] || data.source);
|
|
1240
|
+
}
|
|
1241
|
+
function configStatusLabel(status) {
|
|
1242
|
+
return lookup(`settings.status.${status || "loading"}` || "settings.status.loading");
|
|
1243
|
+
}
|
|
1244
|
+
var STATUS_BADGE = { ready: "", pending: "info", rejected: "warn", unavailable: "err", loading: "info" };
|
|
1245
|
+
function SettingsTab({ notify, onRegistryChanged }) {
|
|
387
1246
|
const reg = useAsync((force) => api("registry", force ? { force: true } : {}), []);
|
|
1247
|
+
const cfgState = useAsync(() => api("registry-config"), []);
|
|
388
1248
|
const self = useAsync(() => api("self-check"), []);
|
|
389
1249
|
const [busy, setBusy] = useState(false);
|
|
390
1250
|
const [upgrading, setUpgrading] = useState(false);
|
|
1251
|
+
const [draftAddress, setDraftAddress] = useState(null);
|
|
1252
|
+
const [applying, setApplying] = useState(false);
|
|
1253
|
+
const [downloading, setDownloading] = useState(false);
|
|
1254
|
+
const [diagnosing, setDiagnosing] = useState(false);
|
|
1255
|
+
const [applyError, setApplyError] = useState(null);
|
|
1256
|
+
const [diagnosticResult, setDiagnosticResult] = useState(null);
|
|
1257
|
+
const diagnoseAbort = useRef(null);
|
|
1258
|
+
const cfgData = cfgState.data;
|
|
1259
|
+
useEffect(() => {
|
|
1260
|
+
if (draftAddress === null && cfgData) setDraftAddress(cfgData.registryUrl ?? "");
|
|
1261
|
+
}, [cfgData, draftAddress]);
|
|
1262
|
+
useEffect(() => () => diagnoseAbort.current?.abort(), []);
|
|
391
1263
|
const refresh = async () => {
|
|
392
1264
|
setBusy(true);
|
|
393
1265
|
try {
|
|
394
1266
|
await reg.reload(true);
|
|
395
|
-
notify({ kind: "ok", text: "
|
|
1267
|
+
notify({ kind: "ok", text: lookup("registry.refreshed"), needsRestart: false });
|
|
396
1268
|
} finally {
|
|
397
1269
|
setBusy(false);
|
|
398
1270
|
}
|
|
399
1271
|
};
|
|
400
|
-
const
|
|
401
|
-
|
|
1272
|
+
const reloadRegistryState = async () => {
|
|
1273
|
+
await cfgState.reload().catch(() => void 0);
|
|
1274
|
+
};
|
|
1275
|
+
const applyAddress = async (raw) => {
|
|
1276
|
+
setApplying(true);
|
|
1277
|
+
setApplyError(null);
|
|
402
1278
|
try {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
1279
|
+
await api("registry-config-apply", { registryUrl: raw });
|
|
1280
|
+
setDraftAddress(typeof raw === "string" ? raw.trim() : "");
|
|
1281
|
+
notify({ kind: "ok", text: raw.trim() === "" ? lookup("settings.reset.ok") : lookup("settings.apply.ok"), needsRestart: false });
|
|
1282
|
+
await reloadRegistryState();
|
|
1283
|
+
onRegistryChanged?.();
|
|
406
1284
|
} catch (e) {
|
|
407
|
-
|
|
1285
|
+
const message = String(e && e.message || e);
|
|
1286
|
+
setApplyError(message);
|
|
1287
|
+
await reloadRegistryState().catch(() => void 0);
|
|
408
1288
|
} finally {
|
|
409
|
-
|
|
1289
|
+
setApplying(false);
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
const downloadDefault = async () => {
|
|
1293
|
+
setDownloading(true);
|
|
1294
|
+
try {
|
|
1295
|
+
const res = await api("registry-default-download");
|
|
1296
|
+
const text = JSON.stringify(res.registry, null, 2);
|
|
1297
|
+
const blob = new Blob([text], { type: "application/json;charset=utf-8" });
|
|
1298
|
+
const url = URL.createObjectURL(blob);
|
|
1299
|
+
const a = document.createElement("a");
|
|
1300
|
+
a.href = url;
|
|
1301
|
+
a.download = "registry.json";
|
|
1302
|
+
document.body.appendChild(a);
|
|
1303
|
+
a.click();
|
|
1304
|
+
a.remove();
|
|
1305
|
+
URL.revokeObjectURL(url);
|
|
1306
|
+
notify({ kind: "ok", text: lookup("settings.download.ok"), needsRestart: false });
|
|
1307
|
+
} catch (e) {
|
|
1308
|
+
notify({ kind: "err", text: lookup("settings.download.failed", { err: e && e.message || e }) });
|
|
1309
|
+
} finally {
|
|
1310
|
+
setDownloading(false);
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
const runDiagnose = async () => {
|
|
1314
|
+
diagnoseAbort.current?.abort();
|
|
1315
|
+
const ac = new AbortController();
|
|
1316
|
+
diagnoseAbort.current = ac;
|
|
1317
|
+
setDiagnosing(true);
|
|
1318
|
+
setDiagnosticResult(null);
|
|
1319
|
+
try {
|
|
1320
|
+
const res = await api("registry-diagnose", {}, ac.signal);
|
|
1321
|
+
if (!ac.signal.aborted) setDiagnosticResult(res.check);
|
|
1322
|
+
} catch (e) {
|
|
1323
|
+
if (!ac.signal.aborted) notify({ kind: "err", text: lookup("settings.diagnose.failed", { err: e && e.message || e }) });
|
|
1324
|
+
} finally {
|
|
1325
|
+
if (!ac.signal.aborted) setDiagnosing(false);
|
|
410
1326
|
}
|
|
411
1327
|
};
|
|
1328
|
+
const snap = cfgData || {};
|
|
1329
|
+
const state = snap.registryState || (reg.data ? reg.data.registryState : null) || null;
|
|
412
1330
|
return h(
|
|
413
1331
|
React.Fragment,
|
|
414
1332
|
null,
|
|
415
1333
|
Section(
|
|
416
|
-
"
|
|
1334
|
+
lookup("settings.registry"),
|
|
417
1335
|
h(
|
|
418
1336
|
"div",
|
|
419
|
-
{ className: "dshm-
|
|
420
|
-
h("
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
h("
|
|
1337
|
+
{ className: "dshm-row", style: { flexDirection: "column", alignItems: "stretch", gap: "4px" } },
|
|
1338
|
+
h("input", {
|
|
1339
|
+
className: "dshm-input",
|
|
1340
|
+
placeholder: lookup("settings.address.ph"),
|
|
1341
|
+
value: draftAddress ?? "",
|
|
1342
|
+
onChange: (e) => setDraftAddress(e.target.value),
|
|
1343
|
+
spellcheck: "false"
|
|
1344
|
+
}),
|
|
1345
|
+
h("div", { className: "dshm-note" }, lookup("settings.address.hint"))
|
|
428
1346
|
),
|
|
429
|
-
reg.data && reg.data.errors && reg.data.errors.length ? h("div", { className: "dshm-err" }, `\u8FDC\u7AEF\u63D0\u793A\uFF1A${reg.data.errors.join("\uFF1B")}`) : null,
|
|
430
1347
|
h(
|
|
431
1348
|
"div",
|
|
432
1349
|
{ className: "dshm-actions" },
|
|
433
|
-
h("button", {
|
|
434
|
-
|
|
1350
|
+
h("button", {
|
|
1351
|
+
className: "dshm-btn primary sm",
|
|
1352
|
+
disabled: applying || diagnosing || draftAddress === null,
|
|
1353
|
+
onClick: () => applyAddress(draftAddress ?? "")
|
|
1354
|
+
}, applying ? h("span", null, lookup("settings.apply.applying"), " ", h(Spin)) : lookup("settings.apply")),
|
|
1355
|
+
h("button", {
|
|
1356
|
+
className: "dshm-btn sm",
|
|
1357
|
+
disabled: applying || draftAddress === null || (draftAddress ?? "").trim() === "",
|
|
1358
|
+
onClick: () => applyAddress("")
|
|
1359
|
+
}, lookup("settings.reset")),
|
|
1360
|
+
h("button", {
|
|
1361
|
+
className: "dshm-btn sm",
|
|
1362
|
+
disabled: downloading,
|
|
1363
|
+
onClick: downloadDefault
|
|
1364
|
+
}, downloading ? h("span", null, lookup("settings.download.downloading"), " ", h(Spin)) : lookup("settings.download")),
|
|
1365
|
+
h("button", {
|
|
1366
|
+
className: "dshm-btn sm",
|
|
1367
|
+
disabled: diagnosing || applying,
|
|
1368
|
+
onClick: runDiagnose
|
|
1369
|
+
}, diagnosing ? h("span", null, lookup("settings.diagnose.running"), " ", h(Spin)) : lookup("settings.diagnose")),
|
|
1370
|
+
h("button", { className: "dshm-btn sm", disabled: busy || reg.loading, onClick: refresh }, busy || reg.loading ? h(Spin) : lookup("settings.force"))
|
|
1371
|
+
),
|
|
1372
|
+
applyError ? h("div", { className: "dshm-err" }, lookup("settings.apply.failed", { err: applyError })) : null,
|
|
1373
|
+
h(
|
|
1374
|
+
"div",
|
|
1375
|
+
{ className: "dshm-kv", style: { marginTop: "4px" } },
|
|
1376
|
+
h("span", { className: "k" }, lookup("settings.configured")),
|
|
1377
|
+
h("span", null, snap.registryUrl ? h("span", { className: "dshm-code" }, snap.registryUrl) : h("span", { className: "dshm-hint" }, "\uFF08\u9ED8\u8BA4\uFF09")),
|
|
1378
|
+
h("span", { className: "k" }, lookup("settings.activecfg")),
|
|
1379
|
+
h("span", null, snap.activeConfigAddress ? h("span", { className: "dshm-code" }, snap.activeConfigAddress) : h("span", { className: "dshm-hint" }, "\uFF08\u9ED8\u8BA4\uFF09")),
|
|
1380
|
+
h("span", { className: "k" }, lookup("settings.status.label")),
|
|
1381
|
+
h("span", null, h("span", { className: `dshm-badge ${STATUS_BADGE[snap.configStatus] || ""}` }, configStatusLabel(snap.configStatus))),
|
|
1382
|
+
h("span", { className: "k" }, lookup("settings.effective")),
|
|
1383
|
+
h("span", null, regSourceLabel(state), state && !state.isDefault ? h("span", { className: "dshm-badge info", style: { marginLeft: "6px" } }, lookup("badge.custom")) : null),
|
|
1384
|
+
h("span", { className: "k" }, lookup("settings.updated")),
|
|
1385
|
+
h("span", null, fmtDate(state && state.fetchedAt)),
|
|
1386
|
+
h("span", { className: "k" }, lookup("settings.count")),
|
|
1387
|
+
h("span", null, state ? lookup("settings.count.v", { n: state.count ?? 0 }) : "\u2014"),
|
|
1388
|
+
h("span", { className: "k" }, lookup("settings.policy")),
|
|
1389
|
+
h("span", null, lookup("settings.policy.v"))
|
|
1390
|
+
),
|
|
1391
|
+
state && state.stale && state.status !== "unavailable" ? h("div", { className: "dshm-note" }, lookup("settings.cache.hint")) : null,
|
|
1392
|
+
state && !state.isDefault ? h("div", { className: "dshm-note warn" }, lookup("settings.trust.hint")) : null,
|
|
1393
|
+
snap.warnings && snap.warnings.length ? h("div", { className: "dshm-note warn" }, `${lookup("settings.warnings")}\uFF1A${snap.warnings.join("\uFF1B")}`) : null,
|
|
1394
|
+
state && state.errors && state.errors.length ? h("div", { className: "dshm-err" }, `${lookup("settings.remotehint")}\uFF1A${state.errors.slice(0, 5).join("\uFF1B")}`) : null,
|
|
1395
|
+
diagnosticResult ? h(
|
|
1396
|
+
"div",
|
|
1397
|
+
{ className: "dshm-hint", style: { wordBreak: "break-all" } },
|
|
1398
|
+
lookup("settings.diagnose.result", {
|
|
1399
|
+
checked: diagnosticResult.checked ?? 0,
|
|
1400
|
+
passed: diagnosticResult.passed ?? 0,
|
|
1401
|
+
failed: diagnosticResult.failed ?? 0,
|
|
1402
|
+
trunc: diagnosticResult.truncated ? lookup("settings.diagnose.truncated") : ""
|
|
1403
|
+
}),
|
|
1404
|
+
diagnosticResult.issues && diagnosticResult.issues.length ? h(
|
|
1405
|
+
"div",
|
|
1406
|
+
{ style: { marginTop: "4px" } },
|
|
1407
|
+
diagnosticResult.issues.slice(0, 100).map((iss, i) => h("div", { key: i, className: "dshm-err" }, `\xB7 [${iss.id}] ${iss.field}: ${iss.message}`))
|
|
1408
|
+
) : h("div", { className: "dshm-note" }, lookup("settings.diagnose.none"))
|
|
1409
|
+
) : null
|
|
435
1410
|
),
|
|
436
1411
|
Section(
|
|
437
|
-
"
|
|
1412
|
+
lookup("settings.self"),
|
|
438
1413
|
h(
|
|
439
1414
|
"div",
|
|
440
1415
|
{ className: "dshm-kv" },
|
|
441
|
-
h("span", { className: "k" }, "
|
|
1416
|
+
h("span", { className: "k" }, lookup("settings.current")),
|
|
442
1417
|
h("span", null, self.data ? `v${self.data.current}` : "\u2014"),
|
|
443
|
-
h("span", { className: "k" }, "
|
|
444
|
-
h("span", null, self.data ? self.data.latest ? `v${self.data.latest}` :
|
|
1418
|
+
h("span", { className: "k" }, lookup("settings.npmlatest")),
|
|
1419
|
+
h("span", null, self.data ? self.data.latest ? `v${self.data.latest}` : lookup("settings.lookupfailed", { err: self.data.error || "" }) : "\u2026")
|
|
445
1420
|
),
|
|
446
1421
|
self.data && self.data.outdated ? h(
|
|
447
1422
|
"div",
|
|
448
1423
|
{ className: "dshm-actions" },
|
|
449
|
-
h("button", { className: "dshm-btn primary sm", disabled: upgrading, onClick: upgradeSelf }, upgrading ? h(Spin) : "
|
|
450
|
-
h("span", { className: "dshm-hint" }, "
|
|
1424
|
+
h("button", { className: "dshm-btn primary sm", disabled: upgrading, onClick: upgradeSelf }, upgrading ? h(Spin) : lookup("settings.upgradeself")),
|
|
1425
|
+
h("span", { className: "dshm-hint" }, lookup("settings.upgradehint"))
|
|
451
1426
|
) : null
|
|
452
1427
|
),
|
|
453
1428
|
Section(
|
|
454
|
-
"
|
|
455
|
-
h(
|
|
456
|
-
"div",
|
|
457
|
-
{ className: "dshm-hint" },
|
|
458
|
-
"DSH Marketplace\uFF08dsh-m\uFF09\u2014 \u4E2A\u4EBA\u81EA\u7528\u7684 DeepSeek Harness \u63D2\u4EF6\u5E02\u573A\u3002\u6536\u5F55\u3001\u5B89\u88C5\u3001\u5378\u8F7D\u3001\u5347\u7EA7\uFF0C\u5168\u90E8\u672C\u673A\u5B8C\u6210\u3002"
|
|
459
|
-
)
|
|
1429
|
+
lookup("settings.about"),
|
|
1430
|
+
h("div", { className: "dshm-hint" }, lookup("settings.about.text"))
|
|
460
1431
|
)
|
|
461
1432
|
);
|
|
1433
|
+
async function upgradeSelf() {
|
|
1434
|
+
setUpgrading(true);
|
|
1435
|
+
try {
|
|
1436
|
+
const res = await api("self-upgrade");
|
|
1437
|
+
notify({ kind: "ok", text: lookup("self.upgraded", { v: res.version }), needsRestart: true });
|
|
1438
|
+
await self.reload();
|
|
1439
|
+
} catch (e) {
|
|
1440
|
+
notify({ kind: "err", text: lookup("failed.selfupdate", { err: e && e.message || e }) });
|
|
1441
|
+
} finally {
|
|
1442
|
+
setUpgrading(false);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
462
1445
|
}
|
|
463
|
-
function
|
|
464
|
-
if (
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
function Section(title, ...children) {
|
|
1446
|
+
function Section(title, sub, ...children) {
|
|
1447
|
+
if (sub != null && typeof sub !== "string") {
|
|
1448
|
+
children = [sub, ...children];
|
|
1449
|
+
sub = null;
|
|
1450
|
+
}
|
|
469
1451
|
return h(
|
|
470
1452
|
"div",
|
|
471
|
-
{
|
|
472
|
-
h(
|
|
1453
|
+
{ className: "dshm-section" },
|
|
1454
|
+
h(
|
|
1455
|
+
"div",
|
|
1456
|
+
{ className: "dshm-section-title" },
|
|
1457
|
+
title,
|
|
1458
|
+
sub ? h("span", { className: "dshm-section-sub" }, sub) : null
|
|
1459
|
+
),
|
|
473
1460
|
...children
|
|
474
1461
|
);
|
|
475
1462
|
}
|
|
@@ -480,11 +1467,11 @@ function DetailRows(rows) {
|
|
|
480
1467
|
{ className: "dshm-kv" },
|
|
481
1468
|
list.flatMap(([k, v]) => [
|
|
482
1469
|
h("span", { className: "k", key: `${k}-k` }, k),
|
|
483
|
-
h("span", { key: `${k}-v`, style: { wordBreak: "break-all" } },
|
|
1470
|
+
h("span", { key: `${k}-v`, style: { wordBreak: "break-all" } }, v)
|
|
484
1471
|
])
|
|
485
1472
|
);
|
|
486
1473
|
}
|
|
487
|
-
function Card({ icon, name, badges, desc, sub, open, onToggle, detail, actions }) {
|
|
1474
|
+
function Card({ icon, name, badges, desc, sub, links, open, onToggle, detail, actions }) {
|
|
488
1475
|
return h(
|
|
489
1476
|
"div",
|
|
490
1477
|
{
|
|
@@ -503,18 +1490,29 @@ function Card({ icon, name, badges, desc, sub, open, onToggle, detail, actions }
|
|
|
503
1490
|
h("div", { className: "dshm-top" }, h("span", { className: "dshm-name" }, name), ...badges.filter(Boolean)),
|
|
504
1491
|
h("div", { className: "dshm-desc", style: open ? { WebkitLineClamp: "unset" } : null }, desc),
|
|
505
1492
|
sub ? h("div", { className: "dshm-sub" }, sub) : null,
|
|
1493
|
+
links || null,
|
|
506
1494
|
open ? h("div", { className: "dshm-detail" }, detail) : null,
|
|
507
1495
|
open && actions && actions.length ? h("div", { className: "dshm-actions" }, ...actions) : null
|
|
508
1496
|
)
|
|
509
1497
|
);
|
|
510
1498
|
}
|
|
511
1499
|
var TABS = [
|
|
512
|
-
["market", "
|
|
513
|
-
["installed", "
|
|
514
|
-
["settings", "
|
|
1500
|
+
["market", "tab.market", "market"],
|
|
1501
|
+
["installed", "tab.installed", "installed"],
|
|
1502
|
+
["settings", "tab.settings", null]
|
|
515
1503
|
];
|
|
516
1504
|
function MarketPanel({ onClose }) {
|
|
517
1505
|
const [tab, setTab] = useState("market");
|
|
1506
|
+
const market = useMarketData();
|
|
1507
|
+
const installed = useAsync(() => api("installed"), []);
|
|
1508
|
+
const onRegistryChanged = useCallback(async () => {
|
|
1509
|
+
await market.reload(false);
|
|
1510
|
+
await installed.reload().catch(() => void 0);
|
|
1511
|
+
}, [market, installed]);
|
|
1512
|
+
const counts = {
|
|
1513
|
+
market: market.data ? market.data.total : null,
|
|
1514
|
+
installed: installed.data ? installed.data.items.length : null
|
|
1515
|
+
};
|
|
518
1516
|
const [banner, setBanner] = useState(null);
|
|
519
1517
|
const [toast, setToast] = useState(null);
|
|
520
1518
|
useEffect(() => {
|
|
@@ -529,9 +1527,9 @@ function MarketPanel({ onClose }) {
|
|
|
529
1527
|
const t = setTimeout(() => setToast(null), 6e3);
|
|
530
1528
|
return () => clearTimeout(t);
|
|
531
1529
|
}, [toast]);
|
|
532
|
-
const notify = useCallback(({ kind, text }) => {
|
|
1530
|
+
const notify = useCallback(({ kind, text, needsRestart }) => {
|
|
533
1531
|
setToast({ kind, text });
|
|
534
|
-
if (kind === "ok") setBanner({ text: "
|
|
1532
|
+
if (kind === "ok" && needsRestart) setBanner({ text: lookup("banner.done") });
|
|
535
1533
|
}, []);
|
|
536
1534
|
return h(
|
|
537
1535
|
"div",
|
|
@@ -542,23 +1540,39 @@ function MarketPanel({ onClose }) {
|
|
|
542
1540
|
h(
|
|
543
1541
|
"div",
|
|
544
1542
|
{ className: "dshm-head" },
|
|
545
|
-
h("span", { className: "dshm-title" }, "
|
|
546
|
-
|
|
547
|
-
|
|
1543
|
+
h("span", { className: "dshm-title" }, lookup("title.full")),
|
|
1544
|
+
h(
|
|
1545
|
+
"div",
|
|
1546
|
+
{ className: "dshm-seg", role: "tablist" },
|
|
1547
|
+
TABS.map(
|
|
1548
|
+
([key, labelKey, countKey]) => h(
|
|
1549
|
+
"button",
|
|
1550
|
+
{
|
|
1551
|
+
key,
|
|
1552
|
+
type: "button",
|
|
1553
|
+
role: "tab",
|
|
1554
|
+
"aria-selected": tab === key,
|
|
1555
|
+
className: tab === key ? "on" : "",
|
|
1556
|
+
onClick: () => setTab(key)
|
|
1557
|
+
},
|
|
1558
|
+
lookup(labelKey),
|
|
1559
|
+
countKey && counts[countKey] != null ? h("span", { className: "dshm-count" }, String(counts[countKey])) : null
|
|
1560
|
+
)
|
|
1561
|
+
)
|
|
548
1562
|
),
|
|
549
1563
|
h("span", { className: "dshm-spacer" }),
|
|
550
|
-
h("button", { className: "dshm-btn", onClick: onClose }, "
|
|
1564
|
+
h("button", { className: "dshm-btn", onClick: onClose }, lookup("common.close"))
|
|
551
1565
|
),
|
|
552
1566
|
h(
|
|
553
1567
|
"div",
|
|
554
1568
|
{ className: "dshm-body" },
|
|
555
|
-
tab === "market" ? h(MarketTab, { notify }) : null,
|
|
556
|
-
tab === "installed" ? h(InstalledTab, { notify }) : null,
|
|
557
|
-
tab === "settings" ? h(SettingsTab, { notify }) : null
|
|
1569
|
+
tab === "market" ? h(MarketTab, { notify, market }) : null,
|
|
1570
|
+
tab === "installed" ? h(InstalledTab, { notify, installed }) : null,
|
|
1571
|
+
tab === "settings" ? h(SettingsTab, { notify, onRegistryChanged }) : null
|
|
558
1572
|
),
|
|
559
1573
|
toast ? h(
|
|
560
1574
|
"div",
|
|
561
|
-
{ className: `dshm-banner`, style: toast.kind === "err" ? { background: "var(--dsw-alias-state-
|
|
1575
|
+
{ className: `dshm-banner`, style: toast.kind === "err" ? { background: "var(--dsw-alias-state-error-secondary,#fee2e2)", color: "var(--dsw-alias-state-error-primary,#b91c1c)" } : null },
|
|
562
1576
|
h("span", { className: "dshm-banner-text" }, toast.text)
|
|
563
1577
|
) : null,
|
|
564
1578
|
banner ? h(RestartBanner, { note: banner.text, onDone: () => setBanner(null) }) : null
|
|
@@ -591,12 +1605,24 @@ function mountPanel() {
|
|
|
591
1605
|
container.remove();
|
|
592
1606
|
}
|
|
593
1607
|
}
|
|
1608
|
+
function MarketIcon() {
|
|
1609
|
+
return h(
|
|
1610
|
+
"svg",
|
|
1611
|
+
{ viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true" },
|
|
1612
|
+
h("rect", { x: "1.75", y: "1.75", width: "5.5", height: "5.5", rx: "1.2", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1613
|
+
h("rect", { x: "8.75", y: "1.75", width: "5.5", height: "5.5", rx: "1.2", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1614
|
+
h("rect", { x: "1.75", y: "8.75", width: "5.5", height: "5.5", rx: "1.2", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1615
|
+
// 右下:放大镜(收一点,光学尺寸与其他宫格图标一致)
|
|
1616
|
+
h("circle", { cx: "10.5", cy: "10.5", r: "2.9", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
1617
|
+
h("path", { d: "M12.6 12.6 14.3 14.3", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
594
1620
|
function MarketEntry(props) {
|
|
595
1621
|
useEffect(() => ensureCss(), []);
|
|
596
1622
|
return h(
|
|
597
1623
|
"button",
|
|
598
1624
|
{
|
|
599
|
-
className: "dshm-
|
|
1625
|
+
className: "dshm-entry",
|
|
600
1626
|
onClick: () => {
|
|
601
1627
|
try {
|
|
602
1628
|
mountPanel();
|
|
@@ -604,11 +1630,10 @@ function MarketEntry(props) {
|
|
|
604
1630
|
console.error("[dsh-m] \u6253\u5F00\u5E02\u573A\u9762\u677F\u5931\u8D25:", e);
|
|
605
1631
|
}
|
|
606
1632
|
},
|
|
607
|
-
title: "
|
|
608
|
-
style: { margin: "4px" }
|
|
1633
|
+
title: "\u63D2\u4EF6\u5E02\u573A"
|
|
609
1634
|
},
|
|
610
|
-
|
|
611
|
-
props && props.wide ? "
|
|
1635
|
+
h(MarketIcon),
|
|
1636
|
+
props && props.wide ? h("span", null, lookup("market.title")) : null
|
|
612
1637
|
);
|
|
613
1638
|
}
|
|
614
1639
|
function registerSlot(slots, options, component) {
|
|
@@ -683,6 +1708,7 @@ function ToolCardRow({ it, onInstalled }) {
|
|
|
683
1708
|
),
|
|
684
1709
|
h("div", { className: "dshm-desc" }, it.description),
|
|
685
1710
|
h("div", { className: "dshm-sub" }, `${it.id} \xB7 ${(it.tags || []).join("\u3001") || it.category}`),
|
|
1711
|
+
h(LinksRow, { npm: it.npm, github: it.github, homepage: it.homepage }),
|
|
686
1712
|
h(
|
|
687
1713
|
"div",
|
|
688
1714
|
{ className: "dshm-actions" },
|
|
@@ -708,10 +1734,10 @@ function SearchToolView(props) {
|
|
|
708
1734
|
let live = true;
|
|
709
1735
|
api("search", { query, category: args.category, limit: args.limit }).then((d) => {
|
|
710
1736
|
if (live) setItems(d.items || []);
|
|
711
|
-
}).catch((
|
|
1737
|
+
}).catch(() => {
|
|
712
1738
|
if (live) {
|
|
713
1739
|
setItems([]);
|
|
714
|
-
setErr(
|
|
1740
|
+
setErr(lookup("notice.toolview.err"));
|
|
715
1741
|
}
|
|
716
1742
|
});
|
|
717
1743
|
return () => {
|
|
@@ -758,10 +1784,21 @@ function apply(ctx) {
|
|
|
758
1784
|
const slots = ctx.slots;
|
|
759
1785
|
if (!slots) return;
|
|
760
1786
|
ctx.effect(() => ensureCss(), "dshm-style");
|
|
1787
|
+
ctx.inject(["locale"], (c) => {
|
|
1788
|
+
if (!c.locale || typeof c.locale.register !== "function") return;
|
|
1789
|
+
c.effect(() => {
|
|
1790
|
+
try {
|
|
1791
|
+
return c.locale.register("dshm", { zh: ZH, en: EN });
|
|
1792
|
+
} catch {
|
|
1793
|
+
return () => {
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
}, "dshm-locale");
|
|
1797
|
+
});
|
|
761
1798
|
slots.inject(
|
|
762
1799
|
"sidebar.footer.action",
|
|
763
1800
|
() => slots.register(
|
|
764
|
-
{ name: "sidebar.footer.action", id: "dshm-market", key: "dshm-market", order: 9, label: () => "
|
|
1801
|
+
{ name: "sidebar.footer.action", id: "dshm-market", key: "dshm-market", order: 9, locale: "dshm", label: () => lookup("market.title") },
|
|
765
1802
|
function DshmMarketEntry(actionProps) {
|
|
766
1803
|
return h(MarketEntry, actionProps);
|
|
767
1804
|
}
|