dsh-m 0.4.3 → 0.5.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
@@ -27,10 +27,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
27
27
  var market_state_exports = {};
28
28
  __export(market_state_exports, {
29
29
  MARKET_PAGE_SIZE: () => MARKET_PAGE_SIZE,
30
+ marketNotice: () => marketNotice,
30
31
  normalizeMarketQuery: () => normalizeMarketQuery,
31
32
  normalizeMarketResponse: () => normalizeMarketResponse,
32
33
  registryNotice: () => registryNotice,
33
- resetPageOnFilterChange: () => resetPageOnFilterChange
34
+ resetPageOnFilterChange: () => resetPageOnFilterChange,
35
+ sortMergedItems: () => sortMergedItems,
36
+ splitCategories: () => splitCategories
34
37
  });
35
38
  function toSafeInt(value, fallback, min, max) {
36
39
  const n = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback;
@@ -41,10 +44,11 @@ function toSafeInt(value, fallback, min, max) {
41
44
  function normalizeMarketQuery(input) {
42
45
  const raw = input && typeof input === "object" ? input : {};
43
46
  const query = typeof raw.query === "string" ? raw.query.trim() : "";
44
- const category = typeof raw.category === "string" && CATEGORIES.includes(raw.category) ? raw.category : null;
47
+ const category = typeof raw.category === "string" && (CATEGORIES.includes(raw.category) || COMMUNITY_SLUG_RE.test(raw.category)) ? raw.category : null;
45
48
  const offset = toSafeInt(raw.offset, 0, 0);
46
49
  const limit = toSafeInt(raw.limit, MARKET_PAGE_SIZE, 1, MARKET_PAGE_SIZE);
47
- return { query, category, offset, limit };
50
+ const primaryOnly = raw.primaryOnly === true;
51
+ return { query, category, offset, limit, primaryOnly };
48
52
  }
49
53
  function resetPageOnFilterChange(previous, next) {
50
54
  const prev = previous && typeof previous === "object" ? previous : {};
@@ -72,6 +76,20 @@ function normalizeMarketResponse(raw) {
72
76
  ...rs,
73
77
  errors: Array.isArray(rs.errors) ? rs.errors.map(String) : []
74
78
  };
79
+ const c = body.community && typeof body.community === "object" && !Array.isArray(body.community) ? body.community : {};
80
+ const community = {
81
+ ...FALLBACK_COMMUNITY,
82
+ ...c,
83
+ enabled: c.enabled === true,
84
+ status: typeof c.status === "string" ? c.status : "disabled",
85
+ acceptedCount: typeof c.acceptedCount === "number" && Number.isFinite(c.acceptedCount) ? c.acceptedCount : 0,
86
+ upstreamCount: typeof c.upstreamCount === "number" && Number.isFinite(c.upstreamCount) ? c.upstreamCount : null,
87
+ displaced: typeof c.displaced === "number" && Number.isFinite(c.displaced) ? c.displaced : 0,
88
+ skippedDirty: typeof c.skippedDirty === "number" && Number.isFinite(c.skippedDirty) ? c.skippedDirty : 0,
89
+ skippedSubpathNoNpm: typeof c.skippedSubpathNoNpm === "number" && Number.isFinite(c.skippedSubpathNoNpm) ? c.skippedSubpathNoNpm : 0,
90
+ errors: Array.isArray(c.errors) ? c.errors.map(String) : [],
91
+ warnings: Array.isArray(c.warnings) ? c.warnings.map(String) : []
92
+ };
75
93
  return {
76
94
  items,
77
95
  total,
@@ -79,6 +97,7 @@ function normalizeMarketResponse(raw) {
79
97
  limit,
80
98
  categoryCounts,
81
99
  registryState,
100
+ community,
82
101
  installedComplete: body.installedComplete === true,
83
102
  latestComplete: body.latestComplete === true,
84
103
  latestTimedOut: body.latestTimedOut === true
@@ -92,7 +111,40 @@ function registryNotice(summary, total) {
92
111
  else if (!s.isDefault) key = "notice.custom";
93
112
  return { key, count: typeof total === "number" && Number.isFinite(total) ? total : 0 };
94
113
  }
95
- var MARKET_PAGE_SIZE, CATEGORIES, FALLBACK_REGISTRY_STATE;
114
+ function splitCategories(categoryCounts) {
115
+ const counts = categoryCounts && typeof categoryCounts === "object" && !Array.isArray(categoryCounts) ? categoryCounts : {};
116
+ const curated = CURATED_CATEGORIES.map(({ id, label }) => ({ id, label, count: typeof counts[id] === "number" ? counts[id] : 0 }));
117
+ const community = [];
118
+ const unknown = [];
119
+ for (const [id, raw] of Object.entries(counts)) {
120
+ const count = typeof raw === "number" && Number.isFinite(raw) ? raw : 0;
121
+ if (CURATED_IDS.has(id)) continue;
122
+ if (COMMUNITY_KNOWN_LABELS[id]) community.push({ id, label: COMMUNITY_KNOWN_LABELS[id], count });
123
+ else if (!SHARED_BUCKETS.has(id)) unknown.push({ id, label: id, count });
124
+ }
125
+ return { curated, community, unknown };
126
+ }
127
+ function marketNotice(registryState, community) {
128
+ const base = registryNotice(registryState, void 0);
129
+ const c = community && typeof community === "object" ? community : {};
130
+ if (c.status === "stale") base.communityStale = true;
131
+ if (registryState && registryState.status === "unavailable" && (c.status === "ready" || c.status === "stale")) {
132
+ base.communityFallback = true;
133
+ }
134
+ return base;
135
+ }
136
+ function sortMergedItems(items) {
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];
146
+ }
147
+ var MARKET_PAGE_SIZE, CATEGORIES, FALLBACK_REGISTRY_STATE, COMMUNITY_SLUG_RE, CURATED_CATEGORIES, CURATED_IDS, SHARED_BUCKETS, COMMUNITY_KNOWN_LABELS, FALLBACK_COMMUNITY;
96
148
  var init_market_state = __esm({
97
149
  "src/client/market-state.js"() {
98
150
  "use strict";
@@ -109,6 +161,53 @@ var init_market_state = __esm({
109
161
  errors: [],
110
162
  count: 0
111
163
  };
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
+ FALLBACK_COMMUNITY = {
197
+ enabled: false,
198
+ status: "disabled",
199
+ version: null,
200
+ checkedAt: null,
201
+ fetchedAt: null,
202
+ route: null,
203
+ acceptedCount: 0,
204
+ upstreamCount: null,
205
+ displaced: 0,
206
+ skippedDirty: 0,
207
+ skippedSubpathNoNpm: 0,
208
+ errors: [],
209
+ warnings: []
210
+ };
112
211
  }
113
212
  });
114
213
 
@@ -309,7 +408,11 @@ function installedViewModel(it) {
309
408
  latestLabelDetail: it.latestTag || (it.latestVersion ? `v${it.latestVersion}` : "\u2014"),
310
409
  // 0.4.0(Task 17):运行相位/开关状态的 sub 行渲染原料;相位点 class 映射归 toggle-view
311
410
  phaseKey: it.phase ? `phase.${it.phase}` : null,
312
- enabledLabelKey: it.enabled === false ? "toggle.state.off" : "toggle.state.on"
411
+ enabledLabelKey: it.enabled === false ? "toggle.state.off" : "toggle.state.on",
412
+ // M1 Task 5:社区收录徽标(卡片/已装卡显示「社区收录」)
413
+ community: it.community === true,
414
+ // M1 Task 7 ⑩:检查未完成标注(latestError 行渲染原料;code → 安全化原因由调用方 i18n/lookup)
415
+ latestIssue: it.latestError ? { code: it.latestErrorCode ?? null, note: it.latestError } : null
313
416
  };
314
417
  }
315
418
  function registrySourceKey(data) {
@@ -482,7 +585,7 @@ var h = React.createElement;
482
585
  var { useState, useEffect, useCallback, useMemo, useRef } = React;
483
586
  var PLUGIN_ID = "dsh-m";
484
587
  var API = "/dshm";
485
- var { MARKET_PAGE_SIZE: MARKET_PAGE_SIZE2, normalizeMarketQuery: normalizeMarketQuery2, resetPageOnFilterChange: resetPageOnFilterChange2, normalizeMarketResponse: normalizeMarketResponse2, registryNotice: registryNotice2 } = (init_market_state(), __toCommonJS(market_state_exports));
588
+ var { MARKET_PAGE_SIZE: MARKET_PAGE_SIZE2, normalizeMarketQuery: normalizeMarketQuery2, resetPageOnFilterChange: resetPageOnFilterChange2, normalizeMarketResponse: normalizeMarketResponse2, registryNotice: registryNotice2, splitCategories: splitCategories2, marketNotice: marketNotice2, sortMergedItems: sortMergedItems2 } = (init_market_state(), __toCommonJS(market_state_exports));
486
589
  var { createMarkdown: createMarkdown2 } = (init_markdown(), __toCommonJS(markdown_exports));
487
590
  var { ExtLink, MdImg, renderMarkdown } = createMarkdown2(h);
488
591
  var { installedViewModel: installedViewModel2, registrySourceKey: registrySourceKey2 } = (init_installed_view(), __toCommonJS(installed_view_exports));
@@ -616,6 +719,30 @@ var ZH = {
616
719
  "market.page.info": "\u7B2C {page} / {pages} \u9875 \xB7 \u5171 {total} \u6761",
617
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",
618
721
  "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
+ "badge.community": "\u793E\u533A\u6536\u5F55",
726
+ "community.stale": "\u793E\u533A\u76EE\u5F55\u4E3A\u7F13\u5B58\u5FEB\u7167\uFF08\u663E\u793A\u7684\u4E0D\u662F\u6700\u65B0\u6570\u636E\uFF09",
727
+ "community.fallback": "\u6536\u5F55\u6E05\u5355\u4E0D\u53EF\u7528\uFF0C\u5F53\u524D\u5C55\u793A\u793E\u533A\u6E05\u5355\u6761\u76EE",
728
+ "detail.capabilities": "\u80FD\u529B\u62AB\u9732",
729
+ "detail.capabilities.unscanned": "\u672A\u626B\u63CF \u2260 \u672A\u68C0\u51FA",
730
+ "detail.redlines": "\u80FD\u529B\u7EA2\u7EBF",
731
+ "guard.blocked": "\u5B89\u88C5\u88AB\u88C5\u540E\u5B88\u536B\u62E6\u622A",
732
+ "guard.compstatus": "\u8865\u507F\u7EC8\u6001",
733
+ "guard.repairbasis": "\u4FEE\u590D\u4F9D\u636E",
734
+ "guard.restartsafenow": "\u53EF\u4EE5\u91CD\u542F DSH Web",
735
+ "guard.restartunsafe": "\u4FEE\u590D\u540E\u518D\u91CD\u542F\uFF08\u4E0D\u8981\u73B0\u5728\u4E00\u952E\u91CD\u542F\uFF09",
736
+ "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
+ "detail.screenshots": "\u622A\u56FE",
738
+ "installed.check.incomplete": "\u68C0\u67E5\u672A\u5B8C\u6210",
739
+ "settings.community": "\u793E\u533A\u6E05\u5355\uFF08awesome-dsh-plugin \u76EE\u5F55\uFF09",
740
+ "settings.community.none": "\u793E\u533A\u6E05\u5355\u672A\u542F\u7528\u6216\u4E0D\u53EF\u7528",
741
+ "settings.community.status": "\u72B6\u6001",
742
+ "settings.community.version": "\u76EE\u5F55\u7248\u672C",
743
+ "settings.community.route": "\u83B7\u53D6\u7EBF\u8DEF",
744
+ "settings.community.accepted": "\u6536\u5F55 / \u4E0A\u6E38",
745
+ "settings.community.displaced": "\u4E0E\u4E3B\u6E05\u5355\u91CD\u590D\u8BA9\u4F4D",
619
746
  "self.upgraded": "dsh-m \u5DF2\u66F4\u65B0\u5230 v{v}\uFF0C\u91CD\u542F\u540E\u751F\u6548",
620
747
  "self.failed": "\u81EA\u66F4\u65B0\u5931\u8D25\uFF1A{err}",
621
748
  "registry.refreshed": "\u6536\u5F55\u6E05\u5355\u5DF2\u5F3A\u5236\u5237\u65B0",
@@ -807,6 +934,30 @@ var EN = {
807
934
  "market.page.info": "Page {page} / {pages} \xB7 {total} listings",
808
935
  "market.perf": "200+ listings: latest versions are queried for the current page only (50 per page); use search/category filters",
809
936
  "notice.toolview.err": "Registry temporarily unavailable",
937
+ "cat.primaryonly": "Primary only",
938
+ "community.group": "Community",
939
+ "community.newgroup": "Community \xB7 New",
940
+ "badge.community": "Community",
941
+ "community.stale": "Community catalog served from cache (not the latest data)",
942
+ "community.fallback": "Registry unavailable \u2014 showing community listings",
943
+ "detail.capabilities": "Capabilities",
944
+ "detail.capabilities.unscanned": "Not scanned \u2260 not detected",
945
+ "detail.redlines": "Capability red lines",
946
+ "guard.blocked": "Install blocked by post-install guard",
947
+ "guard.compstatus": "Compensation status",
948
+ "guard.repairbasis": "Repair basis",
949
+ "guard.restartsafenow": "You can restart DSH Web now",
950
+ "guard.restartunsafe": "Fix before restarting (do not one-click restart now)",
951
+ "guard.noforce": "Guard blocks have no force channel \u2014 repair manually per the basis above",
952
+ "detail.screenshots": "Screenshots",
953
+ "installed.check.incomplete": "Check incomplete",
954
+ "settings.community": "Community catalog (awesome-dsh-plugin)",
955
+ "settings.community.none": "Community catalog disabled or unavailable",
956
+ "settings.community.status": "Status",
957
+ "settings.community.version": "Catalog version",
958
+ "settings.community.route": "Route",
959
+ "settings.community.accepted": "Accepted / upstream",
960
+ "settings.community.displaced": "Displaced (duplicate of primary)",
810
961
  "self.upgraded": "dsh-m updated to v{v} \u2014 restart to take effect",
811
962
  "self.failed": "Self-update failed: {err}",
812
963
  "registry.refreshed": "Registry force-refreshed",
@@ -1031,6 +1182,9 @@ async function api(method, params, signal) {
1031
1182
  if (!res.ok || data.ok === false) {
1032
1183
  const err = new Error(data.error || `API ${res.status}`);
1033
1184
  if (data && typeof data === "object" && data.issue) err.issue = data.issue;
1185
+ if (data && typeof data === "object" && data.kind && Array.isArray(data.violations)) {
1186
+ err.guard = { kind: data.kind, violations: data.violations, compensation: data.compensation, needsRestart: data.needsRestart === true, restartSafe: data.restartSafe === true, repairBasis: data.repairBasis };
1187
+ }
1034
1188
  throw err;
1035
1189
  }
1036
1190
  return data;
@@ -1069,6 +1223,7 @@ function useMarketData() {
1069
1223
  const params = {
1070
1224
  query: nextQuery.query || void 0,
1071
1225
  category: nextQuery.category || void 0,
1226
+ primaryOnly: nextQuery.primaryOnly === true ? true : void 0,
1072
1227
  offset: nextQuery.offset,
1073
1228
  limit: nextQuery.limit,
1074
1229
  ...force ? { force: true } : {}
@@ -1114,6 +1269,19 @@ function Icon({ entry }) {
1114
1269
  function Spin() {
1115
1270
  return h("span", { className: "dshm-spin" });
1116
1271
  }
1272
+ function safeScreenshots(entry) {
1273
+ const list = Array.isArray(entry && entry.screenshots) ? entry.screenshots : [];
1274
+ return list.filter((u) => typeof u === "string" && u.length <= 2048).map((u) => {
1275
+ try {
1276
+ const parsed = new URL(u);
1277
+ if (parsed.protocol !== "https:") return null;
1278
+ if (parsed.hostname === "github.com" || parsed.hostname.endsWith(".githubusercontent.com")) return u;
1279
+ return null;
1280
+ } catch {
1281
+ return null;
1282
+ }
1283
+ }).filter(Boolean).slice(0, 8);
1284
+ }
1117
1285
  function officialLinks({ npm, github, homepage }) {
1118
1286
  const links = [];
1119
1287
  if (github) links.push(["GitHub", `https://github.com/${github}`]);
@@ -1234,14 +1402,14 @@ function MarketTab({ notify, market, onMutation }) {
1234
1402
  const [busyId, setBusyId] = useState(null);
1235
1403
  const [qInput, setQInput] = useState(query.query);
1236
1404
  const debounceRef = useRef(null);
1237
- const items = data && data.items || [];
1405
+ const items = sortMergedItems2(data && data.items || []);
1238
1406
  const total = data && data.total || 0;
1239
1407
  const limit = data && data.limit || MARKET_PAGE_SIZE2;
1240
1408
  const offset = data && data.offset || 0;
1241
1409
  const page = total > 0 ? Math.floor(offset / limit) + 1 : 1;
1242
1410
  const pages = total > 0 ? Math.max(1, Math.ceil(total / limit)) : 1;
1243
1411
  const counts = data && data.categoryCounts || {};
1244
- const notice = data ? registryNotice2(data.registryState, total) : null;
1412
+ const notice = data ? marketNotice2(data.registryState, data.community) : null;
1245
1413
  const onSearchInput = (value) => {
1246
1414
  setQInput(value);
1247
1415
  if (debounceRef.current) clearTimeout(debounceRef.current);
@@ -1262,7 +1430,18 @@ function MarketTab({ notify, market, onMutation }) {
1262
1430
  installDone(res);
1263
1431
  await (onMutation ? onMutation() : reload(false));
1264
1432
  } catch (e) {
1265
- if (e && e.issue) {
1433
+ if (e && e.guard) {
1434
+ notify({
1435
+ kind: "err",
1436
+ text: [
1437
+ lookup("guard.blocked"),
1438
+ `${lookup("guard.compstatus")}: ${e.guard.compensation?.status || "\u2014"}\uFF08${e.guard.compensation?.note || ""}\uFF09`,
1439
+ e.guard.repairBasis ? `${lookup("guard.repairbasis")}: ${e.guard.repairBasis}` : null,
1440
+ lookup("guard.noforce"),
1441
+ e.guard.restartSafe ? lookup("guard.restartsafenow") : lookup("guard.restartunsafe")
1442
+ ].filter(Boolean).join(" | ")
1443
+ });
1444
+ } else if (e && e.issue) {
1266
1445
  setCompatConfirm({ it, version, issue: e.issue });
1267
1446
  } else {
1268
1447
  notify({ kind: "err", text: lookup("failed.install", { err: e && e.message || e }) });
@@ -1316,6 +1495,8 @@ function MarketTab({ notify, market, onMutation }) {
1316
1495
  { className: notice.key === "notice.unavailable" ? "dshm-err" : "dshm-hint" },
1317
1496
  lookup(notice.key, { count: notice.count })
1318
1497
  ) : null,
1498
+ notice && notice.communityFallback ? h("div", { className: "dshm-hint" }, lookup("community.fallback")) : null,
1499
+ notice && notice.communityStale ? h("div", { className: "dshm-hint" }, lookup("community.stale")) : null,
1319
1500
  total > 200 ? h("div", { className: "dshm-hint" }, lookup("market.perf")) : null,
1320
1501
  h(
1321
1502
  "div",
@@ -1331,7 +1512,8 @@ function MarketTab({ notify, market, onMutation }) {
1331
1512
  h(
1332
1513
  "div",
1333
1514
  { className: "dshm-chips" },
1334
- h("button", { className: `dshm-chip${query.category === null ? " on" : ""}`, onClick: () => updateQuery({ category: null, offset: 0 }) }, lookup("cat.all")),
1515
+ h("button", { className: `dshm-chip${query.category === null ? " on" : ""}`, onClick: () => updateQuery({ category: null, primaryOnly: false, offset: 0 }) }, lookup("cat.all")),
1516
+ h("button", { className: `dshm-chip${query.primaryOnly === true ? " on" : ""}`, onClick: () => updateQuery({ primaryOnly: query.primaryOnly !== true, offset: 0 }) }, lookup("cat.primaryonly")),
1335
1517
  CATEGORIES2.map((key) => {
1336
1518
  const n = typeof counts[key] === "number" ? counts[key] : 0;
1337
1519
  return h(
@@ -1341,6 +1523,31 @@ function MarketTab({ notify, market, onMutation }) {
1341
1523
  );
1342
1524
  })
1343
1525
  ),
1526
+ (() => {
1527
+ const groups = splitCategories2(counts);
1528
+ if (!groups.community.length && !groups.unknown.length) return null;
1529
+ const chip = (c) => h(
1530
+ "button",
1531
+ { key: c.id, className: `dshm-chip${query.category === c.id ? " on" : ""}`, onClick: () => updateQuery({ category: query.category === c.id ? null : c.id, offset: 0 }) },
1532
+ `${c.label}${c.count ? ` ${c.count}` : ""}`
1533
+ );
1534
+ return h(
1535
+ React.Fragment,
1536
+ null,
1537
+ groups.community.length ? h(
1538
+ "div",
1539
+ { className: "dshm-chips" },
1540
+ h("span", { className: "dshm-hint" }, lookup("community.group")),
1541
+ ...groups.community.map(chip)
1542
+ ) : null,
1543
+ groups.unknown.length ? h(
1544
+ "div",
1545
+ { className: "dshm-chips" },
1546
+ h("span", { className: "dshm-hint" }, lookup("community.newgroup")),
1547
+ ...groups.unknown.map(chip)
1548
+ ) : null
1549
+ );
1550
+ })(),
1344
1551
  busyId ? h(ProgressLine, { key: "prog" }) : null,
1345
1552
  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(
1346
1553
  React.Fragment,
@@ -1355,6 +1562,7 @@ function MarketTab({ notify, market, onMutation }) {
1355
1562
  badges: [
1356
1563
  it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, lookup("badge.update")) : null,
1357
1564
  it.installed ? h("span", { className: "dshm-badge", key: "i" }, lookup("badge.installed")) : null,
1565
+ it.community === true ? h("span", { className: "dshm-badge info", key: "c" }, lookup("badge.community")) : null,
1358
1566
  h("span", { className: "dshm-badge info", key: "s" }, it.source === "npm" ? "npm" : "github")
1359
1567
  ],
1360
1568
  desc: it.description,
@@ -1372,7 +1580,13 @@ function MarketTab({ notify, market, onMutation }) {
1372
1580
  [lookup("detail.latest"), it.latestVersion ? `v${it.latestVersion}` : it.latestTag ? it.latestTag : it.latestSha ? it.latestSha : it.latestError || "\u2014"],
1373
1581
  [lookup("detail.installed"), it.installedPkg ? `${it.installedPkg} v${it.installedVersion || "?"}` : lookup("installed.none")],
1374
1582
  [lookup("detail.tags"), (it.tags || []).join(", ") || "\u2014"],
1375
- it.latestError ? [lookup("version.failed"), it.latestError] : null
1583
+ it.latestError ? [lookup("version.failed"), it.latestError] : null,
1584
+ it.community === true ? [lookup("detail.capabilities"), Array.isArray(it.capabilities) && it.capabilities.length ? `${it.capabilities.join(", ")}${Array.isArray(it.capabilityRedLines) && it.capabilityRedLines.length ? `\uFF1B${lookup("detail.redlines")}: ${it.capabilityRedLines.join("; ")}` : ""}` : lookup("detail.capabilities.unscanned")] : null,
1585
+ it.community === true && safeScreenshots(it).length ? [lookup("detail.screenshots"), h(
1586
+ "div",
1587
+ { className: "dshm-chips" },
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
1376
1590
  ]),
1377
1591
  actions: [
1378
1592
  it.installed ? h("span", { className: "dshm-hint", key: "hint" }, lookup("manage.hint")) : h(
@@ -1516,7 +1730,20 @@ function InstalledTab({ notify, installed, onMutation }) {
1516
1730
  });
1517
1731
  await (onMutation ? onMutation() : reload());
1518
1732
  } catch (e) {
1519
- notify({ kind: "err", text: lookup("failed.upgrade", { err: e && e.message || e }) });
1733
+ if (e && e.guard) {
1734
+ notify({
1735
+ kind: "err",
1736
+ text: [
1737
+ lookup("guard.blocked"),
1738
+ `${lookup("guard.compstatus")}: ${e.guard.compensation?.status || "\u2014"}\uFF08${e.guard.compensation?.note || ""}\uFF09`,
1739
+ e.guard.repairBasis ? `${lookup("guard.repairbasis")}: ${e.guard.repairBasis}` : null,
1740
+ lookup("guard.noforce"),
1741
+ e.guard.restartSafe ? lookup("guard.restartsafenow") : lookup("guard.restartunsafe")
1742
+ ].filter(Boolean).join(" | ")
1743
+ });
1744
+ } else {
1745
+ notify({ kind: "err", text: lookup("failed.upgrade", { err: e && e.message || e }) });
1746
+ }
1520
1747
  } finally {
1521
1748
  setBusyPkg(null);
1522
1749
  }
@@ -1548,15 +1775,16 @@ function InstalledTab({ notify, installed, onMutation }) {
1548
1775
  }),
1549
1776
  badges: [
1550
1777
  it.outdated ? h("span", { className: "dshm-badge warn", key: "u" }, `\u2B06 ${vm.latestLabel}`.trim()) : null,
1551
- it.registryId ? h("span", { className: "dshm-badge", key: "r" }, lookup("badge.market")) : h("span", { className: "dshm-badge info", key: "r" }, lookup("badge.nonmarket"))
1778
+ it.community === true ? h("span", { className: "dshm-badge info", key: "r" }, lookup("badge.community")) : it.registryId ? h("span", { className: "dshm-badge", key: "r" }, lookup("badge.market")) : h("span", { className: "dshm-badge info", key: "r" }, lookup("badge.nonmarket"))
1552
1779
  ],
1553
1780
  desc: it.description || "\uFF08\u65E0\u63CF\u8FF0\uFF09",
1554
1781
  sub: [
1555
1782
  h(PhaseDot, { key: "dot", vm: tvm }),
1556
1783
  vm.phaseKey ? lookup(vm.phaseKey) : lookup(vm.enabledLabelKey),
1557
1784
  `v${it.version || "?"}`,
1558
- vm.sourceLabelKey ? lookup(vm.sourceLabelKey) : it.source
1559
- ].map((part, i) => i === 0 ? part : [" \xB7 ", part]).flat(),
1785
+ vm.sourceLabelKey ? lookup(vm.sourceLabelKey) : it.source,
1786
+ vm.latestIssue ? lookup("installed.check.incomplete") : null
1787
+ ].filter(Boolean).map((part, i) => i === 0 ? part : [" \xB7 ", part]).flat(),
1560
1788
  links: h(LinksRow, {
1561
1789
  npm: it.source === "npm" ? it.pkg : null,
1562
1790
  github: vm.githubRepo
@@ -1569,6 +1797,7 @@ function InstalledTab({ notify, installed, onMutation }) {
1569
1797
  [lookup("detail.latest"), vm.latestLabelDetail],
1570
1798
  [lookup("detail.listed"), it.registryId || lookup("detail.listed.no")],
1571
1799
  [lookup("detail.path"), it.path],
1800
+ vm.latestIssue ? [lookup("installed.check.incomplete"), vm.latestIssue.note] : null,
1572
1801
  vm.guard.warnKey ? [lookup("detail.note"), lookup(vm.guard.warnKey, vm.guard.warnParams)] : null
1573
1802
  ]),
1574
1803
  actions: [
@@ -1700,9 +1929,27 @@ function SettingsTab({ notify, onRegistryChanged }) {
1700
1929
  };
1701
1930
  const snap = cfgData || {};
1702
1931
  const state = snap.registryState || (reg.data ? reg.data.registryState : null) || null;
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")]];
1703
1940
  return h(
1704
1941
  React.Fragment,
1705
1942
  null,
1943
+ Section(
1944
+ lookup("settings.community"),
1945
+ ...communityRows.map(([k, v]) => h(
1946
+ "div",
1947
+ { key: k, className: "dshm-row", style: { justifyContent: "space-between" } },
1948
+ h("span", { className: "dshm-hint" }, k),
1949
+ h("span", null, v)
1950
+ )),
1951
+ communitySummary && Array.isArray(communitySummary.errors) && communitySummary.errors.length ? h("div", { className: "dshm-err" }, communitySummary.errors.join("\uFF1B")) : null
1952
+ ),
1706
1953
  Section(
1707
1954
  lookup("settings.registry"),
1708
1955
  h(
@@ -1810,7 +2057,20 @@ function SettingsTab({ notify, onRegistryChanged }) {
1810
2057
  notify({ kind: "ok", text: lookup("self.upgraded", { v: res.version }), needsRestart: true });
1811
2058
  await self.reload();
1812
2059
  } catch (e) {
1813
- notify({ kind: "err", text: lookup("failed.selfupdate", { err: e && e.message || 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
+ }
1814
2074
  } finally {
1815
2075
  setUpgrading(false);
1816
2076
  }
@@ -2054,13 +2314,17 @@ function registerSlot(slots, options, component) {
2054
2314
  }
2055
2315
  function ToolCardRow({ it, onInstalled }) {
2056
2316
  const [busy, setBusy] = useState(false);
2317
+ const [guardNote, setGuardNote] = useState(null);
2057
2318
  const install = async (e) => {
2058
2319
  e.stopPropagation();
2059
2320
  setBusy(true);
2060
2321
  try {
2061
2322
  const res = await api("install", { id: it.id });
2062
2323
  onInstalled({ ...it, installed: true, installedPkg: res.pkg, installedVersion: res.version });
2063
- } catch {
2324
+ } catch (e2) {
2325
+ if (e2 && e2.guard) {
2326
+ setGuardNote(`${lookup("guard.blocked")} \xB7 ${e2.guard.compensation?.status || "\u2014"} \xB7 ${lookup("guard.noforce")}`);
2327
+ }
2064
2328
  } finally {
2065
2329
  setBusy(false);
2066
2330
  }
@@ -2080,6 +2344,7 @@ function ToolCardRow({ it, onInstalled }) {
2080
2344
  it.installed ? h("span", { className: "dshm-badge" }, "\u5DF2\u5B89\u88C5") : null
2081
2345
  ),
2082
2346
  h("div", { className: "dshm-desc" }, it.description),
2347
+ guardNote ? h("div", { className: "dshm-err" }, guardNote) : null,
2083
2348
  h("div", { className: "dshm-sub" }, `${it.id} \xB7 ${(it.tags || []).join("\u3001") || it.category}`),
2084
2349
  h(LinksRow, { npm: it.npm, github: it.github, homepage: it.homepage }),
2085
2350
  h(