dsh-plugin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,529 @@
1
+ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2
+
3
+
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ //#region \0dsh-css:src/client/Section.module.css.mjs
9
+ const css = ".XlLbKa_root{min-width:0;height:100%;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;gap:8px;display:flex}.XlLbKa_header{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;padding:2px 2px 0;display:flex}.XlLbKa_brand{align-items:center;gap:10px;min-width:0;display:flex}.XlLbKa_brandText{flex-direction:column;gap:1px;min-width:0;display:flex}.XlLbKa_title{margin:0;font-size:14px;font-weight:600;line-height:20px}.XlLbKa_tagline{color:var(--dsw-alias-label-tertiary,#8b93a1);-webkit-line-clamp:2;-webkit-box-orient:vertical;margin:0;font-size:12px;line-height:18px;display:-webkit-box;overflow:hidden}.XlLbKa_controls{flex-shrink:0;align-items:center;gap:8px;display:flex}.XlLbKa_adBanner{color:#fff;cursor:pointer;background:linear-gradient(90deg,#4f46e5 0%,#7c3aed 100%);border:1px solid #8b5cf68c;border-radius:8px;align-items:center;gap:8px;padding:7px 12px;text-decoration:none;transition:filter .15s,box-shadow .15s;display:flex;box-shadow:0 2px 10px #4f46e547}.XlLbKa_adBanner:hover{filter:brightness(1.1);box-shadow:0 4px 16px #7c3aed6b}.XlLbKa_adBadge{letter-spacing:.04em;color:#fff;white-space:nowrap;background:#ffffff29;border:1px solid #ffffff8c;border-radius:4px;flex-shrink:0;padding:3px 6px;font-size:10px;font-weight:700;line-height:1}.XlLbKa_adText{text-overflow:ellipsis;white-space:nowrap;color:#fff;min-width:0;font-size:12px;line-height:18px;overflow:hidden}.XlLbKa_adArrow{color:#fff;flex-shrink:0;margin-left:auto;font-size:13px}.XlLbKa_search{width:190px;color:inherit;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:0 0;border-radius:6px;outline:none;padding:4px 9px;font-size:12px;line-height:18px;transition:border-color .12s}.XlLbKa_search::placeholder{color:var(--dsw-alias-label-tertiary,#8b93a1)}.XlLbKa_search:focus{border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.XlLbKa_sort{color:var(--dsw-alias-label-secondary,#6b7280);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);cursor:pointer;background:0 0;border-radius:6px;outline:none;padding:4px 8px;font-size:12px;line-height:18px}.XlLbKa_sort:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.XlLbKa_langBtn{color:var(--dsw-alias-label-secondary,#6b7280);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);cursor:pointer;background:0 0;border-radius:6px;flex-shrink:0;padding:4px 9px;font-size:12px;line-height:18px;transition:color .12s,border-color .12s}.XlLbKa_langBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.XlLbKa_openBtn{color:var(--dsw-alias-label-secondary,#6b7280);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);cursor:pointer;white-space:nowrap;background:0 0;border-radius:6px;flex-shrink:0;align-items:center;gap:4px;padding:3px 9px;font-size:12px;font-weight:500;line-height:20px;text-decoration:none;transition:color .12s,border-color .12s;display:inline-flex}.XlLbKa_openBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.XlLbKa_openArrow{font-size:12px;line-height:1}.XlLbKa_tabs{border-bottom:1px solid var(--dsw-alias-border-l1,#eceef1);flex-wrap:wrap;align-items:center;gap:6px;padding:2px 2px 8px;display:flex}.XlLbKa_tab,.XlLbKa_tabActive{cursor:pointer;border:1px solid #0000;border-radius:999px;flex-shrink:0;align-items:center;gap:6px;padding:3px 10px;font-size:12px;line-height:18px;transition:color .12s,background .12s,border-color .12s;display:inline-flex}.XlLbKa_tab{color:var(--dsw-alias-label-secondary,#6b7280);border-color:var(--dsw-alias-border-l2,#e5e7eb);background:0 0}.XlLbKa_tab:hover{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-interactive-bg-hover,#80808014);border-color:var(--dsw-alias-border-l1,#eceef1)}.XlLbKa_tabActive{color:#fff;background:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.XlLbKa_tabCount,.XlLbKa_tabCountActive{text-align:center;border-radius:999px;min-width:16px;padding:0 5px;font-size:10px;line-height:14px}.XlLbKa_tabCount{color:var(--dsw-alias-label-tertiary,#8b93a1);background:var(--dsw-alias-bg-layer-2,#8080801a)}.XlLbKa_tabActive .XlLbKa_tabCountActive{color:#fff;background:#ffffff38}.XlLbKa_body{flex-direction:column;flex:1;min-height:0;display:flex}.XlLbKa_list{flex-direction:column;flex:1;gap:6px;min-height:0;padding:2px 4px 4px 2px;display:flex;overflow-y:auto}.XlLbKa_card{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#8080800f);border-radius:8px;justify-content:space-between;align-items:stretch;gap:12px;padding:9px 12px;transition:border-color .12s,background .12s;display:flex}.XlLbKa_card:hover{border-color:var(--dsw-alias-brand-primary,#4f6ef7);background:var(--dsw-alias-bg-layer-2,#8080801a)}.XlLbKa_cardMain{flex-direction:column;gap:4px;min-width:0;display:flex}.XlLbKa_cardHead{align-items:center;gap:6px;min-width:0;display:flex}.XlLbKa_cardTitle{white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:600;line-height:18px;overflow:hidden}.XlLbKa_categoryBadge,.XlLbKa_verified{border:1px solid #0000;border-radius:4px;flex-shrink:0;padding:0 6px;font-size:10px;line-height:16px}.XlLbKa_categoryBadge{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-button-ghost-active-border,#4f6ef740)}.XlLbKa_verified{color:var(--dsw-alias-state-success-primary,#1a7f37);border-color:var(--dsw-alias-state-success-secondary,#1a7f374d)}.XlLbKa_desc{color:var(--dsw-alias-label-secondary,#6b7280);-webkit-line-clamp:2;-webkit-box-orient:vertical;margin:0;font-size:12px;line-height:18px;display:-webkit-box;overflow:hidden}.XlLbKa_topics{flex-wrap:wrap;align-items:center;gap:4px;min-width:0;display:flex}.XlLbKa_topic{color:var(--dsw-alias-label-tertiary,#8b93a1);background:var(--dsw-alias-bg-layer-2,#8080801a);white-space:nowrap;border-radius:4px;flex-shrink:0;padding:0 6px;font-size:10px;line-height:16px}.XlLbKa_cardSide{flex-direction:column;flex-shrink:0;justify-content:space-between;align-items:flex-end;gap:6px;display:flex}.XlLbKa_stats{flex-direction:column;align-items:flex-end;gap:1px;display:flex}.XlLbKa_star{color:var(--dsw-alias-state-warn-primary,#b8860b);white-space:nowrap;font-size:11px;line-height:16px}.XlLbKa_fork{color:var(--dsw-alias-label-tertiary,#8b93a1);white-space:nowrap;font-size:11px;line-height:16px}.XlLbKa_date{color:var(--dsw-alias-label-tertiary,#8b93a1);white-space:nowrap;font-size:10px;line-height:14px}.XlLbKa_installBtn,.XlLbKa_installBtnCopied,.XlLbKa_detailBtn{cursor:pointer;border-radius:6px;padding:2px 10px;font-size:11px;line-height:18px;transition:color .12s,border-color .12s,background .12s}.XlLbKa_installBtn{color:var(--dsw-alias-label-secondary,#6b7280);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:0 0}.XlLbKa_installBtn:hover{color:#4f6ef7;border-color:#4f6ef7}.XlLbKa_detailBtn{color:#4f6ef7;background:0 0;border:1px solid #4f6ef7;align-items:center;font-weight:500;text-decoration:none;display:inline-flex}.XlLbKa_detailBtn:hover{color:#fff;background:#4f6ef7}.XlLbKa_actions{align-items:center;gap:6px;display:flex}.XlLbKa_installBtnCopied{color:#fff;background:#4f6ef7;border:1px solid #4f6ef7}.XlLbKa_toast{z-index:1000;color:#f9fafb;pointer-events:none;white-space:nowrap;background:#111827;border:1px solid #ffffff1a;border-radius:8px;padding:10px 16px;font-size:12px;line-height:18px;animation:.22s ease-out XlLbKa_toastIn;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 6px 24px #00000038}@keyframes XlLbKa_toastIn{0%{opacity:0;transform:translate(-50%,-44%)}to{opacity:1;transform:translate(-50%,-50%)}}.XlLbKa_state{text-align:center;min-height:160px;color:var(--dsw-alias-label-tertiary,#8b93a1);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px;font-size:12px;line-height:18px;display:flex}.XlLbKa_stateTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;font-weight:600}.XlLbKa_stateDesc{max-width:420px}.XlLbKa_retryBtn{color:var(--dsw-alias-label-secondary,#6b7280);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);cursor:pointer;background:0 0;border-radius:6px;margin-top:4px;padding:4px 12px;font-size:12px;line-height:18px}.XlLbKa_retryBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.XlLbKa_footer{border-top:1px solid var(--dsw-alias-border-l1,#eceef1);flex-shrink:0;justify-content:space-between;align-items:center;gap:8px;padding:6px 4px 0;display:flex}.XlLbKa_footText{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:11px;line-height:16px}.XlLbKa_footLink{color:var(--dsw-alias-brand-primary,#4f6ef7);white-space:nowrap;font-size:11px;line-height:16px;text-decoration:none}.XlLbKa_footLink:hover{text-decoration:underline}";
10
+ const tagId = "dsh-plugin/Section.module.css";
11
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
12
+ const tag = document.createElement("style");
13
+ tag.dataset.plugin = "dsh-plugin";
14
+ tag.dataset.pluginCss = tagId;
15
+ tag.textContent = css;
16
+ document.head.appendChild(tag);
17
+ }
18
+ var Section_module_css_default = {
19
+ "date": "XlLbKa_date",
20
+ "controls": "XlLbKa_controls",
21
+ "sort": "XlLbKa_sort",
22
+ "categoryBadge": "XlLbKa_categoryBadge",
23
+ "detailBtn": "XlLbKa_detailBtn",
24
+ "card": "XlLbKa_card",
25
+ "desc": "XlLbKa_desc",
26
+ "footLink": "XlLbKa_footLink",
27
+ "adBadge": "XlLbKa_adBadge",
28
+ "adText": "XlLbKa_adText",
29
+ "topics": "XlLbKa_topics",
30
+ "actions": "XlLbKa_actions",
31
+ "root": "XlLbKa_root",
32
+ "brandText": "XlLbKa_brandText",
33
+ "list": "XlLbKa_list",
34
+ "installBtn": "XlLbKa_installBtn",
35
+ "retryBtn": "XlLbKa_retryBtn",
36
+ "cardSide": "XlLbKa_cardSide",
37
+ "state": "XlLbKa_state",
38
+ "stateDesc": "XlLbKa_stateDesc",
39
+ "brand": "XlLbKa_brand",
40
+ "tabs": "XlLbKa_tabs",
41
+ "header": "XlLbKa_header",
42
+ "cardMain": "XlLbKa_cardMain",
43
+ "body": "XlLbKa_body",
44
+ "search": "XlLbKa_search",
45
+ "tagline": "XlLbKa_tagline",
46
+ "adArrow": "XlLbKa_adArrow",
47
+ "cardHead": "XlLbKa_cardHead",
48
+ "title": "XlLbKa_title",
49
+ "openBtn": "XlLbKa_openBtn",
50
+ "openArrow": "XlLbKa_openArrow",
51
+ "verified": "XlLbKa_verified",
52
+ "tabCount": "XlLbKa_tabCount",
53
+ "topic": "XlLbKa_topic",
54
+ "stateTitle": "XlLbKa_stateTitle",
55
+ "footText": "XlLbKa_footText",
56
+ "fork": "XlLbKa_fork",
57
+ "langBtn": "XlLbKa_langBtn",
58
+ "installBtnCopied": "XlLbKa_installBtnCopied",
59
+ "toast": "XlLbKa_toast",
60
+ "toastIn": "XlLbKa_toastIn",
61
+ "adBanner": "XlLbKa_adBanner",
62
+ "cardTitle": "XlLbKa_cardTitle",
63
+ "stats": "XlLbKa_stats",
64
+ "star": "XlLbKa_star",
65
+ "footer": "XlLbKa_footer",
66
+ "tab": "XlLbKa_tab",
67
+ "tabCountActive": "XlLbKa_tabCountActive",
68
+ "tabActive": "XlLbKa_tabActive"
69
+ };
70
+ //#endregion
71
+ //#region src/client/locales.ts
72
+ /** zh/en dictionaries for the DSH-Plugin Hub settings section. */
73
+ const zh = {
74
+ nav: "插件中心",
75
+ title: "DSH-Plugin Hub for DeepSeek Harness",
76
+ tagline: "DSH plugin 插件中心:收录 {total} 个 DeepSeek Harness 插件,人工精选验证 {verified},每日更新 · 免费安装",
77
+ adBadge: "推荐",
78
+ ad: "DSH-Plugin 插件中心:收录 {total} 个 DeepSeek Harness 插件,其中 {verified} 人工精选验证,每日更新,免费按分类浏览、搜索、下载与安装,人工验证、来源可溯",
79
+ search: "搜索插件名称、描述、标签…",
80
+ all: "全部",
81
+ sortStars: "Star 最多",
82
+ sortUpdated: "最近更新",
83
+ sortNewest: "最新收录",
84
+ open: "在浏览器中打开",
85
+ openHint: "打开 dsh-plugin.org",
86
+ openBtn: "DSH-Plugin 官网",
87
+ toEn: "Switch to English",
88
+ fork: "Fork",
89
+ loading: "正在加载插件数据…",
90
+ failed: "插件数据加载失败",
91
+ failedDesc: "请稍后重试,或点击右上角按钮在浏览器中打开插件中心。",
92
+ retry: "重试",
93
+ copy: "复制安装命令",
94
+ toastCopied: "安装命令已复制,去 dsh 终端粘贴即可安装",
95
+ detail: "打开详情",
96
+ noResult: "没有找到匹配的插件",
97
+ noResultDesc: "换个关键词试试,或前往网站浏览全部插件。",
98
+ today: "今天更新",
99
+ daysAgo: "更新于 {days} 天前",
100
+ monthsAgo: "更新于 {months} 个月前",
101
+ yearsAgo: "更新于 {years} 年前",
102
+ pluginsTotal: "共 {n} 个插件",
103
+ filteredCount: "匹配 {n} 个",
104
+ more: "还有 {n} 个插件,去网站查看全部",
105
+ browseAll: "浏览全部 {n} 个插件",
106
+ verified: "已验证",
107
+ empty: "该分类暂无插件",
108
+ dataFrom: "数据源 dsh-plugin.org · 每日人工更新"
109
+ };
110
+ const en = {
111
+ nav: "Plugin Hub",
112
+ title: "DSH-Plugin Hub for DeepSeek Harness",
113
+ tagline: "DSH plugin marketplace · {total} DeepSeek Harness plugins indexed · {verified} human-verified · updated daily · free to install",
114
+ adBadge: "Featured",
115
+ ad: "DSH-Plugin Hub: {total} DeepSeek Harness plugins indexed, {verified} of which are human-verified. Updated daily — browse, search & install for free.",
116
+ search: "Search plugins by name, description, tags…",
117
+ all: "All",
118
+ sortStars: "Most stars",
119
+ sortUpdated: "Recently updated",
120
+ sortNewest: "Newest added",
121
+ open: "Open in browser",
122
+ openHint: "Open dsh-plugin.org",
123
+ openBtn: "dsh-plugin.org",
124
+ toZh: "Switch to Chinese",
125
+ fork: "Fork",
126
+ loading: "Loading plugin data…",
127
+ failed: "Failed to load plugin data",
128
+ failedDesc: "Please retry, or open the hub in your browser with the button above.",
129
+ retry: "Retry",
130
+ copy: "Copy install command",
131
+ toastCopied: "Install command copied — paste it in your dsh terminal",
132
+ detail: "Open details",
133
+ noResult: "No plugins match your search",
134
+ noResultDesc: "Try another keyword, or browse all plugins on the website.",
135
+ today: "updated today",
136
+ daysAgo: "updated {days}d ago",
137
+ monthsAgo: "updated {months}mo ago",
138
+ yearsAgo: "updated {years}y ago",
139
+ pluginsTotal: "{n} plugins",
140
+ filteredCount: "{n} matched",
141
+ more: "{n} more on the website",
142
+ browseAll: "Browse all {n} plugins",
143
+ verified: "Verified",
144
+ empty: "No plugins in this category yet",
145
+ dataFrom: "Data from dsh-plugin.org · curated daily"
146
+ };
147
+ //#endregion
148
+ //#region src/client/index.tsx
149
+ /**
150
+ * dsh-plugin client: a native "Plugin Hub" settings section that renders
151
+ * the community marketplace (dsh-plugin.org) as a first-class catalog —
152
+ * category tabs (11 categories + All), search, sorting and one-click
153
+ * install-command copy. Plugin data is fetched live from dsh-plugin.org,
154
+ * so the browser bundle stays small and always in sync with the site.
155
+ *
156
+ * Built by tsdown into the __ModuleLoader__ factory bundle at client/client.js.
157
+ */
158
+ const NS = "dsh-plugin";
159
+ const SITE_URL = "https://dsh-plugin.org/";
160
+ const CATEGORY_ORDER = [
161
+ "interface",
162
+ "session",
163
+ "memory",
164
+ "tools",
165
+ "agent",
166
+ "workflow",
167
+ "integration",
168
+ "model",
169
+ "dev",
170
+ "knowledge",
171
+ "fun"
172
+ ];
173
+ /**
174
+ * 归一化在线 API(dsh-plugin.org/api/plugins.{lang}.json)返回的短 key 结构
175
+ * (s/o/n/c/t/f/d/r/v/u/a/sg/fk),统一为 HubPlugin,保证渲染逻辑只认一种结构。
176
+ */
177
+ function normalize(raw) {
178
+ if (typeof raw.s === "string") return {
179
+ slug: raw.s,
180
+ ownerSlug: typeof raw.o === "string" ? raw.o : void 0,
181
+ displayName: typeof raw.n === "string" ? raw.n : void 0,
182
+ category: typeof raw.c === "string" ? raw.c : void 0,
183
+ topics: Array.isArray(raw.t) ? raw.t : void 0,
184
+ features: Array.isArray(raw.f) ? raw.f : void 0,
185
+ description: typeof raw.d === "string" ? raw.d : void 0,
186
+ source: typeof raw.r === "string" ? { repo: raw.r } : void 0,
187
+ compatibility: typeof raw.v === "string" ? { status: raw.v } : void 0,
188
+ dates: {
189
+ repoUpdatedAt: typeof raw.u === "string" ? raw.u : void 0,
190
+ addedAt: typeof raw.a === "string" ? raw.a : void 0
191
+ },
192
+ stats: {
193
+ stargazers_count: typeof raw.sg === "number" ? raw.sg : void 0,
194
+ forks_count: typeof raw.fk === "number" ? raw.fk : void 0
195
+ }
196
+ };
197
+ return raw;
198
+ }
199
+ const CATEGORY_LABELS = {
200
+ interface: {
201
+ zh: "界面与体验",
202
+ en: "UI & Experience"
203
+ },
204
+ session: {
205
+ zh: "会话与消息",
206
+ en: "Sessions & Messages"
207
+ },
208
+ memory: {
209
+ zh: "记忆与上下文",
210
+ en: "Memory & Context"
211
+ },
212
+ tools: {
213
+ zh: "工具与能力",
214
+ en: "Tools & Capabilities"
215
+ },
216
+ agent: {
217
+ zh: "技能与智能体",
218
+ en: "Skills & Agents"
219
+ },
220
+ workflow: {
221
+ zh: "工作流与自动化",
222
+ en: "Workflow & Automation"
223
+ },
224
+ integration: {
225
+ zh: "集成与连接",
226
+ en: "Integrations & Connections"
227
+ },
228
+ model: {
229
+ zh: "模型与推理",
230
+ en: "Models & Reasoning"
231
+ },
232
+ dev: {
233
+ zh: "开发与运维",
234
+ en: "Development & Operations"
235
+ },
236
+ knowledge: {
237
+ zh: "数据与知识",
238
+ en: "Data & Knowledge"
239
+ },
240
+ fun: {
241
+ zh: "娱乐",
242
+ en: "Entertainment"
243
+ }
244
+ };
245
+ const CATEGORY_SHORT_LABELS = {
246
+ interface: {
247
+ zh: "界面体验",
248
+ en: "UI Exp"
249
+ },
250
+ session: {
251
+ zh: "会话消息",
252
+ en: "Sessions"
253
+ },
254
+ memory: {
255
+ zh: "记忆上下文",
256
+ en: "Memory"
257
+ },
258
+ tools: {
259
+ zh: "工具能力",
260
+ en: "Tools"
261
+ },
262
+ agent: {
263
+ zh: "技能智能体",
264
+ en: "Agents"
265
+ },
266
+ workflow: {
267
+ zh: "工作流",
268
+ en: "Workflow"
269
+ },
270
+ integration: {
271
+ zh: "集成连接",
272
+ en: "Integrations"
273
+ },
274
+ model: {
275
+ zh: "模型推理",
276
+ en: "Models"
277
+ },
278
+ dev: {
279
+ zh: "开发运维",
280
+ en: "Dev"
281
+ },
282
+ knowledge: {
283
+ zh: "数据知识",
284
+ en: "Knowledge"
285
+ },
286
+ fun: {
287
+ zh: "娱乐",
288
+ en: "Fun"
289
+ }
290
+ };
291
+ const SORTS = [
292
+ "sortStars",
293
+ "sortUpdated",
294
+ "sortNewest"
295
+ ];
296
+ function fmtStars(count) {
297
+ if (!count || count <= 0) return "0";
298
+ if (count < 1e3) return String(count);
299
+ const k = count / 1e3;
300
+ return `${k >= 100 ? Math.round(k) : k.toFixed(1).replace(/\.0$/, "")}k`;
301
+ }
302
+ function relTime(iso, t) {
303
+ if (!iso) return "";
304
+ const days = Math.floor((Date.now() - new Date(iso).getTime()) / 864e5);
305
+ if (days <= 0) return t("today");
306
+ if (days < 30) return t("daysAgo", { days });
307
+ if (days < 365) return t("monthsAgo", { months: Math.floor(days / 30) });
308
+ return t("yearsAgo", { years: Math.floor(days / 365) });
309
+ }
310
+ function PluginHubSection({ t: _hostT, locale }) {
311
+ const [lang, setLang] = (0, react.useState)(locale.getSnapshot().active);
312
+ const [plugins, setPlugins] = (0, react.useState)(null);
313
+ /** 收录/精选统计(官网 /api/stats.json 实时拉取) */
314
+ const [stats, setStats] = (0, react.useState)(null);
315
+ const [failed, setFailed] = (0, react.useState)(false);
316
+ const [reloadKey, setReloadKey] = (0, react.useState)(0);
317
+ const [category, setCategory] = (0, react.useState)("all");
318
+ const [query, setQuery] = (0, react.useState)("");
319
+ const [sort, setSort] = (0, react.useState)("sortStars");
320
+ const [copied, setCopied] = (0, react.useState)(null);
321
+ /** 全局复制成功 Toast:{id} 用于重复点击时重新触发入场动画 */
322
+ const [toast, setToast] = (0, react.useState)(null);
323
+ /** 列表滚动容器:分类/搜索切换后列表内容替换但 scrollTop 保留,会让用户误以为列表没更新,需重置回顶部 */
324
+ const listRef = (0, react.useRef)(null);
325
+ (0, react.useEffect)(() => {
326
+ listRef.current?.scrollTo({ top: 0 });
327
+ }, [category]);
328
+ (0, react.useEffect)(() => locale.subscribe(() => setLang(locale.getSnapshot().active)), [locale]);
329
+ (0, react.useEffect)(() => {
330
+ let cancelled = false;
331
+ setPlugins(null);
332
+ setStats(null);
333
+ setFailed(false);
334
+ const apply = (data) => {
335
+ if (cancelled) return;
336
+ const list = (Array.isArray(data) ? data : []).map((item) => normalize(item));
337
+ setPlugins(list.filter((p) => p.compatibility?.status === "verified"));
338
+ };
339
+ const fail = () => {
340
+ if (!cancelled) setFailed(true);
341
+ };
342
+ const fetchData = (url) => fetch(url).then((res) => {
343
+ if (!res.ok) throw new Error(String(res.status));
344
+ return res.json();
345
+ });
346
+ fetchData(`https://dsh-plugin.org/api/plugins.${lang}.json`).then(apply).catch(fail);
347
+ const applyStats = (s) => {
348
+ if (!cancelled && s && typeof s.total === "number" && typeof s.verified === "number") setStats(s);
349
+ };
350
+ fetchData("https://dsh-plugin.org/api/stats.json").then((s) => applyStats(s)).catch(() => {});
351
+ return () => {
352
+ cancelled = true;
353
+ };
354
+ }, [lang, reloadKey]);
355
+ const visible = (0, react.useMemo)(() => {
356
+ if (!plugins) return [];
357
+ const q = query.trim().toLowerCase();
358
+ return [...plugins.filter((p) => {
359
+ if (category !== "all" && p.category !== category) return false;
360
+ if (!q) return true;
361
+ return (p.displayName ?? "").toLowerCase().includes(q) || (p.description ?? "").toLowerCase().includes(q) || (p.topics ?? []).some((topic) => topic.toLowerCase().includes(q));
362
+ })].sort((a, b) => {
363
+ if (sort === "sortStars") return (b.stats?.stargazers_count ?? 0) - (a.stats?.stargazers_count ?? 0);
364
+ if (sort === "sortNewest") return (b.dates?.addedAt ?? "").localeCompare(a.dates?.addedAt ?? "");
365
+ return (b.dates?.repoUpdatedAt ?? "").localeCompare(a.dates?.repoUpdatedAt ?? "");
366
+ });
367
+ }, [
368
+ plugins,
369
+ category,
370
+ query,
371
+ sort
372
+ ]);
373
+ const langKey = lang === "en" ? "en" : "zh";
374
+ const langPath = lang === "zh" ? "zh/" : "";
375
+ const dict = langKey === "en" ? en : zh;
376
+ const t = (key, params) => {
377
+ const raw = dict[key] ?? key;
378
+ return params ? raw.replace(/\{(\w+)\}/g, (_, k) => String(params[k] ?? "")) : raw;
379
+ };
380
+ const catLabel = (map, key) => map[key]?.[langKey] ?? key;
381
+ const copyInstall = async (repo) => {
382
+ const text = `dsh plugin add ${repo}`;
383
+ let ok = false;
384
+ try {
385
+ await navigator.clipboard.writeText(text);
386
+ ok = true;
387
+ } catch {
388
+ const ta = document.createElement("textarea");
389
+ ta.value = text;
390
+ ta.style.position = "fixed";
391
+ ta.style.opacity = "0";
392
+ document.body.appendChild(ta);
393
+ ta.select();
394
+ try {
395
+ ok = document.execCommand("copy");
396
+ } catch {
397
+ ok = false;
398
+ }
399
+ document.body.removeChild(ta);
400
+ }
401
+ if (ok) {
402
+ setCopied(repo);
403
+ setToast({ id: Date.now() });
404
+ window.setTimeout(() => setCopied((cur) => cur === repo ? null : cur), 1600);
405
+ window.setTimeout(() => setToast(null), 1800);
406
+ } else setCopied(null);
407
+ };
408
+ const total = plugins?.length ?? 0;
409
+ const count = visible.length;
410
+ const statsTotal = stats?.total ?? total;
411
+ const statsVerified = stats?.verified ?? 0;
412
+ const categoryCounts = (0, react.useMemo)(() => {
413
+ const counts = { all: total };
414
+ for (const p of plugins ?? []) if (p.category) counts[p.category] = (counts[p.category] ?? 0) + 1;
415
+ return counts;
416
+ }, [plugins, total]);
417
+ return (0, react.createElement)("div", { className: Section_module_css_default.root }, (0, react.createElement)("div", { className: Section_module_css_default.header }, (0, react.createElement)("div", { className: Section_module_css_default.brand }, (0, react.createElement)("div", { className: Section_module_css_default.brandText }, (0, react.createElement)("h1", { className: Section_module_css_default.title }, t("title")), (0, react.createElement)("div", { className: Section_module_css_default.tagline }, t("tagline", {
418
+ total: statsTotal,
419
+ verified: statsVerified
420
+ })))), (0, react.createElement)("div", { className: Section_module_css_default.controls }, (0, react.createElement)("input", {
421
+ className: Section_module_css_default.search,
422
+ type: "search",
423
+ placeholder: t("search"),
424
+ value: query,
425
+ spellCheck: false,
426
+ onInput: (e) => setQuery(e.target.value)
427
+ }), (0, react.createElement)("select", {
428
+ className: Section_module_css_default.sort,
429
+ value: sort,
430
+ onChange: (e) => setSort(e.target.value)
431
+ }, SORTS.map((key) => (0, react.createElement)("option", {
432
+ key,
433
+ value: key
434
+ }, t(key)))), (0, react.createElement)("button", {
435
+ className: Section_module_css_default.langBtn,
436
+ title: lang === "zh" ? t("toEn") : t("toZh"),
437
+ onClick: () => setLang(lang === "zh" ? "en" : "zh")
438
+ }, lang === "zh" ? "EN" : "中文"), (0, react.createElement)("a", {
439
+ className: Section_module_css_default.openBtn,
440
+ href: `${SITE_URL}${langPath}`,
441
+ target: "_blank",
442
+ rel: "noopener noreferrer",
443
+ title: t("openHint")
444
+ }, t("openBtn"), (0, react.createElement)("span", { className: Section_module_css_default.openArrow }, "↗")))), (0, react.createElement)("a", {
445
+ className: Section_module_css_default.adBanner,
446
+ href: `${SITE_URL}${langPath}`,
447
+ target: "_blank",
448
+ rel: "noopener noreferrer"
449
+ }, (0, react.createElement)("span", { className: Section_module_css_default.adBadge }, t("adBadge")), (0, react.createElement)("span", { className: Section_module_css_default.adText }, t("ad", {
450
+ total: statsTotal,
451
+ verified: statsVerified
452
+ })), (0, react.createElement)("span", { className: Section_module_css_default.adArrow }, "↗")), (0, react.createElement)("div", { className: Section_module_css_default.tabs }, (0, react.createElement)("button", {
453
+ key: "all",
454
+ className: category === "all" ? Section_module_css_default.tabActive : Section_module_css_default.tab,
455
+ onClick: () => setCategory("all")
456
+ }, t("all"), (0, react.createElement)("span", { className: category === "all" ? Section_module_css_default.tabCountActive : Section_module_css_default.tabCount }, total)), CATEGORY_ORDER.map((id) => (0, react.createElement)("button", {
457
+ key: id,
458
+ className: category === id ? Section_module_css_default.tabActive : Section_module_css_default.tab,
459
+ onClick: () => setCategory(id)
460
+ }, catLabel(CATEGORY_SHORT_LABELS, id), (0, react.createElement)("span", { className: category === id ? Section_module_css_default.tabCountActive : Section_module_css_default.tabCount }, categoryCounts[id] ?? 0)))), (0, react.createElement)("div", { className: Section_module_css_default.body }, (0, react.createElement)("div", {
461
+ ref: listRef,
462
+ className: Section_module_css_default.list
463
+ }, plugins === null && !failed && (0, react.createElement)("div", { className: Section_module_css_default.state }, t("loading")), failed && (0, react.createElement)("div", { className: Section_module_css_default.state }, (0, react.createElement)("div", { className: Section_module_css_default.stateTitle }, t("failed")), (0, react.createElement)("div", { className: Section_module_css_default.stateDesc }, t("failedDesc")), (0, react.createElement)("button", {
464
+ className: Section_module_css_default.retryBtn,
465
+ onClick: () => setReloadKey((k) => k + 1)
466
+ }, t("retry"))), plugins !== null && !failed && count === 0 && (0, react.createElement)("div", { className: Section_module_css_default.state }, (0, react.createElement)("div", { className: Section_module_css_default.stateTitle }, t("noResult")), (0, react.createElement)("div", { className: Section_module_css_default.stateDesc }, t("noResultDesc"))), plugins !== null && !failed && visible.map((p) => {
467
+ const repo = p.source?.repo ?? "";
468
+ const isCopied = copied === repo;
469
+ return (0, react.createElement)("div", {
470
+ key: p.ownerSlug ? `${p.ownerSlug}/${p.slug}` : p.slug,
471
+ className: Section_module_css_default.card
472
+ }, (0, react.createElement)("div", { className: Section_module_css_default.cardMain }, (0, react.createElement)("div", { className: Section_module_css_default.cardHead }, (0, react.createElement)("div", {
473
+ className: Section_module_css_default.cardTitle,
474
+ title: p.description ?? ""
475
+ }, p.displayName ?? p.slug), p.category ? (0, react.createElement)("span", { className: Section_module_css_default.categoryBadge }, catLabel(CATEGORY_LABELS, p.category)) : null, p.compatibility?.status === "verified" ? (0, react.createElement)("span", { className: Section_module_css_default.verified }, t("verified")) : null), p.description && (langKey === "zh" || !/[\u4e00-\u9fff]/.test(p.description)) ? (0, react.createElement)("p", { className: Section_module_css_default.desc }, p.description) : null, (p.topics?.length ?? 0) > 0 ? (0, react.createElement)("div", { className: Section_module_css_default.topics }, p.topics.slice(0, 3).map((topic) => (0, react.createElement)("span", {
476
+ key: topic,
477
+ className: Section_module_css_default.topic
478
+ }, topic))) : null), (0, react.createElement)("div", { className: Section_module_css_default.cardSide }, (0, react.createElement)("div", { className: Section_module_css_default.stats }, (0, react.createElement)("span", { className: Section_module_css_default.star }, "★ ", fmtStars(p.stats?.stargazers_count)), (0, react.createElement)("span", { className: Section_module_css_default.fork }, t("fork"), " ", fmtStars(p.stats?.forks_count)), (0, react.createElement)("span", { className: Section_module_css_default.date }, relTime(p.dates?.repoUpdatedAt, t))), repo ? (0, react.createElement)("div", { className: Section_module_css_default.actions }, (0, react.createElement)("a", {
479
+ className: Section_module_css_default.detailBtn,
480
+ href: `${SITE_URL}${langPath}plugins/${p.ownerSlug ?? repo.split("/")[0]?.toLowerCase() ?? ""}/${p.slug}`,
481
+ target: "_blank",
482
+ rel: "noopener noreferrer",
483
+ title: p.slug
484
+ }, t("detail")), (0, react.createElement)("button", {
485
+ className: isCopied ? Section_module_css_default.installBtnCopied : Section_module_css_default.installBtn,
486
+ onClick: () => copyInstall(repo)
487
+ }, t("copy"))) : null));
488
+ })), plugins !== null && !failed && (0, react.createElement)("div", { className: Section_module_css_default.footer }, (0, react.createElement)("span", { className: Section_module_css_default.footText }, count === total ? t("pluginsTotal", { n: total }) : t("filteredCount", { n: count })), (0, react.createElement)("a", {
489
+ className: Section_module_css_default.footLink,
490
+ href: `${SITE_URL}${langPath}`,
491
+ target: "_blank",
492
+ rel: "noopener noreferrer"
493
+ }, t("browseAll", { n: total })))), toast && (0, react.createElement)("div", {
494
+ key: toast.id,
495
+ className: Section_module_css_default.toast
496
+ }, t("toastCopied")));
497
+ }
498
+ const name = NS;
499
+ const inject = ["slots", "locale"];
500
+ function apply(ctx) {
501
+ ctx.effect(() => ctx.locale.register(NS, {
502
+ zh,
503
+ en
504
+ }), "dsh-plugin: dictionaries");
505
+ const t = ctx.locale.bind(NS);
506
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
507
+ name: "settings.section",
508
+ id: NS,
509
+ order: 60,
510
+ label: () => t("nav"),
511
+ locale: NS,
512
+ inject: () => ({
513
+ t,
514
+ locale: ctx.locale
515
+ })
516
+ }, () => (0, react.createElement)(PluginHubSection, {
517
+ t,
518
+ locale: ctx.locale
519
+ })));
520
+ }
521
+ //#endregion
522
+ exports.apply = apply;
523
+ exports.inject = inject;
524
+ exports.name = name;
525
+ return module.exports;
526
+ }
527
+ });
528
+
529
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["useState","useRef","useMemo","h","styles"],"sources":["../src/client/locales.ts","../src/client/index.tsx"],"sourcesContent":["/** zh/en dictionaries for the DSH-Plugin Hub settings section. */\nexport const zh = {\n nav: '插件中心',\n title: 'DSH-Plugin Hub for DeepSeek Harness',\n // 收录/精选数量由组件从官网 /api/stats.json 实时拉取填充({total}/{verified}),不写死\n tagline: 'DSH plugin 插件中心:收录 {total} 个 DeepSeek Harness 插件,人工精选验证 {verified},每日更新 · 免费安装',\n adBadge: '推荐',\n ad: 'DSH-Plugin 插件中心:收录 {total} 个 DeepSeek Harness 插件,其中 {verified} 人工精选验证,每日更新,免费按分类浏览、搜索、下载与安装,人工验证、来源可溯',\n search: '搜索插件名称、描述、标签…',\n all: '全部',\n sortStars: 'Star 最多',\n sortUpdated: '最近更新',\n sortNewest: '最新收录',\n open: '在浏览器中打开',\n openHint: '打开 dsh-plugin.org',\n openBtn: 'DSH-Plugin 官网',\n toEn: 'Switch to English',\n fork: 'Fork',\n loading: '正在加载插件数据…',\n failed: '插件数据加载失败',\n failedDesc: '请稍后重试,或点击右上角按钮在浏览器中打开插件中心。',\n retry: '重试',\n copy: '复制安装命令',\n toastCopied: '安装命令已复制,去 dsh 终端粘贴即可安装',\n detail: '打开详情',\n noResult: '没有找到匹配的插件',\n noResultDesc: '换个关键词试试,或前往网站浏览全部插件。',\n today: '今天更新',\n daysAgo: '更新于 {days} 天前',\n monthsAgo: '更新于 {months} 个月前',\n yearsAgo: '更新于 {years} 年前',\n pluginsTotal: '共 {n} 个插件',\n filteredCount: '匹配 {n} 个',\n more: '还有 {n} 个插件,去网站查看全部',\n browseAll: '浏览全部 {n} 个插件',\n verified: '已验证',\n empty: '该分类暂无插件',\n dataFrom: '数据源 dsh-plugin.org · 每日人工更新',\n}\n\nexport const en = {\n nav: 'Plugin Hub',\n title: 'DSH-Plugin Hub for DeepSeek Harness',\n // Plugin counts are filled from the live /api/stats.json ({total}/{verified}), never hardcoded\n tagline: 'DSH plugin marketplace · {total} DeepSeek Harness plugins indexed · {verified} human-verified · updated daily · free to install',\n adBadge: 'Featured',\n ad: 'DSH-Plugin Hub: {total} DeepSeek Harness plugins indexed, {verified} of which are human-verified. Updated daily — browse, search & install for free.',\n search: 'Search plugins by name, description, tags…',\n all: 'All',\n sortStars: 'Most stars',\n sortUpdated: 'Recently updated',\n sortNewest: 'Newest added',\n open: 'Open in browser',\n openHint: 'Open dsh-plugin.org',\n openBtn: 'dsh-plugin.org',\n toZh: 'Switch to Chinese',\n fork: 'Fork',\n loading: 'Loading plugin data…',\n failed: 'Failed to load plugin data',\n failedDesc: 'Please retry, or open the hub in your browser with the button above.',\n retry: 'Retry',\n copy: 'Copy install command',\n toastCopied: 'Install command copied — paste it in your dsh terminal',\n detail: 'Open details',\n noResult: 'No plugins match your search',\n noResultDesc: 'Try another keyword, or browse all plugins on the website.',\n today: 'updated today',\n daysAgo: 'updated {days}d ago',\n monthsAgo: 'updated {months}mo ago',\n yearsAgo: 'updated {years}y ago',\n pluginsTotal: '{n} plugins',\n filteredCount: '{n} matched',\n more: '{n} more on the website',\n browseAll: 'Browse all {n} plugins',\n verified: 'Verified',\n empty: 'No plugins in this category yet',\n dataFrom: 'Data from dsh-plugin.org · curated daily',\n}\n","/**\n * dsh-plugin client: a native \"Plugin Hub\" settings section that renders\n * the community marketplace (dsh-plugin.org) as a first-class catalog —\n * category tabs (11 categories + All), search, sorting and one-click\n * install-command copy. Plugin data is fetched live from dsh-plugin.org,\n * so the browser bundle stays small and always in sync with the site.\n *\n * Built by tsdown into the __ModuleLoader__ factory bundle at client/client.js.\n */\nimport { createElement as h, useEffect, useMemo, useRef, useState } from 'react'\nimport styles from './Section.module.css'\nimport { en, zh } from './locales.ts'\n\nconst NS = 'dsh-plugin'\nconst SITE_URL = 'https://dsh-plugin.org/'\n\ntype LocaleId = 'zh' | 'en'\n\n/** The subset of the locale service this plugin touches. */\ninterface LocaleService {\n register(namespace: string, dicts: { zh: Record<string, string>; en: Record<string, string> }): unknown\n bind(namespace: string): (key: string, params?: Record<string, string | number>) => string\n getSnapshot(): { active: LocaleId }\n subscribe(fn: () => void): () => void\n}\n\n/** The subset of the slots service this plugin touches. */\ninterface SlotsService {\n inject(slot: string, register: () => unknown): void\n register(meta: Record<string, unknown>, component: () => unknown): unknown\n}\n\n/** The client cordis context shape this plugin relies on (structural). */\ninterface HubClientContext {\n effect(callback: () => unknown, label?: string): void\n slots: SlotsService\n locale: LocaleService\n}\n\ninterface SectionProps {\n t: (key: string, params?: Record<string, string | number>) => string\n locale: LocaleService\n}\n\n/** Slim projection of the site's DirectoryPlugin entries. */\ninterface HubPlugin {\n /** 作者小写 key,与 slug 共同构成唯一身份(两级路径 /plugins/{ownerSlug}/{slug});旧数据可能缺省 */\n ownerSlug?: string\n slug: string\n displayName?: string\n category?: string\n topics?: string[]\n features?: string[]\n description?: string\n source?: { repo?: string }\n compatibility?: { status?: string }\n dates?: { repoUpdatedAt?: string; addedAt?: string }\n stats?: { stargazers_count?: number; forks_count?: number }\n}\n\nconst CATEGORY_ORDER = [\n 'interface', 'session', 'memory', 'tools', 'agent', 'workflow',\n 'integration', 'model', 'dev', 'knowledge', 'fun',\n]\n\n/**\n * 归一化在线 API(dsh-plugin.org/api/plugins.{lang}.json)返回的短 key 结构\n * (s/o/n/c/t/f/d/r/v/u/a/sg/fk),统一为 HubPlugin,保证渲染逻辑只认一种结构。\n */\nfunction normalize(raw: Record<string, unknown>): HubPlugin {\n if (typeof raw.s === 'string') {\n return {\n slug: raw.s,\n ownerSlug: typeof raw.o === 'string' ? raw.o : undefined,\n displayName: typeof raw.n === 'string' ? raw.n : undefined,\n category: typeof raw.c === 'string' ? raw.c : undefined,\n topics: Array.isArray(raw.t) ? (raw.t as string[]) : undefined,\n features: Array.isArray(raw.f) ? (raw.f as string[]) : undefined,\n description: typeof raw.d === 'string' ? raw.d : undefined,\n source: typeof raw.r === 'string' ? { repo: raw.r } : undefined,\n compatibility: typeof raw.v === 'string' ? { status: raw.v } : undefined,\n dates: {\n repoUpdatedAt: typeof raw.u === 'string' ? raw.u : undefined,\n addedAt: typeof raw.a === 'string' ? raw.a : undefined,\n },\n stats: {\n stargazers_count: typeof raw.sg === 'number' ? raw.sg : undefined,\n forks_count: typeof raw.fk === 'number' ? raw.fk : undefined,\n },\n }\n }\n return raw as unknown as HubPlugin\n}\n\nconst CATEGORY_LABELS: Record<string, { zh: string; en: string }> = {\n interface: { zh: '界面与体验', en: 'UI & Experience' },\n session: { zh: '会话与消息', en: 'Sessions & Messages' },\n memory: { zh: '记忆与上下文', en: 'Memory & Context' },\n tools: { zh: '工具与能力', en: 'Tools & Capabilities' },\n agent: { zh: '技能与智能体', en: 'Skills & Agents' },\n workflow: { zh: '工作流与自动化', en: 'Workflow & Automation' },\n integration: { zh: '集成与连接', en: 'Integrations & Connections' },\n model: { zh: '模型与推理', en: 'Models & Reasoning' },\n dev: { zh: '开发与运维', en: 'Development & Operations' },\n knowledge: { zh: '数据与知识', en: 'Data & Knowledge' },\n fun: { zh: '娱乐', en: 'Entertainment' },\n}\n\nconst CATEGORY_SHORT_LABELS: Record<string, { zh: string; en: string }> = {\n interface: { zh: '界面体验', en: 'UI Exp' },\n session: { zh: '会话消息', en: 'Sessions' },\n memory: { zh: '记忆上下文', en: 'Memory' },\n tools: { zh: '工具能力', en: 'Tools' },\n agent: { zh: '技能智能体', en: 'Agents' },\n workflow: { zh: '工作流', en: 'Workflow' },\n integration: { zh: '集成连接', en: 'Integrations' },\n model: { zh: '模型推理', en: 'Models' },\n dev: { zh: '开发运维', en: 'Dev' },\n knowledge: { zh: '数据知识', en: 'Knowledge' },\n fun: { zh: '娱乐', en: 'Fun' },\n}\n\nconst SORTS = ['sortStars', 'sortUpdated', 'sortNewest'] as const\ntype SortKey = (typeof SORTS)[number]\n\nfunction fmtStars(count?: number): string {\n if (!count || count <= 0) return '0'\n if (count < 1000) return String(count)\n const k = count / 1000\n return `${k >= 100 ? Math.round(k) : k.toFixed(1).replace(/\\.0$/, '')}k`\n}\n\nfunction relTime(iso: string | undefined, t: SectionProps['t']): string {\n if (!iso) return ''\n const days = Math.floor((Date.now() - new Date(iso).getTime()) / 86400000)\n if (days <= 0) return t('today')\n if (days < 30) return t('daysAgo', { days })\n if (days < 365) return t('monthsAgo', { months: Math.floor(days / 30) })\n return t('yearsAgo', { years: Math.floor(days / 365) })\n}\n\nfunction PluginHubSection({ t: _hostT, locale }: SectionProps) {\n const [lang, setLang] = useState<LocaleId>(locale.getSnapshot().active)\n const [plugins, setPlugins] = useState<HubPlugin[] | null>(null)\n /** 收录/精选统计(官网 /api/stats.json 实时拉取) */\n const [stats, setStats] = useState<{ total: number; verified: number } | null>(null)\n const [failed, setFailed] = useState(false)\n const [reloadKey, setReloadKey] = useState(0)\n const [category, setCategory] = useState('all')\n const [query, setQuery] = useState('')\n const [sort, setSort] = useState<SortKey>('sortStars')\n const [copied, setCopied] = useState<string | null>(null)\n /** 全局复制成功 Toast:{id} 用于重复点击时重新触发入场动画 */\n const [toast, setToast] = useState<{ id: number } | null>(null)\n /** 列表滚动容器:分类/搜索切换后列表内容替换但 scrollTop 保留,会让用户误以为列表没更新,需重置回顶部 */\n const listRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n listRef.current?.scrollTo({ top: 0 })\n }, [category])\n\n useEffect(() => locale.subscribe(() => setLang(locale.getSnapshot().active)), [locale])\n\n useEffect(() => {\n let cancelled = false\n setPlugins(null)\n setStats(null)\n setFailed(false)\n const apply = (data: unknown) => {\n if (cancelled) return\n // 在线 API 已只返回 verified;再过滤一次,保证只展示人工验证通过的插件。\n const list = (Array.isArray(data) ? data : []).map((item) => normalize(item as Record<string, unknown>))\n setPlugins(list.filter((p) => p.compatibility?.status === 'verified'))\n }\n const fail = () => {\n if (!cancelled) setFailed(true)\n }\n const fetchData = (url: string): Promise<unknown> =>\n fetch(url).then((res) => {\n if (!res.ok) throw new Error(String(res.status))\n return res.json()\n })\n // 在线拉取最新数据(dsh-plugin.org,走 CDN 缓存),插件不发布也能拿到每日更新。\n const remote = `https://dsh-plugin.org/api/plugins.${lang}.json`\n fetchData(remote).then(apply).catch(fail)\n // 收录/精选统计:实时拉取官网 /api/stats.json。\n const applyStats = (s: { total: number; verified: number }) => {\n if (!cancelled && s && typeof s.total === 'number' && typeof s.verified === 'number') setStats(s)\n }\n fetchData('https://dsh-plugin.org/api/stats.json')\n .then((s) => applyStats(s as { total: number; verified: number }))\n .catch(() => {})\n return () => { cancelled = true }\n }, [lang, reloadKey])\n\n const visible = useMemo(() => {\n if (!plugins) return []\n const q = query.trim().toLowerCase()\n const list = plugins.filter((p) => {\n if (category !== 'all' && p.category !== category) return false\n if (!q) return true\n return (\n (p.displayName ?? '').toLowerCase().includes(q) ||\n (p.description ?? '').toLowerCase().includes(q) ||\n (p.topics ?? []).some((topic) => topic.toLowerCase().includes(q))\n )\n })\n return [...list].sort((a, b) => {\n if (sort === 'sortStars') return (b.stats?.stargazers_count ?? 0) - (a.stats?.stargazers_count ?? 0)\n if (sort === 'sortNewest') return (b.dates?.addedAt ?? '').localeCompare(a.dates?.addedAt ?? '')\n return (b.dates?.repoUpdatedAt ?? '').localeCompare(a.dates?.repoUpdatedAt ?? '')\n })\n }, [plugins, category, query, sort])\n\n const langKey: LocaleId = lang === 'en' ? 'en' : 'zh'\n // dsh-plugin.org keeps zh pages under the /zh/ prefix; en is the root.\n const langPath = lang === 'zh' ? 'zh/' : ''\n\n // 插件界面语言由组件自身 lang 控制(左上角 EN/中文 按钮),宿主的 t() 绑定宿主 locale、\n // 不跟随组件内 lang,因此这里基于本地字典自建翻译函数,保证切换语言后所有文本即时刷新。\n // (settings.section 的导航 label 仍在 apply 里用宿主 t(),跟随宿主语言。)\n const dict: Record<string, string> = langKey === 'en' ? en : zh\n const t = (key: string, params?: Record<string, string | number>): string => {\n const raw = dict[key] ?? key\n return params ? raw.replace(/\\{(\\w+)\\}/g, (_, k) => String(params[k] ?? '')) : raw\n }\n const catLabel = (map: Record<string, { zh: string; en: string }>, key: string): string =>\n map[key]?.[langKey] ?? key\n\n const copyInstall = async (repo: string) => {\n const text = `dsh plugin add ${repo}`\n let ok = false\n try {\n await navigator.clipboard.writeText(text)\n ok = true\n } catch {\n // Clipboard API unavailable (permissions/iframe) — fall back to the\n // legacy hidden-textarea trick, which works in any context.\n const ta = document.createElement('textarea')\n ta.value = text\n ta.style.position = 'fixed'\n ta.style.opacity = '0'\n document.body.appendChild(ta)\n ta.select()\n try {\n ok = document.execCommand('copy')\n } catch {\n ok = false\n }\n document.body.removeChild(ta)\n }\n if (ok) {\n setCopied(repo)\n setToast({ id: Date.now() })\n window.setTimeout(() => setCopied((cur) => (cur === repo ? null : cur)), 1600)\n window.setTimeout(() => setToast(null), 1800)\n } else {\n setCopied(null)\n }\n }\n\n const total = plugins?.length ?? 0\n const count = visible.length\n\n // 统计数字:官网 /api/stats.json 优先;拉取失败时用已加载列表兜底,避免展示 undefined。\n const statsTotal = stats?.total ?? total\n const statsVerified = stats?.verified ?? 0\n\n // Per-category plugin counts shown on the category chips.\n const categoryCounts = useMemo(() => {\n const counts: Record<string, number> = { all: total }\n for (const p of plugins ?? []) {\n if (p.category) counts[p.category] = (counts[p.category] ?? 0) + 1\n }\n return counts\n }, [plugins, total])\n\n return h('div', { className: styles.root },\n h('div', { className: styles.header },\n h('div', { className: styles.brand },\n h('div', { className: styles.brandText },\n // 市场主标题即本区块的 H1:与官网首页 H1 同文案(DSH-Plugin Hub for DeepSeek\n // Harness),命中品牌词 DSH-Plugin + 产品词 DeepSeek Harness;关键词落在 tagline。\n h('h1', { className: styles.title }, t('title')),\n h('div', { className: styles.tagline }, t('tagline', { total: statsTotal, verified: statsVerified })),\n ),\n ),\n h('div', { className: styles.controls },\n h('input', {\n className: styles.search,\n type: 'search',\n placeholder: t('search'),\n value: query,\n spellCheck: false,\n onInput: (e: React.FormEvent<HTMLInputElement>) =>\n setQuery((e.target as HTMLInputElement).value),\n }),\n h('select', {\n className: styles.sort,\n value: sort,\n onChange: (e: React.FormEvent<HTMLSelectElement>) =>\n setSort((e.target as HTMLSelectElement).value as SortKey),\n }, SORTS.map((key) => h('option', { key, value: key }, t(key)))),\n h('button', {\n className: styles.langBtn,\n title: lang === 'zh' ? t('toEn') : t('toZh'),\n onClick: () => setLang(lang === 'zh' ? 'en' : 'zh'),\n }, lang === 'zh' ? 'EN' : '中文'),\n h('a', {\n className: styles.openBtn,\n href: `${SITE_URL}${langPath}`,\n target: '_blank',\n rel: 'noopener noreferrer',\n title: t('openHint'),\n }, t('openBtn'), h('span', { className: styles.openArrow }, '\\u2197')),\n ),\n ),\n\n h('a', {\n className: styles.adBanner,\n href: `${SITE_URL}${langPath}`,\n target: '_blank',\n rel: 'noopener noreferrer',\n },\n h('span', { className: styles.adBadge }, t('adBadge')),\n h('span', { className: styles.adText }, t('ad', { total: statsTotal, verified: statsVerified })),\n h('span', { className: styles.adArrow }, '\\u2197')),\n\n h('div', { className: styles.tabs },\n h('button', {\n key: 'all',\n className: category === 'all' ? styles.tabActive : styles.tab,\n onClick: () => setCategory('all'),\n },\n t('all'),\n h('span', { className: category === 'all' ? styles.tabCountActive : styles.tabCount }, total)),\n CATEGORY_ORDER.map((id) => h('button', {\n key: id,\n className: category === id ? styles.tabActive : styles.tab,\n onClick: () => setCategory(id),\n },\n catLabel(CATEGORY_SHORT_LABELS, id),\n h('span', { className: category === id ? styles.tabCountActive : styles.tabCount }, categoryCounts[id] ?? 0))),\n ),\n\n h('div', { className: styles.body },\n h('div', { ref: listRef, className: styles.list },\n plugins === null && !failed && h('div', { className: styles.state }, t('loading')),\n failed && h('div', { className: styles.state },\n h('div', { className: styles.stateTitle }, t('failed')),\n h('div', { className: styles.stateDesc }, t('failedDesc')),\n h('button', { className: styles.retryBtn, onClick: () => setReloadKey((k) => k + 1) }, t('retry')),\n ),\n plugins !== null && !failed && count === 0 && h('div', { className: styles.state },\n h('div', { className: styles.stateTitle }, t('noResult')),\n h('div', { className: styles.stateDesc }, t('noResultDesc')),\n ),\n plugins !== null && !failed && visible.map((p) => {\n const repo = p.source?.repo ?? ''\n const isCopied = copied === repo\n return h('div', {\n // 唯一 key:数据重构后同一 slug 可能对应多个作者仓库,仅用 slug 会撞 key,\n // 导致切换分类时 React 复用旧 DOM、列表不刷新。\n key: p.ownerSlug ? `${p.ownerSlug}/${p.slug}` : p.slug,\n className: styles.card,\n },\n h('div', { className: styles.cardMain },\n h('div', { className: styles.cardHead },\n h('div', { className: styles.cardTitle, title: p.description ?? '' }, p.displayName ?? p.slug),\n p.category ? h('span', { className: styles.categoryBadge }, catLabel(CATEGORY_LABELS, p.category)) : null,\n p.compatibility?.status === 'verified'\n ? h('span', { className: styles.verified }, t('verified'))\n : null,\n ),\n // 英文模式下隐藏仍为中文的描述:站点英文数据缺翻译时 description 会回退成中文,\n // 不显示可保证界面语言一致,不残留中文字符。\n p.description && (langKey === 'zh' || !/[\\u4e00-\\u9fff]/.test(p.description))\n ? h('p', { className: styles.desc }, p.description)\n : null,\n (p.topics?.length ?? 0) > 0\n ? h('div', { className: styles.topics },\n p.topics!.slice(0, 3).map((topic) => h('span', { key: topic, className: styles.topic }, topic)),\n )\n : null,\n ),\n h('div', { className: styles.cardSide },\n h('div', { className: styles.stats },\n h('span', { className: styles.star }, '\\u2605 ', fmtStars(p.stats?.stargazers_count)),\n h('span', { className: styles.fork }, t('fork'), ' ', fmtStars(p.stats?.forks_count)),\n h('span', { className: styles.date }, relTime(p.dates?.repoUpdatedAt, t)),\n ),\n repo\n ? h('div', { className: styles.actions },\n h('a', {\n className: styles.detailBtn,\n // 两级路径:/plugins/{ownerSlug}/{slug};旧数据缺 ownerSlug 时从 repo 推导\n href: `${SITE_URL}${langPath}plugins/${p.ownerSlug ?? repo.split('/')[0]?.toLowerCase() ?? ''}/${p.slug}`,\n target: '_blank',\n rel: 'noopener noreferrer',\n title: p.slug,\n }, t('detail')),\n h('button', {\n className: isCopied ? styles.installBtnCopied : styles.installBtn,\n // 文字恒定避免按钮宽度变化导致卡片跳动;成功反馈 = 蓝底高亮 + 右下角 Toast\n onClick: () => copyInstall(repo),\n }, t('copy')),\n )\n : null,\n ),\n )\n }),\n ),\n plugins !== null && !failed && h('div', { className: styles.footer },\n h('span', { className: styles.footText }, count === total ? t('pluginsTotal', { n: total }) : t('filteredCount', { n: count })),\n h('a', {\n className: styles.footLink,\n href: `${SITE_URL}${langPath}`,\n target: '_blank',\n rel: 'noopener noreferrer',\n }, t('browseAll', { n: total })),\n ),\n ),\n // 复制成功提示条:右下角纯黑色文字条,1800ms 后自动消失\n toast && h('div', { key: toast.id, className: styles.toast }, t('toastCopied')),\n )\n}\n\nexport const name = NS\nexport const inject = ['slots', 'locale']\n\nexport function apply(ctx: HubClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-plugin: dictionaries')\n const t = ctx.locale.bind(NS)\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: NS,\n order: 60,\n label: () => t('nav'),\n locale: NS,\n inject: () => ({ t, locale: ctx.locale }),\n }, () => h(PluginHubSection, { t, locale: ctx.locale })))\n}\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ # dsh bundle patch: inserts the DSH-Plugin section into a profile's layer stack.
2
+ - insert:
3
+ - id: dsh-plugin
4
+ name: 'dsh-plugin'
package/lib/index.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * dsh-plugin host entry.
3
+ *
4
+ * The browser half (src/client) renders the DSH-Plugin catalog inside
5
+ * the Settings panel and fetches it live from dsh-plugin.org — no bundled
6
+ * data, no local routes. This node half is intentionally a no-op.
7
+ */
8
+ export const name = 'dsh-plugin'
9
+
10
+ export function apply() {}