dshmarket 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,6 +51,14 @@ Installs prefer npm tarballs over full-repo GitHub downloads whenever a plugin p
51
51
 
52
52
  Live from [awesome-dsh-plugin.com/plugins.json](https://awesome-dsh-plugin.com/plugins.json) — curated entries, npm mapping, and star counts refreshed daily by CI — with a bundled snapshot as offline fallback.
53
53
 
54
+ ## Related projects
55
+
56
+ ### DeepSeek Harness Desktop
57
+
58
+ [DeepSeek Harness Desktop](https://github.com/anywhere-labs/deepseek-harness-desktop) is a modern desktop client for the DeepSeek Harness ecosystem — start and manage a local Harness service without installing Node.js or touching the command line. Plugin marketplace, mobile remote control, and IM Channels are on its roadmap.
59
+
60
+ [Website](https://www.dshdesktop.cn) · [GitHub](https://github.com/anywhere-labs/deepseek-harness-desktop)
61
+
54
62
  ## License
55
63
 
56
64
  MIT · [dshmarket.com](https://dshmarket.com)
package/README.zh.md CHANGED
@@ -51,6 +51,14 @@ dsh plugin --profile web add dshmarket
51
51
 
52
52
  实时来自 [awesome-dsh-plugin.com/plugins.json](https://awesome-dsh-plugin.com/plugins.json)——精选条目、npm 映射、star 数由 CI 每日刷新——内置快照做离线兜底。
53
53
 
54
+ ## 友情链接
55
+
56
+ ### DeepSeek Harness Desktop
57
+
58
+ [DeepSeek Harness Desktop](https://github.com/anywhere-labs/deepseek-harness-desktop) 是一款为 DeepSeek Harness 生态打造的现代化桌面端,让用户无需配置 Node.js 或执行命令,即可启动和管理本地 Harness 服务。项目后续还将支持插件市场、移动端远程控制和 IM Channels。
59
+
60
+ [访问官网](https://www.dshdesktop.cn) · [GitHub](https://github.com/anywhere-labs/deepseek-harness-desktop)
61
+
54
62
  ## 许可
55
63
 
56
64
  MIT · [dshmarket.com](https://dshmarket.com)
package/client/client.js CHANGED
@@ -38,6 +38,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
38
38
  update: "更新",
39
39
  updating: "更新中…",
40
40
  updated: "✓ 已更新,重启后生效",
41
+ updateNow: "立即更新",
41
42
  updateFail: "更新失败",
42
43
  upToDate: "已是最新",
43
44
  linkedDev: "本地开发链接",
@@ -96,6 +97,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
96
97
  update: "Update",
97
98
  updating: "Updating…",
98
99
  updated: "✓ Updated — restart to apply",
100
+ updateNow: "Update now",
99
101
  updateFail: "Update failed",
100
102
  upToDate: "Up to date",
101
103
  linkedDev: "linked (dev)",
@@ -132,10 +134,6 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
132
134
  for (let i = 0; i < name.length; i++) hash = hash * 31 + name.charCodeAt(i) | 0;
133
135
  return "hsl(" + (hash % 360 + 360) % 360 + " 55% 52%)";
134
136
  }
135
- function repoOf(url) {
136
- const m = /^https:\/\/github\.com\/([^/]+\/[^/]+?)(?:\/tree\/.+)?\/?$/.exec(url);
137
- return m ? m[1] : null;
138
- }
139
137
  function readSession(key) {
140
138
  try {
141
139
  return JSON.parse(sessionStorage.getItem(key) || "null");
@@ -148,14 +146,75 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
148
146
  const desc = plugin.description && (plugin.description[lang] || plugin.description.en) || "";
149
147
  return /\b(tui|cli|tty|terminal)\b|终端|命令行/i.test(plugin.name + " " + desc);
150
148
  }
151
- /** A registry plugin counts as installed when its package name, npm name, or GitHub spec appears in the profile dependencies. */
149
+ /**
150
+ * The discover list: category filter, then search across name / owner /
151
+ * localized description, then the selected sort. Pure — the section renders
152
+ * exactly this.
153
+ */
154
+ function visiblePlugins(plugins, options) {
155
+ const query = options.query.trim().toLowerCase();
156
+ const list = plugins.filter((p) => {
157
+ if (options.category !== "all" && p.category !== options.category) return false;
158
+ if (query === "") return true;
159
+ const desc = p.description && (p.description[options.lang] || p.description.en) || "";
160
+ return p.name.toLowerCase().includes(query) || p.owner.toLowerCase().includes(query) || desc.toLowerCase().includes(query);
161
+ });
162
+ if (options.sort === "hot") return [...list].sort((a, b) => (b.stars ?? -1) - (a.stars ?? -1));
163
+ if (options.sort === "new") return [...list].sort((a, b) => String(b.added).localeCompare(String(a.added)));
164
+ return list;
165
+ }
166
+ /** The themes tab listing: theme category only, most-starred first. */
167
+ function themePlugins(plugins) {
168
+ return plugins.filter((p) => p.category === "theme").sort((a, b) => (b.stars || 0) - (a.stars || 0));
169
+ }
170
+ /**
171
+ * Category chip order: collapsed with an active non-'all' chip, the active
172
+ * one moves to the front so it stays visible inside the two-row clip.
173
+ */
174
+ function orderedCategories(categories, active, open) {
175
+ return open || active === "all" ? categories : [active, ...categories.filter((id) => id !== active)];
176
+ }
177
+ /**
178
+ * Unified installed-state matching (#15): both sides collapse to lowercase
179
+ * identity sets — the registry entry contributes its bare name, npm name and
180
+ * owner/repo; the dependency contributes its key and the repo inside its
181
+ * spec — and any exact intersection counts. Exact equality, not substrings,
182
+ * so prefix-related repo names cannot cross-match.
183
+ */
184
+ function entryIdentities(plugin) {
185
+ const ids = /* @__PURE__ */ new Set([plugin.name.toLowerCase()]);
186
+ if (plugin.npm) ids.add(plugin.npm.toLowerCase());
187
+ const m = /^https:\/\/github\.com\/([^/]+\/[^/]+?)(?:\/tree\/[^/]+\/(.+?))?\/?$/.exec(plugin.url);
188
+ if (m !== null) ids.add(m[2] !== void 0 ? `${m[1].toLowerCase()}#path:/${m[2].toLowerCase()}` : m[1].toLowerCase());
189
+ return ids;
190
+ }
191
+ function depIdentities(name, spec) {
192
+ const ids = /* @__PURE__ */ new Set([name.toLowerCase()]);
193
+ const scoped = /^@([^/]+)\/(.+)$/.exec(name);
194
+ if (scoped !== null) ids.add(`${scoped[1].toLowerCase()}/${scoped[2].toLowerCase()}`);
195
+ const match = /github:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)(?:#path:\/([A-Za-z0-9_./-]+))?/i.exec(spec);
196
+ if (match !== null) {
197
+ ids.add(match[1].toLowerCase());
198
+ if (match[2] !== void 0) ids.add(`${match[1].toLowerCase()}#path:/${match[2].toLowerCase()}`);
199
+ }
200
+ return ids;
201
+ }
202
+ /** The installed dependency name a registry entry corresponds to, or null. */
203
+ function matchInstalledName(plugin, installed) {
204
+ const ids = entryIdentities(plugin);
205
+ for (const [name, spec] of Object.entries(installed)) for (const id of depIdentities(name, String(spec))) if (ids.has(id)) return name;
206
+ return null;
207
+ }
208
+ /** The registry entry an installed dependency corresponds to, or undefined. */
209
+ function entryForDep(plugins, name, spec) {
210
+ const ids = depIdentities(name, String(spec));
211
+ return plugins.find((plugin) => {
212
+ for (const id of entryIdentities(plugin)) if (ids.has(id)) return true;
213
+ return false;
214
+ });
215
+ }
152
216
  function isInstalled(plugin, installed) {
153
- if (installed[plugin.name] !== void 0) return true;
154
- if (plugin.npm && installed[plugin.npm] !== void 0) return true;
155
- const repo = repoOf(plugin.url);
156
- if (repo === null) return false;
157
- const needle = ("github:" + repo).toLowerCase();
158
- return Object.values(installed).some((spec) => String(spec).toLowerCase().includes(needle));
217
+ return matchInstalledName(plugin, installed) !== null;
159
218
  }
160
219
  /**
161
220
  * The brand mark (assets/logo.svg — shared block-grid mark with
@@ -205,7 +264,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
205
264
  }
206
265
  //#endregion
207
266
  //#region \0dsh-css:/home/runner/work/dsh-market/dsh-market/src/client/Market.module.css.mjs
208
- const css = ".eGUBIq_root{min-width:0;height:100%;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;display:flex;position:relative}.eGUBIq_head{flex-direction:column;gap:12px;padding:4px 4px 12px;display:flex}.eGUBIq_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.eGUBIq_sub{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0;font-size:14px;line-height:22px}.eGUBIq_searchInline{flex-shrink:0;width:200px;margin-bottom:6px}.eGUBIq_tabs{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-end;gap:2px;display:flex}.eGUBIq_tab{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;padding:7px 12px;font-size:13px}.eGUBIq_tab.eGUBIq_on{color:var(--dsw-alias-brand-primary,#4f6ef7);border-bottom-color:var(--dsw-alias-brand-primary,#4f6ef7);font-weight:600}.eGUBIq_restart{background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;align-items:center;gap:8px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_body{flex:1;padding:12px 4px 24px;overflow-x:hidden;overflow-y:auto}.eGUBIq_cats{z-index:5;background:var(--dsw-alias-bg-layer-2,#f7f8fa);margin:-12px -4px 2px;padding:12px 4px 4px;position:sticky;top:-13px}.eGUBIq_catsRow{align-items:flex-start;gap:8px;display:flex;position:relative}.eGUBIq_sort{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#e5e7eb);border-radius:8px;flex-shrink:0;gap:2px;padding:2px;display:flex}.eGUBIq_sort button{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:3px 10px;font-size:12px}.eGUBIq_sort button.eGUBIq_on{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-weight:600;box-shadow:0 1px 3px #00000014}.eGUBIq_star{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_top{z-index:20;border:1px solid var(--dsw-alias-border-l1,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);width:38px;height:38px;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;border-radius:99px;font-size:16px;position:absolute;bottom:18px;right:18px;box-shadow:0 4px 14px #0000001f}.eGUBIq_top:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_tag{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px}.eGUBIq_okState{color:var(--dsw-alias-state-success-primary,#16a34a);white-space:nowrap;font-size:12px;font-weight:600}.eGUBIq_dangerBtn.eGUBIq_dangerBtn{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_dangerArmed.eGUBIq_dangerArmed{background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}.eGUBIq_warnBtn.eGUBIq_warnBtn{background:var(--dsw-alias-state-warn-primary,#ea580c);color:#fff}.eGUBIq_catsWrap{flex-wrap:wrap;flex:1;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_catsCollapsed{max-height:62px;overflow:hidden}.eGUBIq_catsToggle.eGUBIq_catsToggle{height:26px;min-height:26px;color:var(--dsw-alias-label-secondary,#6b7280);padding:0 6px}.eGUBIq_cmdDetails{margin:0}.eGUBIq_cmdSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.eGUBIq_cmdSummary::-webkit-details-marker{display:none}.eGUBIq_cmdSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.eGUBIq_cmdDetails[open]>.eGUBIq_cmdSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.eGUBIq_cmdSummary:hover{color:var(--dsw-alias-label-primary,#1f2328)}.eGUBIq_cmd{background:var(--dsw-alias-bg-layer-2,#f3f4f6);word-break:break-all;border-radius:6px;margin:8px 0 0;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:18px}.eGUBIq_warnLine{color:var(--dsw-alias-state-warn-primary,#b45309);margin:0;font-size:12px;font-weight:600;line-height:18px}.eGUBIq_modalNote{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:12px 0 0;font-size:12px;line-height:18px}.eGUBIq_grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.eGUBIq_sect{color:var(--dsw-alias-label-secondary,#6b7280);margin:14px 2px 8px;font-size:12px;font-weight:600}.eGUBIq_sect:first-child{margin-top:2px}.eGUBIq_swatches{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;gap:0;height:34px;display:flex;overflow:hidden}.eGUBIq_themesGrid{margin-bottom:12px}.eGUBIq_swatches i{flex:1}.eGUBIq_card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.eGUBIq_row1{align-items:center;gap:10px;min-width:0;display:flex}.eGUBIq_av{color:#fff;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border-radius:8px;flex-shrink:0;place-items:center;width:32px;height:32px;font-size:14px;font-weight:700;display:grid}.eGUBIq_nm{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:22px;overflow:hidden}.eGUBIq_owner{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_desc{color:var(--dsw-alias-label-tertiary,#8b93a1);min-height:36px;margin:0;font-size:12px;line-height:18px}.eGUBIq_foot{align-items:center;gap:8px;margin-top:auto;display:flex}.eGUBIq_grow{flex:1}.eGUBIq_titleRow{align-items:center;gap:10px;display:flex}.eGUBIq_descTight{min-height:0}.eGUBIq_src{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px;text-decoration:none}.eGUBIq_src:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_dot{vertical-align:2px;margin-left:5px}.eGUBIq_loading{color:var(--dsw-alias-label-secondary,#9ca3af);flex-direction:column;align-items:center;gap:12px;padding:48px;font-size:13px;display:flex}.eGUBIq_spin{border:3px solid var(--dsw-alias-border-l1,#e5e7eb);border-top-color:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;width:22px;height:22px;animation:.8s linear infinite eGUBIq_sp}@keyframes eGUBIq_sp{to{transform:rotate(360deg)}}.eGUBIq_progress{background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:8px;align-items:center;gap:9px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_irow .eGUBIq_progress{margin-top:8px}.eGUBIq_progress .eGUBIq_spin{border-width:2px;flex-shrink:0;width:14px;height:14px}.eGUBIq_progress code{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden}.eGUBIq_empty{color:var(--dsw-alias-label-secondary,#9ca3af);text-align:center;padding:32px;font-size:13px}.eGUBIq_err{color:var(--dsw-alias-state-error-primary,#dc2626);white-space:pre-wrap;word-break:break-all;margin:8px 0;font-size:12px}.eGUBIq_irow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;align-items:center;gap:10px;margin-bottom:8px;padding:12px 14px;display:flex}.eGUBIq_irow>.eGUBIq_src,.eGUBIq_irow>.eGUBIq_owner,.eGUBIq_irow>.eGUBIq_btn{white-space:nowrap;flex-shrink:0}.eGUBIq_spec{color:var(--dsw-alias-label-secondary,#9ca3af);font-family:ui-monospace,Menlo,monospace;font-size:11px}";
267
+ const css = ".eGUBIq_root{min-width:0;height:100%;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;display:flex;position:relative}.eGUBIq_head{flex-direction:column;gap:12px;padding:4px 4px 12px;display:flex}.eGUBIq_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.eGUBIq_sub{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0;font-size:14px;line-height:22px}.eGUBIq_searchInline{flex-shrink:0;width:200px;margin-bottom:6px}.eGUBIq_tabs{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-end;gap:2px;display:flex}.eGUBIq_tab{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;padding:7px 12px;font-size:13px}.eGUBIq_tab.eGUBIq_on{color:var(--dsw-alias-brand-primary,#4f6ef7);border-bottom-color:var(--dsw-alias-brand-primary,#4f6ef7);font-weight:600}.eGUBIq_restart{background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;align-items:center;gap:8px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_body{flex:1;padding:12px 4px 24px;overflow-x:hidden;overflow-y:auto}.eGUBIq_cats{z-index:5;background:var(--dsw-alias-bg-layer-2,#f7f8fa);margin:-12px -4px 2px;padding:12px 4px 4px;position:sticky;top:-13px}.eGUBIq_catsRow{align-items:flex-start;gap:8px;display:flex;position:relative}.eGUBIq_sort{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#e5e7eb);border-radius:8px;flex-shrink:0;gap:2px;padding:2px;display:flex}.eGUBIq_sort button{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:3px 10px;font-size:12px}.eGUBIq_sort button.eGUBIq_on{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-weight:600;box-shadow:0 1px 3px #00000014}.eGUBIq_star{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_top{z-index:20;border:1px solid var(--dsw-alias-border-l1,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);width:38px;height:38px;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;border-radius:99px;font-size:16px;position:absolute;bottom:18px;right:18px;box-shadow:0 4px 14px #0000001f}.eGUBIq_top:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_tag{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px}.eGUBIq_okState{color:var(--dsw-alias-state-success-primary,#16a34a);white-space:nowrap;font-size:12px;font-weight:600}.eGUBIq_dangerBtn.eGUBIq_dangerBtn{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_dangerArmed.eGUBIq_dangerArmed{background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}.eGUBIq_warnBtn.eGUBIq_warnBtn{background:var(--dsw-alias-state-warn-primary,#ea580c);color:#fff}.eGUBIq_catsWrap{flex-wrap:wrap;flex:1;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_catsCollapsed{max-height:62px;overflow:hidden}.eGUBIq_catsToggle.eGUBIq_catsToggle{height:26px;min-height:26px;color:var(--dsw-alias-label-secondary,#6b7280);padding:0 6px}.eGUBIq_cmdDetails{margin:0}.eGUBIq_cmdSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.eGUBIq_cmdSummary::-webkit-details-marker{display:none}.eGUBIq_cmdSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.eGUBIq_cmdDetails[open]>.eGUBIq_cmdSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.eGUBIq_cmdSummary:hover{color:var(--dsw-alias-label-primary,#1f2328)}.eGUBIq_cmd{background:var(--dsw-alias-bg-layer-2,#f3f4f6);word-break:break-all;border-radius:6px;margin:8px 0 0;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:18px}.eGUBIq_warnLine{color:var(--dsw-alias-state-warn-primary,#b45309);margin:0;font-size:12px;font-weight:600;line-height:18px}.eGUBIq_modalNote{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:12px 0 0;font-size:12px;line-height:18px}.eGUBIq_grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.eGUBIq_sect{color:var(--dsw-alias-label-secondary,#6b7280);margin:14px 2px 8px;font-size:12px;font-weight:600}.eGUBIq_sect:first-child{margin-top:2px}.eGUBIq_swatches{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;gap:0;height:34px;display:flex;overflow:hidden}.eGUBIq_themesGrid{margin-bottom:12px}.eGUBIq_swatches i{flex:1}.eGUBIq_card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.eGUBIq_row1{align-items:center;gap:10px;min-width:0;display:flex}.eGUBIq_av{color:#fff;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border-radius:8px;flex-shrink:0;place-items:center;width:32px;height:32px;font-size:14px;font-weight:700;display:grid}.eGUBIq_nm{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:22px;overflow:hidden}.eGUBIq_owner{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_desc{color:var(--dsw-alias-label-tertiary,#8b93a1);min-height:36px;margin:0;font-size:12px;line-height:18px}.eGUBIq_foot{align-items:center;gap:8px;margin-top:auto;display:flex}.eGUBIq_grow{flex:1}.eGUBIq_titleRow{align-items:center;gap:10px;display:flex}.eGUBIq_descTight{min-height:0}.eGUBIq_src{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px;text-decoration:none}.eGUBIq_src:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_dot{vertical-align:2px;margin-left:5px}.eGUBIq_loading{color:var(--dsw-alias-label-secondary,#9ca3af);flex-direction:column;align-items:center;gap:12px;padding:48px;font-size:13px;display:flex}.eGUBIq_spin{border:3px solid var(--dsw-alias-border-l1,#e5e7eb);border-top-color:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;width:22px;height:22px;animation:.8s linear infinite eGUBIq_sp}@keyframes eGUBIq_sp{to{transform:rotate(360deg)}}.eGUBIq_progress{background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:8px;align-items:center;gap:9px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_irow .eGUBIq_progress{margin-top:8px}.eGUBIq_progress .eGUBIq_spin{border-width:2px;flex-shrink:0;width:14px;height:14px}.eGUBIq_progress code{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden}.eGUBIq_empty{color:var(--dsw-alias-label-secondary,#9ca3af);text-align:center;padding:32px;font-size:13px}.eGUBIq_err{color:var(--dsw-alias-state-error-primary,#dc2626);white-space:pre-wrap;word-break:break-all;margin:8px 0;font-size:12px}.eGUBIq_irow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;align-items:center;gap:10px;margin-bottom:8px;padding:12px 14px;display:flex}.eGUBIq_irow>.eGUBIq_src,.eGUBIq_irow>.eGUBIq_owner,.eGUBIq_dangerBtn.eGUBIq_dangerBtn,.eGUBIq_warnBtn.eGUBIq_warnBtn,.eGUBIq_dangerArmed.eGUBIq_dangerArmed{white-space:nowrap;flex-shrink:0}.eGUBIq_spec{color:var(--dsw-alias-label-secondary,#9ca3af);font-family:ui-monospace,Menlo,monospace;font-size:11px}.eGUBIq_staleAction{margin-top:8px}";
209
268
  const tagId = "dshmarket/Market.module.css";
210
269
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
211
270
  const tag = document.createElement("style");
@@ -215,59 +274,59 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
215
274
  document.head.appendChild(tag);
216
275
  }
217
276
  var Market_module_css_default = {
218
- "head": "eGUBIq_head",
219
277
  "grid": "eGUBIq_grid",
220
- "restart": "eGUBIq_restart",
221
- "nm": "eGUBIq_nm",
222
- "row1": "eGUBIq_row1",
223
- "err": "eGUBIq_err",
278
+ "cmdDetails": "eGUBIq_cmdDetails",
279
+ "title": "eGUBIq_title",
280
+ "body": "eGUBIq_body",
224
281
  "empty": "eGUBIq_empty",
225
- "owner": "eGUBIq_owner",
226
- "tabs": "eGUBIq_tabs",
282
+ "nm": "eGUBIq_nm",
283
+ "on": "eGUBIq_on",
284
+ "loading": "eGUBIq_loading",
285
+ "card": "eGUBIq_card",
227
286
  "modalNote": "eGUBIq_modalNote",
287
+ "catsRow": "eGUBIq_catsRow",
288
+ "cats": "eGUBIq_cats",
289
+ "foot": "eGUBIq_foot",
290
+ "tabs": "eGUBIq_tabs",
291
+ "catsWrap": "eGUBIq_catsWrap",
292
+ "titleRow": "eGUBIq_titleRow",
293
+ "warnLine": "eGUBIq_warnLine",
294
+ "src": "eGUBIq_src",
228
295
  "cmdSummary": "eGUBIq_cmdSummary",
229
- "swatches": "eGUBIq_swatches",
230
- "spec": "eGUBIq_spec",
231
- "okState": "eGUBIq_okState",
232
- "catsCollapsed": "eGUBIq_catsCollapsed",
233
- "irow": "eGUBIq_irow",
234
- "btn": "eGUBIq_btn",
235
- "star": "eGUBIq_star",
296
+ "restart": "eGUBIq_restart",
297
+ "root": "eGUBIq_root",
298
+ "dangerArmed": "eGUBIq_dangerArmed",
299
+ "desc": "eGUBIq_desc",
300
+ "err": "eGUBIq_err",
301
+ "top": "eGUBIq_top",
302
+ "catsToggle": "eGUBIq_catsToggle",
236
303
  "tab": "eGUBIq_tab",
237
- "cmdDetails": "eGUBIq_cmdDetails",
238
- "searchInline": "eGUBIq_searchInline",
304
+ "av": "eGUBIq_av",
305
+ "dangerBtn": "eGUBIq_dangerBtn",
306
+ "dot": "eGUBIq_dot",
307
+ "irow": "eGUBIq_irow",
239
308
  "cmd": "eGUBIq_cmd",
309
+ "swatches": "eGUBIq_swatches",
310
+ "spin": "eGUBIq_spin",
240
311
  "sort": "eGUBIq_sort",
241
- "body": "eGUBIq_body",
242
- "catsWrap": "eGUBIq_catsWrap",
243
- "sect": "eGUBIq_sect",
312
+ "spec": "eGUBIq_spec",
313
+ "grow": "eGUBIq_grow",
314
+ "star": "eGUBIq_star",
244
315
  "themesGrid": "eGUBIq_themesGrid",
245
- "av": "eGUBIq_av",
246
- "dangerBtn": "eGUBIq_dangerBtn",
247
- "top": "eGUBIq_top",
248
- "desc": "eGUBIq_desc",
249
- "titleRow": "eGUBIq_titleRow",
316
+ "okState": "eGUBIq_okState",
317
+ "sect": "eGUBIq_sect",
318
+ "staleAction": "eGUBIq_staleAction",
319
+ "sub": "eGUBIq_sub",
320
+ "row1": "eGUBIq_row1",
321
+ "owner": "eGUBIq_owner",
250
322
  "sp": "eGUBIq_sp",
323
+ "head": "eGUBIq_head",
251
324
  "warnBtn": "eGUBIq_warnBtn",
252
- "warnLine": "eGUBIq_warnLine",
253
- "src": "eGUBIq_src",
254
- "spin": "eGUBIq_spin",
325
+ "catsCollapsed": "eGUBIq_catsCollapsed",
255
326
  "progress": "eGUBIq_progress",
256
- "sub": "eGUBIq_sub",
257
- "title": "eGUBIq_title",
258
- "descTight": "eGUBIq_descTight",
259
- "catsRow": "eGUBIq_catsRow",
260
- "catsToggle": "eGUBIq_catsToggle",
261
- "cats": "eGUBIq_cats",
327
+ "searchInline": "eGUBIq_searchInline",
262
328
  "tag": "eGUBIq_tag",
263
- "dangerArmed": "eGUBIq_dangerArmed",
264
- "grow": "eGUBIq_grow",
265
- "root": "eGUBIq_root",
266
- "on": "eGUBIq_on",
267
- "card": "eGUBIq_card",
268
- "foot": "eGUBIq_foot",
269
- "dot": "eGUBIq_dot",
270
- "loading": "eGUBIq_loading"
329
+ "descTight": "eGUBIq_descTight"
271
330
  };
272
331
  //#endregion
273
332
  //#region src/client/MarketSection.tsx
@@ -317,6 +376,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
317
376
  const [installError, setInstallError] = (0, react.useState)(null);
318
377
  const [updates, setUpdates] = (0, react.useState)({});
319
378
  const [updatingName, setUpdatingName] = (0, react.useState)(null);
379
+ const [staleName, setStaleName] = (0, react.useState)(null);
320
380
  const [updatingAll, setUpdatingAll] = (0, react.useState)(false);
321
381
  const [updatedNames, setUpdatedNames] = (0, react.useState)([]);
322
382
  const [hotUrls, setHotUrls] = (0, react.useState)([]);
@@ -423,19 +483,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
423
483
  updatingName,
424
484
  data
425
485
  ]);
426
- const plugins = (0, react.useMemo)(() => {
427
- if (data === null) return [];
428
- const query = q.trim().toLowerCase();
429
- const list = data.plugins.filter((p) => {
430
- if (cat !== "all" && p.category !== cat) return false;
431
- if (query === "") return true;
432
- const desc = p.description && (p.description[lang] || p.description.en) || "";
433
- return p.name.toLowerCase().includes(query) || p.owner.toLowerCase().includes(query) || desc.toLowerCase().includes(query);
434
- });
435
- if (sort === "hot") return [...list].sort((a, b) => (b.stars ?? -1) - (a.stars ?? -1));
436
- if (sort === "new") return [...list].sort((a, b) => String(b.added).localeCompare(String(a.added)));
437
- return list;
438
- }, [
486
+ const plugins = (0, react.useMemo)(() => data === null ? [] : visiblePlugins(data.plugins, {
487
+ category: cat,
488
+ query: q,
489
+ lang,
490
+ sort
491
+ }), [
439
492
  data,
440
493
  q,
441
494
  cat,
@@ -479,13 +532,17 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
479
532
  setInstallError(t("installFail") + ": " + String(error));
480
533
  }).finally(() => setBusyUrl(null));
481
534
  }, [refreshInstalled, t]);
482
- const doUpdate = (0, react.useCallback)((name) => {
535
+ const doUpdate = (0, react.useCallback)((name, force = false) => {
483
536
  setInstallError(null);
537
+ setStaleName(null);
484
538
  setUpdatingName(name);
485
539
  return fetch("/dsh-market/update", {
486
540
  method: "POST",
487
541
  headers: { "content-type": "application/json" },
488
- body: JSON.stringify({ name })
542
+ body: JSON.stringify(force ? {
543
+ name,
544
+ force: true
545
+ } : { name })
489
546
  }).then((res) => res.json().then((body) => ({
490
547
  status: res.status,
491
548
  body
@@ -494,6 +551,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
494
551
  setUpdatedNames((names) => names.concat(name));
495
552
  refreshInstalled();
496
553
  } else {
554
+ if (body.stale === true) setStaleName(name);
497
555
  const text = (v) => typeof v === "string" ? v : v && typeof v.text === "string" ? v.text : v == null ? "" : JSON.stringify(v);
498
556
  const detail = text(body.error) || [text(body.stderr), text(body.stdout)].filter(Boolean).join("\n").trim() || "exit " + body.exitCode;
499
557
  setInstallError(t("updateFail") + ": " + name + " — " + detail.trim().slice(-600));
@@ -556,7 +614,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
556
614
  }, [updatableNames, doUpdate]);
557
615
  const pendingRestart = doneUrls.length + updatedNames.length + removedCount;
558
616
  const hasUpdates = Object.keys(installed).some((name) => !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable);
559
- const themePlugins = data === null ? [] : data.plugins.filter((p) => p.category === "theme").sort((a, b) => (b.stars || 0) - (a.stars || 0));
617
+ const themePlugins$1 = data === null ? [] : themePlugins(data.plugins);
560
618
  const pluginCard = (p) => {
561
619
  const desc = p.description && (p.description[lang] || p.description.en) || "";
562
620
  const done = doneUrls.includes(p.url) || hotUrls.includes(p.url);
@@ -641,14 +699,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
641
699
  ]
642
700
  }, p.url);
643
701
  };
644
- const installedNameOf = (p) => {
645
- if (installed[p.name] !== void 0) return p.name;
646
- const repo = repoOf(p.url);
647
- if (repo === null) return null;
648
- const needle = ("github:" + repo).toLowerCase();
649
- for (const [name, spec] of Object.entries(installed)) if (String(spec).toLowerCase().includes(needle)) return name;
650
- return null;
651
- };
702
+ const installedNameOf = (p) => matchInstalledName(p, installed);
652
703
  const bootEntries = typeof window !== "undefined" && window.__DSH_BOOT__ && Array.isArray(window.__DSH_BOOT__.entries) ? window.__DSH_BOOT__.entries : [];
653
704
  const themePluginCard = (p) => {
654
705
  const instName = installedNameOf(p);
@@ -947,9 +998,16 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
947
998
  })
948
999
  ]
949
1000
  }),
950
- installError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1001
+ installError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
951
1002
  className: Market_module_css_default.err,
952
- children: installError
1003
+ children: [installError, staleName !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1004
+ className: Market_module_css_default.staleAction,
1005
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1006
+ size: "sm",
1007
+ onClick: () => doUpdate(staleName, true),
1008
+ children: t("updateNow")
1009
+ })
1010
+ })]
953
1011
  }),
954
1012
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
955
1013
  className: Market_module_css_default.body,
@@ -969,7 +1027,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
969
1027
  ref: catsWrapRef,
970
1028
  className: catsOpen || visibleCats === null ? `${Market_module_css_default.catsWrap} ${Market_module_css_default.catsCollapsed}` : Market_module_css_default.catsWrap,
971
1029
  children: (() => {
972
- const ordered = catsOpen || cat === "all" ? categories : [cat, ...categories.filter((id) => id !== cat)];
1030
+ const ordered = orderedCategories(categories, cat, catsOpen);
973
1031
  const shown = catsOpen || visibleCats === null ? ordered : ordered.slice(0, Math.max(0, visibleCats - 1));
974
1032
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
975
1033
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
@@ -1018,17 +1076,17 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1018
1076
  })(), data === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1019
1077
  className: Market_module_css_default.loading,
1020
1078
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.spin }), t("loading")]
1021
- }) : themePlugins.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1079
+ }) : themePlugins$1.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1022
1080
  className: Market_module_css_default.empty,
1023
1081
  children: t("themeEmpty")
1024
1082
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1025
1083
  className: Market_module_css_default.grid,
1026
- children: themePlugins.map(themePluginCard)
1084
+ children: themePlugins$1.map(themePluginCard)
1027
1085
  })] }) : Object.keys(installed).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1028
1086
  className: Market_module_css_default.empty,
1029
1087
  children: t("installedEmpty")
1030
1088
  }) : Object.entries(installed).map(([name, spec]) => {
1031
- const entry = data === null ? void 0 : data.plugins.find((p) => p.name === name || repoOf(p.url) !== null && String(spec).toLowerCase().includes(("github:" + repoOf(p.url)).toLowerCase()));
1089
+ const entry = data === null ? void 0 : entryForDep(data.plugins, name, String(spec));
1032
1090
  const status = updates[name];
1033
1091
  const version = status && status.version ? "v" + status.version : "";
1034
1092
  const specText = String(spec);