dsh-m 0.6.1 → 0.8.5
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 +83 -10
- package/README.md +82 -9
- package/docs/DESIGN.md +27 -4
- package/lib/cli.js +39 -8
- package/lib/client.js +1728 -471
- package/lib/core/community-adapter.js +16 -0
- package/lib/core/community.js +3 -2
- package/lib/core/host-api.js +39 -5
- package/lib/core/market.js +141 -24
- package/lib/core/registry-controller.js +27 -0
- package/lib/core/search-relevance.js +110 -0
- package/lib/tools.js +55 -9
- package/package.json +2 -2
- package/registry.json +23 -4
package/lib/client.js
CHANGED
|
@@ -26,14 +26,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
26
26
|
// src/client/market-state.js
|
|
27
27
|
var market_state_exports = {};
|
|
28
28
|
__export(market_state_exports, {
|
|
29
|
-
|
|
29
|
+
DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
|
|
30
|
+
MARKET_PAGE_SIZES: () => MARKET_PAGE_SIZES,
|
|
31
|
+
createZoneState: () => createZoneState,
|
|
30
32
|
marketNotice: () => marketNotice,
|
|
31
33
|
normalizeMarketQuery: () => normalizeMarketQuery,
|
|
32
34
|
normalizeMarketResponse: () => normalizeMarketResponse,
|
|
35
|
+
pageItems: () => pageItems,
|
|
33
36
|
registryNotice: () => registryNotice,
|
|
34
37
|
resetPageOnFilterChange: () => resetPageOnFilterChange,
|
|
35
|
-
|
|
36
|
-
splitCategories: () => splitCategories
|
|
38
|
+
zoneChips: () => zoneChips
|
|
37
39
|
});
|
|
38
40
|
function toSafeInt(value, fallback, min, max) {
|
|
39
41
|
const n = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
@@ -41,29 +43,60 @@ function toSafeInt(value, fallback, min, max) {
|
|
|
41
43
|
if (max !== void 0 && n > max) return max;
|
|
42
44
|
return n;
|
|
43
45
|
}
|
|
44
|
-
function
|
|
46
|
+
function createZoneState(zone) {
|
|
47
|
+
const z = zone === "primary" ? "primary" : "community";
|
|
48
|
+
return {
|
|
49
|
+
zone: z,
|
|
50
|
+
query: "",
|
|
51
|
+
category: null,
|
|
52
|
+
sort: z === "community" ? { field: "downloads", dir: "desc" } : null,
|
|
53
|
+
offset: 0,
|
|
54
|
+
limit: z === "primary" ? 96 : DEFAULT_PAGE_SIZE
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function normalizeMarketQuery(input, zone = "community") {
|
|
58
|
+
const z = zone === "primary" ? "primary" : "community";
|
|
45
59
|
const raw = input && typeof input === "object" ? input : {};
|
|
46
60
|
const query = typeof raw.query === "string" ? raw.query.trim() : "";
|
|
47
|
-
const
|
|
61
|
+
const catOk = typeof raw.category === "string" && raw.category !== "" && (z === "primary" ? CURATED_IDS.has(raw.category) || SLUG_RE.test(raw.category) : SLUG_RE.test(raw.category));
|
|
62
|
+
const category = catOk ? raw.category : null;
|
|
48
63
|
const offset = toSafeInt(raw.offset, 0, 0);
|
|
49
|
-
const limit = toSafeInt(raw.limit,
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
const limit = toSafeInt(raw.limit, DEFAULT_PAGE_SIZE, 1, 96);
|
|
65
|
+
let sort = null;
|
|
66
|
+
if (z === "community" && raw.sort && typeof raw.sort === "object" && !Array.isArray(raw.sort)) {
|
|
67
|
+
const field = raw.sort.field;
|
|
68
|
+
const dir = raw.sort.dir;
|
|
69
|
+
if (SORT_FIELDS.has(field) && (dir === "asc" || dir === "desc")) sort = { field, dir };
|
|
70
|
+
}
|
|
71
|
+
return { zone: z, source: z, query, category, sort, offset, limit };
|
|
52
72
|
}
|
|
53
73
|
function resetPageOnFilterChange(previous, next) {
|
|
54
74
|
const prev = previous && typeof previous === "object" ? previous : {};
|
|
55
75
|
const merged = { ...next };
|
|
56
|
-
if (prev.query !== next.query || prev.category !== next.category) {
|
|
76
|
+
if (prev.query !== next.query || prev.category !== next.category || !sameSort(prev.sort, next.sort)) {
|
|
57
77
|
merged.offset = 0;
|
|
58
78
|
}
|
|
59
79
|
return merged;
|
|
60
80
|
}
|
|
81
|
+
function pageItems(current, totalPages) {
|
|
82
|
+
const last = Math.max(1, Math.floor(totalPages));
|
|
83
|
+
const cur = Math.min(Math.max(1, Math.floor(current)), last);
|
|
84
|
+
if (last <= 7) return Array.from({ length: last }, (_, i) => i + 1);
|
|
85
|
+
const out = [1];
|
|
86
|
+
const from = Math.max(2, cur - 1);
|
|
87
|
+
const to = Math.min(last - 1, cur + 1);
|
|
88
|
+
if (from > 2) out.push("...");
|
|
89
|
+
for (let p = from; p <= to; p++) out.push(p);
|
|
90
|
+
if (to < last - 1) out.push("...");
|
|
91
|
+
out.push(last);
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
61
94
|
function normalizeMarketResponse(raw) {
|
|
62
95
|
const body = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
63
96
|
const items = Array.isArray(body.items) ? body.items.filter((it) => it && typeof it === "object") : [];
|
|
64
97
|
const total = toSafeInt(body.total, items.length, 0);
|
|
65
98
|
const offset = toSafeInt(body.offset, 0, 0);
|
|
66
|
-
const limit = toSafeInt(body.limit,
|
|
99
|
+
const limit = toSafeInt(body.limit, DEFAULT_PAGE_SIZE, 1);
|
|
67
100
|
let categoryCounts = {};
|
|
68
101
|
if (body.categoryCounts && typeof body.categoryCounts === "object" && !Array.isArray(body.categoryCounts)) {
|
|
69
102
|
for (const [key, value] of Object.entries(body.categoryCounts)) {
|
|
@@ -88,7 +121,9 @@ function normalizeMarketResponse(raw) {
|
|
|
88
121
|
skippedDirty: typeof c.skippedDirty === "number" && Number.isFinite(c.skippedDirty) ? c.skippedDirty : 0,
|
|
89
122
|
skippedSubpathNoNpm: typeof c.skippedSubpathNoNpm === "number" && Number.isFinite(c.skippedSubpathNoNpm) ? c.skippedSubpathNoNpm : 0,
|
|
90
123
|
errors: Array.isArray(c.errors) ? c.errors.map(String) : [],
|
|
91
|
-
warnings: Array.isArray(c.warnings) ? c.warnings.map(String) : []
|
|
124
|
+
warnings: Array.isArray(c.warnings) ? c.warnings.map(String) : [],
|
|
125
|
+
categoryLabels: narrowCategoryLabels(c.categoryLabels),
|
|
126
|
+
categoryLabelsEn: narrowCategoryLabels(c.categoryLabelsEn)
|
|
92
127
|
};
|
|
93
128
|
return {
|
|
94
129
|
items,
|
|
@@ -103,53 +138,76 @@ function normalizeMarketResponse(raw) {
|
|
|
103
138
|
latestTimedOut: body.latestTimedOut === true
|
|
104
139
|
};
|
|
105
140
|
}
|
|
141
|
+
function narrowCategoryLabels(raw) {
|
|
142
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
143
|
+
const out = {};
|
|
144
|
+
let n = 0;
|
|
145
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
146
|
+
if (typeof value === "string" && value !== "") {
|
|
147
|
+
out[key] = value;
|
|
148
|
+
n += 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return n > 0 ? out : void 0;
|
|
152
|
+
}
|
|
106
153
|
function registryNotice(summary, total) {
|
|
107
154
|
const s = summary && typeof summary === "object" ? summary : {};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
else if (s.stale || s.status === "stale") key = "notice.stale";
|
|
111
|
-
else if (!s.isDefault) key = "notice.custom";
|
|
112
|
-
return { key, count: typeof total === "number" && Number.isFinite(total) ? total : 0 };
|
|
155
|
+
if (s.status === "unavailable") return { key: "notice.unavailable" };
|
|
156
|
+
return null;
|
|
113
157
|
}
|
|
114
|
-
function
|
|
158
|
+
function zoneChips(categoryCounts, categoryLabels, zone) {
|
|
115
159
|
const counts = categoryCounts && typeof categoryCounts === "object" && !Array.isArray(categoryCounts) ? categoryCounts : {};
|
|
116
|
-
|
|
117
|
-
|
|
160
|
+
if (zone === "primary") {
|
|
161
|
+
return CURATED_ORDER.map((id) => ({
|
|
162
|
+
id,
|
|
163
|
+
label: CURATED_LABELS[id],
|
|
164
|
+
labelKey: `cat.${id}`,
|
|
165
|
+
count: typeof counts[id] === "number" && Number.isFinite(counts[id]) ? counts[id] : 0
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
const labels = categoryLabels && typeof categoryLabels === "object" && !Array.isArray(categoryLabels) ? categoryLabels : {};
|
|
169
|
+
const countOf = (id) => typeof counts[id] === "number" && Number.isFinite(counts[id]) ? counts[id] : 0;
|
|
170
|
+
const known = [];
|
|
171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
172
|
+
for (const [id, label] of Object.entries(labels)) {
|
|
173
|
+
if (typeof label !== "string" || label === "") continue;
|
|
174
|
+
const count = countOf(id);
|
|
175
|
+
if (count === 0) continue;
|
|
176
|
+
known.push({ id, label, count });
|
|
177
|
+
seen.add(id);
|
|
178
|
+
}
|
|
118
179
|
const unknown = [];
|
|
119
180
|
for (const [id, raw] of Object.entries(counts)) {
|
|
181
|
+
if (seen.has(id) || CURATED_IDS.has(id)) continue;
|
|
120
182
|
const count = typeof raw === "number" && Number.isFinite(raw) ? raw : 0;
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
else if (!SHARED_BUCKETS.has(id)) unknown.push({ id, label: id, count });
|
|
183
|
+
if (count === 0) continue;
|
|
184
|
+
unknown.push({ id, label: id, count });
|
|
124
185
|
}
|
|
125
|
-
return
|
|
186
|
+
return [...known, ...unknown];
|
|
126
187
|
}
|
|
127
188
|
function marketNotice(registryState, community) {
|
|
128
|
-
const
|
|
189
|
+
const out = {};
|
|
129
190
|
const c = community && typeof community === "object" ? community : {};
|
|
130
|
-
if (c.status === "stale")
|
|
191
|
+
if (c.status === "stale") out.communityStale = true;
|
|
131
192
|
if (registryState && registryState.status === "unavailable" && (c.status === "ready" || c.status === "stale")) {
|
|
132
|
-
|
|
193
|
+
out.communityFallback = true;
|
|
133
194
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const list = Array.isArray(items) ? items.filter((it) => it && typeof it === "object") : [];
|
|
138
|
-
const primary = list.filter((it) => it.community !== true);
|
|
139
|
-
const community = list.filter((it) => it.community === true).slice().sort((a, b) => {
|
|
140
|
-
const da = typeof a.downloads === "number" && Number.isFinite(a.downloads) ? a.downloads : -1;
|
|
141
|
-
const db = typeof b.downloads === "number" && Number.isFinite(b.downloads) ? b.downloads : -1;
|
|
142
|
-
if (da !== db) return db - da;
|
|
143
|
-
return String(a.name).localeCompare(String(b.name));
|
|
144
|
-
});
|
|
145
|
-
return [...primary, ...community];
|
|
195
|
+
const notice = registryNotice(registryState);
|
|
196
|
+
if (notice) out.notice = notice;
|
|
197
|
+
return out;
|
|
146
198
|
}
|
|
147
|
-
var
|
|
199
|
+
var MARKET_PAGE_SIZES, DEFAULT_PAGE_SIZE, CURATED_ORDER, CURATED_LABELS, CURATED_IDS, SLUG_RE, SORT_FIELDS, sameSort, FALLBACK_REGISTRY_STATE, FALLBACK_COMMUNITY;
|
|
148
200
|
var init_market_state = __esm({
|
|
149
201
|
"src/client/market-state.js"() {
|
|
150
202
|
"use strict";
|
|
151
|
-
|
|
152
|
-
|
|
203
|
+
MARKET_PAGE_SIZES = [24, 48, 96];
|
|
204
|
+
DEFAULT_PAGE_SIZE = 24;
|
|
205
|
+
CURATED_ORDER = ["market", "tools", "ui", "search", "other"];
|
|
206
|
+
CURATED_LABELS = { market: "\u5E02\u573A", tools: "\u5DE5\u5177", ui: "\u754C\u9762", search: "\u641C\u7D22", other: "\u5176\u4ED6" };
|
|
207
|
+
CURATED_IDS = new Set(CURATED_ORDER);
|
|
208
|
+
SLUG_RE = /^[a-z0-9-]{1,32}$/;
|
|
209
|
+
SORT_FIELDS = /* @__PURE__ */ new Set(["downloads", "stars", "added"]);
|
|
210
|
+
sameSort = (a, b) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
|
|
153
211
|
FALLBACK_REGISTRY_STATE = {
|
|
154
212
|
configuredAddress: "",
|
|
155
213
|
activeAddress: null,
|
|
@@ -161,38 +219,6 @@ var init_market_state = __esm({
|
|
|
161
219
|
errors: [],
|
|
162
220
|
count: 0
|
|
163
221
|
};
|
|
164
|
-
COMMUNITY_SLUG_RE = /^[a-z0-9-]{1,32}$/;
|
|
165
|
-
CURATED_CATEGORIES = [
|
|
166
|
-
{ id: "market", label: "\u5E02\u573A" },
|
|
167
|
-
{ id: "tools", label: "\u5DE5\u5177" },
|
|
168
|
-
{ id: "ui", label: "\u754C\u9762" },
|
|
169
|
-
{ id: "search", label: "\u641C\u7D22" },
|
|
170
|
-
{ id: "other", label: "\u5176\u4ED6" }
|
|
171
|
-
];
|
|
172
|
-
CURATED_IDS = new Set(CURATED_CATEGORIES.map((c) => c.id));
|
|
173
|
-
SHARED_BUCKETS = /* @__PURE__ */ new Set(["ui", "tools", "market"]);
|
|
174
|
-
COMMUNITY_KNOWN_LABELS = {
|
|
175
|
-
agi: "AGI \u67B6\u6784\u63A2\u7D22",
|
|
176
|
-
usage: "\u7528\u91CF\u4E0E\u8BA1\u8D39",
|
|
177
|
-
theme: "\u4E3B\u9898\u4E0E\u5916\u89C2",
|
|
178
|
-
model: "\u6A21\u578B\u4E0E\u8D26\u53F7\u63A5\u5165",
|
|
179
|
-
identity: "\u8EAB\u4EFD\u4E0E\u901A\u4FE1",
|
|
180
|
-
session: "\u4F1A\u8BDD\u4E0E\u6D88\u606F",
|
|
181
|
-
memory: "\u8BB0\u5FC6",
|
|
182
|
-
wsl: "WSL \u4E0E Windows \u4E92\u64CD\u4F5C",
|
|
183
|
-
browser: "\u6D4F\u89C8\u5668\u4E0E\u7F51\u9875",
|
|
184
|
-
vision: "\u89C6\u89C9\u4E0E\u591A\u6A21\u6001",
|
|
185
|
-
voice: "\u8BED\u97F3\u4E0E\u97F3\u9891",
|
|
186
|
-
docs: "\u6587\u6863\u4E0E\u6E32\u67D3",
|
|
187
|
-
skill: "\u6280\u80FD\u5305",
|
|
188
|
-
workflow: "\u5DE5\u4F5C\u6D41\u4E0E\u81EA\u52A8\u5316",
|
|
189
|
-
git: "Git \u4E0E\u4EE3\u7801\u8BC4\u5BA1",
|
|
190
|
-
notify: "\u901A\u77E5\u4E0E\u96C6\u6210",
|
|
191
|
-
dev: "\u5F00\u53D1\u4E0E\u8FD0\u884C\u65F6",
|
|
192
|
-
security: "\u5B89\u5168\u4E0E\u6743\u9650",
|
|
193
|
-
remote: "\u8FDC\u7A0B\u4E0E\u79FB\u52A8\u7AEF",
|
|
194
|
-
fun: "\u5A31\u4E50"
|
|
195
|
-
};
|
|
196
222
|
FALLBACK_COMMUNITY = {
|
|
197
223
|
enabled: false,
|
|
198
224
|
status: "disabled",
|
|
@@ -578,14 +604,375 @@ var init_view_refresh = __esm({
|
|
|
578
604
|
}
|
|
579
605
|
});
|
|
580
606
|
|
|
607
|
+
// src/client/operations.js
|
|
608
|
+
var operations_exports = {};
|
|
609
|
+
__export(operations_exports, {
|
|
610
|
+
OP_STORAGE_KEY: () => OP_STORAGE_KEY,
|
|
611
|
+
createOperationsStore: () => createOperationsStore,
|
|
612
|
+
createOpsPump: () => createOpsPump,
|
|
613
|
+
drainRestored: () => drainRestored,
|
|
614
|
+
opAppliesTo: () => opAppliesTo,
|
|
615
|
+
restoreRecords: () => restoreRecords
|
|
616
|
+
});
|
|
617
|
+
function safeParse(raw) {
|
|
618
|
+
try {
|
|
619
|
+
const v = JSON.parse(raw);
|
|
620
|
+
return Array.isArray(v) ? v.filter((r) => r && typeof r === "object" && typeof r.id === "string") : [];
|
|
621
|
+
} catch {
|
|
622
|
+
return [];
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
function storageOf(storage) {
|
|
626
|
+
if (!storage || typeof storage.getItem !== "function" || typeof storage.setItem !== "function") {
|
|
627
|
+
return { degraded: true, read: () => null, write: () => {
|
|
628
|
+
throw new Error("degraded");
|
|
629
|
+
} };
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
degraded: false,
|
|
633
|
+
read: () => {
|
|
634
|
+
try {
|
|
635
|
+
return storage.getItem(OP_STORAGE_KEY);
|
|
636
|
+
} catch {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
write: (text) => {
|
|
641
|
+
storage.setItem(OP_STORAGE_KEY, text);
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function createOperationsStore(storage) {
|
|
646
|
+
const s = storageOf(storage);
|
|
647
|
+
let records = safeParse(s.read());
|
|
648
|
+
let persistDegraded = false;
|
|
649
|
+
const persist = () => {
|
|
650
|
+
if (persistDegraded) return;
|
|
651
|
+
try {
|
|
652
|
+
s.write(JSON.stringify(records.map((r) => r.meta && r.meta.session === true ? { ...r, meta: { ...r.meta, session: void 0 } } : r)));
|
|
653
|
+
} catch {
|
|
654
|
+
persistDegraded = true;
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
return {
|
|
658
|
+
/** @returns {OperationRecord[]} 当前记录(浅拷贝) */
|
|
659
|
+
list() {
|
|
660
|
+
return records.map((r) => ({ ...r }));
|
|
661
|
+
},
|
|
662
|
+
get persistDegraded() {
|
|
663
|
+
return persistDegraded;
|
|
664
|
+
},
|
|
665
|
+
/** upsert 按 id 覆盖;不带 id 视为新记录生成 id。@returns 存储后的记录(含 id/时间戳)。 */
|
|
666
|
+
upsert(rec) {
|
|
667
|
+
const now = Date.now();
|
|
668
|
+
const id = typeof rec.id === "string" && rec.id !== "" ? rec.id : `op-${now}-${Math.random().toString(36).slice(2, 8)}`;
|
|
669
|
+
const merged = { createdAt: now, ...rec, id, updatedAt: now };
|
|
670
|
+
const idx = records.findIndex((r) => r.id === id);
|
|
671
|
+
let stored;
|
|
672
|
+
if (idx >= 0) {
|
|
673
|
+
records[idx] = stored = { ...records[idx], ...merged, createdAt: records[idx].createdAt };
|
|
674
|
+
} else {
|
|
675
|
+
records = [...records, merged];
|
|
676
|
+
stored = merged;
|
|
677
|
+
}
|
|
678
|
+
persist();
|
|
679
|
+
return { ...stored };
|
|
680
|
+
},
|
|
681
|
+
remove(id) {
|
|
682
|
+
records = records.filter((r) => r.id !== id);
|
|
683
|
+
persist();
|
|
684
|
+
return this.list();
|
|
685
|
+
},
|
|
686
|
+
/** 只清 done/warned;failed/superseded 保留供回看(评审共识:不把失败静默抹掉)。 */
|
|
687
|
+
clearFinished() {
|
|
688
|
+
records = records.filter((r) => !TERMINAL_CLEARABLE.has(r.status));
|
|
689
|
+
persist();
|
|
690
|
+
return this.list();
|
|
691
|
+
},
|
|
692
|
+
/** 供 restore 之后的批量覆写(恢复校验结果落库)。
|
|
693
|
+
* 合并语义(0.7.0 评审 P1/R2·N1/R3 修正):以**当前**记录为权威按 id 覆写——
|
|
694
|
+
* - 快照外新增(恢复校验窗口内用户发起的操作)保留,不被旧快照抹掉;
|
|
695
|
+
* - 快照内但窗口期被用户删除的记录不复活;
|
|
696
|
+
* - 活会话记录(meta.session)不覆写——泵在拾取(queued→running)时打标,覆盖其任意
|
|
697
|
+
* 后续状态(含 done),防恢复快照旧态回卷;**无标记的 crashed running 照常被改标为
|
|
698
|
+
* failed「进程重启中断」**(恢复语义核心,R3:不得用 status 短路——泵拥有与崩溃残留
|
|
699
|
+
* 在此处不可区分,session 标记才是唯一可信的所有权凭证)。 */
|
|
700
|
+
replaceAll(next) {
|
|
701
|
+
const byId = new Map((Array.isArray(next) ? next : []).map((r) => [r.id, r]));
|
|
702
|
+
records = records.map((r) => {
|
|
703
|
+
const incoming = byId.get(r.id);
|
|
704
|
+
if (!incoming) return { ...r };
|
|
705
|
+
if (r.meta && r.meta.session === true) return { ...r };
|
|
706
|
+
return { ...incoming };
|
|
707
|
+
});
|
|
708
|
+
persist();
|
|
709
|
+
return this.list();
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
async function restoreRecords(records, stillApplies) {
|
|
714
|
+
const list = Array.isArray(records) ? records : [];
|
|
715
|
+
const out = [];
|
|
716
|
+
for (const rec of list) {
|
|
717
|
+
if (!rec || typeof rec !== "object") continue;
|
|
718
|
+
if (rec.meta && rec.meta.session === true) {
|
|
719
|
+
out.push({ ...rec });
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
if (rec.status === "queued") {
|
|
723
|
+
let applies = false;
|
|
724
|
+
try {
|
|
725
|
+
applies = await stillApplies(rec);
|
|
726
|
+
} catch {
|
|
727
|
+
applies = false;
|
|
728
|
+
}
|
|
729
|
+
out.push(
|
|
730
|
+
applies ? { ...rec, status: "queued" } : { ...rec, status: "superseded", error: "\u6062\u590D\u65F6\u524D\u63D0\u6D88\u5931\uFF08\u6821\u9A8C\u4E0D\u6210\u7ACB\uFF09", updatedAt: Date.now() }
|
|
731
|
+
);
|
|
732
|
+
} else if (rec.status === "input") {
|
|
733
|
+
out.push({ ...rec, status: "failed", error: "\u5F85\u51B3\u786E\u8BA4\u4E0D\u53EF\u6062\u590D\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77", updatedAt: Date.now() });
|
|
734
|
+
} else if (rec.status === "running") {
|
|
735
|
+
out.push({ ...rec, status: "failed", error: "\u8FDB\u7A0B\u91CD\u542F\u4E2D\u65AD", updatedAt: Date.now() });
|
|
736
|
+
} else {
|
|
737
|
+
out.push({ ...rec });
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return out;
|
|
741
|
+
}
|
|
742
|
+
function opAppliesTo(rec, installedItems) {
|
|
743
|
+
const items = Array.isArray(installedItems) ? installedItems : [];
|
|
744
|
+
const has = items.some(
|
|
745
|
+
(x) => x && (x.pkg === rec.target || rec.meta && typeof rec.meta.npm === "string" && x.pkg === rec.meta.npm || rec.meta && typeof rec.meta.github === "string" && x.registryGithub === rec.meta.github)
|
|
746
|
+
);
|
|
747
|
+
return rec.kind === "install" ? !has : has;
|
|
748
|
+
}
|
|
749
|
+
function createOpsPump(store, getCtx) {
|
|
750
|
+
const executors = /* @__PURE__ */ new Map();
|
|
751
|
+
const waiters = /* @__PURE__ */ new Map();
|
|
752
|
+
let pumping = false;
|
|
753
|
+
async function pump() {
|
|
754
|
+
if (pumping) return;
|
|
755
|
+
pumping = true;
|
|
756
|
+
try {
|
|
757
|
+
for (; ; ) {
|
|
758
|
+
const queued = store.list().find((r) => r.status === "queued");
|
|
759
|
+
if (!queued) break;
|
|
760
|
+
const waitersEntry = waiters.get(queued.id);
|
|
761
|
+
const executor = executors.get(queued.id);
|
|
762
|
+
waiters.delete(queued.id);
|
|
763
|
+
executors.delete(queued.id);
|
|
764
|
+
const ctx = getCtx();
|
|
765
|
+
const finalize = (patch) => {
|
|
766
|
+
store.upsert({ id: queued.id, ...patch });
|
|
767
|
+
const c = getCtx();
|
|
768
|
+
if (c && c.syncOps) c.syncOps();
|
|
769
|
+
};
|
|
770
|
+
if (ctx && ctx.stillApplies) {
|
|
771
|
+
let applies = true;
|
|
772
|
+
try {
|
|
773
|
+
applies = await ctx.stillApplies(queued);
|
|
774
|
+
} catch {
|
|
775
|
+
applies = false;
|
|
776
|
+
}
|
|
777
|
+
if (!applies) {
|
|
778
|
+
finalize({ status: "superseded", error: "\u6267\u884C\u65F6\u524D\u63D0\u6D88\u5931\uFF08\u5DF2\u624B\u52A8\u5904\u7406\uFF1F\uFF09" });
|
|
779
|
+
if (waitersEntry) {
|
|
780
|
+
waitersEntry.reject(Object.assign(new Error("op superseded"), { opSuperseded: true, opId: queued.id }));
|
|
781
|
+
}
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
store.upsert({ id: queued.id, status: "running", meta: { ...queued.meta || {}, session: true } });
|
|
786
|
+
if (ctx && ctx.syncOps) ctx.syncOps();
|
|
787
|
+
let value;
|
|
788
|
+
let err = null;
|
|
789
|
+
try {
|
|
790
|
+
if (executor) {
|
|
791
|
+
value = await executor.exec();
|
|
792
|
+
} else if (ctx && ctx.dispatchRestored) {
|
|
793
|
+
const r = await ctx.dispatchRestored(queued);
|
|
794
|
+
if (!r || !r.ok) err = Object.assign(new Error(r && r.error || "unknown"), r && r.issue ? { issue: r.issue } : {});
|
|
795
|
+
} else {
|
|
796
|
+
err = new Error("\u65E0\u6267\u884C\u4E0A\u4E0B\u6587\uFF08\u9762\u677F\u672A\u6302\u8F7D\uFF09");
|
|
797
|
+
}
|
|
798
|
+
} catch (e) {
|
|
799
|
+
err = e;
|
|
800
|
+
}
|
|
801
|
+
if (!err) {
|
|
802
|
+
finalize({ status: value && value.opWarning ? "warned" : "done", warning: value && value.opWarning });
|
|
803
|
+
if (waitersEntry) waitersEntry.resolve(value);
|
|
804
|
+
} else {
|
|
805
|
+
const isInput = Boolean(err.issue);
|
|
806
|
+
finalize(isInput ? { status: "input", inputKind: "peer-incompatible" } : { status: "failed", error: String(err.message || err) });
|
|
807
|
+
err.opId = queued.id;
|
|
808
|
+
if (waitersEntry) waitersEntry.reject(err);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
} finally {
|
|
812
|
+
pumping = false;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return {
|
|
816
|
+
/** 入队并返回该记录终态的 Promise(waiters 持原始错误)。 */
|
|
817
|
+
enqueue(record, exec) {
|
|
818
|
+
const stored = store.upsert(record);
|
|
819
|
+
const ctx = getCtx();
|
|
820
|
+
if (ctx && ctx.syncOps) ctx.syncOps();
|
|
821
|
+
return new Promise((resolve, reject) => {
|
|
822
|
+
waiters.set(stored.id, { resolve, reject });
|
|
823
|
+
executors.set(stored.id, { exec });
|
|
824
|
+
void pump();
|
|
825
|
+
});
|
|
826
|
+
},
|
|
827
|
+
/** 启动泵(恢复流程改标后调用,处理 restored queued)。 */
|
|
828
|
+
kick() {
|
|
829
|
+
void pump();
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
async function drainRestored(store, dispatch, stillApplies) {
|
|
834
|
+
const queue = store.list().filter((r) => r.status === "queued");
|
|
835
|
+
for (const rec of queue) {
|
|
836
|
+
let applies = false;
|
|
837
|
+
try {
|
|
838
|
+
applies = await stillApplies(rec);
|
|
839
|
+
} catch {
|
|
840
|
+
applies = false;
|
|
841
|
+
}
|
|
842
|
+
if (!applies) {
|
|
843
|
+
store.upsert({ ...rec, status: "superseded", error: "\u6267\u884C\u65F6\u524D\u63D0\u6D88\u5931\uFF08\u5DF2\u624B\u52A8\u5904\u7406\uFF1F\uFF09" });
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
store.upsert({ ...rec, status: "running" });
|
|
847
|
+
let result;
|
|
848
|
+
try {
|
|
849
|
+
result = await dispatch(rec);
|
|
850
|
+
} catch (e) {
|
|
851
|
+
result = { ok: false, error: String(e && e.message || e) };
|
|
852
|
+
}
|
|
853
|
+
if (result && result.ok) {
|
|
854
|
+
store.upsert({ ...rec, status: result.warning ? "warned" : "done", warning: result.warning });
|
|
855
|
+
} else {
|
|
856
|
+
store.upsert({ ...rec, status: "failed", error: result && result.error || "unknown" });
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
var OP_STORAGE_KEY, TERMINAL_CLEARABLE;
|
|
861
|
+
var init_operations = __esm({
|
|
862
|
+
"src/client/operations.js"() {
|
|
863
|
+
"use strict";
|
|
864
|
+
OP_STORAGE_KEY = "dshm-operations";
|
|
865
|
+
TERMINAL_CLEARABLE = /* @__PURE__ */ new Set(["done", "warned"]);
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
// src/client/favorites.js
|
|
870
|
+
var favorites_exports = {};
|
|
871
|
+
__export(favorites_exports, {
|
|
872
|
+
FAV_STORAGE_KEY: () => FAV_STORAGE_KEY,
|
|
873
|
+
createFavoritesStore: () => createFavoritesStore,
|
|
874
|
+
partitionStale: () => partitionStale
|
|
875
|
+
});
|
|
876
|
+
function safeParse2(raw) {
|
|
877
|
+
try {
|
|
878
|
+
const v = JSON.parse(raw);
|
|
879
|
+
return Array.isArray(v) ? v.filter((f) => f && typeof f === "object" && typeof f.id === "string") : [];
|
|
880
|
+
} catch {
|
|
881
|
+
return [];
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
function createFavoritesStore(storage) {
|
|
885
|
+
const memory = { list: [] };
|
|
886
|
+
const canPersist = () => storage && typeof storage.getItem === "function" && typeof storage.setItem === "function";
|
|
887
|
+
let degraded = false;
|
|
888
|
+
const persist = (list) => {
|
|
889
|
+
if (!canPersist() || degraded) return;
|
|
890
|
+
try {
|
|
891
|
+
storage.setItem(FAV_STORAGE_KEY, JSON.stringify(list));
|
|
892
|
+
} catch {
|
|
893
|
+
degraded = true;
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
return {
|
|
897
|
+
get persistDegraded() {
|
|
898
|
+
return degraded || !canPersist();
|
|
899
|
+
},
|
|
900
|
+
/** @returns {FavoriteEntry[]} */
|
|
901
|
+
list() {
|
|
902
|
+
let raw = null;
|
|
903
|
+
if (canPersist() && !degraded) {
|
|
904
|
+
try {
|
|
905
|
+
raw = storage.getItem(FAV_STORAGE_KEY);
|
|
906
|
+
} catch {
|
|
907
|
+
degraded = true;
|
|
908
|
+
raw = null;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if (!canPersist() || degraded) return memory.list.map((f) => ({ ...f, snapshot: { ...f.snapshot } }));
|
|
912
|
+
return safeParse2(raw).map((f) => ({ ...f, snapshot: { ...f.snapshot } }));
|
|
913
|
+
},
|
|
914
|
+
/** toggle 收藏:已存在则移除,否则存快照。@returns 操作后的全量列表。 */
|
|
915
|
+
toggle(snapshot) {
|
|
916
|
+
const list = this.list();
|
|
917
|
+
const next = list.some((f) => f.id === snapshot.id) ? list.filter((f) => f.id !== snapshot.id) : [...list, { id: snapshot.id, savedAt: Date.now(), snapshot: { ...snapshot } }];
|
|
918
|
+
if (!canPersist() || degraded) memory.list = next;
|
|
919
|
+
persist(next);
|
|
920
|
+
return this.list();
|
|
921
|
+
},
|
|
922
|
+
/** 批量移除(清理失效收藏)。 */
|
|
923
|
+
removeIds(ids) {
|
|
924
|
+
const set = new Set(ids);
|
|
925
|
+
const next = this.list().filter((f) => !set.has(f.id));
|
|
926
|
+
if (!canPersist() || degraded) memory.list = next;
|
|
927
|
+
persist(next);
|
|
928
|
+
return this.list();
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
async function partitionStale(favorites, lookup2) {
|
|
933
|
+
const list = Array.isArray(favorites) ? favorites : [];
|
|
934
|
+
const live = [];
|
|
935
|
+
const stale = [];
|
|
936
|
+
const CONCURRENCY = 8;
|
|
937
|
+
let cursor = 0;
|
|
938
|
+
const workers = Array.from({ length: Math.min(CONCURRENCY, list.length) }, async () => {
|
|
939
|
+
for (; ; ) {
|
|
940
|
+
const i = cursor;
|
|
941
|
+
cursor += 1;
|
|
942
|
+
if (i >= list.length) return;
|
|
943
|
+
const fav = list[i];
|
|
944
|
+
let exists = false;
|
|
945
|
+
try {
|
|
946
|
+
exists = await lookup2(fav);
|
|
947
|
+
} catch {
|
|
948
|
+
exists = false;
|
|
949
|
+
}
|
|
950
|
+
if (exists) live.push(fav);
|
|
951
|
+
else stale.push(fav);
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
await Promise.all(workers);
|
|
955
|
+
const order = new Map(list.map((f, i) => [f.id, i]));
|
|
956
|
+
live.sort((a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0));
|
|
957
|
+
stale.sort((a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0));
|
|
958
|
+
return { live, stale };
|
|
959
|
+
}
|
|
960
|
+
var FAV_STORAGE_KEY;
|
|
961
|
+
var init_favorites = __esm({
|
|
962
|
+
"src/client/favorites.js"() {
|
|
963
|
+
"use strict";
|
|
964
|
+
FAV_STORAGE_KEY = "dshm-favorites";
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
|
|
581
968
|
// src/client/main.jsx
|
|
582
969
|
var React = require("react");
|
|
583
970
|
var rd = require("react-dom");
|
|
584
971
|
var h = React.createElement;
|
|
585
|
-
var { useState, useEffect, useCallback, useMemo, useRef } = React;
|
|
972
|
+
var { useState, useEffect, useLayoutEffect, useCallback, useMemo, useRef } = React;
|
|
586
973
|
var PLUGIN_ID = "dsh-m";
|
|
587
974
|
var API = "/dshm";
|
|
588
|
-
var {
|
|
975
|
+
var { DEFAULT_PAGE_SIZE: DEFAULT_PAGE_SIZE2, MARKET_PAGE_SIZES: MARKET_PAGE_SIZES2, pageItems: pageItems2, createZoneState: createZoneState2, normalizeMarketQuery: normalizeMarketQuery2, resetPageOnFilterChange: resetPageOnFilterChange2, normalizeMarketResponse: normalizeMarketResponse2, registryNotice: registryNotice2, zoneChips: zoneChips2, marketNotice: marketNotice2 } = (init_market_state(), __toCommonJS(market_state_exports));
|
|
589
976
|
var { createMarkdown: createMarkdown2 } = (init_markdown(), __toCommonJS(markdown_exports));
|
|
590
977
|
var { ExtLink, MdImg, renderMarkdown } = createMarkdown2(h);
|
|
591
978
|
var { installedViewModel: installedViewModel2, registrySourceKey: registrySourceKey2 } = (init_installed_view(), __toCommonJS(installed_view_exports));
|
|
@@ -593,6 +980,8 @@ var { toggleViewModel: toggleViewModel2, toggleNoticeKeys: toggleNoticeKeys2 } =
|
|
|
593
980
|
var { pickPayload: pickPayload2, parseToolArgs: parseToolArgs2 } = (init_tool_view(), __toCommonJS(tool_view_exports));
|
|
594
981
|
var { RESTART_POLL_MS: RESTART_POLL_MS2, RESTART_DEADLINE_MS: RESTART_DEADLINE_MS2, nextRestartWait: nextRestartWait2, isAmbiguousRestartRequestError: isAmbiguousRestartRequestError2 } = (init_restart_wait(), __toCommonJS(restart_wait_exports));
|
|
595
982
|
var { refreshAfterMutation: refreshAfterMutation2 } = (init_view_refresh(), __toCommonJS(view_refresh_exports));
|
|
983
|
+
var { createOperationsStore: createOperationsStore2, restoreRecords: restoreRecords2, createOpsPump: createOpsPump2, opAppliesTo: opAppliesTo2 } = (init_operations(), __toCommonJS(operations_exports));
|
|
984
|
+
var { createFavoritesStore: createFavoritesStore2, partitionStale: partitionStale2 } = (init_favorites(), __toCommonJS(favorites_exports));
|
|
596
985
|
var ZH = {
|
|
597
986
|
"market.title": "\u63D2\u4EF6\u5E02\u573A",
|
|
598
987
|
"tab.market": "\u5E02\u573A",
|
|
@@ -604,6 +993,47 @@ var ZH = {
|
|
|
604
993
|
"cat.ui": "\u754C\u9762",
|
|
605
994
|
"cat.search": "\u641C\u7D22",
|
|
606
995
|
"cat.other": "\u5176\u4ED6",
|
|
996
|
+
"zone.community": "\u793E\u533A",
|
|
997
|
+
"zone.primary": "\u7CBE\u9009",
|
|
998
|
+
"zone.favorites": "\u6536\u85CF",
|
|
999
|
+
"badge.deprecated": "\u5DF2\u5F03\u7528",
|
|
1000
|
+
"sub.snapshot": "v{v}\uFF08\u76EE\u5F55\u5FEB\u7167\uFF09",
|
|
1001
|
+
"badge.verified": "\u5DF2\u5B9E\u6D4B",
|
|
1002
|
+
"modal.category": "\u5206\u7C7B",
|
|
1003
|
+
"modal.added": "\u6536\u5F55\u65E5\u671F",
|
|
1004
|
+
"modal.dlwindow": "\u4E0B\u8F7D\u91CF\uFF0830 \u5929\u7A97\u53E3\uFF09",
|
|
1005
|
+
"modal.checkedat": "\u6838\u5BF9\u4E8E",
|
|
1006
|
+
"modal.dlnone": "\u65E0\u7A97\u53E3\u6570\u636E",
|
|
1007
|
+
"modal.verified": "\u5B9E\u6D4B\u7248\u672C",
|
|
1008
|
+
"modal.tags": "\u6807\u7B7E",
|
|
1009
|
+
"modal.replacement": "\u5DF2\u5F03\u7528 \xB7 \u66FF\u4EE3",
|
|
1010
|
+
"modal.installcmd": "\u5B89\u88C5\u547D\u4EE4",
|
|
1011
|
+
"modal.copy": "\u590D\u5236",
|
|
1012
|
+
"modal.copied": "\u5DF2\u590D\u5236",
|
|
1013
|
+
"op.clear": "\u6E05\u9664\u5DF2\u5B8C\u6210",
|
|
1014
|
+
"op.superseded.note": "{target} \u5DF2\u8DF3\u8FC7\uFF08\u524D\u63D0\u5DF2\u4E0D\u6210\u7ACB\u6216\u5DF2\u624B\u52A8\u5904\u7406\uFF09",
|
|
1015
|
+
"op.cancelled": "\u7528\u6237\u653E\u5F03\u786E\u8BA4",
|
|
1016
|
+
"op.remove": "\u79FB\u9664\u8BB0\u5F55",
|
|
1017
|
+
"op.panel.jump": "\u67E5\u770B\u64CD\u4F5C\u9762\u677F",
|
|
1018
|
+
"op.kind.install": "\u5B89\u88C5",
|
|
1019
|
+
"op.kind.upgrade": "\u5347\u7EA7",
|
|
1020
|
+
"op.kind.uninstall": "\u5378\u8F7D",
|
|
1021
|
+
"op.kind.toggle": "\u5F00\u5173",
|
|
1022
|
+
"op.status.queued": "\u6392\u961F\u4E2D",
|
|
1023
|
+
"op.status.running": "\u8FDB\u884C\u4E2D",
|
|
1024
|
+
"op.status.input": "\u5F85\u51B3",
|
|
1025
|
+
"op.status.done": "\u5B8C\u6210",
|
|
1026
|
+
"op.status.warned": "\u5E26\u8B66\u544A",
|
|
1027
|
+
"op.status.failed": "\u5931\u8D25",
|
|
1028
|
+
"op.status.superseded": "\u5DF2\u8DF3\u8FC7",
|
|
1029
|
+
"favorites.hint": "\u8FD8\u6CA1\u6709\u6536\u85CF\u2014\u2014\u53BB\u793E\u533A/\u7CBE\u9009\u9875\u70B9\u63D2\u4EF6\u5361\u7247\u53F3\u4E0A\u89D2\u7684 \u2606 \u6536\u85CF",
|
|
1030
|
+
"favorites.stale": "{n} \u6761\u6536\u85CF\u5DF2\u4ECE\u76EE\u5F55\u4E0B\u67B6",
|
|
1031
|
+
"favorites.clean": "\u6E05\u7406\u5931\u6548\u6536\u85CF",
|
|
1032
|
+
"favorites.checking": "\u6821\u9A8C\u6536\u85CF\u6709\u6548\u6027\u4E2D\u2026",
|
|
1033
|
+
"favorites.stalebadge": "\u5DF2\u4E0B\u67B6",
|
|
1034
|
+
"fav.add": "\u6536\u85CF",
|
|
1035
|
+
"fav.remove": "\u53D6\u6D88\u6536\u85CF",
|
|
1036
|
+
"common.clear": "\u6E05\u7A7A",
|
|
607
1037
|
"search.ph": "\u641C\u7D22\u540D\u79F0 / \u63CF\u8FF0 / \u6807\u7B7E\u2026",
|
|
608
1038
|
"common.refresh": "\u5237\u65B0",
|
|
609
1039
|
"common.close": "\u5173\u95ED",
|
|
@@ -611,11 +1041,12 @@ var ZH = {
|
|
|
611
1041
|
"common.ok": "\u77E5\u9053\u4E86",
|
|
612
1042
|
"common.none": "\u2014",
|
|
613
1043
|
"market.loading": "\u52A0\u8F7D\u6536\u5F55\u6E05\u5355\u4E2D\u2026 ",
|
|
614
|
-
"market.empty": "\
|
|
1044
|
+
"market.empty": "\u65E0\u5339\u914D\u63D2\u4EF6\uFF0C\u8BD5\u8BD5\u5176\u4ED6\u5173\u952E\u8BCD\u6216\u5206\u7C7B",
|
|
615
1045
|
"installed.loading": "\u8BFB\u53D6 web profile \u4E2D\u2026 ",
|
|
616
1046
|
"installed.empty": "web profile \u5C1A\u672A\u5B89\u88C5\u4EFB\u4F55 dsh \u63D2\u4EF6",
|
|
617
1047
|
"installed.none": "\u672A\u5B89\u88C5",
|
|
618
1048
|
"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",
|
|
1049
|
+
"installed.upgradeAll": "\u5168\u90E8\u66F4\u65B0 ({n})",
|
|
619
1050
|
"badge.installed": "\u5DF2\u5B89\u88C5",
|
|
620
1051
|
"badge.update": "\u53EF\u5347\u7EA7",
|
|
621
1052
|
"badge.market": "\u5E02\u573A\u5B89\u88C5",
|
|
@@ -650,23 +1081,17 @@ var ZH = {
|
|
|
650
1081
|
"sub.latest": "\u6700\u65B0 v{v}",
|
|
651
1082
|
"sub.head": "HEAD {sha}",
|
|
652
1083
|
"sub.installed": "\u5DF2\u88C5 v{v}",
|
|
653
|
-
"settings.registry": "\
|
|
1084
|
+
"settings.registry": "\u7CBE\u9009\u6E05\u5355\uFF08registry.json\uFF09",
|
|
654
1085
|
"settings.source": "\u5F53\u524D\u6765\u6E90",
|
|
655
1086
|
"settings.updated": "\u66F4\u65B0\u65F6\u95F4",
|
|
656
1087
|
"settings.count": "\u6761\u76EE\u6570",
|
|
657
|
-
"settings.count.v": "{n}
|
|
658
|
-
"settings.policy": "\u7F13\u5B58\u7B56\u7565",
|
|
659
|
-
"settings.policy.v": "TTL 60 \u5206\u949F\uFF1B\u8BBE\u7F6E registryUrl \u53EF\u8986\u76D6\u6E90",
|
|
1088
|
+
"settings.count.v": "{n}",
|
|
660
1089
|
"settings.remotehint": "\u8FDC\u7AEF\u63D0\u793A",
|
|
661
1090
|
"settings.force": "\u5F3A\u5236\u5237\u65B0",
|
|
662
|
-
"settings.self": "dsh-m \u81EA\u8EAB",
|
|
663
|
-
"settings.current": "\u5F53\u524D\u7248\u672C",
|
|
664
|
-
"settings.npmlatest": "npm \u6700\u65B0",
|
|
665
|
-
"settings.lookupfailed": "\u67E5\u8BE2\u5931\u8D25\uFF1A{err}",
|
|
666
|
-
"settings.upgradeself": "\u5347\u7EA7 dsh-m",
|
|
667
|
-
"settings.upgradehint": "\u5347\u7EA7\u540E\u540C\u6837\u9700\u8981\u91CD\u542F\u751F\u6548",
|
|
668
1091
|
"settings.about": "\u5173\u4E8E",
|
|
669
|
-
"settings.about.text": "
|
|
1092
|
+
"settings.about.text": "dsh-m \u63D2\u4EF6\u5E02\u573A\uFF1A\u300CAwesome DSH Plugin\u300D\u793E\u533A + \u300C\u7CBE\u9009\u7B56\u5C55\u300D\u7684\u53CC\u6E05\u5355\u5206\u533A\u5E02\u573A\uFF0C\u6536\u5F55\u3001\u5B89\u88C5\u3001\u5378\u8F7D\u3001\u5347\u7EA7\u4E00\u7AD9\u5B8C\u6210\u3002",
|
|
1093
|
+
"settings.about.issues": "\u95EE\u9898\u53CD\u9988",
|
|
1094
|
+
"settings.about.npm": "npm",
|
|
670
1095
|
"src.override": "\u81EA\u5B9A\u4E49\u6E90",
|
|
671
1096
|
"src.jsdelivr": "GitHub \u955C\u50CF\uFF08\u5907\u7528\uFF09",
|
|
672
1097
|
"src.raw": "raw.githubusercontent\uFF08@main\uFF09",
|
|
@@ -680,14 +1105,14 @@ var ZH = {
|
|
|
680
1105
|
"src.custom.cache": "\u81EA\u5B9A\u4E49\u6E90\uFF08\u7F13\u5B58\uFF09",
|
|
681
1106
|
"src.custom.unavailable": "\u81EA\u5B9A\u4E49\u6E90\uFF08\u4E0D\u53EF\u7528\uFF09",
|
|
682
1107
|
"settings.address": "Registry \u5730\u5740",
|
|
683
|
-
"settings.address.
|
|
1108
|
+
"settings.address.default": "\u5185\u7F6E\u9ED8\u8BA4\u6E05\u5355",
|
|
1109
|
+
"settings.note.custom": "\u81EA\u5B9A\u4E49\u6E90\u5C06\u6574\u4F53\u66FF\u6362\u300C\u7CBE\u9009\u6E05\u5355\u300D\uFF1B\u6761\u76EE\u683C\u5F0F\u53EF\u53C2\u7167\u4E0B\u8F7D\u7684\u9ED8\u8BA4\u6E05\u5355\u3002\u7559\u7A7A = \u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u6E05\u5355\u3002",
|
|
684
1110
|
"settings.address.ph": "https://example.com/registry.json \u6216 /path/to/registry.json",
|
|
685
1111
|
"settings.configured": "\u914D\u7F6E\u5730\u5740",
|
|
686
|
-
"settings.activecfg": "\u5F53\u524D\u751F\u6548\u914D\u7F6E",
|
|
687
1112
|
"settings.effective": "\u751F\u6548\u6765\u6E90",
|
|
688
1113
|
"settings.status.label": "\u914D\u7F6E\u72B6\u6001",
|
|
689
1114
|
"settings.status.loading": "\u52A0\u8F7D\u4E2D",
|
|
690
|
-
"settings.status.ready": "\
|
|
1115
|
+
"settings.status.ready": "\u6B63\u5E38",
|
|
691
1116
|
"settings.status.pending": "\u5F85\u5199\u5165\uFF08\u6821\u9A8C\u5DF2\u901A\u8FC7\uFF09",
|
|
692
1117
|
"settings.status.rejected": "\u5DF2\u62D2\u7EDD\uFF08\u4FDD\u6301\u65E7\u914D\u7F6E\uFF09",
|
|
693
1118
|
"settings.status.unavailable": "\u4E0D\u53EF\u7528",
|
|
@@ -697,31 +1122,31 @@ var ZH = {
|
|
|
697
1122
|
"settings.apply.failed": "\u5E94\u7528\u5931\u8D25\uFF1A{err}",
|
|
698
1123
|
"settings.reset": "\u6062\u590D\u9ED8\u8BA4",
|
|
699
1124
|
"settings.reset.ok": "\u5DF2\u6062\u590D\u9ED8\u8BA4\u6536\u5F55\u6E05\u5355",
|
|
700
|
-
"settings.download": "\u4E0B\u8F7D\u9ED8\u8BA4
|
|
1125
|
+
"settings.download": "\u4E0B\u8F7D\u9ED8\u8BA4\u6E05\u5355",
|
|
1126
|
+
"settings.download.title": "\u4E0B\u8F7D\u9ED8\u8BA4 registry.json\uFF08\u53EF\u4F5C\u81EA\u5B9A\u4E49\u6A21\u677F\u4E0E\u683C\u5F0F\u53C2\u7167\uFF09",
|
|
701
1127
|
"settings.download.downloading": "\u4E0B\u8F7D\u4E2D\u2026",
|
|
702
1128
|
"settings.download.ok": "\u9ED8\u8BA4\u6E05\u5355\u5DF2\u4E0B\u8F7D\uFF08\u5F53\u524D\u914D\u7F6E\u4E0D\u53D8\uFF09",
|
|
703
1129
|
"settings.download.failed": "\u4E0B\u8F7D\u5931\u8D25\uFF1A{err}",
|
|
704
|
-
"settings.diagnose": "\u68C0\u67E5\u6761\u76EE\u53EF\u8FBE\u6027",
|
|
705
|
-
"settings.diagnose.running": "\u8BCA\u65AD\u4E2D\u2026",
|
|
706
|
-
"settings.diagnose.failed": "\u8BCA\u65AD\u5931\u8D25\uFF1A{err}",
|
|
707
|
-
"settings.diagnose.result": "\u63A2\u6D4B {checked} \u9879 \xB7 \u901A\u8FC7 {passed} \xB7 \u5931\u8D25 {failed}{trunc}",
|
|
708
|
-
"settings.diagnose.truncated": "\uFF08\u4EC5\u663E\u793A\u524D 100 \u6761\u95EE\u9898\uFF09",
|
|
709
|
-
"settings.diagnose.none": "\u672A\u53D1\u73B0\u95EE\u9898",
|
|
710
1130
|
"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",
|
|
711
|
-
"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",
|
|
712
1131
|
"settings.warnings": "\u7EF4\u62A4\u63D0\u793A",
|
|
713
|
-
"notice.default": "\u5B98\u65B9\u9ED8\u8BA4\u6536\u5F55\u6E05\u5355 \xB7 \u5171 {count} \u6761",
|
|
714
|
-
"notice.custom": "\u81EA\u5B9A\u4E49\u6536\u5F55\u6E05\u5355 \xB7 \u5171 {count} \u6761",
|
|
715
|
-
"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",
|
|
716
1132
|
"notice.unavailable": "\u6536\u5F55\u6E05\u5355\u4E0D\u53EF\u7528 \xB7 \u8BF7\u5230\u8BBE\u7F6E\u9875\u68C0\u67E5\u5730\u5740",
|
|
1133
|
+
"pager.jump": "\u8DF3\u8F6C",
|
|
1134
|
+
"pager.jump.ph": "\u9875\u53F7",
|
|
1135
|
+
"panel.fullscreen": "\u5168\u5C4F",
|
|
1136
|
+
"panel.restore": "\u8FD8\u539F",
|
|
1137
|
+
"filter.title": "\u7B5B\u9009",
|
|
1138
|
+
"filter.sortfield": "\u6392\u5E8F\u5B57\u6BB5",
|
|
1139
|
+
"filter.sortdir": "\u6392\u5217\u65B9\u5411",
|
|
1140
|
+
"filter.pagesize": "\u6BCF\u9875\u6761\u6570",
|
|
1141
|
+
"filter.field.downloads": "npm \u4E0B\u8F7D\u91CF\uFF08\u8FD1 30 \u5929\uFF09",
|
|
1142
|
+
"filter.field.stars": "Star \u6570",
|
|
1143
|
+
"filter.field.added": "\u6536\u5F55\u65E5\u671F",
|
|
1144
|
+
"filter.dir.desc": "\u964D\u5E8F",
|
|
1145
|
+
"filter.dir.asc": "\u5347\u5E8F",
|
|
717
1146
|
"market.page.prev": "\u4E0A\u4E00\u9875",
|
|
718
1147
|
"market.page.next": "\u4E0B\u4E00\u9875",
|
|
719
1148
|
"market.page.info": "\u7B2C {page} / {pages} \u9875 \xB7 \u5171 {total} \u6761",
|
|
720
|
-
"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",
|
|
721
1149
|
"notice.toolview.err": "\u6536\u5F55\u6E05\u5355\u6682\u4E0D\u53EF\u7528",
|
|
722
|
-
"cat.primaryonly": "\u53EA\u770B\u4E3B\u6E05\u5355",
|
|
723
|
-
"community.group": "\u793E\u533A",
|
|
724
|
-
"community.newgroup": "\u793E\u533A \xB7 \u65B0\u5206\u7C7B",
|
|
725
1150
|
"badge.community": "\u793E\u533A\u6536\u5F55",
|
|
726
1151
|
"community.stale": "\u793E\u533A\u76EE\u5F55\u4E3A\u7F13\u5B58\u5FEB\u7167\uFF08\u663E\u793A\u7684\u4E0D\u662F\u6700\u65B0\u6570\u636E\uFF09",
|
|
727
1152
|
"community.fallback": "\u6536\u5F55\u6E05\u5355\u4E0D\u53EF\u7528\uFF0C\u5F53\u524D\u5C55\u793A\u793E\u533A\u6E05\u5355\u6761\u76EE",
|
|
@@ -736,15 +1161,20 @@ var ZH = {
|
|
|
736
1161
|
"guard.noforce": "\u5B88\u536B\u62E6\u622A\u65E0\u300C\u4ECD\u8981\u5B89\u88C5\u300D\u901A\u9053\uFF0C\u8BF7\u6309\u4FEE\u590D\u4F9D\u636E\u4EBA\u5DE5\u5904\u7406",
|
|
737
1162
|
"detail.screenshots": "\u622A\u56FE",
|
|
738
1163
|
"installed.check.incomplete": "\u68C0\u67E5\u672A\u5B8C\u6210",
|
|
739
|
-
"settings.community": "\u793E\u533A\u6E05\u5355\
|
|
740
|
-
"settings.community.
|
|
1164
|
+
"settings.community": "\u793E\u533A\u6E05\u5355\uFF08from awesome-dsh-plugin\uFF09",
|
|
1165
|
+
"settings.community.toggle": "\u793E\u533A\u76EE\u5F55\u5F00\u5173",
|
|
1166
|
+
"settings.community.off": "\u793E\u533A\u76EE\u5F55\u5DF2\u5173\u95ED\uFF1A\u5E02\u573A\u4EC5\u663E\u793A\u7CBE\u9009\u6E05\u5355\uFF0C\u91CD\u65B0\u5F00\u542F\u5373\u65F6\u751F\u6548\u3002",
|
|
1167
|
+
"settings.community.on.ok": "\u793E\u533A\u76EE\u5F55\u5DF2\u5F00\u542F\uFF08\u5373\u65F6\u751F\u6548\uFF09",
|
|
1168
|
+
"settings.community.off.ok": "\u793E\u533A\u76EE\u5F55\u5DF2\u5173\u95ED\uFF08\u5373\u65F6\u751F\u6548\uFF09",
|
|
1169
|
+
"settings.community.toggle.failed": "\u5207\u6362\u5931\u8D25\uFF1A{err}",
|
|
741
1170
|
"settings.community.status": "\u72B6\u6001",
|
|
1171
|
+
"settings.community.status.stale": "\u7F13\u5B58\u5FEB\u7167",
|
|
742
1172
|
"settings.community.version": "\u76EE\u5F55\u7248\u672C",
|
|
743
1173
|
"settings.community.route": "\u83B7\u53D6\u7EBF\u8DEF",
|
|
744
|
-
"settings.community.
|
|
745
|
-
"settings.community.
|
|
746
|
-
"
|
|
747
|
-
"
|
|
1174
|
+
"settings.community.count": "\u6536\u5F55\u6761\u76EE",
|
|
1175
|
+
"settings.community.count.v": "{n}\uFF08\u4E0A\u6E38 {up}\uFF09",
|
|
1176
|
+
"settings.community.displaced": "\u4E0E\u7CBE\u9009\u91CD\u590D",
|
|
1177
|
+
"settings.community.displaced.v": "{n} \u6761",
|
|
748
1178
|
"registry.refreshed": "\u6536\u5F55\u6E05\u5355\u5DF2\u5F3A\u5236\u5237\u65B0",
|
|
749
1179
|
"notify.installed": "\u5DF2\u5B89\u88C5 {pkg}{version}",
|
|
750
1180
|
"notify.allowbuilds": "\uFF08\u6CE8\u610F\uFF1A\u8BE5\u63D2\u4EF6\u6267\u884C\u4E86\u6784\u5EFA\u811A\u672C\uFF0C\u5DF2\u6309\u7B56\u7565\u653E\u884C\uFF09",
|
|
@@ -779,7 +1209,6 @@ var ZH = {
|
|
|
779
1209
|
"failed.install": "\u5B89\u88C5\u5931\u8D25\uFF1A{err}",
|
|
780
1210
|
"failed.uninstall": "\u5378\u8F7D\u5931\u8D25\uFF1A{err}",
|
|
781
1211
|
"failed.upgrade": "\u5347\u7EA7\u5931\u8D25\uFF1A{err}",
|
|
782
|
-
"failed.selfupdate": "\u81EA\u66F4\u65B0\u5931\u8D25\uFF1A{err}",
|
|
783
1212
|
"failed.load": "\u52A0\u8F7D\u5931\u8D25\uFF1A{err}",
|
|
784
1213
|
"failed.read": "\u8BFB\u53D6\u5931\u8D25\uFF1A{err}",
|
|
785
1214
|
"failed.open": "\u6253\u5F00\u5E02\u573A\u9762\u677F\u5931\u8D25:",
|
|
@@ -803,8 +1232,6 @@ var ZH = {
|
|
|
803
1232
|
"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",
|
|
804
1233
|
"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",
|
|
805
1234
|
"profile.hint": "web profile\uFF1A{path}",
|
|
806
|
-
"dsh.chip.copyhint": "\u70B9\u51FB\u590D\u5236\u7248\u672C\u53F7",
|
|
807
|
-
"dsh.chip.copied": "\u5DF2\u590D\u5236 \u2713",
|
|
808
1235
|
"title.panel": "\u63D2\u4EF6\u5E02\u573A",
|
|
809
1236
|
"title.full": "DeepSeek Harness \u63D2\u4EF6\u5E02\u573A"
|
|
810
1237
|
};
|
|
@@ -819,6 +1246,47 @@ var EN = {
|
|
|
819
1246
|
"cat.ui": "UI",
|
|
820
1247
|
"cat.search": "Search",
|
|
821
1248
|
"cat.other": "Other",
|
|
1249
|
+
"zone.community": "Community",
|
|
1250
|
+
"zone.primary": "Curated",
|
|
1251
|
+
"zone.favorites": "Favorites",
|
|
1252
|
+
"badge.deprecated": "Deprecated",
|
|
1253
|
+
"sub.snapshot": "v{v} (catalog snapshot)",
|
|
1254
|
+
"badge.verified": "Verified",
|
|
1255
|
+
"modal.category": "Category",
|
|
1256
|
+
"modal.added": "Added",
|
|
1257
|
+
"modal.dlwindow": "Downloads (30-day window)",
|
|
1258
|
+
"modal.checkedat": "checked at",
|
|
1259
|
+
"modal.dlnone": "No window data",
|
|
1260
|
+
"modal.verified": "Verified runtimes",
|
|
1261
|
+
"modal.tags": "Tags",
|
|
1262
|
+
"modal.replacement": "Deprecated \xB7 replacement",
|
|
1263
|
+
"modal.installcmd": "Install command",
|
|
1264
|
+
"modal.copy": "Copy",
|
|
1265
|
+
"modal.copied": "Copied",
|
|
1266
|
+
"op.clear": "Clear finished",
|
|
1267
|
+
"op.superseded.note": "{target} skipped (precondition gone or already handled)",
|
|
1268
|
+
"op.cancelled": "user cancelled",
|
|
1269
|
+
"op.remove": "Dismiss",
|
|
1270
|
+
"op.panel.jump": "Open operations panel",
|
|
1271
|
+
"op.kind.install": "Install",
|
|
1272
|
+
"op.kind.upgrade": "Upgrade",
|
|
1273
|
+
"op.kind.uninstall": "Uninstall",
|
|
1274
|
+
"op.kind.toggle": "Toggle",
|
|
1275
|
+
"op.status.queued": "Queued",
|
|
1276
|
+
"op.status.running": "Running",
|
|
1277
|
+
"op.status.input": "Pending",
|
|
1278
|
+
"op.status.done": "Done",
|
|
1279
|
+
"op.status.warned": "Warned",
|
|
1280
|
+
"op.status.failed": "Failed",
|
|
1281
|
+
"op.status.superseded": "Skipped",
|
|
1282
|
+
"favorites.hint": "No favorites yet \u2014 tap \u2606 on a plugin card in Community/Curated to bookmark it",
|
|
1283
|
+
"favorites.stale": "{n} favorites no longer in the catalog",
|
|
1284
|
+
"favorites.clean": "Clean up stale favorites",
|
|
1285
|
+
"favorites.checking": "Checking favorites\u2026",
|
|
1286
|
+
"favorites.stalebadge": "Delisted",
|
|
1287
|
+
"fav.add": "Bookmark",
|
|
1288
|
+
"fav.remove": "Remove bookmark",
|
|
1289
|
+
"common.clear": "Clear",
|
|
822
1290
|
"search.ph": "Search name, description, tags\u2026",
|
|
823
1291
|
"common.refresh": "Refresh",
|
|
824
1292
|
"common.close": "Close",
|
|
@@ -826,11 +1294,12 @@ var EN = {
|
|
|
826
1294
|
"common.ok": "OK",
|
|
827
1295
|
"common.none": "\u2014",
|
|
828
1296
|
"market.loading": "Loading listings\u2026 ",
|
|
829
|
-
"market.empty": "No matching
|
|
1297
|
+
"market.empty": "No matching plugins \u2014 try another keyword or category",
|
|
830
1298
|
"installed.loading": "Reading web profile\u2026 ",
|
|
831
1299
|
"installed.empty": "No DSH plugins installed in this web profile",
|
|
832
1300
|
"installed.none": "Not installed",
|
|
833
1301
|
"installed.others": "{n} non-DSH dependencies (not recognized as plugins) are collapsed.",
|
|
1302
|
+
"installed.upgradeAll": "Update all ({n})",
|
|
834
1303
|
"badge.installed": "Installed",
|
|
835
1304
|
"badge.update": "Update",
|
|
836
1305
|
"badge.market": "Via market",
|
|
@@ -865,23 +1334,17 @@ var EN = {
|
|
|
865
1334
|
"sub.latest": "Latest v{v}",
|
|
866
1335
|
"sub.head": "HEAD {sha}",
|
|
867
1336
|
"sub.installed": "Installed v{v}",
|
|
868
|
-
"settings.registry": "
|
|
1337
|
+
"settings.registry": "Curated registry (registry.json)",
|
|
869
1338
|
"settings.source": "Source",
|
|
870
1339
|
"settings.updated": "Updated",
|
|
871
1340
|
"settings.count": "Listings",
|
|
872
|
-
"settings.count.v": "{n}
|
|
873
|
-
"settings.policy": "Caching",
|
|
874
|
-
"settings.policy.v": "60 min TTL; override via registryUrl",
|
|
1341
|
+
"settings.count.v": "{n}",
|
|
875
1342
|
"settings.remotehint": "Remote notice",
|
|
876
1343
|
"settings.force": "Force refresh",
|
|
877
|
-
"settings.self": "dsh-m itself",
|
|
878
|
-
"settings.current": "Current version",
|
|
879
|
-
"settings.npmlatest": "npm latest",
|
|
880
|
-
"settings.lookupfailed": "lookup failed: {err}",
|
|
881
|
-
"settings.upgradeself": "Upgrade dsh-m",
|
|
882
|
-
"settings.upgradehint": "A restart is required after upgrading",
|
|
883
1344
|
"settings.about": "About",
|
|
884
|
-
"settings.about.text":
|
|
1345
|
+
"settings.about.text": 'The DeepSeek Harness plugin marketplace \u2014 a zoned dual-catalog market of the "Awesome DSH Plugin" community plus a curated registry; install, uninstall and upgrade in one place.',
|
|
1346
|
+
"settings.about.issues": "Issues",
|
|
1347
|
+
"settings.about.npm": "npm",
|
|
885
1348
|
"src.override": "Custom source",
|
|
886
1349
|
"src.jsdelivr": "GitHub mirror (backup)",
|
|
887
1350
|
"src.raw": "raw.githubusercontent (@main)",
|
|
@@ -895,14 +1358,14 @@ var EN = {
|
|
|
895
1358
|
"src.custom.cache": "Custom source (cache)",
|
|
896
1359
|
"src.custom.unavailable": "Custom source (unavailable)",
|
|
897
1360
|
"settings.address": "Registry address",
|
|
898
|
-
"settings.address.
|
|
1361
|
+
"settings.address.default": "Built-in default registry",
|
|
1362
|
+
"settings.note.custom": "A custom source replaces the whole Curated registry; use the downloaded default registry as the format reference. Leave empty for the built-in default.",
|
|
899
1363
|
"settings.address.ph": "https://example.com/registry.json or /path/to/registry.json",
|
|
900
1364
|
"settings.configured": "Configured address",
|
|
901
|
-
"settings.activecfg": "Active config",
|
|
902
1365
|
"settings.effective": "Effective source",
|
|
903
1366
|
"settings.status.label": "Config status",
|
|
904
1367
|
"settings.status.loading": "Loading",
|
|
905
|
-
"settings.status.ready": "
|
|
1368
|
+
"settings.status.ready": "Online",
|
|
906
1369
|
"settings.status.pending": "Pending write (validated)",
|
|
907
1370
|
"settings.status.rejected": "Rejected (previous config kept)",
|
|
908
1371
|
"settings.status.unavailable": "Unavailable",
|
|
@@ -912,31 +1375,31 @@ var EN = {
|
|
|
912
1375
|
"settings.apply.failed": "Apply failed: {err}",
|
|
913
1376
|
"settings.reset": "Restore default",
|
|
914
1377
|
"settings.reset.ok": "Restored to the default registry",
|
|
915
|
-
"settings.download": "Download default registry
|
|
1378
|
+
"settings.download": "Download default registry",
|
|
1379
|
+
"settings.download.title": "Download the default registry.json (a template and format reference for custom registries)",
|
|
916
1380
|
"settings.download.downloading": "Downloading\u2026",
|
|
917
1381
|
"settings.download.ok": "Default registry downloaded (current config unchanged)",
|
|
918
1382
|
"settings.download.failed": "Download failed: {err}",
|
|
919
|
-
"settings.diagnose": "Check entries reachability",
|
|
920
|
-
"settings.diagnose.running": "Checking\u2026",
|
|
921
|
-
"settings.diagnose.failed": "Diagnose failed: {err}",
|
|
922
|
-
"settings.diagnose.result": "Probes {checked} \xB7 passed {passed} \xB7 failed {failed}{trunc}",
|
|
923
|
-
"settings.diagnose.truncated": " (showing first 100 issues)",
|
|
924
|
-
"settings.diagnose.none": "No issues found",
|
|
925
1383
|
"settings.trust.hint": "\u26A0\uFE0F Custom registries are not validated by official CI. Only install entries from sources you trust.",
|
|
926
|
-
"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.",
|
|
927
1384
|
"settings.warnings": "Maintenance notice",
|
|
928
|
-
"notice.default": "Official default registry \xB7 {count} listings",
|
|
929
|
-
"notice.custom": "Custom registry \xB7 {count} listings",
|
|
930
|
-
"notice.stale": "Served from local cache ({count} listings) \u2014 force refresh to update",
|
|
931
1385
|
"notice.unavailable": "Registry unavailable \xB7 check the address in Settings",
|
|
1386
|
+
"pager.jump": "Go",
|
|
1387
|
+
"pager.jump.ph": "Page",
|
|
1388
|
+
"panel.fullscreen": "Fullscreen",
|
|
1389
|
+
"panel.restore": "Restore",
|
|
1390
|
+
"filter.title": "Filter",
|
|
1391
|
+
"filter.sortfield": "Sort by",
|
|
1392
|
+
"filter.sortdir": "Direction",
|
|
1393
|
+
"filter.pagesize": "Per page",
|
|
1394
|
+
"filter.field.downloads": "npm downloads (30-day)",
|
|
1395
|
+
"filter.field.stars": "Stars",
|
|
1396
|
+
"filter.field.added": "Date added",
|
|
1397
|
+
"filter.dir.desc": "Descending",
|
|
1398
|
+
"filter.dir.asc": "Ascending",
|
|
932
1399
|
"market.page.prev": "Previous",
|
|
933
1400
|
"market.page.next": "Next",
|
|
934
1401
|
"market.page.info": "Page {page} / {pages} \xB7 {total} listings",
|
|
935
|
-
"market.perf": "200+ listings: latest versions are queried for the current page only (50 per page); use search/category filters",
|
|
936
1402
|
"notice.toolview.err": "Registry temporarily unavailable",
|
|
937
|
-
"cat.primaryonly": "Primary only",
|
|
938
|
-
"community.group": "Community",
|
|
939
|
-
"community.newgroup": "Community \xB7 New",
|
|
940
1403
|
"badge.community": "Community",
|
|
941
1404
|
"community.stale": "Community catalog served from cache (not the latest data)",
|
|
942
1405
|
"community.fallback": "Registry unavailable \u2014 showing community listings",
|
|
@@ -951,15 +1414,20 @@ var EN = {
|
|
|
951
1414
|
"guard.noforce": "Guard blocks have no force channel \u2014 repair manually per the basis above",
|
|
952
1415
|
"detail.screenshots": "Screenshots",
|
|
953
1416
|
"installed.check.incomplete": "Check incomplete",
|
|
954
|
-
"settings.community": "Community catalog (awesome-dsh-plugin)",
|
|
955
|
-
"settings.community.
|
|
1417
|
+
"settings.community": "Community catalog (from awesome-dsh-plugin)",
|
|
1418
|
+
"settings.community.toggle": "Community catalog toggle",
|
|
1419
|
+
"settings.community.off": "Community catalog is off: the market shows only the Curated registry. Toggle back on anytime \u2014 it applies live.",
|
|
1420
|
+
"settings.community.on.ok": "Community catalog enabled (live)",
|
|
1421
|
+
"settings.community.off.ok": "Community catalog disabled (live)",
|
|
1422
|
+
"settings.community.toggle.failed": "Toggle failed: {err}",
|
|
956
1423
|
"settings.community.status": "Status",
|
|
1424
|
+
"settings.community.status.stale": "Cached snapshot",
|
|
957
1425
|
"settings.community.version": "Catalog version",
|
|
958
1426
|
"settings.community.route": "Route",
|
|
959
|
-
"settings.community.
|
|
960
|
-
"settings.community.
|
|
961
|
-
"
|
|
962
|
-
"
|
|
1427
|
+
"settings.community.count": "Entries",
|
|
1428
|
+
"settings.community.count.v": "{n} (upstream {up})",
|
|
1429
|
+
"settings.community.displaced": "Displaced (duplicates)",
|
|
1430
|
+
"settings.community.displaced.v": "{n}",
|
|
963
1431
|
"registry.refreshed": "Registry force-refreshed",
|
|
964
1432
|
"notify.installed": "Installed {pkg}{version}",
|
|
965
1433
|
"notify.allowbuilds": " (note: this plugin ran build scripts, allowed by policy)",
|
|
@@ -994,7 +1462,6 @@ var EN = {
|
|
|
994
1462
|
"failed.install": "Install failed: {err}",
|
|
995
1463
|
"failed.uninstall": "Uninstall failed: {err}",
|
|
996
1464
|
"failed.upgrade": "Upgrade failed: {err}",
|
|
997
|
-
"failed.selfupdate": "Self-update failed: {err}",
|
|
998
1465
|
"failed.load": "Load failed: {err}",
|
|
999
1466
|
"failed.read": "Read failed: {err}",
|
|
1000
1467
|
"failed.open": "Failed to open the marketplace panel:",
|
|
@@ -1018,8 +1485,6 @@ var EN = {
|
|
|
1018
1485
|
"warn.unlink": "Uninstalling only removes the profile's reference to the local directory ({path}); the directory itself is kept.",
|
|
1019
1486
|
"warn.core": "This is a core/archive package installed via file:. Uninstalling may affect DSH features and requires manual restore.",
|
|
1020
1487
|
"profile.hint": "web profile: {path}",
|
|
1021
|
-
"dsh.chip.copyhint": "Click to copy version",
|
|
1022
|
-
"dsh.chip.copied": "Copied \u2713",
|
|
1023
1488
|
"title.panel": "Plugin Marketplace",
|
|
1024
1489
|
"title.full": "DeepSeek Harness Plugin Marketplace"
|
|
1025
1490
|
};
|
|
@@ -1035,16 +1500,37 @@ function lookup(key, params) {
|
|
|
1035
1500
|
const dict = browserLang() === "en" ? EN : ZH;
|
|
1036
1501
|
return interpolate(dict[key] ?? ZH[key] ?? key, params);
|
|
1037
1502
|
}
|
|
1038
|
-
|
|
1503
|
+
function communityLabels(data) {
|
|
1504
|
+
const c = data && data.community && typeof data.community === "object" ? data.community : {};
|
|
1505
|
+
const zh = c.categoryLabels && typeof c.categoryLabels === "object" ? c.categoryLabels : {};
|
|
1506
|
+
if (browserLang() !== "en") return zh;
|
|
1507
|
+
const en = c.categoryLabelsEn && typeof c.categoryLabelsEn === "object" ? c.categoryLabelsEn : {};
|
|
1508
|
+
return { ...zh, ...en };
|
|
1509
|
+
}
|
|
1039
1510
|
var CSS = `
|
|
1040
1511
|
.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}
|
|
1041
1512
|
.dshm-panel{width:min(920px,100%);height:min(680px,86vh);display:flex;flex-direction:column;background:var(--dsw-alias-bg-elevated,var(--dsw-alias-bg-base,#fff));background:color-mix(in srgb,var(--dsw-alias-bg-base,#fff) 86%,transparent);backdrop-filter:blur(14px) saturate(1.3);-webkit-backdrop-filter:blur(14px) saturate(1.3);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)}
|
|
1513
|
+
.dshm-overlay.full{padding:0}
|
|
1514
|
+
.dshm-panel.full{width:100%;height:100%;border-radius:0}
|
|
1515
|
+
/* \u7A97\u53E3\u63A7\u5236\u7EC4\uFF080.7.7\uFF09\uFF1A\u6700\u5927\u5316/\u5173\u95ED\u8FDE\u4F53\u6309\u94AE\u7EC4\u2014\u2014\u7B49\u5BBD\u4E24\u683C + \u53D1\u4E1D\u5206\u9694\u7EBF\uFF0C\u7EDF\u4E00\u7EBF\u6761\u56FE\u6807 */
|
|
1516
|
+
/* \u7A97\u53E3\u63A7\u5236\u7EC4\uFF080.7.7/0.7.8\uFF09\uFF1A\u6700\u5927\u5316/\u5173\u95ED\u8FDE\u4F53\u6309\u94AE\u7EC4\u2014\u2014\u900F\u660E\u5E95 + \u53D1\u4E1D\u5916\u6846\uFF0C\u4E0E\u641C\u7D22\u6E05\u9664\u94AE\u540C\u4E00\u914D\u8272\u8BED\u8A00\uFF1B\u5BBD\u683C\u9632\u8BEF\u89E6 */
|
|
1517
|
+
.dshm-winctl{display:inline-flex;align-items:stretch;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:9px;background:transparent;overflow:hidden;flex:none}
|
|
1518
|
+
.dshm-winctl button{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);width:44px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .15s,color .15s}
|
|
1519
|
+
.dshm-winctl button + button{border-left:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.18))}
|
|
1520
|
+
.dshm-winctl button:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12));color:var(--dsw-alias-label-primary,inherit)}
|
|
1521
|
+
.dshm-winctl button:active{background:var(--dsw-alias-fill-secondary,rgba(127,127,127,.2))}
|
|
1522
|
+
.dshm-winctl button.danger:hover{background:#e5484d;color:#fff}
|
|
1042
1523
|
.dshm-head{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
|
|
1043
|
-
.dshm-title{font-weight:
|
|
1524
|
+
.dshm-title{font-weight:600;font-size:15px;margin-right:2px}
|
|
1525
|
+
.dshm-head-divider{width:1px;height:18px;background:var(--dsw-alias-border-l2,#e5e7eb);flex:none}
|
|
1044
1526
|
.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)}
|
|
1045
1527
|
.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}
|
|
1046
1528
|
.dshm-seg button:hover{color:var(--dsw-alias-label-secondary,#4b5058)}
|
|
1047
1529
|
.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%))}
|
|
1530
|
+
.dsvm-filterbtn{appearance:none;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-elevated,transparent);color:var(--dsw-alias-label-secondary,#4b5563);border-radius:8px;padding:4px 12px;font:inherit;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;transition:color .15s,border-color .15s,background .15s}
|
|
1531
|
+
.dsvm-filterbtn:hover{color:var(--dsw-alias-label-primary,inherit);border-color:var(--dsw-alias-label-caption,#9ca3af)}
|
|
1532
|
+
.dsvm-filterbtn.on{color:var(--dsw-alias-state-business-primary,#4d6bfe);border-color:var(--dsw-alias-state-business-primary,#4d6bfe);background:color-mix(in srgb,var(--dsw-alias-state-business-primary,#4d6bfe) 8%,transparent)}
|
|
1533
|
+
.dsvm-filterchev{font-size:10px;line-height:1}
|
|
1048
1534
|
.dshm-seg .dshm-count{font-size:11px;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-caption,#9ca3af);margin:0}
|
|
1049
1535
|
.dshm-seg button.on .dshm-count{color:var(--dsw-alias-state-business-primary,#4d6bfe)}
|
|
1050
1536
|
.dshm-spacer{flex:1}
|
|
@@ -1058,12 +1544,82 @@ var CSS = `
|
|
|
1058
1544
|
.dshm-btn.primary:hover{filter:brightness(1.08)}
|
|
1059
1545
|
.dshm-btn.danger{color:var(--dsw-alias-state-error-primary,#b91c1c);border-color:var(--dsw-alias-state-error-primary,#b91c1c)}
|
|
1060
1546
|
.dshm-btn.sm{padding:3px 9px;font-size:11px}
|
|
1061
|
-
.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:
|
|
1547
|
+
.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:999px;padding:7px 14px;font:inherit;font-size:12px;outline:none}
|
|
1062
1548
|
.dshm-input:focus{border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5)}
|
|
1063
1549
|
.dshm-chips{display:flex;flex-wrap:wrap;gap:6px}
|
|
1550
|
+
.dsvm-searchrow{display:flex;align-items:center;gap:8px}
|
|
1551
|
+
.dsvm-searchrow .dshm-search{flex:1;display:flex}
|
|
1552
|
+
.dshm-search{position:relative}
|
|
1553
|
+
.dshm-search .dshm-xbtn-infield{position:absolute;right:4px;top:50%;transform:translateY(-50%)}
|
|
1554
|
+
.dshm-xbtn{appearance:none;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);width:26px;height:26px;padding:0;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;flex:none;transition:border-color .15s,color .15s,box-shadow .15s}
|
|
1555
|
+
.dshm-xbtn:hover{border-color:var(--dsw-alias-label-caption,#9ca3af);color:var(--dsw-alias-label-primary,inherit);box-shadow:inset 0 1px 0 rgba(255,255,255,.06),0 1px 2px rgba(0,0,0,.18)}
|
|
1556
|
+
.dshm-xbtn:active{box-shadow:inset 0 2px 3px rgba(0,0,0,.15)}
|
|
1557
|
+
.dsvm-filterwrap{position:relative;margin-left:auto;display:inline-flex}
|
|
1558
|
+
.dsvm-filterback{position:fixed;inset:0;z-index:25}
|
|
1559
|
+
.dsvm-filterpop{position:absolute;top:calc(100% + 6px);right:0;z-index:30;width:250px;box-sizing:border-box;display:flex;flex-direction:column;gap:10px;background:var(--dsw-alias-bg-elevated,var(--dsw-alias-bg-base,#fff));background:color-mix(in srgb,var(--dsw-alias-bg-base,#fff) 92%,transparent);backdrop-filter:blur(18px) saturate(1.4);-webkit-backdrop-filter:blur(18px) saturate(1.4);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;box-shadow:0 18px 48px rgba(2,6,23,.25);padding:12px}
|
|
1560
|
+
.dsvm-filtergroup{display:flex;flex-direction:column}
|
|
1561
|
+
.dsvm-filtergt{font-size:11px;color:var(--dsw-alias-label-caption,#9ca3af);margin-bottom:4px}
|
|
1562
|
+
.dsvm-filteropt{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);font:inherit;font-size:12px;text-align:left;padding:5px 6px;border-radius:7px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;gap:8px}
|
|
1563
|
+
.dsvm-filteropt:hover{background:var(--dsw-alias-fill-secondary,rgba(127,127,127,.12))}
|
|
1564
|
+
.dsvm-filteropt.on{color:var(--dsw-alias-state-business-primary,#4d6bfe);font-weight:600}
|
|
1565
|
+
.dsvm-filtercheck{font-size:11px}
|
|
1064
1566
|
.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}
|
|
1065
1567
|
.dshm-chip:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
|
|
1066
1568
|
.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}
|
|
1569
|
+
.dsvm-chipswrap{position:sticky;top:-14px;z-index:5;background:var(--dsw-alias-bg-base,#fff);padding:8px 0;margin:-8px 0;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.14))}
|
|
1570
|
+
.dsvm-pagejump{display:inline-flex;align-items:center;gap:4px;margin-left:8px}
|
|
1571
|
+
.dsvm-pagejump-input{width:56px;height:24px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.28));background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);border-radius:999px;padding:0 10px;font:inherit;font-size:12px;outline:none;text-align:center;transition:border-color .15s,color .15s}
|
|
1572
|
+
.dsvm-pagejump-input:hover{color:var(--dsw-alias-label-primary,inherit)}
|
|
1573
|
+
.dsvm-pagejump-input:focus{border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5);color:var(--dsw-alias-label-primary,inherit)}
|
|
1574
|
+
.dsvm-pagejump-btn{color:var(--dsw-alias-state-business-primary,#4d6bfe)}
|
|
1575
|
+
.dsvm-pagejump-btn:disabled{opacity:.4;cursor:default}
|
|
1576
|
+
.dsvm-sortrow{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
|
|
1577
|
+
.dsvm-byline{display:flex;flex-wrap:wrap;gap:8px;color:var(--dsw-alias-label-caption,#6b7280);font-size:11px;line-height:16px;margin:2px 0 0;font-variant-numeric:tabular-nums}
|
|
1578
|
+
.dsvm-pager{display:flex;flex-wrap:wrap;gap:4px;align-items:center;justify-content:center;margin-top:4px}
|
|
1579
|
+
.dsvm-pagebtn{min-width:26px;height:24px;border:1px solid transparent;border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);font:inherit;font-size:12px;cursor:pointer}
|
|
1580
|
+
.dsvm-pagebtn.on{border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5);color:var(--dsw-alias-interactive-bg-selected,#4f46e5);font-weight:600}
|
|
1581
|
+
.dsvm-pagebtn:disabled{opacity:.4;cursor:default}
|
|
1582
|
+
.dsvm-modal{position:fixed;inset:0;z-index:2147483100;background:var(--dsw-alias-bg-mask-3,rgba(15,23,42,.55));display:flex;align-items:center;justify-content:center;padding:24px 16px}
|
|
1583
|
+
.dsvm-modalbox{width:min(720px,100%);max-height:86vh;overflow:auto;background:var(--dsw-alias-bg-elevated,#fff);background:color-mix(in srgb,var(--dsw-alias-bg-base,#fff) 94%,transparent);backdrop-filter:blur(14px);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(2,6,23,.25);padding:16px;display:flex;flex-direction:column;gap:10px}
|
|
1584
|
+
.dsvm-modalhead{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
1585
|
+
.dsvm-kv{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;font-size:12px;margin:0}
|
|
1586
|
+
.dsvm-kv dt{color:var(--dsw-alias-label-caption,#6b7280);white-space:nowrap}
|
|
1587
|
+
.dsvm-kv dd{margin:0;color:var(--dsw-alias-label-secondary,#4b5563);overflow-wrap:anywhere}
|
|
1588
|
+
.dsvm-fold{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;padding:6px 10px;font-size:12px}
|
|
1589
|
+
.dsvm-fold summary{cursor:pointer;color:var(--dsw-alias-label-secondary,#4b5563)}
|
|
1590
|
+
.dsvm-cmdrow{display:flex;align-items:center;gap:8px;margin-top:6px;flex-wrap:wrap}
|
|
1591
|
+
.dsvm-code{background:rgba(127,127,127,.12);padding:3px 8px;border-radius:6px;font-size:11px;overflow-wrap:anywhere}
|
|
1592
|
+
.dsvm-modalactions{display:flex;justify-content:flex-end;gap:8px}
|
|
1593
|
+
.dsvm-shotrow{display:flex;gap:6px;overflow-x:auto;padding:2px 0}
|
|
1594
|
+
.dsvm-shotbox{min-width:120px;min-height:84px;display:flex;align-items:center;justify-content:center;background:rgba(127,127,127,.08);border-radius:6px;cursor:zoom-in;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25))}
|
|
1595
|
+
.dsvm-shot{max-width:220px;max-height:130px;border-radius:6px;display:block}
|
|
1596
|
+
.dsvm-lightbox{position:fixed;inset:0;z-index:2147483200;background:rgba(0,0,0,.88);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px}
|
|
1597
|
+
.dsvm-lightbox img{max-width:94vw;max-height:80vh;border-radius:8px}
|
|
1598
|
+
.dsvm-lbnav{display:flex;align-items:center;gap:10px}
|
|
1599
|
+
.dsvm-lbcount{color:rgba(255,255,255,.75);font-size:12px}
|
|
1600
|
+
.dsvm-lbdots{display:flex;gap:6px}
|
|
1601
|
+
.dsvm-lbdot{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,.3);cursor:pointer}
|
|
1602
|
+
.dsvm-lbdot.on{background:#fff}
|
|
1603
|
+
.dsvm-card-clickable, .dshm-card{cursor:pointer}
|
|
1604
|
+
.dshm-card:hover{border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5)}
|
|
1605
|
+
.dsvm-btn{border:1px solid rgba(255,255,255,.35);background:rgba(255,255,255,.14);color:#fff;border-radius:8px;padding:5px 14px;font:inherit;font-size:13px;cursor:pointer}
|
|
1606
|
+
.dsvm-btn:hover{background:rgba(255,255,255,.24)}
|
|
1607
|
+
.dsvm-ops{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:10px;padding:8px 10px;display:flex;flex-direction:column;gap:4px;font-size:12px}
|
|
1608
|
+
.dsvm-opgroup{display:flex;flex-direction:column;gap:3px}
|
|
1609
|
+
.dsvm-opgroup.done{opacity:.75}
|
|
1610
|
+
.dsvm-oprow{display:flex;align-items:center;gap:8px;min-height:22px}
|
|
1611
|
+
.dsvm-opstatus{min-width:44px;font-size:11px;color:var(--dsw-alias-label-caption,#6b7280)}
|
|
1612
|
+
.dsvm-oprow.ok .dsvm-opstatus{color:#15803d}
|
|
1613
|
+
.dsvm-oprow.warn .dsvm-opstatus, .dsvm-oprow.run .dsvm-opstatus{color:#b45309}
|
|
1614
|
+
.dsvm-oprow.err .dsvm-opstatus{color:var(--dsw-alias-state-error-primary,#b91c1c)}
|
|
1615
|
+
.dsvm-oprow.sup .dsvm-opstatus{color:var(--dsw-alias-label-caption,#6b7280)}
|
|
1616
|
+
.dsvm-opkind{color:var(--dsw-alias-label-secondary,#4b5563)}
|
|
1617
|
+
.dsvm-optarget{font-weight:500;overflow-wrap:anywhere}
|
|
1618
|
+
.dsvm-opnote{color:var(--dsw-alias-label-caption,#6b7280);font-size:11px;overflow-wrap:anywhere}
|
|
1619
|
+
.dsvm-favbtn{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-caption,#9ca3af);font-size:15px;line-height:1;cursor:pointer;padding:0 2px;margin-left:auto}
|
|
1620
|
+
.dsvm-favbtn:hover{color:var(--dsw-alias-state-business-primary,#4d6bfe)}
|
|
1621
|
+
.dsvm-favbtn.on{color:#e0a33c}
|
|
1622
|
+
.dsvm-reddot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-state-error-primary,#ef4444);margin-left:5px;vertical-align:middle}
|
|
1067
1623
|
.dshm-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
|
|
1068
1624
|
@media (max-width:680px){.dshm-cards{grid-template-columns:1fr}}
|
|
1069
1625
|
.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}
|
|
@@ -1072,7 +1628,9 @@ var CSS = `
|
|
|
1072
1628
|
.dshm-meta{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px}
|
|
1073
1629
|
.dshm-top{display:flex;align-items:center;gap:8px;min-width:0}
|
|
1074
1630
|
.dshm-name{flex:1;min-width:0;font-weight:600;font-size:14px;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
1075
|
-
.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)}
|
|
1631
|
+
.dshm-badge{appearance:none;border:0;font:inherit;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)}
|
|
1632
|
+
button.dshm-badge{cursor:pointer}
|
|
1633
|
+
button.dshm-badge:hover{filter:brightness(.95)}
|
|
1076
1634
|
.dshm-badge.warn{background:var(--dsw-alias-state-warn-tertiary,#fffbeb);color:var(--dsw-alias-state-warn-primary,#b45309)}
|
|
1077
1635
|
.dshm-badge.info{background:var(--dsw-alias-state-business-tertiary,#eef2ff);color:var(--dsw-alias-state-business-primary,#4338ca)}
|
|
1078
1636
|
.dshm-badge.err{background:var(--dsw-alias-state-error-secondary,#fee2e2);color:var(--dsw-alias-state-error-primary,#b91c1c)}
|
|
@@ -1135,14 +1693,9 @@ var CSS = `
|
|
|
1135
1693
|
[data-slot="sidebar.footer.action"]{display:flex!important;flex-direction:column;width:100%;min-width:0}
|
|
1136
1694
|
[data-slot="sidebar.footer.action"]>*{flex:none;min-width:0}
|
|
1137
1695
|
.dshm-empty{text-align:center;color:var(--dsw-alias-label-caption,#6b7280);font-size:13px;padding:32px 0}
|
|
1138
|
-
/* \u5934\u90E8
|
|
1139
|
-
.dshm-dshchip{
|
|
1140
|
-
.dshm-dshchip
|
|
1141
|
-
.dshm-dshchip-v{font-weight:600;color:var(--dsw-alias-label-primary,inherit)}
|
|
1142
|
-
.dshm-dshchip-tip{position:absolute;top:calc(100% + 7px);right:0;visibility:hidden;opacity:0;transition:opacity .12s;z-index:60;background:var(--dsw-alias-bg-elevated,var(--dsw-alias-bg-base,#fff));background:color-mix(in srgb,var(--dsw-alias-bg-base,#fff) 86%,transparent);backdrop-filter:blur(14px) saturate(1.3);-webkit-backdrop-filter:blur(14px) saturate(1.3);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:9px;box-shadow:var(--dsw-shadow-lv1,0 2px 8px rgb(20 24 32 / 8%));padding:7px 10px;text-align:left;pointer-events:none}
|
|
1143
|
-
.dshm-dshchip:hover .dshm-dshchip-tip,.dshm-dshchip:focus-visible .dshm-dshchip-tip{visibility:visible;opacity:1}
|
|
1144
|
-
.dshm-dshchip-tiprow{font:12px/16px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-weight:600;color:var(--dsw-alias-label-primary,inherit)}
|
|
1145
|
-
.dshm-dshchip-tipsub{display:block;margin-top:2px;font-size:10px;line-height:14px;color:var(--dsw-alias-label-caption,#9ca3af)}
|
|
1696
|
+
/* \u5934\u90E8 dsh-m \u7248\u672C\u89D2\u6807\uFF1A\u7B49\u5BBD\u5C0F\u5B57\u5706\u89D2\uFF0C\u9759\u6001\u5C55\u793A\u4E0D\u52A0\u7C97\u4E0D\u53EF\u70B9\uFF080.7.5 \u8D77\u6539\u663E dsh-m \u7248\u672C\uFF09 */
|
|
1697
|
+
.dshm-dshchip{display:inline-flex;align-items:center;gap:4px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#f5f6f8);color:var(--dsw-alias-label-secondary,#4b5563);border-radius:999px;padding:5px 10px;font:11px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:nowrap;flex:none}
|
|
1698
|
+
.dshm-dshchip-v{font-weight:400;color:inherit}
|
|
1146
1699
|
|
|
1147
1700
|
/* 0.4.0\uFF1A\u5F00\u5173 / \u76F8\u4F4D\u70B9 / \u517C\u5BB9\u786E\u8BA4\u5F39\u7A97 */
|
|
1148
1701
|
.dshm-dot{font-size:9px;line-height:1;vertical-align:middle;margin-right:2px}
|
|
@@ -1164,12 +1717,24 @@ var CSS = `
|
|
|
1164
1717
|
.dshm-compat-peers li{font-family:ui-monospace,monospace}
|
|
1165
1718
|
.dshm-compat-risk{font-size:12px;color:#f87171;margin-bottom:12px}
|
|
1166
1719
|
`;
|
|
1720
|
+
function cssVersionOf(s) {
|
|
1721
|
+
let h2 = 5381;
|
|
1722
|
+
for (let i = 0; i < s.length; i += 1) h2 = (h2 << 5) + h2 + s.charCodeAt(i) | 0;
|
|
1723
|
+
return (h2 >>> 0).toString(36);
|
|
1724
|
+
}
|
|
1167
1725
|
function ensureCss() {
|
|
1168
|
-
if (typeof document === "undefined"
|
|
1169
|
-
const
|
|
1170
|
-
el
|
|
1171
|
-
el
|
|
1172
|
-
|
|
1726
|
+
if (typeof document === "undefined") return;
|
|
1727
|
+
const ver = cssVersionOf(CSS);
|
|
1728
|
+
const el = document.getElementById("dshm-css");
|
|
1729
|
+
if (el) {
|
|
1730
|
+
if (el.getAttribute("data-ver") === ver) return;
|
|
1731
|
+
el.remove();
|
|
1732
|
+
}
|
|
1733
|
+
const n = document.createElement("style");
|
|
1734
|
+
n.id = "dshm-css";
|
|
1735
|
+
n.setAttribute("data-ver", ver);
|
|
1736
|
+
n.textContent = CSS;
|
|
1737
|
+
document.head.appendChild(n);
|
|
1173
1738
|
}
|
|
1174
1739
|
async function api(method, params, signal) {
|
|
1175
1740
|
const res = await fetch(API, {
|
|
@@ -1205,8 +1770,8 @@ function useAsync(fn, deps) {
|
|
|
1205
1770
|
}, [run]);
|
|
1206
1771
|
return { ...state, reload: run };
|
|
1207
1772
|
}
|
|
1208
|
-
function useMarketData() {
|
|
1209
|
-
const [query, setQuery] = useState(() => normalizeMarketQuery2(
|
|
1773
|
+
function useMarketData(zone = "community") {
|
|
1774
|
+
const [query, setQuery] = useState(() => normalizeMarketQuery2(createZoneState2(zone), zone));
|
|
1210
1775
|
const [data, setData] = useState(null);
|
|
1211
1776
|
const [loading, setLoading] = useState(true);
|
|
1212
1777
|
const [error, setError] = useState(null);
|
|
@@ -1223,7 +1788,8 @@ function useMarketData() {
|
|
|
1223
1788
|
const params = {
|
|
1224
1789
|
query: nextQuery.query || void 0,
|
|
1225
1790
|
category: nextQuery.category || void 0,
|
|
1226
|
-
|
|
1791
|
+
source: nextQuery.source || "community",
|
|
1792
|
+
...nextQuery.sort ? { sort: nextQuery.sort } : {},
|
|
1227
1793
|
offset: nextQuery.offset,
|
|
1228
1794
|
limit: nextQuery.limit,
|
|
1229
1795
|
...force ? { force: true } : {}
|
|
@@ -1239,7 +1805,7 @@ function useMarketData() {
|
|
|
1239
1805
|
});
|
|
1240
1806
|
}, []);
|
|
1241
1807
|
const updateQuery = useCallback((patch, opts = {}) => {
|
|
1242
|
-
const next = resetPageOnFilterChange2(queryRef.current, normalizeMarketQuery2({ ...queryRef.current, ...patch }));
|
|
1808
|
+
const next = resetPageOnFilterChange2(queryRef.current, normalizeMarketQuery2({ ...queryRef.current, ...patch }, zone));
|
|
1243
1809
|
queryRef.current = next;
|
|
1244
1810
|
setQuery(next);
|
|
1245
1811
|
if (opts.fetch !== false) fetchPage(next, opts.force);
|
|
@@ -1396,26 +1962,385 @@ function RestartBanner({ note, onDone }) {
|
|
|
1396
1962
|
phase === "idle" && !err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, lookup("common.later")) : null
|
|
1397
1963
|
);
|
|
1398
1964
|
}
|
|
1399
|
-
function
|
|
1400
|
-
const
|
|
1401
|
-
const [
|
|
1402
|
-
const
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
1965
|
+
function SearchBox({ placeholder, initial, onCommit }) {
|
|
1966
|
+
const [draft, setDraft] = useState(initial || "");
|
|
1967
|
+
const [composing, setComposing] = useState(false);
|
|
1968
|
+
const timerRef = useRef(null);
|
|
1969
|
+
const inputRef = useRef(null);
|
|
1970
|
+
useEffect(() => () => {
|
|
1971
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1972
|
+
}, []);
|
|
1973
|
+
const schedule = (v) => {
|
|
1974
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1975
|
+
timerRef.current = setTimeout(() => onCommit(v), 250);
|
|
1976
|
+
};
|
|
1977
|
+
const onChange = (v) => {
|
|
1978
|
+
setDraft(v);
|
|
1979
|
+
if (composing) return;
|
|
1980
|
+
schedule(v);
|
|
1981
|
+
};
|
|
1982
|
+
const submitNow = () => {
|
|
1983
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1984
|
+
onCommit(draft);
|
|
1985
|
+
};
|
|
1986
|
+
return h(
|
|
1987
|
+
"div",
|
|
1988
|
+
{ className: "dshm-search" },
|
|
1989
|
+
h("input", {
|
|
1990
|
+
ref: inputRef,
|
|
1991
|
+
className: "dshm-input",
|
|
1992
|
+
placeholder,
|
|
1993
|
+
value: draft,
|
|
1994
|
+
style: draft ? { paddingRight: "32px" } : null,
|
|
1995
|
+
onChange: (e) => onChange(e.target.value),
|
|
1996
|
+
onCompositionStart: () => setComposing(true),
|
|
1997
|
+
onCompositionEnd: (e) => {
|
|
1998
|
+
setComposing(false);
|
|
1999
|
+
schedule(e && e.target ? e.target.value : draft);
|
|
2000
|
+
},
|
|
2001
|
+
onKeyDown: (e) => {
|
|
2002
|
+
if (e.key === "Enter" && !e.isComposing && e.keyCode !== 229) {
|
|
2003
|
+
e.preventDefault();
|
|
2004
|
+
submitNow();
|
|
2005
|
+
}
|
|
2006
|
+
},
|
|
2007
|
+
onBlur: submitNow
|
|
2008
|
+
}),
|
|
2009
|
+
draft ? h("button", {
|
|
2010
|
+
className: "dshm-xbtn dshm-xbtn-infield",
|
|
2011
|
+
title: lookup("common.clear"),
|
|
2012
|
+
onClick: () => {
|
|
2013
|
+
setDraft("");
|
|
2014
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
2015
|
+
onCommit("");
|
|
2016
|
+
if (inputRef.current) inputRef.current.focus();
|
|
2017
|
+
}
|
|
2018
|
+
}, h(XIcon)) : null
|
|
2019
|
+
);
|
|
2020
|
+
}
|
|
2021
|
+
function ZoneChips({ zone, counts, labels, active, onPick, trailing }) {
|
|
2022
|
+
const chips = useMemo(() => zoneChips2(counts, labels, zone), [counts, labels, zone]);
|
|
2023
|
+
const [expanded, setExpanded] = useState(false);
|
|
2024
|
+
const [stuck, setStuck] = useState(false);
|
|
2025
|
+
const [fit, setFit] = useState({ rows2: 99, rows1: 99 });
|
|
2026
|
+
const wrapRef = useRef(null);
|
|
2027
|
+
const sentinelRef = useRef(null);
|
|
2028
|
+
const autoRef = useRef(null);
|
|
2029
|
+
const collapsedBudget = Math.min(stuck ? fit.rows1 : fit.rows2, chips.length);
|
|
2030
|
+
const activeIdx = active ? chips.findIndex((c) => c.id === active) : -1;
|
|
2031
|
+
useEffect(() => {
|
|
2032
|
+
if (!active) {
|
|
2033
|
+
autoRef.current = null;
|
|
2034
|
+
setExpanded(false);
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
if (activeIdx >= collapsedBudget && autoRef.current !== active) {
|
|
2038
|
+
autoRef.current = active;
|
|
2039
|
+
setExpanded(true);
|
|
2040
|
+
}
|
|
2041
|
+
}, [active, activeIdx, collapsedBudget]);
|
|
2042
|
+
useLayoutEffect(() => {
|
|
2043
|
+
const el = wrapRef.current;
|
|
2044
|
+
if (!el || expanded) return;
|
|
2045
|
+
let rows2 = 0;
|
|
2046
|
+
let rows1 = 0;
|
|
2047
|
+
let rowCount = 0;
|
|
2048
|
+
let lastTop = null;
|
|
2049
|
+
for (const k of el.children) {
|
|
2050
|
+
if (!(k instanceof HTMLElement) || k.getAttribute("data-chip") !== "1") continue;
|
|
2051
|
+
const t = k.offsetTop;
|
|
2052
|
+
if (lastTop === null || t !== lastTop) {
|
|
2053
|
+
rowCount += 1;
|
|
2054
|
+
lastTop = t;
|
|
2055
|
+
}
|
|
2056
|
+
if (rowCount <= 2) rows2 += 1;
|
|
2057
|
+
if (rowCount <= 1) rows1 += 1;
|
|
2058
|
+
}
|
|
2059
|
+
setFit({ rows2, rows1 });
|
|
2060
|
+
}, [chips.length, expanded, zone]);
|
|
2061
|
+
useEffect(() => {
|
|
2062
|
+
const s = sentinelRef.current;
|
|
2063
|
+
if (!s || typeof IntersectionObserver === "undefined") return;
|
|
2064
|
+
const io = new IntersectionObserver((entries) => setStuck(!entries[0].isIntersecting), { threshold: 0 });
|
|
2065
|
+
io.observe(s);
|
|
2066
|
+
return () => io.disconnect();
|
|
2067
|
+
}, []);
|
|
2068
|
+
const budget = expanded ? chips.length : collapsedBudget;
|
|
2069
|
+
const shown = chips.slice(0, budget);
|
|
2070
|
+
const hidden = chips.length - shown.length;
|
|
2071
|
+
const btn = (c) => h(
|
|
2072
|
+
"button",
|
|
2073
|
+
{ key: c.id, "data-chip": "1", className: `dshm-chip${active === c.id ? " on" : ""}`, onClick: () => onPick(active === c.id ? null : c.id) },
|
|
2074
|
+
`${c.labelKey ? lookup(c.labelKey) : c.label}${c.count ? ` ${c.count}` : ""}`
|
|
2075
|
+
);
|
|
2076
|
+
return h(
|
|
2077
|
+
React.Fragment,
|
|
2078
|
+
null,
|
|
2079
|
+
h("div", { ref: sentinelRef, style: { height: "1px" } }),
|
|
2080
|
+
h(
|
|
2081
|
+
"div",
|
|
2082
|
+
{ className: "dsvm-chipswrap" },
|
|
2083
|
+
h(
|
|
2084
|
+
"div",
|
|
2085
|
+
{ ref: wrapRef, className: "dshm-chips" },
|
|
2086
|
+
h("button", { "data-chip": "1", className: `dshm-chip${active == null ? " on" : ""}`, onClick: () => onPick(null) }, lookup("cat.all")),
|
|
2087
|
+
...shown.map(btn),
|
|
2088
|
+
hidden > 0 ? h(
|
|
2089
|
+
"button",
|
|
2090
|
+
{ "data-chip": "1", className: "dshm-chip", onClick: () => setExpanded(!expanded) },
|
|
2091
|
+
expanded ? "\u2303" : `+${hidden}`
|
|
2092
|
+
) : null,
|
|
2093
|
+
trailing || null
|
|
2094
|
+
)
|
|
2095
|
+
)
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
function compactCount(n) {
|
|
2099
|
+
if (typeof n !== "number" || !Number.isFinite(n)) return "\u2014";
|
|
2100
|
+
if (Math.abs(n) >= 1e3) {
|
|
2101
|
+
const k = n / 1e3;
|
|
2102
|
+
return `${k >= 100 ? Math.round(k) : Number(k.toFixed(1))}k`;
|
|
2103
|
+
}
|
|
2104
|
+
return String(n);
|
|
2105
|
+
}
|
|
2106
|
+
function Shot({ src, onClick }) {
|
|
2107
|
+
const [show, setShow] = useState(false);
|
|
2108
|
+
const ref = useRef(null);
|
|
2109
|
+
useEffect(() => {
|
|
2110
|
+
const el = ref.current;
|
|
2111
|
+
if (!el || typeof IntersectionObserver === "undefined") {
|
|
2112
|
+
setShow(true);
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2115
|
+
const io = new IntersectionObserver(
|
|
2116
|
+
(entries) => {
|
|
2117
|
+
if (entries[0].isIntersecting) {
|
|
2118
|
+
setShow(true);
|
|
2119
|
+
io.disconnect();
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
{ rootMargin: "200px" }
|
|
2123
|
+
);
|
|
2124
|
+
io.observe(el);
|
|
2125
|
+
return () => io.disconnect();
|
|
2126
|
+
}, []);
|
|
2127
|
+
return h(
|
|
2128
|
+
"div",
|
|
2129
|
+
{ ref, className: "dsvm-shotbox", onClick, role: "button", tabIndex: 0 },
|
|
2130
|
+
show ? h("img", { className: "dsvm-shot", src, alt: "", loading: "lazy", referrerPolicy: "no-referrer", fetchPriority: "low" }) : null
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2133
|
+
function Lightbox({ shots, index, onNav, onClose }) {
|
|
2134
|
+
return h(
|
|
2135
|
+
"div",
|
|
2136
|
+
{ className: "dsvm-lightbox", onClick: onClose },
|
|
2137
|
+
h("img", { src: shots[index], alt: "", onClick: (e) => e.stopPropagation(), referrerPolicy: "no-referrer" }),
|
|
2138
|
+
h(
|
|
2139
|
+
"div",
|
|
2140
|
+
{ className: "dsvm-lbnav", onClick: (e) => e.stopPropagation() },
|
|
2141
|
+
h("button", { className: "dsvm-btn", onClick: () => onNav((index - 1 + shots.length) % shots.length) }, "\u2039"),
|
|
2142
|
+
h("span", { className: "dsvm-lbcount" }, `${index + 1} / ${shots.length}`),
|
|
2143
|
+
h("button", { className: "dsvm-btn", onClick: () => onNav((index + 1) % shots.length) }, "\u203A")
|
|
2144
|
+
),
|
|
2145
|
+
h(
|
|
2146
|
+
"div",
|
|
2147
|
+
{ className: "dsvm-lbdots", onClick: (e) => e.stopPropagation() },
|
|
2148
|
+
...shots.map((s, i) => h("span", { key: s, className: `dsvm-lbdot${i === index ? " on" : ""}`, onClick: () => onNav(i) }))
|
|
2149
|
+
)
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
var dsvmModalDepth = 0;
|
|
2153
|
+
function useModalDepth(active) {
|
|
2154
|
+
useEffect(() => {
|
|
2155
|
+
if (!active) return;
|
|
2156
|
+
dsvmModalDepth += 1;
|
|
2157
|
+
return () => {
|
|
2158
|
+
dsvmModalDepth -= 1;
|
|
2159
|
+
};
|
|
2160
|
+
}, [active]);
|
|
2161
|
+
}
|
|
2162
|
+
function DetailModal({ it, labels, busy, onClose, onInstall }) {
|
|
2163
|
+
useModalDepth(true);
|
|
2164
|
+
const shots = it.community === true ? safeScreenshots(it) : [];
|
|
2165
|
+
const [lb, setLb] = useState(null);
|
|
2166
|
+
const [copied, setCopied] = useState(false);
|
|
2167
|
+
useEffect(() => {
|
|
2168
|
+
const onKey = (e) => {
|
|
2169
|
+
if (e.key === "Escape") {
|
|
2170
|
+
if (lb !== null) setLb(null);
|
|
2171
|
+
else onClose();
|
|
2172
|
+
}
|
|
2173
|
+
if (lb !== null && shots.length > 1) {
|
|
2174
|
+
if (e.key === "ArrowLeft") setLb((i) => (i - 1 + shots.length) % shots.length);
|
|
2175
|
+
if (e.key === "ArrowRight") setLb((i) => (i + 1) % shots.length);
|
|
2176
|
+
}
|
|
2177
|
+
};
|
|
2178
|
+
document.addEventListener("keydown", onKey);
|
|
2179
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
2180
|
+
}, [lb, shots.length, onClose]);
|
|
2181
|
+
const catLabel = labels && labels[it.category] || (["market", "tools", "ui", "search", "other"].includes(it.category) ? lookup("cat." + it.category) : it.category);
|
|
2182
|
+
const installCmd = typeof it.install === "string" && it.install !== "" ? it.install : it.source === "npm" ? `dsh plugin --profile web add ${it.npm}` : `dsh plugin --profile web add github:${it.github}`;
|
|
2183
|
+
const copyCmd = async () => {
|
|
2184
|
+
try {
|
|
2185
|
+
await navigator.clipboard.writeText(installCmd);
|
|
2186
|
+
setCopied(true);
|
|
2187
|
+
setTimeout(() => setCopied(false), 1600);
|
|
2188
|
+
} catch {
|
|
2189
|
+
}
|
|
2190
|
+
};
|
|
2191
|
+
const descFull = browserLang() === "en" && typeof it.descriptionEn === "string" && it.descriptionEn !== "" ? it.descriptionEn : it.description;
|
|
2192
|
+
const kv = (k, v) => h(React.Fragment, { key: k }, h("dt", null, k), h("dd", null, v));
|
|
2193
|
+
return h(
|
|
2194
|
+
"div",
|
|
2195
|
+
{ className: "dsvm-modal", onClick: onClose },
|
|
2196
|
+
h(
|
|
2197
|
+
"div",
|
|
2198
|
+
{ className: "dsvm-modalbox", onClick: (e) => e.stopPropagation() },
|
|
2199
|
+
h(
|
|
2200
|
+
"div",
|
|
2201
|
+
{ className: "dsvm-modalhead" },
|
|
2202
|
+
h(Icon, { entry: it }),
|
|
2203
|
+
h("span", { className: "dshm-name" }, it.name),
|
|
2204
|
+
it.deprecated === true ? h("span", { className: "dshm-badge warn" }, lookup("badge.deprecated")) : null,
|
|
2205
|
+
it.community !== true && Array.isArray(it.verified) && it.verified.length ? h("span", { className: "dshm-badge", title: it.verified.join("\u3001") }, lookup("badge.verified")) : null,
|
|
2206
|
+
it.installed ? h("span", { className: "dshm-badge" }, lookup("badge.installed")) : null,
|
|
2207
|
+
it.community === true ? h("span", { className: "dshm-badge info" }, lookup("badge.community")) : null,
|
|
2208
|
+
h("span", { className: "dshm-badge info" }, it.source === "npm" ? "npm" : "github"),
|
|
2209
|
+
h("span", { className: "dshm-spacer" }),
|
|
2210
|
+
h("button", { className: "dshm-xbtn", "aria-label": lookup("common.close"), title: lookup("common.close"), onClick: onClose }, h(XIcon))
|
|
2211
|
+
),
|
|
2212
|
+
it.community === true ? h(
|
|
2213
|
+
"div",
|
|
2214
|
+
{ className: "dsvm-byline" },
|
|
2215
|
+
it.owner ? h("span", null, `by ${it.owner}`) : null,
|
|
2216
|
+
typeof it.stars === "number" ? h("span", { title: String(it.stars) }, `${compactCount(it.stars)} \u2605`) : null,
|
|
2217
|
+
typeof it.downloads === "number" ? h("span", { title: String(it.downloads) }, `${compactCount(it.downloads)} \u2193`) : null
|
|
2218
|
+
) : null,
|
|
2219
|
+
h(LinksRow, { npm: it.npm, github: it.github, homepage: it.homepage }),
|
|
2220
|
+
h(
|
|
2221
|
+
"dl",
|
|
2222
|
+
{ className: "dsvm-kv" },
|
|
2223
|
+
kv(lookup("modal.category"), catLabel),
|
|
2224
|
+
it.added ? kv(lookup("modal.added"), it.added) : null,
|
|
2225
|
+
it.community === true ? kv(
|
|
2226
|
+
lookup("modal.dlwindow"),
|
|
2227
|
+
typeof it.downloads === "number" ? h("span", { title: String(it.downloads) }, `${compactCount(it.downloads)}\uFF08${it.downloadsStart || "?"} ~ ${it.downloadsEnd || "?"}${it.downloadsCheckedAt ? `\uFF0C${lookup("modal.checkedat")} ${it.downloadsCheckedAt}` : ""}\uFF09`) : lookup("modal.dlnone")
|
|
2228
|
+
) : null,
|
|
2229
|
+
kv(lookup("detail.latest"), it.latestVersion ? `v${it.latestVersion}` : it.latestTag ? it.latestTag : it.latestSha ? it.latestSha : it.latestError ? it.version ? lookup("sub.snapshot", { v: it.version }) : it.latestError : "\u2014"),
|
|
2230
|
+
kv(lookup("detail.installed"), it.installedPkg ? `${it.installedPkg} v${it.installedVersion || "?"}` : lookup("installed.none")),
|
|
2231
|
+
it.community !== true && Array.isArray(it.verified) && it.verified.length ? kv(lookup("modal.verified"), it.verified.join("\u3001")) : null,
|
|
2232
|
+
(it.tags || []).length ? kv(lookup("modal.tags"), it.tags.join(", ")) : null,
|
|
2233
|
+
it.deprecated === true && it.replacement ? kv(lookup("modal.replacement"), it.replacement) : null
|
|
2234
|
+
),
|
|
2235
|
+
h("div", { className: "dshm-desc", style: { WebkitLineClamp: "unset" } }, descFull),
|
|
2236
|
+
shots.length ? h(
|
|
2237
|
+
"div",
|
|
2238
|
+
{ className: "dsvm-shotrow" },
|
|
2239
|
+
...shots.map((src, i) => h(Shot, { key: src, src, onClick: () => setLb(i) }))
|
|
2240
|
+
) : null,
|
|
2241
|
+
it.community === true ? h(
|
|
2242
|
+
"details",
|
|
2243
|
+
{ className: "dsvm-fold" },
|
|
2244
|
+
h("summary", null, lookup("detail.capabilities")),
|
|
2245
|
+
Array.isArray(it.capabilities) && it.capabilities.length ? h(
|
|
2246
|
+
"div",
|
|
2247
|
+
null,
|
|
2248
|
+
h("div", null, it.capabilities.join(", ")),
|
|
2249
|
+
Array.isArray(it.capabilityRedLines) && it.capabilityRedLines.length ? h("div", { className: "dshm-err" }, `${lookup("detail.redlines")}: ${it.capabilityRedLines.join("; ")}`) : null
|
|
2250
|
+
) : h("div", { className: "dshm-hint" }, lookup("detail.capabilities.unscanned"))
|
|
2251
|
+
) : null,
|
|
2252
|
+
h(
|
|
2253
|
+
"details",
|
|
2254
|
+
{ className: "dsvm-fold" },
|
|
2255
|
+
h("summary", null, lookup("modal.installcmd")),
|
|
2256
|
+
h(
|
|
2257
|
+
"div",
|
|
2258
|
+
{ className: "dsvm-cmdrow" },
|
|
2259
|
+
h("code", { className: "dsvm-code" }, installCmd),
|
|
2260
|
+
h("button", { className: "dshm-btn sm", onClick: copyCmd }, copied ? lookup("modal.copied") : lookup("modal.copy"))
|
|
2261
|
+
)
|
|
2262
|
+
),
|
|
2263
|
+
h(
|
|
2264
|
+
"div",
|
|
2265
|
+
{ className: "dsvm-modalactions" },
|
|
2266
|
+
it.installed ? h("span", { className: "dshm-hint" }, lookup("manage.hint")) : h("button", { className: "dshm-btn primary", disabled: busy, onClick: () => onInstall(it) }, busy ? h(Spin) : lookup("action.install"))
|
|
2267
|
+
)
|
|
2268
|
+
),
|
|
2269
|
+
lb !== null && shots.length ? h(Lightbox, { shots, index: lb, onNav: setLb, onClose: () => setLb(null) }) : null
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
var ZONE_TABS = [
|
|
2273
|
+
{ id: "community", labelKey: "zone.community" },
|
|
2274
|
+
{ id: "primary", labelKey: "zone.primary" },
|
|
2275
|
+
{ id: "favorites", labelKey: "zone.favorites" }
|
|
2276
|
+
];
|
|
2277
|
+
function MarketTab({ notify, markets, onMutation, ops, favorites }) {
|
|
2278
|
+
const [zone, setZone] = useState("community");
|
|
2279
|
+
const market = zone === "favorites" ? null : markets[zone];
|
|
2280
|
+
const { data, loading, error, reload, query, updateQuery } = market || {};
|
|
2281
|
+
const [detailId, setDetailId] = useState(null);
|
|
2282
|
+
const [compatConfirm, setCompatConfirm] = useState(null);
|
|
2283
|
+
useModalDepth(compatConfirm != null);
|
|
2284
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
2285
|
+
useModalDepth(filterOpen);
|
|
2286
|
+
useEffect(() => {
|
|
2287
|
+
if (!filterOpen) return;
|
|
2288
|
+
const onKey = (e) => {
|
|
2289
|
+
if (e.key === "Escape") setFilterOpen(false);
|
|
2290
|
+
};
|
|
2291
|
+
document.addEventListener("keydown", onKey);
|
|
2292
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
2293
|
+
}, [filterOpen]);
|
|
2294
|
+
const [favDetailItem, setFavDetailItem] = useState(null);
|
|
2295
|
+
const [pageJump, setPageJump] = useState("");
|
|
2296
|
+
const activeInstallTarget = (ops.records.find((r) => r.kind === "install" && (r.status === "running" || r.status === "queued" || r.status === "input")) || {}).target || null;
|
|
2297
|
+
const items = data && data.items || [];
|
|
1406
2298
|
const total = data && data.total || 0;
|
|
1407
|
-
const limit = data && data.limit ||
|
|
2299
|
+
const limit = data && data.limit || DEFAULT_PAGE_SIZE2;
|
|
1408
2300
|
const offset = data && data.offset || 0;
|
|
1409
2301
|
const page = total > 0 ? Math.floor(offset / limit) + 1 : 1;
|
|
1410
2302
|
const pages = total > 0 ? Math.max(1, Math.ceil(total / limit)) : 1;
|
|
1411
2303
|
const counts = data && data.categoryCounts || {};
|
|
1412
2304
|
const notice = data ? marketNotice2(data.registryState, data.community) : null;
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
if (
|
|
1416
|
-
|
|
2305
|
+
const gotoPage = (p) => {
|
|
2306
|
+
updateQuery({ offset: Math.max(0, (p - 1) * limit) });
|
|
2307
|
+
if (typeof document !== "undefined") {
|
|
2308
|
+
const el = document.querySelector(".dsvm-chipswrap");
|
|
2309
|
+
if (el && el.scrollIntoView) el.scrollIntoView({ block: "start" });
|
|
2310
|
+
}
|
|
1417
2311
|
};
|
|
1418
|
-
const
|
|
2312
|
+
const pageJumpNum = pageJump ? parseInt(pageJump, 10) : 0;
|
|
2313
|
+
const pageJumpValid = pageJumpNum >= 1 && pageJumpNum <= pages && pageJumpNum !== page;
|
|
2314
|
+
const jumpToPage = () => {
|
|
2315
|
+
if (!pageJumpValid) return;
|
|
2316
|
+
gotoPage(pageJumpNum);
|
|
2317
|
+
setPageJump("");
|
|
2318
|
+
};
|
|
2319
|
+
const detailItem = detailId ? items.find((x) => x.id === detailId) : null;
|
|
2320
|
+
const detailLabels = communityLabels(data);
|
|
2321
|
+
const zoneChips3 = h(
|
|
2322
|
+
"div",
|
|
2323
|
+
{ className: "dshm-chips" },
|
|
2324
|
+
...ZONE_TABS.map((z) => {
|
|
2325
|
+
const zCount = z.id === "community" ? markets.community.data && markets.community.data.total || 0 : z.id === "primary" ? markets.primary.data && markets.primary.data.total || 0 : favorites && favorites.list.length;
|
|
2326
|
+
return h(
|
|
2327
|
+
"button",
|
|
2328
|
+
{
|
|
2329
|
+
key: z.id,
|
|
2330
|
+
className: `dshm-chip${zone === z.id ? " on" : ""}`,
|
|
2331
|
+
onClick: () => setZone(z.id)
|
|
2332
|
+
},
|
|
2333
|
+
lookup(z.labelKey),
|
|
2334
|
+
zCount ? ` ${zCount}` : ""
|
|
2335
|
+
);
|
|
2336
|
+
})
|
|
2337
|
+
);
|
|
2338
|
+
const searchRow = market ? h(
|
|
2339
|
+
"div",
|
|
2340
|
+
{ className: "dsvm-searchrow" },
|
|
2341
|
+
h(SearchBox, { key: zone, placeholder: lookup("search.ph"), initial: query.query, onCommit: (v) => updateQuery({ query: v }) })
|
|
2342
|
+
) : null;
|
|
2343
|
+
const zoneBar = zoneChips3;
|
|
1419
2344
|
const installDone = (res) => {
|
|
1420
2345
|
notify({
|
|
1421
2346
|
kind: "ok",
|
|
@@ -1423,14 +2348,23 @@ function MarketTab({ notify, market, onMutation }) {
|
|
|
1423
2348
|
text: lookup("notify.installed", { pkg: res.pkg, version: res.version ? ` v${res.version}` : "" }) + (res.buildApprovals && res.buildApprovals.length ? lookup("notify.builds", { names: res.buildApprovals.join(", ") }) : res.fallbackAllBuilds ? lookup("notify.builds.fallback") : "") + (res.bundleWarning === "no-patch-layer" ? lookup("notify.bundlewarning") : "")
|
|
1424
2349
|
});
|
|
1425
2350
|
};
|
|
1426
|
-
const doInstall = async (it, version, forceIncompatible) => {
|
|
1427
|
-
setBusyId(it.id);
|
|
2351
|
+
const doInstall = async (it, version, forceIncompatible, reuseOpId) => {
|
|
1428
2352
|
try {
|
|
1429
|
-
const res = await
|
|
2353
|
+
const res = await ops.runOp(
|
|
2354
|
+
"install",
|
|
2355
|
+
it.id,
|
|
2356
|
+
() => api("install", { id: it.id, ...version ? { version } : {}, ...forceIncompatible ? { forceIncompatible: true } : {} }),
|
|
2357
|
+
{ version, npm: it.npm, github: it.github },
|
|
2358
|
+
// 审计 #6:meta 携带 npm 供 stillApplies 比对
|
|
2359
|
+
reuseOpId
|
|
2360
|
+
// 审计 #12:确认/重试复用同一记录,不再新开
|
|
2361
|
+
);
|
|
1430
2362
|
installDone(res);
|
|
1431
2363
|
await (onMutation ? onMutation() : reload(false));
|
|
1432
2364
|
} catch (e) {
|
|
1433
|
-
if (e && e.
|
|
2365
|
+
if (e && e.opSuperseded) {
|
|
2366
|
+
notify({ kind: "ok", text: lookup("op.superseded.note", { target: it.name }) });
|
|
2367
|
+
} else if (e && e.guard) {
|
|
1434
2368
|
notify({
|
|
1435
2369
|
kind: "err",
|
|
1436
2370
|
text: [
|
|
@@ -1442,12 +2376,10 @@ function MarketTab({ notify, market, onMutation }) {
|
|
|
1442
2376
|
].filter(Boolean).join(" | ")
|
|
1443
2377
|
});
|
|
1444
2378
|
} else if (e && e.issue) {
|
|
1445
|
-
setCompatConfirm({ it, version, issue: e.issue });
|
|
2379
|
+
setCompatConfirm({ it, version, issue: e.issue, opId: e.opId });
|
|
1446
2380
|
} else {
|
|
1447
2381
|
notify({ kind: "err", text: lookup("failed.install", { err: e && e.message || e }) });
|
|
1448
2382
|
}
|
|
1449
|
-
} finally {
|
|
1450
|
-
setBusyId(null);
|
|
1451
2383
|
}
|
|
1452
2384
|
};
|
|
1453
2385
|
const CompatDialog = compatConfirm ? h(
|
|
@@ -1473,82 +2405,118 @@ function MarketTab({ notify, market, onMutation }) {
|
|
|
1473
2405
|
{ className: "dshm-actions" },
|
|
1474
2406
|
h("button", {
|
|
1475
2407
|
className: "dshm-btn sm",
|
|
1476
|
-
onClick: () =>
|
|
2408
|
+
onClick: () => {
|
|
2409
|
+
if (compatConfirm.opId && ops.dispose) ops.dispose(compatConfirm.opId, lookup("op.cancelled"));
|
|
2410
|
+
setCompatConfirm(null);
|
|
2411
|
+
}
|
|
1477
2412
|
}, lookup("common.cancel")),
|
|
1478
2413
|
h("button", {
|
|
1479
2414
|
className: "dshm-btn primary sm",
|
|
1480
2415
|
onClick: () => {
|
|
1481
2416
|
const pending = compatConfirm;
|
|
1482
2417
|
setCompatConfirm(null);
|
|
1483
|
-
doInstall(pending.it, pending.version, true);
|
|
2418
|
+
doInstall(pending.it, pending.version, true, pending.opId);
|
|
1484
2419
|
}
|
|
1485
2420
|
}, lookup("compat.force"))
|
|
1486
2421
|
)
|
|
1487
2422
|
)
|
|
1488
2423
|
) : null;
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
null
|
|
1492
|
-
|
|
1493
|
-
|
|
2424
|
+
const openFavDetail = async (fav) => {
|
|
2425
|
+
const id = fav && fav.id;
|
|
2426
|
+
const scan = (d) => d && Array.isArray(d.items) ? d.items.find((x) => x && x.id === id) : null;
|
|
2427
|
+
const hit = scan(markets.community.data) || scan(markets.primary.data);
|
|
2428
|
+
if (hit) {
|
|
2429
|
+
setFavDetailItem(hit);
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
try {
|
|
2433
|
+
const res = await api("market", { query: id, source: "all", limit: 8 });
|
|
2434
|
+
const exact = res && Array.isArray(res.items) ? res.items.find((x) => x && x.id === id) : null;
|
|
2435
|
+
if (exact) {
|
|
2436
|
+
setFavDetailItem(exact);
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
} catch {
|
|
2440
|
+
}
|
|
2441
|
+
setFavDetailItem(fav && fav.snapshot || null);
|
|
2442
|
+
};
|
|
2443
|
+
if (zone === "favorites") {
|
|
2444
|
+
return h(
|
|
2445
|
+
React.Fragment,
|
|
2446
|
+
null,
|
|
2447
|
+
CompatDialog,
|
|
2448
|
+
zoneBar,
|
|
2449
|
+
h(FavoriteZone, { favorites, onOpen: openFavDetail }),
|
|
2450
|
+
favDetailItem ? h(DetailModal, {
|
|
2451
|
+
it: favDetailItem,
|
|
2452
|
+
labels: communityLabels(markets.community.data),
|
|
2453
|
+
busy: activeInstallTarget === favDetailItem.id,
|
|
2454
|
+
onClose: () => setFavDetailItem(null),
|
|
2455
|
+
onInstall: (it2) => doInstall(it2)
|
|
2456
|
+
}) : null
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
const optRow = (label, activeOpt, onPick) => h(
|
|
2460
|
+
"button",
|
|
2461
|
+
{ key: label, className: `dsvm-filteropt${activeOpt ? " on" : ""}`, onClick: onPick },
|
|
2462
|
+
h("span", null, label),
|
|
2463
|
+
activeOpt ? h("span", { className: "dsvm-filtercheck" }, "\u2713") : null
|
|
2464
|
+
);
|
|
2465
|
+
const curSortField = query && query.sort && query.sort.field || "downloads";
|
|
2466
|
+
const curSortDir = query && query.sort && query.sort.dir || "desc";
|
|
2467
|
+
const filterPop = zone === "community" && filterOpen ? h(
|
|
2468
|
+
"div",
|
|
2469
|
+
{ className: "dsvm-filterpop", onClick: (e) => e.stopPropagation() },
|
|
2470
|
+
h(
|
|
1494
2471
|
"div",
|
|
1495
|
-
{ className:
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
notice && notice.communityStale ? h("div", { className: "dshm-hint" }, lookup("community.stale")) : null,
|
|
1500
|
-
total > 200 ? h("div", { className: "dshm-hint" }, lookup("market.perf")) : null,
|
|
2472
|
+
{ className: "dsvm-filtergroup" },
|
|
2473
|
+
h("div", { className: "dsvm-filtergt" }, lookup("filter.sortfield")),
|
|
2474
|
+
...[["downloads", "filter.field.downloads"], ["stars", "filter.field.stars"], ["added", "filter.field.added"]].map(([f, key]) => optRow(lookup(key), curSortField === f, () => updateQuery({ sort: { field: f, dir: curSortDir } })))
|
|
2475
|
+
),
|
|
1501
2476
|
h(
|
|
1502
2477
|
"div",
|
|
1503
|
-
{ className: "
|
|
1504
|
-
h("
|
|
1505
|
-
|
|
1506
|
-
placeholder: lookup("search.ph"),
|
|
1507
|
-
value: qInput,
|
|
1508
|
-
onChange: (e) => onSearchInput(e.target.value)
|
|
1509
|
-
}),
|
|
1510
|
-
h("button", { className: "dshm-btn", onClick: () => reload(true), title: lookup("settings.policy.v") }, loading ? Spin() : `\u21BB ${lookup("common.refresh")}`)
|
|
2478
|
+
{ className: "dsvm-filtergroup" },
|
|
2479
|
+
h("div", { className: "dsvm-filtergt" }, lookup("filter.sortdir")),
|
|
2480
|
+
...[["desc", "filter.dir.desc"], ["asc", "filter.dir.asc"]].map(([d, key]) => optRow(lookup(key), curSortDir === d, () => updateQuery({ sort: { field: curSortField, dir: d } })))
|
|
1511
2481
|
),
|
|
1512
2482
|
h(
|
|
1513
2483
|
"div",
|
|
1514
|
-
{ className: "
|
|
1515
|
-
h("
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
2484
|
+
{ className: "dsvm-filtergroup" },
|
|
2485
|
+
h("div", { className: "dsvm-filtergt" }, lookup("filter.pagesize")),
|
|
2486
|
+
...MARKET_PAGE_SIZES2.map((n) => optRow(String(n), limit === n, () => updateQuery({ limit: n, offset: 0 })))
|
|
2487
|
+
)
|
|
2488
|
+
) : null;
|
|
2489
|
+
const filterTrigger = zone === "community" ? h(
|
|
2490
|
+
"div",
|
|
2491
|
+
{ className: "dsvm-filterwrap", key: "__filter" },
|
|
2492
|
+
filterOpen ? h("div", { className: "dsvm-filterback", onClick: () => setFilterOpen(false) }) : null,
|
|
2493
|
+
h(
|
|
2494
|
+
"button",
|
|
2495
|
+
// 0.7.3:独立于分类 chips 的筛选按钮样式(方角矩形 + 前置 chevron,参考 dsh-market)
|
|
2496
|
+
{ className: `dsvm-filterbtn${filterOpen ? " on" : ""}`, onClick: () => setFilterOpen(!filterOpen) },
|
|
2497
|
+
h("span", { className: "dsvm-filterchev", "aria-hidden": "true" }, filterOpen ? "\u2303" : "\u2304"),
|
|
2498
|
+
lookup("filter.title")
|
|
1525
2499
|
),
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
h("span", { className: "dshm-hint" }, lookup("community.newgroup")),
|
|
1547
|
-
...groups.unknown.map(chip)
|
|
1548
|
-
) : null
|
|
1549
|
-
);
|
|
1550
|
-
})(),
|
|
1551
|
-
busyId ? h(ProgressLine, { key: "prog" }) : null,
|
|
2500
|
+
filterPop
|
|
2501
|
+
) : null;
|
|
2502
|
+
return h(
|
|
2503
|
+
React.Fragment,
|
|
2504
|
+
null,
|
|
2505
|
+
CompatDialog,
|
|
2506
|
+
zoneBar,
|
|
2507
|
+
searchRow,
|
|
2508
|
+
notice && notice.notice ? h("div", { className: "dshm-err" }, lookup(notice.notice.key)) : null,
|
|
2509
|
+
notice && notice.communityFallback ? h("div", { className: "dshm-hint" }, lookup("community.fallback")) : null,
|
|
2510
|
+
notice && notice.communityStale ? h("div", { className: "dshm-hint" }, lookup("community.stale")) : null,
|
|
2511
|
+
h(ZoneChips, {
|
|
2512
|
+
zone,
|
|
2513
|
+
counts,
|
|
2514
|
+
labels: communityLabels(data),
|
|
2515
|
+
active: query.category,
|
|
2516
|
+
onPick: (id) => updateQuery({ category: id, offset: 0 }),
|
|
2517
|
+
trailing: filterTrigger
|
|
2518
|
+
}),
|
|
2519
|
+
activeInstallTarget ? h(ProgressLine, { key: "prog" }) : null,
|
|
1552
2520
|
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(
|
|
1553
2521
|
React.Fragment,
|
|
1554
2522
|
null,
|
|
@@ -1560,67 +2528,89 @@ function MarketTab({ notify, market, onMutation }) {
|
|
|
1560
2528
|
icon: h(Icon, { entry: it }),
|
|
1561
2529
|
name: it.name,
|
|
1562
2530
|
badges: [
|
|
2531
|
+
activeInstallTarget === it.id ? h(
|
|
2532
|
+
"button",
|
|
2533
|
+
{
|
|
2534
|
+
className: "dshm-badge warn",
|
|
2535
|
+
key: "busy",
|
|
2536
|
+
title: lookup("op.panel.jump"),
|
|
2537
|
+
onClick: (e) => {
|
|
2538
|
+
e.stopPropagation();
|
|
2539
|
+
if (typeof document !== "undefined") {
|
|
2540
|
+
const el = document.querySelector(".dsvm-ops");
|
|
2541
|
+
if (el && el.scrollIntoView) el.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
},
|
|
2545
|
+
lookup("op.status.running")
|
|
2546
|
+
) : null,
|
|
2547
|
+
it.deprecated === true ? h("span", { className: "dshm-badge warn", key: "dep" }, lookup("badge.deprecated")) : null,
|
|
2548
|
+
it.community !== true && Array.isArray(it.verified) && it.verified.length ? h("span", { className: "dshm-badge", key: "v", title: it.verified.join("\u3001") }, lookup("badge.verified")) : null,
|
|
1563
2549
|
it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, lookup("badge.update")) : null,
|
|
1564
2550
|
it.installed ? h("span", { className: "dshm-badge", key: "i" }, lookup("badge.installed")) : null,
|
|
1565
2551
|
it.community === true ? h("span", { className: "dshm-badge info", key: "c" }, lookup("badge.community")) : null,
|
|
1566
2552
|
h("span", { className: "dshm-badge info", key: "s" }, it.source === "npm" ? "npm" : "github")
|
|
1567
2553
|
],
|
|
1568
|
-
|
|
2554
|
+
byline: it.community === true ? [
|
|
2555
|
+
it.owner ? { text: `by ${it.owner}` } : null,
|
|
2556
|
+
typeof it.downloads === "number" ? { text: `${compactCount(it.downloads)} \u2193`, title: String(it.downloads) } : null,
|
|
2557
|
+
typeof it.stars === "number" ? { text: `${compactCount(it.stars)} \u2605`, title: String(it.stars) } : null
|
|
2558
|
+
].filter(Boolean) : (it.tags || []).map((t) => ({ text: `#${t}` })),
|
|
2559
|
+
desc: browserLang() === "en" && typeof it.descriptionEn === "string" && it.descriptionEn !== "" ? it.descriptionEn : it.description,
|
|
2560
|
+
clampLines: it.community === true ? 5 : 2,
|
|
1569
2561
|
sub: [
|
|
1570
2562
|
it.latestVersion ? lookup("sub.latest", { v: it.latestVersion }) : it.latestTag ? it.latestTag : it.latestSha ? lookup("sub.head", { sha: it.latestSha.slice(0, 7) }) : null,
|
|
1571
2563
|
it.installedVersion ? lookup("sub.installed", { v: it.installedVersion }) : null,
|
|
1572
|
-
it.latestError ? lookup("version.failed") : null
|
|
2564
|
+
it.latestError ? it.version ? lookup("sub.snapshot", { v: it.version }) : lookup("version.failed") : null
|
|
1573
2565
|
].filter(Boolean).join(" \xB7 "),
|
|
1574
2566
|
links: h(LinksRow, { npm: it.npm, github: it.github, homepage: it.homepage }),
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
...safeScreenshots(it).map((src) => h("img", { key: src, src, alt: "", loading: "lazy", referrerPolicy: "no-referrer", style: { maxWidth: "220px", maxHeight: "140px", borderRadius: "6px", border: "1px solid rgba(127,127,127,.25)" } }))
|
|
1589
|
-
)] : null
|
|
1590
|
-
]),
|
|
1591
|
-
actions: [
|
|
1592
|
-
it.installed ? h("span", { className: "dshm-hint", key: "hint" }, lookup("manage.hint")) : h(
|
|
1593
|
-
"button",
|
|
1594
|
-
{
|
|
1595
|
-
key: "install",
|
|
1596
|
-
className: "dshm-btn primary sm",
|
|
1597
|
-
disabled: busyId === it.id,
|
|
1598
|
-
onClick: (e) => {
|
|
1599
|
-
e.stopPropagation();
|
|
1600
|
-
doInstall(it);
|
|
1601
|
-
}
|
|
1602
|
-
},
|
|
1603
|
-
busyId === it.id ? h(Spin) : lookup("action.install")
|
|
1604
|
-
)
|
|
1605
|
-
]
|
|
2567
|
+
onToggle: () => setDetailId(it.id),
|
|
2568
|
+
topRight: favorites ? h(
|
|
2569
|
+
"button",
|
|
2570
|
+
{
|
|
2571
|
+
className: `dsvm-favbtn${favorites.list.some((f) => f.id === it.id) ? " on" : ""}`,
|
|
2572
|
+
title: favorites.list.some((f) => f.id === it.id) ? lookup("fav.remove") : lookup("fav.add"),
|
|
2573
|
+
onClick: (e) => {
|
|
2574
|
+
e.stopPropagation();
|
|
2575
|
+
favorites.toggle(snapshotOf(it));
|
|
2576
|
+
}
|
|
2577
|
+
},
|
|
2578
|
+
favorites.list.some((f) => f.id === it.id) ? "\u2605" : "\u2606"
|
|
2579
|
+
) : null
|
|
1606
2580
|
}))
|
|
1607
2581
|
),
|
|
1608
2582
|
pages > 1 ? h(
|
|
1609
2583
|
"div",
|
|
1610
|
-
{ className: "
|
|
1611
|
-
h("button", {
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
2584
|
+
{ className: "dsvm-pager" },
|
|
2585
|
+
h("button", { className: "dsvm-pagebtn", disabled: page <= 1 || loading, onClick: () => gotoPage(page - 1) }, "\u2039"),
|
|
2586
|
+
...pageItems2(page, pages).map((p, i) => p === "..." ? h("span", { key: `e${i}`, className: "dshm-hint" }, "\u2026") : h("button", { key: p, className: `dsvm-pagebtn${p === page ? " on" : ""}`, onClick: () => gotoPage(p) }, String(p))),
|
|
2587
|
+
h("button", { className: "dsvm-pagebtn", disabled: page >= pages || loading, onClick: () => gotoPage(page + 1) }, "\u203A"),
|
|
2588
|
+
// 页号跳转(0.7.3):输入有效页号回车或点「跳转」直达
|
|
2589
|
+
h(
|
|
2590
|
+
"span",
|
|
2591
|
+
{ className: "dsvm-pagejump" },
|
|
2592
|
+
h("input", {
|
|
2593
|
+
className: "dsvm-pagejump-input",
|
|
2594
|
+
value: pageJump,
|
|
2595
|
+
placeholder: lookup("pager.jump.ph"),
|
|
2596
|
+
inputMode: "numeric",
|
|
2597
|
+
"aria-label": lookup("pager.jump.ph"),
|
|
2598
|
+
onChange: (e) => setPageJump(e.target.value.replace(/\D/g, "").slice(0, 4)),
|
|
2599
|
+
onKeyDown: (e) => {
|
|
2600
|
+
if (e.key === "Enter" && !e.isComposing) jumpToPage();
|
|
2601
|
+
}
|
|
2602
|
+
}),
|
|
2603
|
+
h("button", { className: "dsvm-pagebtn dsvm-pagejump-btn", disabled: !pageJumpValid || loading, onClick: jumpToPage }, lookup("pager.jump"))
|
|
2604
|
+
)
|
|
1622
2605
|
) : null
|
|
1623
|
-
)
|
|
2606
|
+
),
|
|
2607
|
+
detailItem ? h(DetailModal, {
|
|
2608
|
+
it: detailItem,
|
|
2609
|
+
labels: detailLabels,
|
|
2610
|
+
busy: activeInstallTarget === detailItem.id,
|
|
2611
|
+
onClose: () => setDetailId(null),
|
|
2612
|
+
onInstall: (it2) => doInstall(it2)
|
|
2613
|
+
}) : null
|
|
1624
2614
|
);
|
|
1625
2615
|
}
|
|
1626
2616
|
var PHASE_LABEL = { resolving: "phase.resolving", downloading: "phase.downloading", linking: "phase.linking", building: "phase.building" };
|
|
@@ -1673,36 +2663,37 @@ function ReadmeBlock({ pkg }) {
|
|
|
1673
2663
|
state.truncated ? h("div", { className: "dshm-md-note" }, lookup("readme.truncated")) : null
|
|
1674
2664
|
);
|
|
1675
2665
|
}
|
|
1676
|
-
function InstalledTab({ notify, installed, onMutation }) {
|
|
2666
|
+
function InstalledTab({ notify, installed, onMutation, ops }) {
|
|
1677
2667
|
const { loading, data, error, reload } = installed;
|
|
1678
2668
|
const [openPkg, setOpenPkg] = useState(null);
|
|
1679
2669
|
const [readmePkg, setReadmePkg] = useState(null);
|
|
1680
|
-
const
|
|
2670
|
+
const activeMutateTarget = (ops.records.find((r) => r.kind !== "install" && (r.status === "running" || r.status === "queued" || r.status === "input")) || {}).target || null;
|
|
1681
2671
|
const doToggle = async (it, enabled) => {
|
|
1682
|
-
setBusyPkg(it.pkg);
|
|
1683
2672
|
const call = () => api("set-enabled", { pkg: it.pkg, enabled });
|
|
1684
2673
|
try {
|
|
1685
2674
|
let res;
|
|
1686
2675
|
try {
|
|
1687
|
-
res = await call
|
|
2676
|
+
res = await ops.runOp("toggle", it.pkg, call, { on: enabled });
|
|
1688
2677
|
} catch (first) {
|
|
2678
|
+
if (first && first.opSuperseded) throw first;
|
|
1689
2679
|
await new Promise((r) => setTimeout(r, 1500));
|
|
1690
|
-
res = await call
|
|
2680
|
+
res = await ops.runOp("toggle", it.pkg, call, { on: enabled }, first && first.opId);
|
|
1691
2681
|
}
|
|
1692
2682
|
const note = toggleNoticeKeys2(res);
|
|
1693
2683
|
const extra = res.warnings && res.warnings.length ? `\uFF08${res.warnings.join("\uFF1B")}\uFF09` : "";
|
|
1694
2684
|
notify({ kind: "ok", needsRestart: note.needsRestart, text: lookup(note.textKey, note.params) + extra });
|
|
1695
2685
|
await (onMutation ? onMutation() : reload());
|
|
1696
2686
|
} catch (e) {
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
2687
|
+
if (e && e.opSuperseded) {
|
|
2688
|
+
notify({ kind: "ok", text: lookup("op.superseded.note", { target: it.pkg }) });
|
|
2689
|
+
} else {
|
|
2690
|
+
notify({ kind: "err", text: lookup("toggle.failed", { err: e && e.message || e }) });
|
|
2691
|
+
}
|
|
1700
2692
|
}
|
|
1701
2693
|
};
|
|
1702
2694
|
const doUninstall = async (it) => {
|
|
1703
|
-
setBusyPkg(it.pkg);
|
|
1704
2695
|
try {
|
|
1705
|
-
const res = await api("uninstall", { pkg: it.pkg });
|
|
2696
|
+
const res = await ops.runOp("uninstall", it.pkg, () => api("uninstall", { pkg: it.pkg }));
|
|
1706
2697
|
notify({
|
|
1707
2698
|
kind: "ok",
|
|
1708
2699
|
needsRestart: true,
|
|
@@ -1710,15 +2701,16 @@ function InstalledTab({ notify, installed, onMutation }) {
|
|
|
1710
2701
|
});
|
|
1711
2702
|
await (onMutation ? onMutation() : reload());
|
|
1712
2703
|
} catch (e) {
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
2704
|
+
if (e && e.opSuperseded) {
|
|
2705
|
+
notify({ kind: "ok", text: lookup("op.superseded.note", { target: it.pkg }) });
|
|
2706
|
+
} else {
|
|
2707
|
+
notify({ kind: "err", text: lookup("failed.uninstall", { err: e && e.message || e }) });
|
|
2708
|
+
}
|
|
1716
2709
|
}
|
|
1717
2710
|
};
|
|
1718
2711
|
const doUpgrade = async (it) => {
|
|
1719
|
-
setBusyPkg(it.pkg);
|
|
1720
2712
|
try {
|
|
1721
|
-
const res = await api("upgrade", { pkg: it.pkg });
|
|
2713
|
+
const res = await ops.runOp("upgrade", it.pkg, () => api("upgrade", { pkg: it.pkg }));
|
|
1722
2714
|
notify({
|
|
1723
2715
|
kind: "ok",
|
|
1724
2716
|
needsRestart: true,
|
|
@@ -1741,11 +2733,11 @@ function InstalledTab({ notify, installed, onMutation }) {
|
|
|
1741
2733
|
e.guard.restartSafe ? lookup("guard.restartsafenow") : lookup("guard.restartunsafe")
|
|
1742
2734
|
].filter(Boolean).join(" | ")
|
|
1743
2735
|
});
|
|
2736
|
+
} else if (e && e.opSuperseded) {
|
|
2737
|
+
notify({ kind: "ok", text: lookup("op.superseded.note", { target: it.pkg }) });
|
|
1744
2738
|
} else {
|
|
1745
2739
|
notify({ kind: "err", text: lookup("failed.upgrade", { err: e && e.message || e }) });
|
|
1746
2740
|
}
|
|
1747
|
-
} finally {
|
|
1748
|
-
setBusyPkg(null);
|
|
1749
2741
|
}
|
|
1750
2742
|
};
|
|
1751
2743
|
if (loading && !data) return h("div", { className: "dshm-empty" }, lookup("installed.loading"), Spin());
|
|
@@ -1757,7 +2749,22 @@ function InstalledTab({ notify, installed, onMutation }) {
|
|
|
1757
2749
|
null,
|
|
1758
2750
|
h("div", { className: "dshm-hint" }, lookup("profile.hint", { path: data.profileDir })),
|
|
1759
2751
|
data.others > 0 ? h("div", { className: "dshm-others" }, lookup("installed.others", { n: data.others })) : null,
|
|
1760
|
-
|
|
2752
|
+
items.some((x) => x && x.outdated) ? h(
|
|
2753
|
+
"div",
|
|
2754
|
+
{ className: "dsvm-sortrow" },
|
|
2755
|
+
h(
|
|
2756
|
+
"button",
|
|
2757
|
+
{
|
|
2758
|
+
className: "dshm-btn primary sm",
|
|
2759
|
+
disabled: activeMutateTarget != null,
|
|
2760
|
+
onClick: () => {
|
|
2761
|
+
for (const it of items.filter((x) => x && x.outdated)) void doUpgrade(it);
|
|
2762
|
+
}
|
|
2763
|
+
},
|
|
2764
|
+
lookup("installed.upgradeAll", { n: items.filter((x) => x && x.outdated).length })
|
|
2765
|
+
)
|
|
2766
|
+
) : null,
|
|
2767
|
+
activeMutateTarget ? h(ProgressLine, { key: "prog" }) : null,
|
|
1761
2768
|
h(
|
|
1762
2769
|
"div",
|
|
1763
2770
|
{ className: "dshm-cards" },
|
|
@@ -1770,7 +2777,7 @@ function InstalledTab({ notify, installed, onMutation }) {
|
|
|
1770
2777
|
name: it.name,
|
|
1771
2778
|
topRight: h(ToggleSwitch, {
|
|
1772
2779
|
vm: tvm,
|
|
1773
|
-
disabled:
|
|
2780
|
+
disabled: activeMutateTarget === it.pkg,
|
|
1774
2781
|
onChange: (enabled) => doToggle(it, enabled)
|
|
1775
2782
|
}),
|
|
1776
2783
|
badges: [
|
|
@@ -1814,20 +2821,20 @@ function InstalledTab({ notify, installed, onMutation }) {
|
|
|
1814
2821
|
{
|
|
1815
2822
|
key: "up",
|
|
1816
2823
|
className: "dshm-btn primary sm",
|
|
1817
|
-
disabled:
|
|
2824
|
+
disabled: activeMutateTarget === it.pkg,
|
|
1818
2825
|
onClick: (e) => {
|
|
1819
2826
|
e.stopPropagation();
|
|
1820
2827
|
doUpgrade(it);
|
|
1821
2828
|
}
|
|
1822
2829
|
},
|
|
1823
|
-
|
|
2830
|
+
activeMutateTarget === it.pkg ? h(Spin) : lookup("action.upgrade")
|
|
1824
2831
|
) : null,
|
|
1825
2832
|
h(TwoStepButton, {
|
|
1826
2833
|
key: "un",
|
|
1827
2834
|
label: lookup("action.uninstall"),
|
|
1828
2835
|
confirmLabel: lookup(vm.guard.confirmKey),
|
|
1829
2836
|
className: "dshm-btn sm",
|
|
1830
|
-
disabled:
|
|
2837
|
+
disabled: activeMutateTarget === it.pkg,
|
|
1831
2838
|
onConfirm: () => doUninstall(it)
|
|
1832
2839
|
})
|
|
1833
2840
|
]
|
|
@@ -1843,25 +2850,23 @@ function regSourceLabel(data) {
|
|
|
1843
2850
|
function configStatusLabel(status) {
|
|
1844
2851
|
return lookup(`settings.status.${status || "loading"}` || "settings.status.loading");
|
|
1845
2852
|
}
|
|
1846
|
-
var STATUS_BADGE = { ready: "", pending: "info", rejected: "warn", unavailable: "err", loading: "info" };
|
|
1847
2853
|
function SettingsTab({ notify, onRegistryChanged }) {
|
|
1848
2854
|
const reg = useAsync((force) => api("registry", force ? { force: true } : {}), []);
|
|
1849
2855
|
const cfgState = useAsync(() => api("registry-config"), []);
|
|
1850
|
-
const self = useAsync(() => api("self-check"), []);
|
|
1851
2856
|
const [busy, setBusy] = useState(false);
|
|
1852
|
-
const [upgrading, setUpgrading] = useState(false);
|
|
1853
2857
|
const [draftAddress, setDraftAddress] = useState(null);
|
|
1854
2858
|
const [applying, setApplying] = useState(false);
|
|
1855
2859
|
const [downloading, setDownloading] = useState(false);
|
|
1856
|
-
const [diagnosing, setDiagnosing] = useState(false);
|
|
1857
2860
|
const [applyError, setApplyError] = useState(null);
|
|
1858
|
-
const [
|
|
1859
|
-
const
|
|
2861
|
+
const [communityOn, setCommunityOn] = useState(null);
|
|
2862
|
+
const [communityBusy, setCommunityBusy] = useState(false);
|
|
1860
2863
|
const cfgData = cfgState.data;
|
|
1861
2864
|
useEffect(() => {
|
|
1862
2865
|
if (draftAddress === null && cfgData) setDraftAddress(cfgData.registryUrl ?? "");
|
|
1863
2866
|
}, [cfgData, draftAddress]);
|
|
1864
|
-
useEffect(() =>
|
|
2867
|
+
useEffect(() => {
|
|
2868
|
+
if (communityOn === null && cfgData) setCommunityOn(cfgData.communityCatalog === true);
|
|
2869
|
+
}, [cfgData, communityOn]);
|
|
1865
2870
|
const refresh = async () => {
|
|
1866
2871
|
setBusy(true);
|
|
1867
2872
|
try {
|
|
@@ -1912,44 +2917,71 @@ function SettingsTab({ notify, onRegistryChanged }) {
|
|
|
1912
2917
|
setDownloading(false);
|
|
1913
2918
|
}
|
|
1914
2919
|
};
|
|
1915
|
-
const
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
2920
|
+
const snap = cfgData || {};
|
|
2921
|
+
const state = snap.registryState || (reg.data ? reg.data.registryState : null) || null;
|
|
2922
|
+
const communitySummary = reg.data && reg.data.community && typeof reg.data.community === "object" ? reg.data.community : null;
|
|
2923
|
+
const applyCommunity = async (enabled) => {
|
|
2924
|
+
const prev = communityOn;
|
|
2925
|
+
setCommunityOn(enabled);
|
|
2926
|
+
setCommunityBusy(true);
|
|
1921
2927
|
try {
|
|
1922
|
-
|
|
1923
|
-
|
|
2928
|
+
await api("set-community", { enabled });
|
|
2929
|
+
await reloadRegistryState();
|
|
2930
|
+
await (onRegistryChanged ? onRegistryChanged() : reg.reload(false));
|
|
2931
|
+
notify({ kind: "ok", text: lookup(enabled ? "settings.community.on.ok" : "settings.community.off.ok"), needsRestart: false });
|
|
1924
2932
|
} catch (e) {
|
|
1925
|
-
|
|
2933
|
+
setCommunityOn(prev);
|
|
2934
|
+
notify({ kind: "err", text: lookup("settings.community.toggle.failed", { err: e && e.message || e }) });
|
|
1926
2935
|
} finally {
|
|
1927
|
-
|
|
2936
|
+
setCommunityBusy(false);
|
|
1928
2937
|
}
|
|
1929
2938
|
};
|
|
1930
|
-
const
|
|
1931
|
-
const
|
|
1932
|
-
const communitySummary = reg.data && reg.data.community && typeof reg.data.community === "object" ? reg.data.community : null;
|
|
1933
|
-
const communityRows = communitySummary && (communitySummary.status === "ready" || communitySummary.status === "stale") ? [
|
|
1934
|
-
[lookup("settings.community.status"), communitySummary.status === "stale" ? lookup("community.stale") : lookup("settings.status.ready")],
|
|
1935
|
-
[lookup("settings.community.version"), communitySummary.version || "\u2014"],
|
|
1936
|
-
[lookup("settings.community.route"), communitySummary.route || "\u2014"],
|
|
1937
|
-
[lookup("settings.community.accepted"), `${communitySummary.acceptedCount} / ${communitySummary.upstreamCount ?? "\u2014"}`],
|
|
1938
|
-
[lookup("settings.community.displaced"), String(communitySummary.displaced ?? 0)]
|
|
1939
|
-
] : [[lookup("settings.community.status"), lookup("settings.community.none")]];
|
|
2939
|
+
const communityLive = communitySummary && (communitySummary.status === "ready" || communitySummary.status === "stale");
|
|
2940
|
+
const communityStatusBadge = !communityLive ? { cls: "err", label: lookup("settings.status.unavailable") } : communitySummary.status === "stale" ? { cls: "warn", label: lookup("settings.community.status.stale") } : { cls: "ok", label: lookup("settings.status.ready") };
|
|
1940
2941
|
return h(
|
|
1941
2942
|
React.Fragment,
|
|
1942
2943
|
null,
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
2944
|
+
// ① 社区清单(from awesome-dsh-plugin):标题行右侧开关;开态紧凑 kv,关态单行说明
|
|
2945
|
+
h(
|
|
2946
|
+
"div",
|
|
2947
|
+
{ className: "dshm-section" },
|
|
2948
|
+
h(
|
|
1946
2949
|
"div",
|
|
1947
|
-
{
|
|
1948
|
-
|
|
1949
|
-
h("span",
|
|
1950
|
-
|
|
1951
|
-
|
|
2950
|
+
{ className: "dshm-section-title" },
|
|
2951
|
+
lookup("settings.community"),
|
|
2952
|
+
h("span", { className: "dshm-spacer" }),
|
|
2953
|
+
h(
|
|
2954
|
+
"label",
|
|
2955
|
+
{
|
|
2956
|
+
className: `dshm-switch${communityOn ? " on" : ""}${communityBusy || communityOn === null ? " locked" : ""}`,
|
|
2957
|
+
title: lookup("settings.community.toggle")
|
|
2958
|
+
},
|
|
2959
|
+
h("input", {
|
|
2960
|
+
type: "checkbox",
|
|
2961
|
+
checked: communityOn === true,
|
|
2962
|
+
disabled: communityBusy || communityOn === null,
|
|
2963
|
+
onChange: (e) => applyCommunity(e.target.checked)
|
|
2964
|
+
}),
|
|
2965
|
+
h("span", { className: "dshm-switch-slider" })
|
|
2966
|
+
)
|
|
2967
|
+
),
|
|
2968
|
+
communityOn === false ? h("div", { className: "dshm-hint" }, lookup("settings.community.off")) : h(
|
|
2969
|
+
"div",
|
|
2970
|
+
{ className: "dshm-kv" },
|
|
2971
|
+
h("span", { className: "k" }, lookup("settings.community.status")),
|
|
2972
|
+
h("span", null, h("span", { className: `dshm-badge ${communityStatusBadge.cls}` }, communityStatusBadge.label)),
|
|
2973
|
+
h("span", { className: "k" }, lookup("settings.community.version")),
|
|
2974
|
+
h("span", null, communityLive ? communitySummary.version || "\u2014" : "\u2014"),
|
|
2975
|
+
h("span", { className: "k" }, lookup("settings.community.route")),
|
|
2976
|
+
h("span", null, communityLive ? communitySummary.route || "\u2014" : "\u2014"),
|
|
2977
|
+
h("span", { className: "k" }, lookup("settings.community.count")),
|
|
2978
|
+
h("span", null, communityLive ? lookup("settings.community.count.v", { n: communitySummary.acceptedCount ?? 0, up: communitySummary.upstreamCount ?? "\u2014" }) : "\u2014"),
|
|
2979
|
+
h("span", { className: "k" }, lookup("settings.community.displaced")),
|
|
2980
|
+
h("span", null, communityLive ? lookup("settings.community.displaced.v", { n: communitySummary.displaced ?? 0 }) : "\u2014")
|
|
2981
|
+
),
|
|
2982
|
+
communitySummary && Array.isArray(communitySummary.errors) && communitySummary.errors.length && communityOn !== false ? h("div", { className: "dshm-err" }, communitySummary.errors.join("\uFF1B")) : null
|
|
1952
2983
|
),
|
|
2984
|
+
// ② 精选清单(registry.json):自定义源整体替换主清单,仅影响市场「精选」区
|
|
1953
2985
|
Section(
|
|
1954
2986
|
lookup("settings.registry"),
|
|
1955
2987
|
h(
|
|
@@ -1962,14 +2994,16 @@ function SettingsTab({ notify, onRegistryChanged }) {
|
|
|
1962
2994
|
onChange: (e) => setDraftAddress(e.target.value),
|
|
1963
2995
|
spellcheck: "false"
|
|
1964
2996
|
}),
|
|
1965
|
-
h("div", { className: "dshm-note" }, lookup("settings.
|
|
2997
|
+
h("div", { className: "dshm-note" }, lookup("settings.note.custom"))
|
|
1966
2998
|
),
|
|
1967
2999
|
h(
|
|
1968
3000
|
"div",
|
|
1969
3001
|
{ className: "dshm-actions" },
|
|
3002
|
+
h("button", { className: "dshm-btn sm", disabled: busy || reg.loading, onClick: refresh }, busy || reg.loading ? h(Spin) : lookup("settings.force")),
|
|
1970
3003
|
h("button", {
|
|
1971
3004
|
className: "dshm-btn primary sm",
|
|
1972
|
-
|
|
3005
|
+
// 空草稿时禁用:回到默认只走「恢复默认」一条路,避免与右侧按钮双通道等价
|
|
3006
|
+
disabled: applying || draftAddress === null || (draftAddress ?? "").trim() === "",
|
|
1973
3007
|
onClick: () => applyAddress(draftAddress ?? "")
|
|
1974
3008
|
}, applying ? h("span", null, lookup("settings.apply.applying"), " ", h(Spin)) : lookup("settings.apply")),
|
|
1975
3009
|
h("button", {
|
|
@@ -1980,101 +3014,48 @@ function SettingsTab({ notify, onRegistryChanged }) {
|
|
|
1980
3014
|
h("button", {
|
|
1981
3015
|
className: "dshm-btn sm",
|
|
1982
3016
|
disabled: downloading,
|
|
3017
|
+
title: lookup("settings.download.title"),
|
|
1983
3018
|
onClick: downloadDefault
|
|
1984
|
-
}, downloading ? h("span", null, lookup("settings.download.downloading"), " ", h(Spin)) : lookup("settings.download"))
|
|
1985
|
-
h("button", {
|
|
1986
|
-
className: "dshm-btn sm",
|
|
1987
|
-
disabled: diagnosing || applying,
|
|
1988
|
-
onClick: runDiagnose
|
|
1989
|
-
}, diagnosing ? h("span", null, lookup("settings.diagnose.running"), " ", h(Spin)) : lookup("settings.diagnose")),
|
|
1990
|
-
h("button", { className: "dshm-btn sm", disabled: busy || reg.loading, onClick: refresh }, busy || reg.loading ? h(Spin) : lookup("settings.force"))
|
|
3019
|
+
}, downloading ? h("span", null, lookup("settings.download.downloading"), " ", h(Spin)) : lookup("settings.download"))
|
|
1991
3020
|
),
|
|
1992
3021
|
applyError ? h("div", { className: "dshm-err" }, lookup("settings.apply.failed", { err: applyError })) : null,
|
|
3022
|
+
// 配置状态仅在异常态出现(ready 是无信息量的常态,不再常驻)
|
|
3023
|
+
snap.configStatus && snap.configStatus !== "ready" ? h(
|
|
3024
|
+
"div",
|
|
3025
|
+
{ className: snap.configStatus === "pending" ? "dshm-note warn" : "dshm-err" },
|
|
3026
|
+
`${lookup("settings.status.label")}\uFF1A${configStatusLabel(snap.configStatus)}${snap.configErrors && snap.configErrors.length ? `\uFF08${snap.configErrors.slice(0, 2).join("\uFF1B")}\uFF09` : ""}`
|
|
3027
|
+
) : null,
|
|
1993
3028
|
h(
|
|
1994
3029
|
"div",
|
|
1995
3030
|
{ className: "dshm-kv", style: { marginTop: "4px" } },
|
|
1996
3031
|
h("span", { className: "k" }, lookup("settings.configured")),
|
|
1997
|
-
h("span", null, snap.registryUrl ? h("span", { className: "dshm-code" }, snap.registryUrl) : h("span", { className: "dshm-hint" }, "
|
|
1998
|
-
h("span", { className: "k" }, lookup("settings.activecfg")),
|
|
1999
|
-
h("span", null, snap.activeConfigAddress ? h("span", { className: "dshm-code" }, snap.activeConfigAddress) : h("span", { className: "dshm-hint" }, "\uFF08\u9ED8\u8BA4\uFF09")),
|
|
2000
|
-
h("span", { className: "k" }, lookup("settings.status.label")),
|
|
2001
|
-
h("span", null, h("span", { className: `dshm-badge ${STATUS_BADGE[snap.configStatus] || ""}` }, configStatusLabel(snap.configStatus))),
|
|
3032
|
+
h("span", null, snap.registryUrl ? h("span", { className: "dshm-code" }, snap.registryUrl) : h("span", { className: "dshm-hint" }, lookup("settings.address.default"))),
|
|
2002
3033
|
h("span", { className: "k" }, lookup("settings.effective")),
|
|
2003
3034
|
h("span", null, regSourceLabel(state), state && !state.isDefault ? h("span", { className: "dshm-badge info", style: { marginLeft: "6px" } }, lookup("badge.custom")) : null),
|
|
2004
3035
|
h("span", { className: "k" }, lookup("settings.updated")),
|
|
2005
3036
|
h("span", null, fmtDate(state && state.fetchedAt)),
|
|
2006
3037
|
h("span", { className: "k" }, lookup("settings.count")),
|
|
2007
|
-
h("span", null, state ? lookup("settings.count.v", { n: state.count ?? 0 }) : "\u2014")
|
|
2008
|
-
h("span", { className: "k" }, lookup("settings.policy")),
|
|
2009
|
-
h("span", null, lookup("settings.policy.v"))
|
|
3038
|
+
h("span", null, state ? lookup("settings.count.v", { n: state.count ?? 0 }) : "\u2014")
|
|
2010
3039
|
),
|
|
2011
|
-
state && state.stale && state.status !== "unavailable" ? h("div", { className: "dshm-note" }, lookup("settings.cache.hint")) : null,
|
|
2012
3040
|
state && !state.isDefault ? h("div", { className: "dshm-note warn" }, lookup("settings.trust.hint")) : null,
|
|
2013
3041
|
snap.warnings && snap.warnings.length ? h("div", { className: "dshm-note warn" }, `${lookup("settings.warnings")}\uFF1A${snap.warnings.join("\uFF1B")}`) : null,
|
|
2014
|
-
state && state.errors && state.errors.length ? h("div", { className: "dshm-err" }, `${lookup("settings.remotehint")}\uFF1A${state.errors.slice(0, 5).join("\uFF1B")}`) : null
|
|
2015
|
-
diagnosticResult ? h(
|
|
2016
|
-
"div",
|
|
2017
|
-
{ className: "dshm-hint", style: { wordBreak: "break-all" } },
|
|
2018
|
-
lookup("settings.diagnose.result", {
|
|
2019
|
-
checked: diagnosticResult.checked ?? 0,
|
|
2020
|
-
passed: diagnosticResult.passed ?? 0,
|
|
2021
|
-
failed: diagnosticResult.failed ?? 0,
|
|
2022
|
-
trunc: diagnosticResult.truncated ? lookup("settings.diagnose.truncated") : ""
|
|
2023
|
-
}),
|
|
2024
|
-
diagnosticResult.issues && diagnosticResult.issues.length ? h(
|
|
2025
|
-
"div",
|
|
2026
|
-
{ style: { marginTop: "4px" } },
|
|
2027
|
-
diagnosticResult.issues.slice(0, 100).map((iss, i) => h("div", { key: i, className: "dshm-err" }, `\xB7 [${iss.id}] ${iss.field}: ${iss.message}`))
|
|
2028
|
-
) : h("div", { className: "dshm-note" }, lookup("settings.diagnose.none"))
|
|
2029
|
-
) : null
|
|
3042
|
+
state && state.errors && state.errors.length ? h("div", { className: "dshm-err" }, `${lookup("settings.remotehint")}\uFF1A${state.errors.slice(0, 5).join("\uFF1B")}`) : null
|
|
2030
3043
|
),
|
|
3044
|
+
// ③ 关于:当前定位 + 仓库/反馈链接(dsh-m 自身卡已按主人要求移除——升级走市场卡片/CLI,self-check/self-upgrade API 保留)
|
|
2031
3045
|
Section(
|
|
2032
|
-
lookup("settings.
|
|
3046
|
+
lookup("settings.about"),
|
|
3047
|
+
h("div", { className: "dshm-hint" }, lookup("settings.about.text")),
|
|
2033
3048
|
h(
|
|
2034
3049
|
"div",
|
|
2035
|
-
{ className: "dshm-
|
|
2036
|
-
h(
|
|
2037
|
-
h("span",
|
|
2038
|
-
h(
|
|
2039
|
-
h("span",
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
"div",
|
|
2043
|
-
{ className: "dshm-actions" },
|
|
2044
|
-
h("button", { className: "dshm-btn primary sm", disabled: upgrading, onClick: upgradeSelf }, upgrading ? h(Spin) : lookup("settings.upgradeself")),
|
|
2045
|
-
h("span", { className: "dshm-hint" }, lookup("settings.upgradehint"))
|
|
2046
|
-
) : null
|
|
2047
|
-
),
|
|
2048
|
-
Section(
|
|
2049
|
-
lookup("settings.about"),
|
|
2050
|
-
h("div", { className: "dshm-hint" }, lookup("settings.about.text"))
|
|
3050
|
+
{ className: "dshm-links" },
|
|
3051
|
+
h(ExtLink, { href: "https://github.com/iasiv5/dsh-m" }, "GitHub"),
|
|
3052
|
+
h("span", { className: "dshm-links-sep" }, "\xB7"),
|
|
3053
|
+
h(ExtLink, { href: "https://github.com/iasiv5/dsh-m/issues" }, lookup("settings.about.issues")),
|
|
3054
|
+
h("span", { className: "dshm-links-sep" }, "\xB7"),
|
|
3055
|
+
h(ExtLink, { href: "https://www.npmjs.com/package/dsh-m" }, lookup("settings.about.npm"))
|
|
3056
|
+
)
|
|
2051
3057
|
)
|
|
2052
3058
|
);
|
|
2053
|
-
async function upgradeSelf() {
|
|
2054
|
-
setUpgrading(true);
|
|
2055
|
-
try {
|
|
2056
|
-
const res = await api("self-upgrade");
|
|
2057
|
-
notify({ kind: "ok", text: lookup("self.upgraded", { v: res.version }), needsRestart: true });
|
|
2058
|
-
await self.reload();
|
|
2059
|
-
} catch (e) {
|
|
2060
|
-
if (e && e.guard) {
|
|
2061
|
-
notify({
|
|
2062
|
-
kind: "err",
|
|
2063
|
-
text: [
|
|
2064
|
-
lookup("guard.blocked"),
|
|
2065
|
-
`${lookup("guard.compstatus")}: ${e.guard.compensation?.status || "\u2014"}\uFF08${e.guard.compensation?.note || ""}\uFF09`,
|
|
2066
|
-
e.guard.repairBasis ? `${lookup("guard.repairbasis")}: ${e.guard.repairBasis}` : null,
|
|
2067
|
-
lookup("guard.noforce"),
|
|
2068
|
-
e.guard.restartSafe ? lookup("guard.restartsafenow") : lookup("guard.restartunsafe")
|
|
2069
|
-
].filter(Boolean).join(" | ")
|
|
2070
|
-
});
|
|
2071
|
-
} else {
|
|
2072
|
-
notify({ kind: "err", text: lookup("failed.selfupdate", { err: e && e.message || e }) });
|
|
2073
|
-
}
|
|
2074
|
-
} finally {
|
|
2075
|
-
setUpgrading(false);
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
3059
|
}
|
|
2079
3060
|
function Section(title, sub, ...children) {
|
|
2080
3061
|
if (sub != null && typeof sub !== "string") {
|
|
@@ -2104,7 +3085,7 @@ function DetailRows(rows) {
|
|
|
2104
3085
|
])
|
|
2105
3086
|
);
|
|
2106
3087
|
}
|
|
2107
|
-
function Card({ icon, name, badges, desc, sub, links, open, onToggle, detail, actions, topRight }) {
|
|
3088
|
+
function Card({ icon, name, badges, desc, sub, links, open, onToggle, detail, actions, topRight, byline, clampLines }) {
|
|
2108
3089
|
return h(
|
|
2109
3090
|
"div",
|
|
2110
3091
|
{
|
|
@@ -2121,7 +3102,8 @@ function Card({ icon, name, badges, desc, sub, links, open, onToggle, detail, ac
|
|
|
2121
3102
|
"div",
|
|
2122
3103
|
{ className: "dshm-meta" },
|
|
2123
3104
|
h("div", { className: "dshm-top" }, h("span", { className: "dshm-name" }, name), ...badges.filter(Boolean), topRight || null),
|
|
2124
|
-
h("div", { className: "
|
|
3105
|
+
byline && byline.length ? h("div", { className: "dsvm-byline" }, ...byline.map((b, i) => h("span", { key: i, title: b.title || void 0 }, b.text))) : null,
|
|
3106
|
+
h("div", { className: "dshm-desc", style: open ? { WebkitLineClamp: "unset" } : clampLines ? { WebkitLineClamp: String(clampLines) } : null }, desc),
|
|
2125
3107
|
sub ? h("div", { className: "dshm-sub" }, sub) : null,
|
|
2126
3108
|
links || null,
|
|
2127
3109
|
open ? h("div", { className: "dshm-detail" }, detail) : null,
|
|
@@ -2129,63 +3111,305 @@ function Card({ icon, name, badges, desc, sub, links, open, onToggle, detail, ac
|
|
|
2129
3111
|
)
|
|
2130
3112
|
);
|
|
2131
3113
|
}
|
|
3114
|
+
function snapshotOf(it) {
|
|
3115
|
+
const s = {
|
|
3116
|
+
id: it.id,
|
|
3117
|
+
name: it.name,
|
|
3118
|
+
description: it.description,
|
|
3119
|
+
category: it.category,
|
|
3120
|
+
categoryLabel: it.categoryLabel,
|
|
3121
|
+
source: it.source
|
|
3122
|
+
};
|
|
3123
|
+
for (const k of ["descriptionEn", "npm", "github", "homepage", "owner", "downloads", "stars", "added", "deprecated"]) {
|
|
3124
|
+
if (it[k] !== void 0 && it[k] !== null) s[k] = it[k];
|
|
3125
|
+
}
|
|
3126
|
+
return s;
|
|
3127
|
+
}
|
|
3128
|
+
function FavoriteZone({ favorites, onOpen }) {
|
|
3129
|
+
const list = favorites.list;
|
|
3130
|
+
const [check, setCheck] = useState(null);
|
|
3131
|
+
useEffect(() => {
|
|
3132
|
+
let live = true;
|
|
3133
|
+
setCheck(null);
|
|
3134
|
+
if (!list.length) return;
|
|
3135
|
+
const timer = setTimeout(() => {
|
|
3136
|
+
void (async () => {
|
|
3137
|
+
const lookupLive = async (fav) => {
|
|
3138
|
+
try {
|
|
3139
|
+
const res = await api("market", { query: fav.id, source: "all", limit: 8 });
|
|
3140
|
+
const items = res && Array.isArray(res.items) ? res.items : [];
|
|
3141
|
+
return items.some((x) => x && x.id === fav.id);
|
|
3142
|
+
} catch {
|
|
3143
|
+
return false;
|
|
3144
|
+
}
|
|
3145
|
+
};
|
|
3146
|
+
const { stale } = await partitionStale2(list, lookupLive);
|
|
3147
|
+
if (live) setCheck({ staleIds: stale.map((f) => f.id) });
|
|
3148
|
+
})();
|
|
3149
|
+
}, 800);
|
|
3150
|
+
return () => {
|
|
3151
|
+
live = false;
|
|
3152
|
+
clearTimeout(timer);
|
|
3153
|
+
};
|
|
3154
|
+
}, [list]);
|
|
3155
|
+
if (!list.length) {
|
|
3156
|
+
return h("div", { className: "dshm-empty" }, lookup("favorites.hint"));
|
|
3157
|
+
}
|
|
3158
|
+
const staleSet = new Set(check && check.staleIds || []);
|
|
3159
|
+
return h(
|
|
3160
|
+
React.Fragment,
|
|
3161
|
+
null,
|
|
3162
|
+
check && staleSet.size ? h(
|
|
3163
|
+
"div",
|
|
3164
|
+
{ className: "dshm-hint" },
|
|
3165
|
+
lookup("favorites.stale", { n: staleSet.size }),
|
|
3166
|
+
" ",
|
|
3167
|
+
h("button", { className: "dshm-btn sm", onClick: () => favorites.removeIds([...staleSet]) }, lookup("favorites.clean"))
|
|
3168
|
+
) : null,
|
|
3169
|
+
!check ? h("div", { className: "dshm-hint" }, lookup("favorites.checking")) : null,
|
|
3170
|
+
h(
|
|
3171
|
+
"div",
|
|
3172
|
+
{ className: "dshm-cards" },
|
|
3173
|
+
...list.map((fav) => {
|
|
3174
|
+
const s = fav.snapshot;
|
|
3175
|
+
const isStale = staleSet.has(fav.id);
|
|
3176
|
+
return Card({
|
|
3177
|
+
key: fav.id,
|
|
3178
|
+
icon: h(Icon, { entry: s }),
|
|
3179
|
+
name: s.name,
|
|
3180
|
+
badges: [
|
|
3181
|
+
s.deprecated === true ? h("span", { className: "dshm-badge warn", key: "dep" }, lookup("badge.deprecated")) : null,
|
|
3182
|
+
s.owner ? h("span", { className: "dshm-badge info", key: "c" }, lookup("badge.community")) : null,
|
|
3183
|
+
h("span", { className: "dshm-badge info", key: "s" }, s.source === "npm" ? "npm" : "github"),
|
|
3184
|
+
isStale ? h("span", { className: "dshm-badge warn", key: "st" }, lookup("favorites.stalebadge")) : null
|
|
3185
|
+
],
|
|
3186
|
+
byline: s.owner ? [
|
|
3187
|
+
s.owner ? { text: `by ${s.owner}` } : null,
|
|
3188
|
+
typeof s.downloads === "number" ? { text: `${compactCount(s.downloads)} \u2193`, title: String(s.downloads) } : null,
|
|
3189
|
+
typeof s.stars === "number" ? { text: `${compactCount(s.stars)} \u2605`, title: String(s.stars) } : null
|
|
3190
|
+
].filter(Boolean) : null,
|
|
3191
|
+
desc: s.description,
|
|
3192
|
+
clampLines: 5,
|
|
3193
|
+
links: h(LinksRow, { npm: s.npm, github: s.github, homepage: s.homepage }),
|
|
3194
|
+
// 0.7.2 修复:收藏卡点击开详情(此前无 onToggle,点击无响应);
|
|
3195
|
+
// 解析在 onOpen 内做(内存/API/快照三级),★ 移除按钮 stopPropagation 不受影响
|
|
3196
|
+
onToggle: () => {
|
|
3197
|
+
if (onOpen) onOpen(fav);
|
|
3198
|
+
},
|
|
3199
|
+
topRight: h("button", {
|
|
3200
|
+
className: "dsvm-favbtn on",
|
|
3201
|
+
title: lookup("fav.remove"),
|
|
3202
|
+
onClick: (e) => {
|
|
3203
|
+
e.stopPropagation();
|
|
3204
|
+
favorites.toggle(s);
|
|
3205
|
+
}
|
|
3206
|
+
}, "\u2605")
|
|
3207
|
+
});
|
|
3208
|
+
})
|
|
3209
|
+
)
|
|
3210
|
+
);
|
|
3211
|
+
}
|
|
3212
|
+
var opsStore = createOperationsStore2(typeof window !== "undefined" && window.localStorage ? window.localStorage : null);
|
|
3213
|
+
var opsPumpCtx = null;
|
|
3214
|
+
var opsPump = createOpsPump2(opsStore, () => opsPumpCtx);
|
|
3215
|
+
var OP_STATUS_CLS = {
|
|
3216
|
+
queued: "",
|
|
3217
|
+
running: "run",
|
|
3218
|
+
input: "warn",
|
|
3219
|
+
done: "ok",
|
|
3220
|
+
warned: "warn",
|
|
3221
|
+
failed: "err",
|
|
3222
|
+
superseded: "sup"
|
|
3223
|
+
};
|
|
3224
|
+
function OperationsPanel({ records, onClearFinished, onRemove }) {
|
|
3225
|
+
const active = records.filter((r) => r.status === "queued" || r.status === "running" || r.status === "input");
|
|
3226
|
+
const finished = records.filter((r) => active.indexOf(r) < 0).slice(-6);
|
|
3227
|
+
if (!active.length && !finished.length) return null;
|
|
3228
|
+
const row = (r) => h(
|
|
3229
|
+
"div",
|
|
3230
|
+
{ key: r.id, className: `dsvm-oprow ${OP_STATUS_CLS[r.status] || ""}` },
|
|
3231
|
+
h("span", { className: "dsvm-opstatus" }, lookup("op.status." + r.status)),
|
|
3232
|
+
h("span", { className: "dsvm-opkind" }, lookup("op.kind." + r.kind)),
|
|
3233
|
+
h("span", { className: "dsvm-optarget", title: r.error || r.warning || void 0 }, r.target),
|
|
3234
|
+
r.status === "running" ? Spin() : null,
|
|
3235
|
+
r.error ? h("span", { className: "dsvm-opnote" }, r.error) : null,
|
|
3236
|
+
r.warning ? h("span", { className: "dsvm-opnote" }, r.warning) : null,
|
|
3237
|
+
// 单条移除(终审·新伤2):input/failed/superseded 行给出口(对话区来源的 input 不再永挂)
|
|
3238
|
+
(r.status === "input" || r.status === "failed" || r.status === "superseded") && onRemove ? h("button", { className: "dshm-xbtn", title: lookup("op.remove"), onClick: () => onRemove(r.id) }, h(XIcon)) : null
|
|
3239
|
+
);
|
|
3240
|
+
return h(
|
|
3241
|
+
"div",
|
|
3242
|
+
{ className: "dsvm-ops" },
|
|
3243
|
+
active.length ? h("div", { className: "dsvm-opgroup" }, ...active.map(row)) : null,
|
|
3244
|
+
finished.length ? h(
|
|
3245
|
+
"div",
|
|
3246
|
+
{ className: "dsvm-opgroup done" },
|
|
3247
|
+
...finished.map(row),
|
|
3248
|
+
h("button", { className: "dshm-btn sm", onClick: onClearFinished }, lookup("op.clear"))
|
|
3249
|
+
) : null
|
|
3250
|
+
);
|
|
3251
|
+
}
|
|
2132
3252
|
var TABS = [
|
|
2133
3253
|
["market", "tab.market", "market"],
|
|
2134
3254
|
["installed", "tab.installed", "installed"],
|
|
2135
3255
|
["settings", "tab.settings", null]
|
|
2136
3256
|
];
|
|
2137
|
-
function
|
|
2138
|
-
const [copied, setCopied] = useState(false);
|
|
2139
|
-
const copy = async () => {
|
|
2140
|
-
try {
|
|
2141
|
-
await navigator.clipboard.writeText(version);
|
|
2142
|
-
setCopied(true);
|
|
2143
|
-
setTimeout(() => setCopied(false), 1600);
|
|
2144
|
-
} catch {
|
|
2145
|
-
}
|
|
2146
|
-
};
|
|
3257
|
+
function DshmVersionChip({ version }) {
|
|
2147
3258
|
return h(
|
|
2148
|
-
"
|
|
2149
|
-
{
|
|
2150
|
-
"
|
|
2151
|
-
h("span", { className: "dshm-dshchip-v" }, version)
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
3259
|
+
"span",
|
|
3260
|
+
{ className: "dshm-dshchip", title: `dsh-m v${version}` },
|
|
3261
|
+
"dsh-m ",
|
|
3262
|
+
h("span", { className: "dshm-dshchip-v" }, `v${version}`)
|
|
3263
|
+
);
|
|
3264
|
+
}
|
|
3265
|
+
function XIcon() {
|
|
3266
|
+
return h(
|
|
3267
|
+
"svg",
|
|
3268
|
+
{ viewBox: "0 0 24 24", width: "12", height: "12", fill: "none", "aria-hidden": "true" },
|
|
3269
|
+
h("path", { d: "M6 6l12 12M18 6L6 18", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round" })
|
|
3270
|
+
);
|
|
3271
|
+
}
|
|
3272
|
+
function FsIcon({ full }) {
|
|
3273
|
+
return full ? h(
|
|
3274
|
+
"svg",
|
|
3275
|
+
{ viewBox: "0 0 24 24", width: "12", height: "12", fill: "none", "aria-hidden": "true" },
|
|
3276
|
+
h("path", { d: "M16 8V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
3277
|
+
h("rect", { x: "8", y: "8", width: "12", height: "12", rx: "1", stroke: "currentColor", strokeWidth: "2" })
|
|
3278
|
+
) : h(
|
|
3279
|
+
"svg",
|
|
3280
|
+
{ viewBox: "0 0 24 24", width: "12", height: "12", fill: "none", "aria-hidden": "true" },
|
|
3281
|
+
h("path", { d: "M9 4H4v5M15 4h5v5M9 20H4v-5M15 20h5v-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
2158
3282
|
);
|
|
2159
3283
|
}
|
|
2160
3284
|
function MarketPanel({ onClose }) {
|
|
2161
3285
|
const [tab, setTab] = useState("market");
|
|
2162
|
-
const
|
|
3286
|
+
const FS_KEY = "dshm-panel-fullscreen";
|
|
3287
|
+
const [full, setFull] = useState(() => {
|
|
3288
|
+
try {
|
|
3289
|
+
return typeof window !== "undefined" && window.localStorage ? window.localStorage.getItem(FS_KEY) === "1" : false;
|
|
3290
|
+
} catch {
|
|
3291
|
+
return false;
|
|
3292
|
+
}
|
|
3293
|
+
});
|
|
3294
|
+
const toggleFull = useCallback(() => {
|
|
3295
|
+
setFull((v) => {
|
|
3296
|
+
const next = !v;
|
|
3297
|
+
try {
|
|
3298
|
+
if (typeof window !== "undefined" && window.localStorage) window.localStorage.setItem(FS_KEY, next ? "1" : "0");
|
|
3299
|
+
} catch {
|
|
3300
|
+
}
|
|
3301
|
+
return next;
|
|
3302
|
+
});
|
|
3303
|
+
}, []);
|
|
3304
|
+
const marketCommunity = useMarketData("community");
|
|
3305
|
+
const marketPrimary = useMarketData("primary");
|
|
3306
|
+
const markets = { community: marketCommunity, primary: marketPrimary };
|
|
2163
3307
|
const installed = useAsync(() => api("installed"), []);
|
|
2164
|
-
const [
|
|
3308
|
+
const [opRecords, setOpRecords] = useState(() => opsStore.list().map((r) => ({ ...r })));
|
|
3309
|
+
const syncOps = useCallback(() => setOpRecords(opsStore.list().map((r) => ({ ...r }))), []);
|
|
3310
|
+
const runOp = useCallback(
|
|
3311
|
+
(kind, target, exec, meta = {}, reuseId) => (
|
|
3312
|
+
// session:true = 活会话标记(终审·新伤1):重挂载的 restore 跳过(泵仍拥有);persist 时剥离。
|
|
3313
|
+
// 入队与 waiters/原始错误保真都在 createOpsPump(评审 P4:实现与测试同源)。
|
|
3314
|
+
opsPump.enqueue({ ...reuseId ? { id: reuseId } : {}, kind, target, status: "queued", meta: { ...meta, session: true } }, exec)
|
|
3315
|
+
),
|
|
3316
|
+
[]
|
|
3317
|
+
);
|
|
3318
|
+
const disposeOp = useCallback((id, note) => {
|
|
3319
|
+
opsStore.upsert({ id, status: "superseded", error: note });
|
|
3320
|
+
syncOps();
|
|
3321
|
+
}, [syncOps]);
|
|
3322
|
+
const ops = { records: opRecords, runOp, dispose: disposeOp };
|
|
3323
|
+
const favStoreRef = useRef(null);
|
|
3324
|
+
if (!favStoreRef.current) {
|
|
3325
|
+
favStoreRef.current = createFavoritesStore2(typeof window !== "undefined" && window.localStorage ? window.localStorage : null);
|
|
3326
|
+
}
|
|
3327
|
+
const favStore = favStoreRef.current;
|
|
3328
|
+
const [favList, setFavList] = useState(() => favStore.list());
|
|
3329
|
+
const favorites = {
|
|
3330
|
+
list: favList,
|
|
3331
|
+
toggle: useCallback((snapshot) => setFavList(favStore.toggle(snapshot)), [favStore]),
|
|
3332
|
+
removeIds: useCallback((ids) => setFavList(favStore.removeIds(ids)), [favStore])
|
|
3333
|
+
};
|
|
3334
|
+
const [pluginVersion, setPluginVersion] = useState(null);
|
|
2165
3335
|
useEffect(() => {
|
|
2166
3336
|
let live = true;
|
|
2167
3337
|
api("ping").then((r) => {
|
|
2168
|
-
if (live)
|
|
3338
|
+
if (live) setPluginVersion(typeof r?.version === "string" && r.version ? r.version : null);
|
|
2169
3339
|
}).catch(() => {
|
|
2170
3340
|
});
|
|
2171
3341
|
return () => {
|
|
2172
3342
|
live = false;
|
|
2173
3343
|
};
|
|
2174
3344
|
}, []);
|
|
3345
|
+
const marketReloadAll = useCallback(
|
|
3346
|
+
(force) => {
|
|
3347
|
+
marketCommunity.reload(force);
|
|
3348
|
+
marketPrimary.reload(force);
|
|
3349
|
+
},
|
|
3350
|
+
[marketCommunity.reload, marketPrimary.reload]
|
|
3351
|
+
);
|
|
2175
3352
|
const refreshViews = useCallback(
|
|
2176
|
-
() => refreshAfterMutation2({ marketReload:
|
|
2177
|
-
[
|
|
3353
|
+
() => refreshAfterMutation2({ marketReload: marketReloadAll, installedReload: installed.reload }),
|
|
3354
|
+
[marketReloadAll, installed.reload]
|
|
2178
3355
|
);
|
|
2179
3356
|
const onRegistryChanged = refreshViews;
|
|
3357
|
+
const stillApplies = useCallback(async (r) => {
|
|
3358
|
+
let fresh = null;
|
|
3359
|
+
try {
|
|
3360
|
+
fresh = await api("installed");
|
|
3361
|
+
} catch {
|
|
3362
|
+
fresh = null;
|
|
3363
|
+
}
|
|
3364
|
+
const items = fresh && Array.isArray(fresh.items) ? fresh.items : null;
|
|
3365
|
+
if (!items) return true;
|
|
3366
|
+
return opAppliesTo2(r, items);
|
|
3367
|
+
}, []);
|
|
3368
|
+
const dispatchRestored = useCallback(async (r) => {
|
|
3369
|
+
try {
|
|
3370
|
+
if (r.kind === "install") {
|
|
3371
|
+
await api("install", { id: r.target, ...r.meta && r.meta.version ? { version: r.meta.version } : {} });
|
|
3372
|
+
} else if (r.kind === "upgrade") {
|
|
3373
|
+
await api("upgrade", { pkg: r.target, ...r.meta && r.meta.force ? { force: true } : {} });
|
|
3374
|
+
} else if (r.kind === "uninstall") {
|
|
3375
|
+
await api("uninstall", { pkg: r.target });
|
|
3376
|
+
} else {
|
|
3377
|
+
await api("set-enabled", { pkg: r.target, enabled: r.meta && r.meta.on === true });
|
|
3378
|
+
}
|
|
3379
|
+
return { ok: true };
|
|
3380
|
+
} catch (e) {
|
|
3381
|
+
return { ok: false, error: String(e && e.message || e), ...e && e.issue ? { issue: e.issue } : {} };
|
|
3382
|
+
}
|
|
3383
|
+
}, []);
|
|
3384
|
+
useEffect(() => {
|
|
3385
|
+
opsPumpCtx = { stillApplies, dispatchRestored, syncOps, refreshViews };
|
|
3386
|
+
return () => {
|
|
3387
|
+
if (opsPumpCtx && opsPumpCtx.syncOps === syncOps) opsPumpCtx = null;
|
|
3388
|
+
};
|
|
3389
|
+
}, [stillApplies, dispatchRestored, syncOps, refreshViews]);
|
|
3390
|
+
const restoredRef = useRef(false);
|
|
3391
|
+
useEffect(() => {
|
|
3392
|
+
if (restoredRef.current || !installed.data) return;
|
|
3393
|
+
restoredRef.current = true;
|
|
3394
|
+
void (async () => {
|
|
3395
|
+
const restored = await restoreRecords2(opsStore.list(), stillApplies);
|
|
3396
|
+
opsStore.replaceAll(restored);
|
|
3397
|
+
syncOps();
|
|
3398
|
+
if (restored.some((r) => r.status === "queued")) {
|
|
3399
|
+
void opsPump.kick();
|
|
3400
|
+
}
|
|
3401
|
+
})();
|
|
3402
|
+
}, [installed.data, stillApplies, syncOps]);
|
|
2180
3403
|
const counts = {
|
|
2181
|
-
market:
|
|
3404
|
+
market: marketCommunity.data ? marketCommunity.data.total : null,
|
|
2182
3405
|
installed: installed.data ? installed.data.items.length : null
|
|
2183
3406
|
};
|
|
3407
|
+
const outdatedCount = installed.data && Array.isArray(installed.data.items) ? installed.data.items.filter((x) => x && x.outdated).length : 0;
|
|
2184
3408
|
const [banner, setBanner] = useState(null);
|
|
2185
3409
|
const [toast, setToast] = useState(null);
|
|
2186
3410
|
useEffect(() => {
|
|
2187
3411
|
const onKey = (e) => {
|
|
2188
|
-
if (e.key === "Escape" && onClose) onClose();
|
|
3412
|
+
if (e.key === "Escape" && dsvmModalDepth === 0 && onClose) onClose();
|
|
2189
3413
|
};
|
|
2190
3414
|
document.addEventListener("keydown", onKey);
|
|
2191
3415
|
return () => document.removeEventListener("keydown", onKey);
|
|
@@ -2201,14 +3425,15 @@ function MarketPanel({ onClose }) {
|
|
|
2201
3425
|
}, []);
|
|
2202
3426
|
return h(
|
|
2203
3427
|
"div",
|
|
2204
|
-
{ className: "dshm-overlay", onClick: onClose },
|
|
3428
|
+
{ className: full ? "dshm-overlay full" : "dshm-overlay", onClick: onClose },
|
|
2205
3429
|
h(
|
|
2206
3430
|
"div",
|
|
2207
|
-
{ className: "dshm-panel", onClick: (e) => e.stopPropagation() },
|
|
3431
|
+
{ className: full ? "dshm-panel full" : "dshm-panel", onClick: (e) => e.stopPropagation() },
|
|
2208
3432
|
h(
|
|
2209
3433
|
"div",
|
|
2210
3434
|
{ className: "dshm-head" },
|
|
2211
3435
|
h("span", { className: "dshm-title" }, lookup("title.full")),
|
|
3436
|
+
h("span", { className: "dshm-head-divider", "aria-hidden": "true" }),
|
|
2212
3437
|
h(
|
|
2213
3438
|
"div",
|
|
2214
3439
|
{ className: "dshm-seg", role: "tablist" },
|
|
@@ -2224,20 +3449,49 @@ function MarketPanel({ onClose }) {
|
|
|
2224
3449
|
onClick: () => setTab(key)
|
|
2225
3450
|
},
|
|
2226
3451
|
lookup(labelKey),
|
|
2227
|
-
countKey && counts[countKey] != null ? h("span", { className: "dshm-count" }, String(counts[countKey])) : null
|
|
3452
|
+
countKey && counts[countKey] != null ? h("span", { className: "dshm-count" }, String(counts[countKey])) : null,
|
|
3453
|
+
key === "installed" && outdatedCount > 0 ? h("span", { className: "dsvm-reddot", title: lookup("installed.upgradeAll", { n: outdatedCount }) }) : null
|
|
2228
3454
|
)
|
|
2229
3455
|
)
|
|
2230
3456
|
),
|
|
2231
3457
|
h("span", { className: "dshm-spacer" }),
|
|
2232
|
-
|
|
2233
|
-
|
|
3458
|
+
pluginVersion ? h(DshmVersionChip, { version: pluginVersion }) : null,
|
|
3459
|
+
// 窗口控制组(0.7.7):最大化/还原 + 关闭,连体设计(系统化窗口按钮)
|
|
3460
|
+
h(
|
|
3461
|
+
"div",
|
|
3462
|
+
{ className: "dshm-winctl", role: "group", "aria-label": lookup("panel.fullscreen") },
|
|
3463
|
+
h("button", {
|
|
3464
|
+
type: "button",
|
|
3465
|
+
title: lookup(full ? "panel.restore" : "panel.fullscreen"),
|
|
3466
|
+
"aria-label": lookup(full ? "panel.restore" : "panel.fullscreen"),
|
|
3467
|
+
onClick: toggleFull
|
|
3468
|
+
}, h(FsIcon, { full })),
|
|
3469
|
+
h("button", {
|
|
3470
|
+
type: "button",
|
|
3471
|
+
className: "danger",
|
|
3472
|
+
title: lookup("common.close"),
|
|
3473
|
+
"aria-label": lookup("common.close"),
|
|
3474
|
+
onClick: onClose
|
|
3475
|
+
}, h(XIcon))
|
|
3476
|
+
)
|
|
2234
3477
|
),
|
|
2235
3478
|
h(
|
|
2236
3479
|
"div",
|
|
2237
3480
|
{ className: "dshm-body" },
|
|
2238
|
-
tab === "market" ? h(MarketTab, { notify,
|
|
2239
|
-
tab === "installed" ? h(InstalledTab, { notify, installed, onMutation: refreshViews }) : null,
|
|
2240
|
-
tab === "settings" ? h(SettingsTab, { notify, onRegistryChanged }) : null
|
|
3481
|
+
tab === "market" ? h(MarketTab, { notify, markets, onMutation: refreshViews, ops, favorites }) : null,
|
|
3482
|
+
tab === "installed" ? h(InstalledTab, { notify, installed, onMutation: refreshViews, ops }) : null,
|
|
3483
|
+
tab === "settings" ? h(SettingsTab, { notify, onRegistryChanged }) : null,
|
|
3484
|
+
h(OperationsPanel, {
|
|
3485
|
+
records: opRecords,
|
|
3486
|
+
onClearFinished: () => {
|
|
3487
|
+
opsStore.clearFinished();
|
|
3488
|
+
syncOps();
|
|
3489
|
+
},
|
|
3490
|
+
onRemove: (id) => {
|
|
3491
|
+
opsStore.remove(id);
|
|
3492
|
+
syncOps();
|
|
3493
|
+
}
|
|
3494
|
+
})
|
|
2241
3495
|
),
|
|
2242
3496
|
toast ? h(
|
|
2243
3497
|
"div",
|
|
@@ -2316,12 +3570,15 @@ function ToolCardRow({ it, onInstalled }) {
|
|
|
2316
3570
|
const install = async (e) => {
|
|
2317
3571
|
e.stopPropagation();
|
|
2318
3572
|
setBusy(true);
|
|
3573
|
+
const rec = opsStore.upsert({ kind: "install", target: it.id, status: "running", meta: { npm: it.npm, github: it.github, session: true } });
|
|
2319
3574
|
try {
|
|
2320
3575
|
const res = await api("install", { id: it.id });
|
|
3576
|
+
opsStore.upsert({ id: rec.id, status: "done" });
|
|
2321
3577
|
onInstalled({ ...it, installed: true, installedPkg: res.pkg, installedVersion: res.version });
|
|
2322
|
-
} catch (
|
|
2323
|
-
|
|
2324
|
-
|
|
3578
|
+
} catch (err) {
|
|
3579
|
+
opsStore.upsert({ id: rec.id, status: err && err.issue ? "input" : "failed", error: String(err && err.message || err) });
|
|
3580
|
+
if (err && err.guard) {
|
|
3581
|
+
setGuardNote(`${lookup("guard.blocked")} \xB7 ${err.guard.compensation?.status || "\u2014"} \xB7 ${lookup("guard.noforce")}`);
|
|
2325
3582
|
}
|
|
2326
3583
|
} finally {
|
|
2327
3584
|
setBusy(false);
|