dsh-m 0.1.0 → 0.1.1

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/lib/client.js CHANGED
@@ -10,38 +10,283 @@ var h = React.createElement;
10
10
  var { useState, useEffect, useCallback, useMemo } = React;
11
11
  var PLUGIN_ID = "dsh-m";
12
12
  var API = "/dshm";
13
- var CATEGORIES = [
14
- ["market", "\u5E02\u573A"],
15
- ["tools", "\u5DE5\u5177"],
16
- ["ui", "\u754C\u9762"],
17
- ["search", "\u641C\u7D22"],
18
- ["media", "\u591A\u5A92\u4F53"],
19
- ["other", "\u5176\u4ED6"]
20
- ];
13
+ var ZH = {
14
+ "market.title": "\u63D2\u4EF6\u5E02\u573A",
15
+ "tab.market": "\u5E02\u573A",
16
+ "tab.installed": "\u5DF2\u88C5",
17
+ "tab.settings": "\u8BBE\u7F6E",
18
+ "cat.all": "\u5168\u90E8",
19
+ "cat.market": "\u5E02\u573A",
20
+ "cat.tools": "\u5DE5\u5177",
21
+ "cat.ui": "\u754C\u9762",
22
+ "cat.search": "\u641C\u7D22",
23
+ "cat.media": "\u591A\u5A92\u4F53",
24
+ "cat.other": "\u5176\u4ED6",
25
+ "search.ph": "\u641C\u7D22\u540D\u79F0 / \u63CF\u8FF0 / \u6807\u7B7E\u2026",
26
+ "common.refresh": "\u5237\u65B0",
27
+ "common.close": "\u5173\u95ED",
28
+ "common.later": "\u7A0D\u540E",
29
+ "common.ok": "\u77E5\u9053\u4E86",
30
+ "common.none": "\u2014",
31
+ "market.loading": "\u52A0\u8F7D\u6536\u5F55\u6E05\u5355\u4E2D\u2026 ",
32
+ "market.empty": "\u6CA1\u6709\u5339\u914D\u7684\u6536\u5F55\u6761\u76EE",
33
+ "installed.loading": "\u8BFB\u53D6 web profile \u4E2D\u2026 ",
34
+ "installed.empty": "web profile \u5C1A\u672A\u5B89\u88C5\u4EFB\u4F55 dsh \u63D2\u4EF6",
35
+ "installed.none": "\u672A\u5B89\u88C5",
36
+ "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",
37
+ "badge.installed": "\u5DF2\u5B89\u88C5",
38
+ "badge.update": "\u53EF\u5347\u7EA7",
39
+ "badge.market": "\u5E02\u573A\u5B89\u88C5",
40
+ "badge.nonmarket": "\u975E\u5E02\u573A\u5B89\u88C5",
41
+ "action.install": "\u5B89\u88C5",
42
+ "action.upgrade": "\u5347\u7EA7",
43
+ "action.uninstall": "\u5378\u8F7D",
44
+ "confirm.uninstall": "\u786E\u8BA4\u5378\u8F7D\uFF1F",
45
+ "confirm.unlink": "\u786E\u8BA4\u79FB\u9664\u672C\u5730\u5F15\u7528\uFF1F",
46
+ "confirm.core": "\u26A0\uFE0F \u786E\u8BA4\u5378\u8F7D\u6838\u5FC3\u5305\uFF1F",
47
+ "detail.id": "\u6536\u5F55 id",
48
+ "detail.source": "\u6765\u6E90",
49
+ "detail.latest": "\u6700\u65B0",
50
+ "detail.installed": "\u5DF2\u88C5",
51
+ "detail.tags": "\u6807\u7B7E",
52
+ "detail.pkg": "\u5305\u540D",
53
+ "detail.spec": "\u5B89\u88C5 spec",
54
+ "detail.listed": "\u6536\u5F55",
55
+ "detail.listed.no": "\u4E0D\u5728\u6536\u5F55\u6E05\u5355\u4E2D",
56
+ "detail.path": "\u8DEF\u5F84",
57
+ "detail.note": "\u6CE8\u610F",
58
+ "manage.hint": "\u5DF2\u5B89\u88C5\uFF0C\u53EF\u5728\u300C\u5DF2\u88C5\u300D\u9875\u7BA1\u7406",
59
+ "version.failed": "\u7248\u672C\u67E5\u8BE2\u5931\u8D25",
60
+ "src.npm": "npm",
61
+ "src.github": "github",
62
+ "src.link": "\u672C\u5730 link",
63
+ "src.file": "\u672C\u5730 file",
64
+ "src.unknown": "\u672A\u77E5",
65
+ "sub.latest": "\u6700\u65B0 v{v}",
66
+ "sub.head": "HEAD {sha}",
67
+ "sub.installed": "\u5DF2\u88C5 v{v}",
68
+ "settings.registry": "\u6536\u5F55\u6E05\u5355\uFF08registry\uFF09",
69
+ "settings.source": "\u5F53\u524D\u6765\u6E90",
70
+ "settings.updated": "\u66F4\u65B0\u65F6\u95F4",
71
+ "settings.count": "\u6761\u76EE\u6570",
72
+ "settings.count.v": "{n} \u6761",
73
+ "settings.policy": "\u7F13\u5B58\u7B56\u7565",
74
+ "settings.policy.v": "TTL 60 \u5206\u949F\uFF1B\u8BBE\u7F6E registryUrl \u53EF\u8986\u76D6\u6E90",
75
+ "settings.remotehint": "\u8FDC\u7AEF\u63D0\u793A",
76
+ "settings.force": "\u5F3A\u5236\u5237\u65B0",
77
+ "settings.self": "dsh-m \u81EA\u8EAB",
78
+ "settings.current": "\u5F53\u524D\u7248\u672C",
79
+ "settings.npmlatest": "npm \u6700\u65B0",
80
+ "settings.lookupfailed": "\u67E5\u8BE2\u5931\u8D25\uFF1A{err}",
81
+ "settings.upgradeself": "\u5347\u7EA7 dsh-m",
82
+ "settings.upgradehint": "\u5347\u7EA7\u540E\u540C\u6837\u9700\u8981\u91CD\u542F\u751F\u6548",
83
+ "settings.about": "\u5173\u4E8E",
84
+ "settings.about.text": "DSH Marketplace\uFF08dsh-m\uFF09\u2014 \u4E2A\u4EBA\u81EA\u7528\u7684 DeepSeek Harness \u63D2\u4EF6\u5E02\u573A\u3002\u6536\u5F55\u3001\u5B89\u88C5\u3001\u5378\u8F7D\u3001\u5347\u7EA7\uFF0C\u5168\u90E8\u672C\u673A\u5B8C\u6210\u3002",
85
+ "src.override": "\u81EA\u5B9A\u4E49\u6E90",
86
+ "src.jsdelivr": "jsDelivr\uFF08@main\uFF09",
87
+ "src.raw": "raw.githubusercontent\uFF08@main\uFF09",
88
+ "src.cache": "\u672C\u5730\u7F13\u5B58",
89
+ "src.bundled": "\u5305\u5185\u5FEB\u7167\uFF08\u515C\u5E95\uFF09",
90
+ "self.upgraded": "dsh-m \u5DF2\u66F4\u65B0\u5230 v{v}\uFF0C\u91CD\u542F\u540E\u751F\u6548",
91
+ "self.failed": "\u81EA\u66F4\u65B0\u5931\u8D25\uFF1A{err}",
92
+ "registry.refreshed": "\u6536\u5F55\u6E05\u5355\u5DF2\u5F3A\u5236\u5237\u65B0",
93
+ "notify.installed": "\u5DF2\u5B89\u88C5 {pkg}{version}",
94
+ "notify.allowbuilds": "\uFF08\u6CE8\u610F\uFF1A\u8BE5\u63D2\u4EF6\u6267\u884C\u4E86\u6784\u5EFA\u811A\u672C\uFF0C\u5DF2\u6309\u7B56\u7565\u653E\u884C\uFF09",
95
+ "notify.uninstalled": "\u5DF2\u5378\u8F7D {pkg}",
96
+ "notify.livedisabled": "\uFF08\u5DF2\u5148\u4E0B\u7EBF\u8FD0\u884C\u4E2D\u7684\u754C\u9762\uFF09",
97
+ "notify.leftovers": "\uFF1B\u68C0\u6D4B\u5230\u7591\u4F3C\u6B8B\u7559\u6570\u636E\uFF1A{paths}",
98
+ "notify.upgraded": "\u5DF2\u5347\u7EA7 {pkg}\uFF08{from} \u2192 {to}\uFF09",
99
+ "notify.upgradehint": "\uFF08\u6CE8\u610F\uFF1A\u8BE5\u63D2\u4EF6\u6267\u884C\u4E86\u6784\u5EFA\u811A\u672C\uFF09",
100
+ "failed.install": "\u5B89\u88C5\u5931\u8D25\uFF1A{err}",
101
+ "failed.uninstall": "\u5378\u8F7D\u5931\u8D25\uFF1A{err}",
102
+ "failed.upgrade": "\u5347\u7EA7\u5931\u8D25\uFF1A{err}",
103
+ "failed.selfupdate": "\u81EA\u66F4\u65B0\u5931\u8D25\uFF1A{err}",
104
+ "failed.load": "\u52A0\u8F7D\u5931\u8D25\uFF1A{err}",
105
+ "failed.read": "\u8BFB\u53D6\u5931\u8D25\uFF1A{err}",
106
+ "failed.open": "\u6253\u5F00\u5E02\u573A\u9762\u677F\u5931\u8D25:",
107
+ "banner.done": "\u53D8\u66F4\u5B8C\u6210\uFF0C\u9700\u8981\u91CD\u542F DSH Web \u540E\u751F\u6548\u3002",
108
+ "restart.doing": "\u6B63\u5728\u8BF7\u6C42\u91CD\u542F\u2026",
109
+ "restart.waiting": "\u5DF2\u8BF7\u6C42\u91CD\u542F\uFF0C\u7B49\u5F85 DSH Web \u6062\u590D\u2026",
110
+ "restart.now": "\u26A1 \u4E00\u952E\u91CD\u542F",
111
+ "restart.failed": "\u91CD\u542F\u5931\u8D25\uFF1A{err}",
112
+ "restart.timeout": "\u91CD\u542F\u8D85\u65F6\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5 dsh web \u670D\u52A1\u72B6\u6001",
113
+ "restart.hint.done": "\u5DF2\u8BF7\u6C42\u91CD\u542F DSH web\uFF08via {via}\uFF09\u3002\u670D\u52A1\u51E0\u79D2\u5185\u6062\u590D\uFF0C\u4E4B\u540E\u8BA9\u7528\u6237\u5237\u65B0\u9875\u9762\u5373\u53EF\u3002",
114
+ "phase.resolving": "\u89E3\u6790\u4F9D\u8D56",
115
+ "phase.downloading": "\u4E0B\u8F7D",
116
+ "phase.linking": "\u94FE\u63A5\u5B89\u88C5",
117
+ "phase.building": "\u6784\u5EFA\u811A\u672C",
118
+ "phase.ready": "\u51C6\u5907\u4E2D",
119
+ "readme.show": "\u{1F4D6} README",
120
+ "readme.hide": "\u6536\u8D77 README",
121
+ "readme.loading": "\u52A0\u8F7D README\u2026 ",
122
+ "readme.none": "\uFF08\u8BE5\u63D2\u4EF6\u6CA1\u6709 README\uFF09",
123
+ "readme.truncated": "\n\n\u2026\uFF08\u8D85\u8FC7 64KB \u5DF2\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9\u89C1\u63D2\u4EF6\u76EE\u5F55\uFF09",
124
+ "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",
125
+ "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",
126
+ "profile.hint": "web profile\uFF1A{path}",
127
+ "title.panel": "\u63D2\u4EF6\u5E02\u573A",
128
+ "title.full": "DeepSeek Harness \u63D2\u4EF6\u5E02\u573A"
129
+ };
130
+ var EN = {
131
+ "market.title": "Plugin Marketplace",
132
+ "title.panel": "Plugin Marketplace",
133
+ "title.full": "DeepSeek Harness Plugin Marketplace",
134
+ "tab.market": "Market",
135
+ "tab.installed": "Installed",
136
+ "tab.settings": "Settings",
137
+ "cat.all": "All",
138
+ "cat.market": "Market",
139
+ "cat.tools": "Tools",
140
+ "cat.ui": "UI",
141
+ "cat.search": "Search",
142
+ "cat.media": "Media",
143
+ "cat.other": "Other",
144
+ "search.ph": "Search name, description, tags\u2026",
145
+ "common.refresh": "Refresh",
146
+ "common.close": "Close",
147
+ "common.later": "Later",
148
+ "common.ok": "OK",
149
+ "common.none": "\u2014",
150
+ "market.loading": "Loading listings\u2026 ",
151
+ "market.empty": "No matching listings",
152
+ "installed.loading": "Reading web profile\u2026 ",
153
+ "installed.empty": "No DSH plugins installed in this web profile",
154
+ "installed.none": "Not installed",
155
+ "installed.others": "{n} non-DSH dependencies (not recognized as plugins) are collapsed.",
156
+ "badge.installed": "Installed",
157
+ "badge.update": "Update",
158
+ "badge.market": "Via market",
159
+ "badge.nonmarket": "Non-market",
160
+ "action.install": "Install",
161
+ "action.upgrade": "Upgrade",
162
+ "action.uninstall": "Uninstall",
163
+ "confirm.uninstall": "Confirm uninstall?",
164
+ "confirm.unlink": "Confirm remove link?",
165
+ "confirm.core": "\u26A0\uFE0F Remove core package?",
166
+ "detail.id": "Listing id",
167
+ "detail.source": "Source",
168
+ "detail.latest": "Latest",
169
+ "detail.installed": "Installed",
170
+ "detail.tags": "Tags",
171
+ "detail.pkg": "Package",
172
+ "detail.spec": "Spec",
173
+ "detail.listed": "Listed",
174
+ "detail.listed.no": "Not in the registry",
175
+ "detail.path": "Path",
176
+ "detail.note": "Note",
177
+ "manage.hint": "Installed \u2014 manage it on the Installed tab",
178
+ "version.failed": "version lookup failed",
179
+ "src.npm": "npm",
180
+ "src.github": "github",
181
+ "src.link": "local link",
182
+ "src.file": "local file",
183
+ "src.unknown": "unknown",
184
+ "sub.latest": "Latest v{v}",
185
+ "sub.head": "HEAD {sha}",
186
+ "sub.installed": "Installed v{v}",
187
+ "settings.registry": "Registry",
188
+ "settings.source": "Source",
189
+ "settings.updated": "Updated",
190
+ "settings.count": "Listings",
191
+ "settings.count.v": "{n} listings",
192
+ "settings.policy": "Caching",
193
+ "settings.policy.v": "60 min TTL; override via registryUrl",
194
+ "settings.remotehint": "Remote notice",
195
+ "settings.force": "Force refresh",
196
+ "settings.self": "dsh-m itself",
197
+ "settings.current": "Current version",
198
+ "settings.npmlatest": "npm latest",
199
+ "settings.lookupfailed": "lookup failed: {err}",
200
+ "settings.upgradeself": "Upgrade dsh-m",
201
+ "settings.upgradehint": "A restart is required after upgrading",
202
+ "settings.about": "About",
203
+ "settings.about.text": "DSH Marketplace (dsh-m) \u2014 your personal DeepSeek Harness plugin marketplace. Browse, install, uninstall and upgrade, all local.",
204
+ "src.override": "Custom source",
205
+ "src.jsdelivr": "jsDelivr (@main)",
206
+ "src.raw": "raw.githubusercontent (@main)",
207
+ "src.cache": "Local cache",
208
+ "src.bundled": "Bundled snapshot (fallback)",
209
+ "self.upgraded": "dsh-m updated to v{v} \u2014 restart to take effect",
210
+ "self.failed": "Self-update failed: {err}",
211
+ "registry.refreshed": "Registry force-refreshed",
212
+ "notify.installed": "Installed {pkg}{version}",
213
+ "notify.allowbuilds": " (note: this plugin ran build scripts, allowed by policy)",
214
+ "notify.uninstalled": "Uninstalled {pkg}",
215
+ "notify.livedisabled": " (live UI disabled first)",
216
+ "notify.leftovers": "; possible leftover data: {paths}",
217
+ "notify.upgraded": "Upgraded {pkg} ({from} \u2192 {to})",
218
+ "notify.upgradehint": " (note: this plugin ran build scripts)",
219
+ "failed.install": "Install failed: {err}",
220
+ "failed.uninstall": "Uninstall failed: {err}",
221
+ "failed.upgrade": "Upgrade failed: {err}",
222
+ "failed.selfupdate": "Self-update failed: {err}",
223
+ "failed.load": "Load failed: {err}",
224
+ "failed.read": "Read failed: {err}",
225
+ "failed.open": "Failed to open the marketplace panel:",
226
+ "banner.done": "Changes applied. Restart DSH Web to take effect.",
227
+ "restart.doing": "Requesting restart\u2026",
228
+ "restart.waiting": "Restart requested, waiting for DSH Web\u2026",
229
+ "restart.now": "\u26A1 Restart",
230
+ "restart.failed": "Restart failed: {err}",
231
+ "restart.timeout": "Restart timed out \u2014 check the dsh web service manually",
232
+ "restart.hint.done": "Restart requested (via {via}). The service will be back in seconds; ask the user to refresh afterwards.",
233
+ "phase.resolving": "Resolving",
234
+ "phase.downloading": "Downloading",
235
+ "phase.linking": "Linking",
236
+ "phase.building": "Building",
237
+ "phase.ready": "Preparing",
238
+ "readme.show": "\u{1F4D6} README",
239
+ "readme.hide": "Hide README",
240
+ "readme.loading": "Loading README\u2026 ",
241
+ "readme.none": "(No README)",
242
+ "readme.truncated": "\n\n\u2026(truncated at 64KB \u2014 see the plugin directory for full content)",
243
+ "warn.unlink": "Uninstalling only removes the profile's reference to the local directory ({path}); the directory itself is kept.",
244
+ "warn.core": "This is a core/archive package installed via file:. Uninstalling may affect DSH features and requires manual restore.",
245
+ "profile.hint": "web profile: {path}",
246
+ "title.panel": "Plugin Marketplace",
247
+ "title.full": "DeepSeek Harness Plugin Marketplace"
248
+ };
249
+ function browserLang() {
250
+ const lang = typeof document !== "undefined" && document.documentElement.lang || typeof navigator !== "undefined" && navigator.language || "zh";
251
+ return /^en\b/i.test(String(lang)) ? "en" : "zh";
252
+ }
253
+ function interpolate(tpl, params) {
254
+ if (!params) return String(tpl);
255
+ return String(tpl).replace(/\{(\w+)\}/g, (_, k) => params[k] != null ? String(params[k]) : `{${k}}`);
256
+ }
257
+ function lookup(key, params) {
258
+ const dict = browserLang() === "en" ? EN : ZH;
259
+ return interpolate(dict[key] ?? ZH[key] ?? key, params);
260
+ }
261
+ var CATEGORIES = ["market", "tools", "ui", "search", "media", "other"];
21
262
  var CSS = `
22
263
  .dshm-overlay{position:fixed;inset:0;z-index:2147483000;background:var(--dsw-alias-bg-mask-3,rgba(15,23,42,.48));display:flex;align-items:center;justify-content:center;padding:24px 16px;box-sizing:border-box}
23
- .dshm-panel{width:min(920px,100%);height:min(680px,86vh);display:flex;flex-direction:column;background:var(--dsw-alias-bg-layer-3,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(2,6,23,.25);overflow:hidden;font-family:inherit;color:var(--dsw-alias-label-primary,inherit)}
264
+ .dshm-panel{width:min(920px,100%);height:min(680px,86vh);display:flex;flex-direction:column;background:var(--dsw-alias-bg-overlay,var(--dsw-alias-bg-layer-3,#fff));border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(2,6,23,.25);overflow:hidden;font-family:inherit;color:var(--dsw-alias-label-primary,inherit)}
24
265
  .dshm-head{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
25
266
  .dshm-title{font-weight:700;font-size:15px;margin-right:6px}
26
- .dshm-tab{border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);border-radius:8px;padding:5px 12px;font:inherit;font-size:13px;cursor:pointer}
27
- .dshm-tab:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
28
- .dshm-tab.on{background:var(--dsw-alias-bg-layer-4,#eef2ff);color:var(--dsw-alias-label-primary,inherit);border-color:var(--dsw-alias-border-l2,#e5e7eb);font-weight:600}
267
+ .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)}
268
+ .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}
269
+ .dshm-seg button:hover{color:var(--dsw-alias-label-secondary,#4b5058)}
270
+ .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%))}
271
+ .dshm-seg .dshm-count{font-size:11px;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-caption,#9ca3af);margin:0}
272
+ .dshm-seg button.on .dshm-count{color:var(--dsw-alias-state-business-primary,#4d6bfe)}
29
273
  .dshm-spacer{flex:1}
30
274
  .dshm-body{flex:1;overflow:auto;padding:14px;display:flex;flex-direction:column;gap:12px}
31
275
  .dshm-hint{color:var(--dsw-alias-label-caption,#6b7280);font-size:12px;line-height:18px;margin:0}
32
- .dshm-err{color:var(--dsw-alias-state-danger-primary,#dc2626);font-size:12px;line-height:18px}
276
+ .dshm-err{color:var(--dsw-alias-state-error-primary,#b91c1c);font-size:12px;line-height:18px}
33
277
  .dshm-btn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-primary,inherit);border-radius:8px;padding:5px 12px;font:inherit;font-size:12px;cursor:pointer;white-space:nowrap}
34
278
  .dshm-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
35
279
  .dshm-btn:disabled{opacity:.5;cursor:default}
36
280
  .dshm-btn.primary{background:var(--dsw-alias-interactive-bg-selected,#4f46e5);border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5);color:#fff}
37
281
  .dshm-btn.primary:hover{filter:brightness(1.08)}
38
- .dshm-btn.danger{color:var(--dsw-alias-state-danger-primary,#dc2626);border-color:var(--dsw-alias-state-danger-primary,#dc2626)}
282
+ .dshm-btn.danger{color:var(--dsw-alias-state-error-primary,#b91c1c);border-color:var(--dsw-alias-state-error-primary,#b91c1c)}
39
283
  .dshm-btn.sm{padding:3px 9px;font-size:11px}
40
284
  .dshm-input{flex:1;min-width:120px;border:1px solid var(--dsw-alias-border-l2,#c7d2fe);background:var(--dsw-alias-bg-layer-2,transparent);color:var(--dsw-alias-label-primary,inherit);border-radius:8px;padding:6px 10px;font:inherit;font-size:13px;outline:none}
41
285
  .dshm-input:focus{border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5)}
42
286
  .dshm-chips{display:flex;flex-wrap:wrap;gap:6px}
43
287
  .dshm-chip{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);border-radius:999px;padding:2px 10px;font:inherit;font-size:11px;cursor:pointer}
44
- .dshm-chip.on{background:var(--dsw-alias-bg-layer-4,#eef2ff);color:var(--dsw-alias-label-primary,inherit);border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5)}
288
+ .dshm-chip:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
289
+ .dshm-chip.on{background:var(--dsw-specific-sidebar-nav-item-active,rgba(38,49,72,.08));border-color:transparent;color:var(--dsw-alias-label-primary,inherit);font-weight:500}
45
290
  .dshm-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
46
291
  @media (max-width:680px){.dshm-cards{grid-template-columns:1fr}}
47
292
  .dshm-card{display:flex;gap:12px;align-items:flex-start;background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.04));border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;padding:12px;cursor:pointer;text-align:left;width:100%;box-sizing:border-box;min-width:0;font:inherit;color:var(--dsw-alias-label-primary,inherit);transition:border-color .16s,background .16s}
@@ -51,20 +296,31 @@ var CSS = `
51
296
  .dshm-top{display:flex;align-items:center;gap:8px;min-width:0}
52
297
  .dshm-name{flex:1;min-width:0;font-weight:600;font-size:14px;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
53
298
  .dshm-badge{flex:none;font-size:11px;line-height:16px;padding:0 6px;border-radius:999px;background:var(--dsw-alias-state-success-tertiary,#ecfdf5);color:var(--dsw-alias-state-success-primary,#047857)}
54
- .dshm-badge.warn{background:var(--dsw-alias-state-warning-tertiary,#fffbeb);color:var(--dsw-alias-state-warning-primary,#b45309)}
55
- .dshm-badge.info{background:var(--dsw-alias-bg-layer-4,#eef2ff);color:var(--dsw-alias-label-secondary,#4b5563)}
56
- .dshm-badge.err{background:var(--dsw-alias-state-danger-tertiary,#fef2f2);color:var(--dsw-alias-state-danger-primary,#dc2626)}
299
+ .dshm-badge.warn{background:var(--dsw-alias-state-warn-tertiary,#fffbeb);color:var(--dsw-alias-state-warn-primary,#b45309)}
300
+ .dshm-badge.info{background:var(--dsw-alias-state-business-tertiary,#eef2ff);color:var(--dsw-alias-state-business-primary,#4338ca)}
301
+ .dshm-badge.err{background:var(--dsw-alias-state-error-secondary,#fee2e2);color:var(--dsw-alias-state-error-primary,#b91c1c)}
57
302
  .dshm-desc{color:var(--dsw-alias-label-tertiary,#6b7280);font-size:12px;line-height:18px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
58
303
  .dshm-sub{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--dsw-alias-label-caption,#6b7280)}
59
304
  .dshm-detail{margin-top:8px;border-top:1px dashed var(--dsw-alias-border-l2,#e5e7eb);padding-top:8px;display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#4b5563)}
60
305
  .dshm-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:4px}
61
- .dshm-banner{display:flex;align-items:center;gap:10px;padding:10px 14px;border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-state-warning-tertiary,#fffbeb);color:var(--dsw-alias-state-warning-primary,#b45309);font-size:12px}
306
+ .dshm-banner{display:flex;align-items:center;gap:10px;padding:10px 14px;border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-state-warn-tertiary,#fffbeb);color:var(--dsw-alias-state-warn-primary,#b45309);font-size:12px}
62
307
  .dshm-banner .dshm-banner-text{flex:1}
63
308
  .dshm-row{display:flex;align-items:center;gap:8px}
64
309
  .dshm-kv{display:grid;grid-template-columns:110px 1fr;gap:4px 10px;font-size:12px}
65
310
  .dshm-kv .k{color:var(--dsw-alias-label-caption,#6b7280)}
66
311
  .dshm-spin{display:inline-block;width:12px;height:12px;border:2px solid var(--dsw-alias-border-l2,#c7d2fe);border-top-color:var(--dsw-alias-interactive-bg-selected,#4f46e5);border-radius:50%;animation:dshm-rot .8s linear infinite;vertical-align:-2px}
67
312
  @keyframes dshm-rot{to{transform:rotate(360deg)}}
313
+ .dshm-others{display:flex;align-items:center;gap:8px;padding:10px 12px;border:1px dashed var(--dsw-alias-border-l2,#e2e4e8);border-radius:10px;color:var(--dsw-alias-label-caption,#9ca3af);font-size:12px;line-height:18px}
314
+ .dshm-readme{max-height:240px;overflow:auto;white-space:pre-wrap;word-break:break-word;background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.04));border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;padding:10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#4b5563);margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
315
+ .dshm-prog{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#4b5563)}
316
+ .dshm-prog .bar{flex:1;height:6px;border-radius:3px;background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.08));overflow:hidden;min-width:80px}
317
+ .dshm-prog .bar i{display:block;height:100%;background:var(--dsw-alias-interactive-bg-selected,#4f46e5);transition:width .3s}
318
+ .dshm-entry{box-sizing:border-box;display:flex;align-items:center;gap:8px;width:calc(100% + 4px);height:42px;margin:4px -2px;padding:0 10px 0 8px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,inherit);font:inherit;font-size:14px;line-height:22px;cursor:pointer;overflow:hidden}
319
+ .dshm-entry:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
320
+ .dshm-entry svg{flex:none;width:16px;height:16px}
321
+ .dshm-entry span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
322
+ [data-slot="sidebar.footer.action"]{display:flex!important;flex-direction:column;width:100%;min-width:0}
323
+ [data-slot="sidebar.footer.action"]>*{flex:none;min-width:0}
68
324
  .dshm-empty{text-align:center;color:var(--dsw-alias-label-caption,#6b7280);font-size:13px;padding:32px 0}
69
325
  `;
70
326
  function ensureCss() {
@@ -156,7 +412,7 @@ function RestartBanner({ note, onDone }) {
156
412
  const deadline = Date.now() + 9e4;
157
413
  for (; ; ) {
158
414
  await new Promise((r) => setTimeout(r, 2e3));
159
- if (Date.now() > deadline) throw new Error("\u91CD\u542F\u8D85\u65F6\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5 dsh web \u670D\u52A1\u72B6\u6001");
415
+ if (Date.now() > deadline) throw new Error(lookup("restart.timeout"));
160
416
  try {
161
417
  const ping = await api("ping");
162
418
  if (ping.boot !== ping0.boot) break;
@@ -176,15 +432,15 @@ function RestartBanner({ note, onDone }) {
176
432
  h(
177
433
  "span",
178
434
  { className: "dshm-banner-text" },
179
- phase === "restarting" ? "\u6B63\u5728\u8BF7\u6C42\u91CD\u542F\u2026" : phase === "waiting" ? "\u5DF2\u8BF7\u6C42\u91CD\u542F\uFF0C\u7B49\u5F85 DSH Web \u6062\u590D\u2026" : err ? `\u91CD\u542F\u5931\u8D25\uFF1A${err}` : note || "\u672C\u6B21\u53D8\u66F4\u9700\u8981\u91CD\u542F DSH Web \u540E\u751F\u6548\u3002"
435
+ phase === "restarting" ? lookup("restart.doing") : phase === "waiting" ? lookup("restart.waiting") : err ? lookup("restart.failed", { err }) : note || lookup("banner.done")
180
436
  ),
181
- phase === "idle" && !err ? h("button", { className: "dshm-btn primary sm", onClick: restart }, "\u26A1 \u4E00\u952E\u91CD\u542F") : null,
437
+ phase === "idle" && !err ? h("button", { className: "dshm-btn primary sm", onClick: restart }, lookup("restart.now")) : null,
182
438
  phase === "restarting" || phase === "waiting" ? Spin() : null,
183
- phase === "idle" && err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, "\u77E5\u9053\u4E86") : null,
184
- phase === "idle" && !err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, "\u7A0D\u540E") : null
439
+ phase === "idle" && err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, lookup("common.ok")) : null,
440
+ phase === "idle" && !err ? h("button", { className: "dshm-btn sm", onClick: () => onDone(false) }, lookup("common.later")) : null
185
441
  );
186
442
  }
187
- function MarketTab({ notify }) {
443
+ function MarketTab({ notify, onCount }) {
188
444
  const { loading, data, error, reload } = useAsync((force) => api("market", force ? { force: true } : {}), []);
189
445
  const [q, setQ] = useState("");
190
446
  const [cat, setCat] = useState(null);
@@ -206,11 +462,11 @@ function MarketTab({ notify }) {
206
462
  const res = await api("install", { id: it.id, ...version ? { version } : {} });
207
463
  notify({
208
464
  kind: "ok",
209
- text: `\u5DF2\u5B89\u88C5 ${res.pkg}${res.version ? ` v${res.version}` : ""}` + (res.usedAllowAllBuilds ? "\uFF08\u6CE8\u610F\uFF1A\u8BE5\u63D2\u4EF6\u6267\u884C\u4E86\u6784\u5EFA\u811A\u672C\uFF0C\u5DF2\u6309\u7B56\u7565\u653E\u884C\uFF09" : "")
465
+ text: lookup("notify.installed", { pkg: res.pkg, version: res.version ? ` v${res.version}` : "" }) + (res.usedAllowAllBuilds ? lookup("notify.allowbuilds") : "")
210
466
  });
211
467
  await reload(false);
212
468
  } catch (e) {
213
- notify({ kind: "err", text: `\u5B89\u88C5\u5931\u8D25\uFF1A${e && e.message || e}` });
469
+ notify({ kind: "err", text: lookup("failed.install", { err: e && e.message || e }) });
214
470
  } finally {
215
471
  setBusyId(null);
216
472
  }
@@ -223,26 +479,27 @@ function MarketTab({ notify }) {
223
479
  { className: "dshm-row" },
224
480
  h("input", {
225
481
  className: "dshm-input",
226
- placeholder: "\u641C\u7D22\u540D\u79F0 / \u63CF\u8FF0 / \u6807\u7B7E\u2026",
482
+ placeholder: lookup("search.ph"),
227
483
  value: q,
228
484
  onChange: (e) => setQ(e.target.value)
229
485
  }),
230
- h("button", { className: "dshm-btn", onClick: () => reload(true), title: "\u5F3A\u5236\u5237\u65B0\uFF08\u8DF3\u8FC7\u7F13\u5B58\uFF09" }, loading ? Spin() : "\u21BB \u5237\u65B0")
486
+ h("button", { className: "dshm-btn", onClick: () => reload(true), title: lookup("settings.policy.v") }, loading ? Spin() : `\u21BB ${lookup("common.refresh")}`)
231
487
  ),
232
488
  h(
233
489
  "div",
234
490
  { className: "dshm-chips" },
235
- h("button", { className: `dshm-chip${cat === null ? " on" : ""}`, onClick: () => setCat(null) }, "\u5168\u90E8"),
236
- CATEGORIES.map(([key, label]) => {
491
+ h("button", { className: `dshm-chip${cat === null ? " on" : ""}`, onClick: () => setCat(null) }, lookup("cat.all")),
492
+ CATEGORIES.map((key) => {
237
493
  const n = (data && data.items || []).filter((it) => it.category === key).length;
238
494
  return h(
239
495
  "button",
240
496
  { key, className: `dshm-chip${cat === key ? " on" : ""}`, onClick: () => setCat(cat === key ? null : key) },
241
- `${label}${n ? ` ${n}` : ""}`
497
+ `${lookup("cat." + key)}${n ? ` ${n}` : ""}`
242
498
  );
243
499
  })
244
500
  ),
245
- loading && !data ? h("div", { className: "dshm-empty" }, "\u52A0\u8F7D\u6536\u5F55\u6E05\u5355\u4E2D\u2026 ", Spin()) : error ? h("div", { className: "dshm-err" }, `\u52A0\u8F7D\u5931\u8D25\uFF1A${error}`) : items.length === 0 ? h("div", { className: "dshm-empty" }, "\u6CA1\u6709\u5339\u914D\u7684\u6536\u5F55\u6761\u76EE") : h(
501
+ busyId ? h(ProgressLine, { key: "prog" }) : null,
502
+ 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(
246
503
  "div",
247
504
  { className: "dshm-cards" },
248
505
  items.map((it) => Card({
@@ -250,28 +507,28 @@ function MarketTab({ notify }) {
250
507
  icon: h(Icon, { entry: it }),
251
508
  name: it.name,
252
509
  badges: [
253
- it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, "\u53EF\u5347\u7EA7") : null,
254
- it.installed ? h("span", { className: "dshm-badge", key: "i" }, "\u5DF2\u5B89\u88C5") : null,
510
+ it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, lookup("badge.update")) : null,
511
+ it.installed ? h("span", { className: "dshm-badge", key: "i" }, lookup("badge.installed")) : null,
255
512
  h("span", { className: "dshm-badge info", key: "s" }, it.source === "npm" ? "npm" : "github")
256
513
  ],
257
514
  desc: it.description,
258
515
  sub: [
259
- it.latestVersion ? `\u6700\u65B0 v${it.latestVersion}` : it.latestSha ? `HEAD ${it.latestSha.slice(0, 7)}` : null,
260
- it.installedVersion ? `\u5DF2\u88C5 v${it.installedVersion}` : null,
261
- it.latestError ? `\u7248\u672C\u67E5\u8BE2\u5931\u8D25` : null
516
+ it.latestVersion ? lookup("sub.latest", { v: it.latestVersion }) : it.latestTag ? it.latestTag : it.latestSha ? lookup("sub.head", { sha: it.latestSha.slice(0, 7) }) : null,
517
+ it.installedVersion ? lookup("sub.installed", { v: it.installedVersion }) : null,
518
+ it.latestError ? lookup("version.failed") : null
262
519
  ].filter(Boolean).join(" \xB7 "),
263
520
  open: openId === it.id,
264
521
  onToggle: () => setOpenId(openId === it.id ? null : it.id),
265
522
  detail: DetailRows([
266
- ["\u6536\u5F55 id", it.id],
267
- ["\u6765\u6E90", it.source === "npm" ? `npm \xB7 ${it.npm}` : `GitHub \xB7 ${it.github}`],
268
- ["\u6700\u65B0", it.latestVersion ? `v${it.latestVersion}` : it.latestSha ? it.latestSha : it.latestError || "\u2014"],
269
- ["\u5DF2\u88C5", it.installedPkg ? `${it.installedPkg} v${it.installedVersion || "?"}` : "\u672A\u5B89\u88C5"],
270
- ["\u6807\u7B7E", (it.tags || []).join("\u3001") || "\u2014"],
271
- it.latestError ? ["\u63D0\u793A", `\u7248\u672C\u67E5\u8BE2\u5931\u8D25\uFF1A${it.latestError}`] : null
523
+ [lookup("detail.id"), it.id],
524
+ [lookup("detail.source"), it.source === "npm" ? `npm \xB7 ${it.npm}` : `GitHub \xB7 ${it.github}`],
525
+ [lookup("detail.latest"), it.latestVersion ? `v${it.latestVersion}` : it.latestTag ? it.latestTag : it.latestSha ? it.latestSha : it.latestError || "\u2014"],
526
+ [lookup("detail.installed"), it.installedPkg ? `${it.installedPkg} v${it.installedVersion || "?"}` : lookup("installed.none")],
527
+ [lookup("detail.tags"), (it.tags || []).join(", ") || "\u2014"],
528
+ it.latestError ? [lookup("version.failed"), it.latestError] : null
272
529
  ]),
273
530
  actions: [
274
- it.installed ? h("span", { className: "dshm-hint", key: "hint" }, "\u5DF2\u5B89\u88C5\uFF0C\u53EF\u5728\u300C\u5DF2\u88C5\u300D\u9875\u7BA1\u7406") : h(
531
+ it.installed ? h("span", { className: "dshm-hint", key: "hint" }, lookup("manage.hint")) : h(
275
532
  "button",
276
533
  {
277
534
  key: "install",
@@ -282,16 +539,74 @@ function MarketTab({ notify }) {
282
539
  doInstall(it);
283
540
  }
284
541
  },
285
- busyId === it.id ? h(Spin) : "\u5B89\u88C5"
542
+ busyId === it.id ? h(Spin) : lookup("action.install")
286
543
  )
287
544
  ]
288
545
  }))
289
546
  )
290
547
  );
291
548
  }
292
- function InstalledTab({ notify }) {
293
- const { loading, data, error, reload } = useAsync(() => api("installed"), []);
549
+ var PHASE_LABEL = { resolving: "phase.resolving", downloading: "phase.downloading", linking: "phase.linking", building: "phase.building" };
550
+ function ProgressLine() {
551
+ const [st, setSt] = useState(null);
552
+ useEffect(() => {
553
+ let live = true;
554
+ const tick = async () => {
555
+ try {
556
+ const d = await api("status");
557
+ if (live) setSt(d);
558
+ } catch {
559
+ }
560
+ };
561
+ tick();
562
+ const iv = setInterval(tick, 1e3);
563
+ return () => {
564
+ live = false;
565
+ clearInterval(iv);
566
+ };
567
+ }, []);
568
+ if (!st) return null;
569
+ const pct = st.total ? Math.min(100, Math.round(st.done / st.total * 100)) : null;
570
+ const phaseLabel = lookup(PHASE_LABEL[st.phase] || "phase.ready");
571
+ return h(
572
+ "div",
573
+ { className: "dshm-prog" },
574
+ Spin(),
575
+ h("span", null, `${st.target} \xB7 ${phaseLabel}${st.done ? ` ${st.done}${st.total ? "/" + st.total : ""}` : ""}`),
576
+ pct !== null ? h("span", { className: "bar" }, h("i", { style: { width: pct + "%" } })) : null,
577
+ st.currentPackage ? h("span", { className: "dshm-hint" }, String(st.currentPackage).slice(0, 44)) : null
578
+ );
579
+ }
580
+ function ReadmeBlock({ pkg }) {
581
+ const [state, setState] = useState({ loading: true, text: "", err: "", truncated: false });
582
+ useEffect(() => {
583
+ let live = true;
584
+ api("readme", { pkg }).then((d) => live && setState({ loading: false, text: d.readme, err: "", truncated: d.truncated })).catch((e) => live && setState({ loading: false, text: "", err: String(e && e.message || e) }));
585
+ return () => {
586
+ live = false;
587
+ };
588
+ }, [pkg]);
589
+ if (state.loading) return h("div", { className: "dshm-hint" }, "\u52A0\u8F7D README\u2026 ", Spin());
590
+ if (state.err) return h("div", { className: "dshm-err" }, state.err);
591
+ if (!state.text) return h("div", { className: "dshm-hint" }, "\uFF08\u8BE5\u63D2\u4EF6\u6CA1\u6709 README\uFF09");
592
+ return h("pre", { className: "dshm-readme" }, state.text + (state.truncated ? "\n\n\u2026\uFF08\u8D85\u8FC7 64KB \u5DF2\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9\u89C1\u63D2\u4EF6\u76EE\u5F55\uFF09" : ""));
593
+ }
594
+ function uninstallGuard(it) {
595
+ if (it.source === "link") {
596
+ return {
597
+ confirm: lookup("confirm.unlink"),
598
+ warn: lookup("warn.unlink", { path: it.path })
599
+ };
600
+ }
601
+ if (it.source === "file") {
602
+ return { confirm: lookup("confirm.core"), warn: lookup("warn.core") };
603
+ }
604
+ return { confirm: lookup("confirm.uninstall"), warn: null };
605
+ }
606
+ function InstalledTab({ notify, installed }) {
607
+ const { loading, data, error, reload } = installed;
294
608
  const [openPkg, setOpenPkg] = useState(null);
609
+ const [readmePkg, setReadmePkg] = useState(null);
295
610
  const [busyPkg, setBusyPkg] = useState(null);
296
611
  const doUninstall = async (it) => {
297
612
  setBusyPkg(it.pkg);
@@ -299,11 +614,11 @@ function InstalledTab({ notify }) {
299
614
  const res = await api("uninstall", { pkg: it.pkg });
300
615
  notify({
301
616
  kind: "ok",
302
- text: `\u5DF2\u5378\u8F7D ${res.pkg}${res.liveDisabled ? "\uFF08\u5DF2\u5148\u4E0B\u7EBF\u8FD0\u884C\u4E2D\u7684\u754C\u9762\uFF09" : ""}` + (res.leftovers && res.leftovers.length ? `\uFF1B\u68C0\u6D4B\u5230\u7591\u4F3C\u6B8B\u7559\u6570\u636E\uFF1A${res.leftovers.join("\u3001")}` : "")
617
+ text: lookup("notify.uninstalled", { pkg: res.pkg }) + (res.liveDisabled ? lookup("notify.livedisabled") : "") + (res.leftovers && res.leftovers.length ? lookup("notify.leftovers", { paths: res.leftovers.join(", ") }) : "")
303
618
  });
304
619
  await reload();
305
620
  } catch (e) {
306
- notify({ kind: "err", text: `\u5378\u8F7D\u5931\u8D25\uFF1A${e && e.message || e}` });
621
+ notify({ kind: "err", text: lookup("failed.uninstall", { err: e && e.message || e }) });
307
622
  } finally {
308
623
  setBusyPkg(null);
309
624
  }
@@ -314,72 +629,90 @@ function InstalledTab({ notify }) {
314
629
  const res = await api("upgrade", { pkg: it.pkg });
315
630
  notify({
316
631
  kind: "ok",
317
- text: `\u5DF2\u5347\u7EA7 ${res.pkg}${res.fromVersion ? `\uFF08v${res.fromVersion} \u2192 ${res.version ? `v${res.version}` : res.sha ? res.sha.slice(0, 7) : "\u6700\u65B0"}\uFF09` : ""}` + (res.usedAllowAllBuilds ? "\uFF08\u6CE8\u610F\uFF1A\u8BE5\u63D2\u4EF6\u6267\u884C\u4E86\u6784\u5EFA\u811A\u672C\uFF09" : "")
632
+ text: lookup("notify.upgraded", {
633
+ pkg: res.pkg,
634
+ from: res.fromVersion ? `v${res.fromVersion}` : "\u2014",
635
+ to: res.version ? `v${res.version}` : res.sha ? res.sha.slice(0, 7) : "latest"
636
+ }) + (res.usedAllowAllBuilds ? lookup("notify.upgradehint") : "")
318
637
  });
319
638
  await reload();
320
639
  } catch (e) {
321
- notify({ kind: "err", text: `\u5347\u7EA7\u5931\u8D25\uFF1A${e && e.message || e}` });
640
+ notify({ kind: "err", text: lookup("failed.upgrade", { err: e && e.message || e }) });
322
641
  } finally {
323
642
  setBusyPkg(null);
324
643
  }
325
644
  };
326
- if (loading && !data) return h("div", { className: "dshm-empty" }, "\u8BFB\u53D6 web profile \u4E2D\u2026 ", Spin());
327
- if (error) return h("div", { className: "dshm-err" }, `\u8BFB\u53D6\u5931\u8D25\uFF1A${error}`);
645
+ if (loading && !data) return h("div", { className: "dshm-empty" }, lookup("installed.loading"), Spin());
646
+ if (error) return h("div", { className: "dshm-err" }, lookup("failed.read", { err: error }));
328
647
  const items = data && data.items || [];
329
- if (!items.length) return h("div", { className: "dshm-empty" }, `web profile \u5C1A\u672A\u5B89\u88C5\u4EFB\u4F55 dsh \u63D2\u4EF6\uFF08${data.profileDir}\uFF09`);
648
+ if (!items.length) return h("div", { className: "dshm-empty" }, `${lookup("installed.empty")} (${data.profileDir})`);
330
649
  return h(
331
650
  React.Fragment,
332
651
  null,
333
- h("div", { className: "dshm-hint" }, `web profile\uFF1A${data.profileDir}${data.others ? ` \xB7 \u53E6\u6709 ${data.others} \u4E2A\u975E dsh \u4F9D\u8D56\u672A\u5217\u51FA` : ""}`),
652
+ h("div", { className: "dshm-hint" }, lookup("profile.hint", { path: data.profileDir })),
653
+ data.others > 0 ? h("div", { className: "dshm-others" }, lookup("installed.others", { n: data.others })) : null,
654
+ busyPkg ? h(ProgressLine, { key: "prog" }) : null,
334
655
  h(
335
656
  "div",
336
657
  { className: "dshm-cards" },
337
- items.map((it) => Card({
338
- key: it.pkg,
339
- icon: h(Icon, { entry: { name: it.name, github: it.spec.startsWith("github:") ? it.spec.slice(7).split("#")[0] : null, icon: null } }),
340
- name: it.name,
341
- badges: [
342
- it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, `\u53EF\u5347\u7EA7${it.latestVersion ? ` \u2192 v${it.latestVersion}` : ""}`) : null,
343
- it.registryId ? h("span", { className: "dshm-badge", key: "r" }, "\u5E02\u573A\u5B89\u88C5") : h("span", { className: "dshm-badge info", key: "r" }, "\u975E\u5E02\u573A\u5B89\u88C5")
344
- ],
345
- desc: it.description || "\uFF08\u65E0\u63CF\u8FF0\uFF09",
346
- sub: [
347
- `v${it.version || "?"}`,
348
- { npm: "npm", github: "github", link: "\u672C\u5730 link", file: "\u672C\u5730 file", unknown: "\u672A\u77E5" }[it.source] || it.source
349
- ].join(" \xB7 "),
350
- open: openPkg === it.pkg,
351
- onToggle: () => setOpenPkg(openPkg === it.pkg ? null : it.pkg),
352
- detail: DetailRows([
353
- ["\u5305\u540D", it.pkg],
354
- ["\u5B89\u88C5 spec", it.spec],
355
- ["\u6700\u65B0", it.latestVersion ? `v${it.latestVersion}` : "\u2014"],
356
- ["\u6536\u5F55", it.registryId || "\u4E0D\u5728\u6536\u5F55\u6E05\u5355\u4E2D"],
357
- ["\u8DEF\u5F84", it.path]
358
- ]),
359
- actions: [
360
- it.outdated ? h(
361
- "button",
362
- {
363
- key: "up",
364
- className: "dshm-btn primary sm",
365
- disabled: busyPkg === it.pkg,
658
+ items.map((it) => {
659
+ const guard = uninstallGuard(it);
660
+ return Card({
661
+ key: it.pkg,
662
+ icon: h(Icon, { entry: { name: it.name, github: it.registryGithub || it.githubRepo || (it.spec.startsWith("github:") ? it.spec.slice(7).split("#")[0] : null), icon: null } }),
663
+ name: it.name,
664
+ badges: [
665
+ it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, `\u2B06 ${it.latestTag || (it.latestVersion ? `v${it.latestVersion}` : "")}`.trim()) : null,
666
+ it.registryId ? h("span", { className: "dshm-badge", key: "r" }, lookup("badge.market")) : h("span", { className: "dshm-badge info", key: "r" }, lookup("badge.nonmarket"))
667
+ ],
668
+ desc: it.description || "\uFF08\u65E0\u63CF\u8FF0\uFF09",
669
+ sub: [
670
+ `v${it.version || "?"}`,
671
+ { npm: lookup("src.npm"), github: lookup("src.github"), link: lookup("src.link"), file: lookup("src.file"), unknown: lookup("src.unknown") }[it.source] || it.source
672
+ ].join(" \xB7 "),
673
+ open: openPkg === it.pkg,
674
+ onToggle: () => setOpenPkg(openPkg === it.pkg ? null : it.pkg),
675
+ detail: readmePkg === it.pkg ? h(ReadmeBlock, { pkg: it.pkg }) : DetailRows([
676
+ [lookup("detail.pkg"), it.pkg],
677
+ [lookup("detail.spec"), it.spec],
678
+ [lookup("detail.latest"), it.latestTag || (it.latestVersion ? `v${it.latestVersion}` : "\u2014")],
679
+ [lookup("detail.listed"), it.registryId || lookup("detail.listed.no")],
680
+ [lookup("detail.path"), it.path],
681
+ guard.warn ? [lookup("detail.note"), guard.warn] : null
682
+ ]),
683
+ actions: [
684
+ h("button", {
685
+ key: "rd",
686
+ className: "dshm-btn sm",
366
687
  onClick: (e) => {
367
688
  e.stopPropagation();
368
- doUpgrade(it);
689
+ setReadmePkg(readmePkg === it.pkg ? null : it.pkg);
369
690
  }
370
- },
371
- busyPkg === it.pkg ? h(Spin) : "\u5347\u7EA7"
372
- ) : null,
373
- it.registryId || it.source === "npm" ? h(TwoStepButton, {
374
- key: "un",
375
- label: "\u5378\u8F7D",
376
- confirmLabel: "\u786E\u8BA4\u5378\u8F7D\uFF1F",
377
- className: "dshm-btn sm",
378
- disabled: busyPkg === it.pkg,
379
- onConfirm: () => doUninstall(it)
380
- }) : null
381
- ]
382
- }))
691
+ }, readmePkg === it.pkg ? lookup("readme.hide") : lookup("readme.show")),
692
+ it.outdated ? h(
693
+ "button",
694
+ {
695
+ key: "up",
696
+ className: "dshm-btn primary sm",
697
+ disabled: busyPkg === it.pkg,
698
+ onClick: (e) => {
699
+ e.stopPropagation();
700
+ doUpgrade(it);
701
+ }
702
+ },
703
+ busyPkg === it.pkg ? h(Spin) : lookup("action.upgrade")
704
+ ) : null,
705
+ h(TwoStepButton, {
706
+ key: "un",
707
+ label: lookup("action.uninstall"),
708
+ confirmLabel: guard.confirm,
709
+ className: "dshm-btn sm",
710
+ disabled: busyPkg === it.pkg,
711
+ onConfirm: () => doUninstall(it)
712
+ })
713
+ ]
714
+ });
715
+ })
383
716
  )
384
717
  );
385
718
  }
@@ -392,7 +725,7 @@ function SettingsTab({ notify }) {
392
725
  setBusy(true);
393
726
  try {
394
727
  await reg.reload(true);
395
- notify({ kind: "ok", text: "\u6536\u5F55\u6E05\u5355\u5DF2\u5F3A\u5236\u5237\u65B0" });
728
+ notify({ kind: "ok", text: lookup("registry.refreshed") });
396
729
  } finally {
397
730
  setBusy(false);
398
731
  }
@@ -401,10 +734,10 @@ function SettingsTab({ notify }) {
401
734
  setUpgrading(true);
402
735
  try {
403
736
  const res = await api("self-upgrade");
404
- notify({ kind: "ok", text: `dsh-m \u5DF2\u66F4\u65B0\u5230 v${res.version}\uFF0C\u91CD\u542F\u540E\u751F\u6548` });
737
+ notify({ kind: "ok", text: lookup("self.upgraded", { v: res.version }) });
405
738
  await self.reload();
406
739
  } catch (e) {
407
- notify({ kind: "err", text: `\u81EA\u66F4\u65B0\u5931\u8D25\uFF1A${e && e.message || e}` });
740
+ notify({ kind: "err", text: lookup("failed.selfupdate", { err: e && e.message || e }) });
408
741
  } finally {
409
742
  setUpgrading(false);
410
743
  }
@@ -413,57 +746,53 @@ function SettingsTab({ notify }) {
413
746
  React.Fragment,
414
747
  null,
415
748
  Section(
416
- "\u6536\u5F55\u6E05\u5355\uFF08registry\uFF09",
749
+ lookup("settings.registry"),
417
750
  h(
418
751
  "div",
419
752
  { className: "dshm-kv" },
420
- h("span", { className: "k" }, "\u5F53\u524D\u6765\u6E90"),
753
+ h("span", { className: "k" }, lookup("settings.source")),
421
754
  h("span", null, regSourceLabel(reg.data)),
422
- h("span", { className: "k" }, "\u66F4\u65B0\u65F6\u95F4"),
755
+ h("span", { className: "k" }, lookup("settings.updated")),
423
756
  h("span", null, fmtDate(reg.data && reg.data.fetchedAt)),
424
- h("span", { className: "k" }, "\u6761\u76EE\u6570"),
425
- h("span", null, reg.data ? `${reg.data.plugins.length} \u6761` : "\u2014"),
426
- h("span", { className: "k" }, "\u7F13\u5B58\u7B56\u7565"),
427
- h("span", null, "TTL 60 \u5206\u949F\uFF1B\u8BBE\u7F6E registryUrl \u53EF\u8986\u76D6\u6E90")
757
+ h("span", { className: "k" }, lookup("settings.count")),
758
+ h("span", null, reg.data ? lookup("settings.count.v", { n: reg.data.plugins.length }) : "\u2014"),
759
+ h("span", { className: "k" }, lookup("settings.policy")),
760
+ h("span", null, lookup("settings.policy.v"))
428
761
  ),
429
- reg.data && reg.data.errors && reg.data.errors.length ? h("div", { className: "dshm-err" }, `\u8FDC\u7AEF\u63D0\u793A\uFF1A${reg.data.errors.join("\uFF1B")}`) : null,
762
+ reg.data && reg.data.errors && reg.data.errors.length ? h("div", { className: "dshm-err" }, `${lookup("settings.remotehint")}\uFF1A${reg.data.errors.join("\uFF1B")}`) : null,
430
763
  h(
431
764
  "div",
432
765
  { className: "dshm-actions" },
433
- h("button", { className: "dshm-btn sm", disabled: busy || reg.loading, onClick: refresh }, busy || reg.loading ? h(Spin) : "\u5F3A\u5236\u5237\u65B0")
766
+ h("button", { className: "dshm-btn sm", disabled: busy || reg.loading, onClick: refresh }, busy || reg.loading ? h(Spin) : lookup("settings.force"))
434
767
  )
435
768
  ),
436
769
  Section(
437
- "dsh-m \u81EA\u8EAB",
770
+ lookup("settings.self"),
438
771
  h(
439
772
  "div",
440
773
  { className: "dshm-kv" },
441
- h("span", { className: "k" }, "\u5F53\u524D\u7248\u672C"),
774
+ h("span", { className: "k" }, lookup("settings.current")),
442
775
  h("span", null, self.data ? `v${self.data.current}` : "\u2014"),
443
- h("span", { className: "k" }, "npm \u6700\u65B0"),
444
- h("span", null, self.data ? self.data.latest ? `v${self.data.latest}` : `\u67E5\u8BE2\u5931\u8D25${self.data.error ? `\uFF1A${self.data.error}` : ""}` : "\u2026")
776
+ h("span", { className: "k" }, lookup("settings.npmlatest")),
777
+ h("span", null, self.data ? self.data.latest ? `v${self.data.latest}` : lookup("settings.lookupfailed", { err: self.data.error || "" }) : "\u2026")
445
778
  ),
446
779
  self.data && self.data.outdated ? h(
447
780
  "div",
448
781
  { className: "dshm-actions" },
449
- h("button", { className: "dshm-btn primary sm", disabled: upgrading, onClick: upgradeSelf }, upgrading ? h(Spin) : "\u5347\u7EA7 dsh-m"),
450
- h("span", { className: "dshm-hint" }, "\u5347\u7EA7\u540E\u540C\u6837\u9700\u8981\u91CD\u542F\u751F\u6548")
782
+ h("button", { className: "dshm-btn primary sm", disabled: upgrading, onClick: upgradeSelf }, upgrading ? h(Spin) : lookup("settings.upgradeself")),
783
+ h("span", { className: "dshm-hint" }, lookup("settings.upgradehint"))
451
784
  ) : null
452
785
  ),
453
786
  Section(
454
- "\u5173\u4E8E",
455
- h(
456
- "div",
457
- { className: "dshm-hint" },
458
- "DSH Marketplace\uFF08dsh-m\uFF09\u2014 \u4E2A\u4EBA\u81EA\u7528\u7684 DeepSeek Harness \u63D2\u4EF6\u5E02\u573A\u3002\u6536\u5F55\u3001\u5B89\u88C5\u3001\u5378\u8F7D\u3001\u5347\u7EA7\uFF0C\u5168\u90E8\u672C\u673A\u5B8C\u6210\u3002"
459
- )
787
+ lookup("settings.about"),
788
+ h("div", { className: "dshm-hint" }, lookup("settings.about.text"))
460
789
  )
461
790
  );
462
791
  }
463
792
  function regSourceLabel(data) {
464
793
  if (!data) return "\u2014";
465
- const map = { override: "\u81EA\u5B9A\u4E49\u6E90", jsdelivr: "jsDelivr\uFF08@main\uFF09", raw: "raw.githubusercontent\uFF08@main\uFF09", cache: "\u672C\u5730\u7F13\u5B58", bundled: "\u5305\u5185\u5FEB\u7167\uFF08\u515C\u5E95\uFF09" };
466
- return map[data.source] || data.source;
794
+ const map = { override: "src.override", jsdelivr: "src.jsdelivr", raw: "src.raw", cache: "src.cache", bundled: "src.bundled" };
795
+ return lookup(map[data.source] || data.source);
467
796
  }
468
797
  function Section(title, ...children) {
469
798
  return h(
@@ -509,12 +838,18 @@ function Card({ icon, name, badges, desc, sub, open, onToggle, detail, actions }
509
838
  );
510
839
  }
511
840
  var TABS = [
512
- ["market", "\u5E02\u573A"],
513
- ["installed", "\u5DF2\u88C5"],
514
- ["settings", "\u8BBE\u7F6E"]
841
+ ["market", "tab.market", "market"],
842
+ ["installed", "tab.installed", "installed"],
843
+ ["settings", "tab.settings", null]
515
844
  ];
516
845
  function MarketPanel({ onClose }) {
517
846
  const [tab, setTab] = useState("market");
847
+ const market = useAsync((force) => api("market", force ? { force: true } : {}), []);
848
+ const installed = useAsync(() => api("installed"), []);
849
+ const counts = {
850
+ market: market.data ? market.data.items.length : null,
851
+ installed: installed.data ? installed.data.items.length : null
852
+ };
518
853
  const [banner, setBanner] = useState(null);
519
854
  const [toast, setToast] = useState(null);
520
855
  useEffect(() => {
@@ -531,7 +866,7 @@ function MarketPanel({ onClose }) {
531
866
  }, [toast]);
532
867
  const notify = useCallback(({ kind, text }) => {
533
868
  setToast({ kind, text });
534
- if (kind === "ok") setBanner({ text: "\u53D8\u66F4\u5B8C\u6210\uFF0C\u9700\u8981\u91CD\u542F DSH Web \u540E\u751F\u6548\u3002" });
869
+ if (kind === "ok") setBanner({ text: lookup("banner.done") });
535
870
  }, []);
536
871
  return h(
537
872
  "div",
@@ -542,23 +877,39 @@ function MarketPanel({ onClose }) {
542
877
  h(
543
878
  "div",
544
879
  { className: "dshm-head" },
545
- h("span", { className: "dshm-title" }, "\u{1F6CD} DSH \u5E02\u573A"),
546
- TABS.map(
547
- ([key, label]) => h("button", { key, className: `dshm-tab${tab === key ? " on" : ""}`, onClick: () => setTab(key) }, label)
880
+ h("span", { className: "dshm-title" }, lookup("title.full")),
881
+ h(
882
+ "div",
883
+ { className: "dshm-seg", role: "tablist" },
884
+ TABS.map(
885
+ ([key, labelKey, countKey]) => h(
886
+ "button",
887
+ {
888
+ key,
889
+ type: "button",
890
+ role: "tab",
891
+ "aria-selected": tab === key,
892
+ className: tab === key ? "on" : "",
893
+ onClick: () => setTab(key)
894
+ },
895
+ lookup(labelKey),
896
+ countKey && counts[countKey] != null ? h("span", { className: "dshm-count" }, String(counts[countKey])) : null
897
+ )
898
+ )
548
899
  ),
549
900
  h("span", { className: "dshm-spacer" }),
550
- h("button", { className: "dshm-btn", onClick: onClose }, "\u5173\u95ED")
901
+ h("button", { className: "dshm-btn", onClick: onClose }, lookup("common.close"))
551
902
  ),
552
903
  h(
553
904
  "div",
554
905
  { className: "dshm-body" },
555
- tab === "market" ? h(MarketTab, { notify }) : null,
556
- tab === "installed" ? h(InstalledTab, { notify }) : null,
906
+ tab === "market" ? h(MarketTab, { notify, market }) : null,
907
+ tab === "installed" ? h(InstalledTab, { notify, installed }) : null,
557
908
  tab === "settings" ? h(SettingsTab, { notify }) : null
558
909
  ),
559
910
  toast ? h(
560
911
  "div",
561
- { className: `dshm-banner`, style: toast.kind === "err" ? { background: "var(--dsw-alias-state-danger-tertiary,#fef2f2)", color: "var(--dsw-alias-state-danger-primary,#dc2626)" } : null },
912
+ { className: `dshm-banner`, style: toast.kind === "err" ? { background: "var(--dsw-alias-state-error-secondary,#fee2e2)", color: "var(--dsw-alias-state-error-primary,#b91c1c)" } : null },
562
913
  h("span", { className: "dshm-banner-text" }, toast.text)
563
914
  ) : null,
564
915
  banner ? h(RestartBanner, { note: banner.text, onDone: () => setBanner(null) }) : null
@@ -591,12 +942,24 @@ function mountPanel() {
591
942
  container.remove();
592
943
  }
593
944
  }
945
+ function MarketIcon() {
946
+ return h(
947
+ "svg",
948
+ { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true" },
949
+ h("rect", { x: "1.75", y: "1.75", width: "5.5", height: "5.5", rx: "1.2", stroke: "currentColor", strokeWidth: "1.4" }),
950
+ h("rect", { x: "8.75", y: "1.75", width: "5.5", height: "5.5", rx: "1.2", stroke: "currentColor", strokeWidth: "1.4" }),
951
+ h("rect", { x: "1.75", y: "8.75", width: "5.5", height: "5.5", rx: "1.2", stroke: "currentColor", strokeWidth: "1.4" }),
952
+ // 右下:放大镜(收一点,光学尺寸与其他宫格图标一致)
953
+ h("circle", { cx: "10.5", cy: "10.5", r: "2.9", stroke: "currentColor", strokeWidth: "1.4" }),
954
+ h("path", { d: "M12.6 12.6 14.3 14.3", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
955
+ );
956
+ }
594
957
  function MarketEntry(props) {
595
958
  useEffect(() => ensureCss(), []);
596
959
  return h(
597
960
  "button",
598
961
  {
599
- className: "dshm-btn dshm-entry",
962
+ className: "dshm-entry",
600
963
  onClick: () => {
601
964
  try {
602
965
  mountPanel();
@@ -604,11 +967,10 @@ function MarketEntry(props) {
604
967
  console.error("[dsh-m] \u6253\u5F00\u5E02\u573A\u9762\u677F\u5931\u8D25:", e);
605
968
  }
606
969
  },
607
- title: "DSH Marketplace",
608
- style: { margin: "4px" }
970
+ title: "\u63D2\u4EF6\u5E02\u573A"
609
971
  },
610
- "\u{1F6CD}",
611
- props && props.wide ? " DSH \u5E02\u573A" : null
972
+ h(MarketIcon),
973
+ props && props.wide ? h("span", null, lookup("market.title")) : null
612
974
  );
613
975
  }
614
976
  function registerSlot(slots, options, component) {
@@ -758,10 +1120,21 @@ function apply(ctx) {
758
1120
  const slots = ctx.slots;
759
1121
  if (!slots) return;
760
1122
  ctx.effect(() => ensureCss(), "dshm-style");
1123
+ ctx.inject(["locale"], (c) => {
1124
+ if (!c.locale || typeof c.locale.register !== "function") return;
1125
+ c.effect(() => {
1126
+ try {
1127
+ return c.locale.register("dshm", { zh: ZH, en: EN });
1128
+ } catch {
1129
+ return () => {
1130
+ };
1131
+ }
1132
+ }, "dshm-locale");
1133
+ });
761
1134
  slots.inject(
762
1135
  "sidebar.footer.action",
763
1136
  () => slots.register(
764
- { name: "sidebar.footer.action", id: "dshm-market", key: "dshm-market", order: 9, label: () => "DSH \u5E02\u573A" },
1137
+ { name: "sidebar.footer.action", id: "dshm-market", key: "dshm-market", order: 9, locale: "dshm", label: () => lookup("market.title") },
765
1138
  function DshmMarketEntry(actionProps) {
766
1139
  return h(MarketEntry, actionProps);
767
1140
  }