dshmarket 1.2.0 → 1.2.2

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/client/client.js CHANGED
@@ -148,14 +148,44 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
148
148
  const desc = plugin.description && (plugin.description[lang] || plugin.description.en) || "";
149
149
  return /\b(tui|cli|tty|terminal)\b|终端|命令行/i.test(plugin.name + " " + desc);
150
150
  }
151
- /** A registry plugin counts as installed when its package name, npm name, or GitHub spec appears in the profile dependencies. */
152
- function isInstalled(plugin, installed) {
153
- if (installed[plugin.name] !== void 0) return true;
154
- if (plugin.npm && installed[plugin.npm] !== void 0) return true;
151
+ /**
152
+ * Unified installed-state matching (#15): both sides collapse to lowercase
153
+ * identity sets the registry entry contributes its bare name, npm name and
154
+ * owner/repo; the dependency contributes its key and the repo inside its
155
+ * spec — and any exact intersection counts. Exact equality, not substrings,
156
+ * so prefix-related repo names cannot cross-match.
157
+ */
158
+ function entryIdentities(plugin) {
159
+ const ids = /* @__PURE__ */ new Set([plugin.name.toLowerCase()]);
160
+ if (plugin.npm) ids.add(plugin.npm.toLowerCase());
155
161
  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));
162
+ if (repo !== null) ids.add(repo.toLowerCase());
163
+ return ids;
164
+ }
165
+ function depIdentities(name, spec) {
166
+ const ids = /* @__PURE__ */ new Set([name.toLowerCase()]);
167
+ const scoped = /^@([^/]+)\/(.+)$/.exec(name);
168
+ if (scoped !== null) ids.add(`${scoped[1].toLowerCase()}/${scoped[2].toLowerCase()}`);
169
+ const match = /github:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)/i.exec(spec);
170
+ if (match !== null) ids.add(match[1].toLowerCase());
171
+ return ids;
172
+ }
173
+ /** The installed dependency name a registry entry corresponds to, or null. */
174
+ function matchInstalledName(plugin, installed) {
175
+ const ids = entryIdentities(plugin);
176
+ for (const [name, spec] of Object.entries(installed)) for (const id of depIdentities(name, String(spec))) if (ids.has(id)) return name;
177
+ return null;
178
+ }
179
+ /** The registry entry an installed dependency corresponds to, or undefined. */
180
+ function entryForDep(plugins, name, spec) {
181
+ const ids = depIdentities(name, String(spec));
182
+ return plugins.find((plugin) => {
183
+ for (const id of entryIdentities(plugin)) if (ids.has(id)) return true;
184
+ return false;
185
+ });
186
+ }
187
+ function isInstalled(plugin, installed) {
188
+ return matchInstalledName(plugin, installed) !== null;
159
189
  }
160
190
  /**
161
191
  * The brand mark (assets/logo.svg — shared block-grid mark with
@@ -215,59 +245,59 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
215
245
  document.head.appendChild(tag);
216
246
  }
217
247
  var Market_module_css_default = {
218
- "dangerBtn": "eGUBIq_dangerBtn",
219
- "cmdDetails": "eGUBIq_cmdDetails",
220
- "catsRow": "eGUBIq_catsRow",
248
+ "tabs": "eGUBIq_tabs",
249
+ "swatches": "eGUBIq_swatches",
250
+ "star": "eGUBIq_star",
251
+ "okState": "eGUBIq_okState",
221
252
  "themesGrid": "eGUBIq_themesGrid",
253
+ "dangerBtn": "eGUBIq_dangerBtn",
254
+ "modalNote": "eGUBIq_modalNote",
222
255
  "nm": "eGUBIq_nm",
223
- "desc": "eGUBIq_desc",
224
- "loading": "eGUBIq_loading",
225
- "warnBtn": "eGUBIq_warnBtn",
226
- "root": "eGUBIq_root",
227
- "title": "eGUBIq_title",
228
- "tab": "eGUBIq_tab",
229
- "restart": "eGUBIq_restart",
230
- "av": "eGUBIq_av",
231
- "owner": "eGUBIq_owner",
232
- "titleRow": "eGUBIq_titleRow",
233
- "searchInline": "eGUBIq_searchInline",
234
- "body": "eGUBIq_body",
235
- "grid": "eGUBIq_grid",
256
+ "btn": "eGUBIq_btn",
236
257
  "descTight": "eGUBIq_descTight",
237
- "progress": "eGUBIq_progress",
238
- "card": "eGUBIq_card",
239
258
  "empty": "eGUBIq_empty",
240
- "irow": "eGUBIq_irow",
241
- "catsToggle": "eGUBIq_catsToggle",
259
+ "cats": "eGUBIq_cats",
260
+ "title": "eGUBIq_title",
261
+ "on": "eGUBIq_on",
262
+ "row1": "eGUBIq_row1",
263
+ "body": "eGUBIq_body",
264
+ "top": "eGUBIq_top",
265
+ "searchInline": "eGUBIq_searchInline",
266
+ "sub": "eGUBIq_sub",
242
267
  "catsWrap": "eGUBIq_catsWrap",
243
- "spec": "eGUBIq_spec",
244
268
  "head": "eGUBIq_head",
245
- "swatches": "eGUBIq_swatches",
246
- "grow": "eGUBIq_grow",
247
- "spin": "eGUBIq_spin",
248
- "catsCollapsed": "eGUBIq_catsCollapsed",
249
- "sp": "eGUBIq_sp",
250
- "tabs": "eGUBIq_tabs",
269
+ "av": "eGUBIq_av",
270
+ "warnBtn": "eGUBIq_warnBtn",
271
+ "owner": "eGUBIq_owner",
272
+ "cmdDetails": "eGUBIq_cmdDetails",
273
+ "restart": "eGUBIq_restart",
274
+ "catsRow": "eGUBIq_catsRow",
275
+ "foot": "eGUBIq_foot",
251
276
  "src": "eGUBIq_src",
252
- "on": "eGUBIq_on",
277
+ "root": "eGUBIq_root",
278
+ "tab": "eGUBIq_tab",
279
+ "desc": "eGUBIq_desc",
280
+ "sect": "eGUBIq_sect",
281
+ "catsToggle": "eGUBIq_catsToggle",
282
+ "card": "eGUBIq_card",
283
+ "titleRow": "eGUBIq_titleRow",
253
284
  "dot": "eGUBIq_dot",
254
- "cats": "eGUBIq_cats",
255
- "sort": "eGUBIq_sort",
256
- "row1": "eGUBIq_row1",
285
+ "progress": "eGUBIq_progress",
257
286
  "tag": "eGUBIq_tag",
287
+ "sp": "eGUBIq_sp",
258
288
  "cmd": "eGUBIq_cmd",
259
- "top": "eGUBIq_top",
260
- "sub": "eGUBIq_sub",
261
- "warnLine": "eGUBIq_warnLine",
262
- "sect": "eGUBIq_sect",
263
289
  "cmdSummary": "eGUBIq_cmdSummary",
264
- "btn": "eGUBIq_btn",
265
- "star": "eGUBIq_star",
266
- "foot": "eGUBIq_foot",
290
+ "grid": "eGUBIq_grid",
291
+ "catsCollapsed": "eGUBIq_catsCollapsed",
292
+ "sort": "eGUBIq_sort",
293
+ "grow": "eGUBIq_grow",
294
+ "warnLine": "eGUBIq_warnLine",
295
+ "dangerArmed": "eGUBIq_dangerArmed",
296
+ "loading": "eGUBIq_loading",
297
+ "spin": "eGUBIq_spin",
298
+ "irow": "eGUBIq_irow",
267
299
  "err": "eGUBIq_err",
268
- "modalNote": "eGUBIq_modalNote",
269
- "okState": "eGUBIq_okState",
270
- "dangerArmed": "eGUBIq_dangerArmed"
300
+ "spec": "eGUBIq_spec"
271
301
  };
272
302
  //#endregion
273
303
  //#region src/client/MarketSection.tsx
@@ -471,7 +501,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
471
501
  refreshInstalled();
472
502
  } else {
473
503
  const text = (v) => typeof v === "string" ? v : v && typeof v.text === "string" ? v.text : v == null ? "" : JSON.stringify(v);
474
- const detail = text(body.error) || text(body.stderr) || text(body.stdout) || "exit " + body.exitCode;
504
+ const detail = text(body.error) || [text(body.stderr), text(body.stdout)].filter(Boolean).join("\n").trim() || "exit " + body.exitCode;
475
505
  setInstallError(t("installFail") + ": " + plugin.name + " — " + detail.trim().slice(-600));
476
506
  }
477
507
  }).catch((error) => {
@@ -495,7 +525,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
495
525
  refreshInstalled();
496
526
  } else {
497
527
  const text = (v) => typeof v === "string" ? v : v && typeof v.text === "string" ? v.text : v == null ? "" : JSON.stringify(v);
498
- const detail = text(body.error) || text(body.stderr) || text(body.stdout) || "exit " + body.exitCode;
528
+ const detail = text(body.error) || [text(body.stderr), text(body.stdout)].filter(Boolean).join("\n").trim() || "exit " + body.exitCode;
499
529
  setInstallError(t("updateFail") + ": " + name + " — " + detail.trim().slice(-600));
500
530
  }
501
531
  }).catch((error) => setInstallError(t("updateFail") + ": " + String(error))).finally(() => setUpdatingName(null));
@@ -641,14 +671,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
641
671
  ]
642
672
  }, p.url);
643
673
  };
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
- };
674
+ const installedNameOf = (p) => matchInstalledName(p, installed);
652
675
  const bootEntries = typeof window !== "undefined" && window.__DSH_BOOT__ && Array.isArray(window.__DSH_BOOT__.entries) ? window.__DSH_BOOT__.entries : [];
653
676
  const themePluginCard = (p) => {
654
677
  const instName = installedNameOf(p);
@@ -1028,7 +1051,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1028
1051
  className: Market_module_css_default.empty,
1029
1052
  children: t("installedEmpty")
1030
1053
  }) : 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()));
1054
+ const entry = data === null ? void 0 : entryForDep(data.plugins, name, String(spec));
1032
1055
  const status = updates[name];
1033
1056
  const version = status && status.version ? "v" + status.version : "";
1034
1057
  const specText = String(spec);
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["useState","Toast","useState","css","useSyncExternalStore","useRef","useCallback","useMemo","Button","StateDot","Input","IconSearchOutline16","Pill","IconChevronUpOutline14","IconChevronDownOutline14","Modal","h"],"sources":["../src/client/locales.ts","../src/client/market-data.ts","../src/client/InstallToast.tsx","../src/client/MarketSection.tsx","../src/client/index.ts"],"sourcesContent":["/** zh/en dictionaries for the Market settings section and install toast. */\n\nexport const zh = {\n nav: '插件市场',\n subtitle: '发现社区为 DeepSeek Harness 打造的能力',\n searchPh: '搜索插件,比如:通知、终端、记忆…',\n tabDiscover: '发现',\n tabInstalled: '已安装',\n all: '全部',\n install: '安装',\n installing: '安装中…',\n installedBadge: '✓ 已装好',\n alreadyInstalled: '✓ 已安装',\n restartBanner: '项变更完成,重启 DeepSeek Harness 后生效',\n uninstall: '卸载',\n confirmRemove: '确认卸载?',\n uninstalling: '卸载中…',\n restartHint: '重启方式:关闭当前 dsh 进程后重新运行(例如 dsh web)',\n confirmTitle: '安装',\n confirmWarn: '插件是社区第三方代码。安装即表示你信任该来源;构建脚本默认被禁止执行。',\n cancel: '取消',\n empty: '没有匹配的插件',\n installedEmpty: '还没有装过社区插件,去「发现」页逛逛吧',\n loadFail: '插件目录加载失败,请稍后重试',\n installFail: '安装失败',\n viewSource: '源码',\n hotBanner: '个新插件已装好,刷新页面即可使用',\n refresh: '刷新页面',\n update: '更新',\n updating: '更新中…',\n updated: '✓ 已更新,重启后生效',\n updateFail: '更新失败',\n upToDate: '已是最新',\n linkedDev: '本地开发链接',\n exportLog: '导出日志',\n readme: '使用说明',\n terminalWarn: '这看起来是终端/命令行插件:装进网页版可能无效,甚至导致 DeepSeek Harness 无法启动。建议先看它的使用说明,按说明装进对应的 profile。',\n envMissing: '还差一个小组件才能安装插件',\n envFix: '自动装好',\n envFixing: '正在准备…',\n envFixFail: '自动准备没成功,请点\"导出日志\"把文件发给我们反馈',\n loading: '正在加载插件目录…',\n backTop: '回到顶部',\n confirm: '确认',\n cmdDetails: '安装命令',\n catsMore: '更多分类',\n catsLess: '收起',\n sortHot: '最热',\n sortNew: '最新',\n marketUpdate: '市场有新版本,升级',\n updateAll: '全部更新',\n tabThemes: '主题',\n themeApply: '使用',\n themeActive: '使用中',\n themeEmpty: '目录里暂时还没有主题,敬请期待',\n progressHint: '首次安装需要下载与解析依赖,大插件可能要 1-3 分钟',\n toastReady: '已装好并已生效',\n toastTheme: '已启用。到 设置 → 插件市场 → 主题 可随时切换',\n gotIt: '知道了',\n}\n\n/** A dictionary key; zh is the source of truth, en mirrors it 1:1. */\nexport type MarketKey = keyof typeof zh\n\nexport const en: Record<MarketKey, string> = {\n nav: 'Plugin Market',\n subtitle: 'Discover community plugins for DeepSeek Harness',\n searchPh: 'Search plugins: notify, terminal, memory…',\n tabDiscover: 'Discover',\n tabInstalled: 'Installed',\n all: 'All',\n install: 'Install',\n installing: 'Installing…',\n installedBadge: '✓ Installed',\n alreadyInstalled: '✓ Installed',\n restartBanner: 'change(s) done — restart DeepSeek Harness to apply',\n uninstall: 'Uninstall',\n confirmRemove: 'Confirm?',\n uninstalling: 'Removing…',\n restartHint: 'To restart: stop the current dsh process and run it again (e.g. dsh web)',\n confirmTitle: 'Install',\n confirmWarn: 'Plugins are third-party community code. Installing means you trust this source; build scripts are blocked by default.',\n cancel: 'Cancel',\n empty: 'No plugins match',\n installedEmpty: 'No community plugins yet — browse the Discover tab',\n loadFail: 'Failed to load the plugin catalog, please retry later',\n installFail: 'Install failed',\n viewSource: 'Source',\n hotBanner: 'new plugin(s) ready — refresh the page to use them',\n refresh: 'Refresh',\n update: 'Update',\n updating: 'Updating…',\n updated: '✓ Updated — restart to apply',\n updateFail: 'Update failed',\n upToDate: 'Up to date',\n linkedDev: 'linked (dev)',\n exportLog: 'Export log',\n readme: 'README',\n terminalWarn: 'This looks like a terminal/CLI plugin: installing it into the web profile may do nothing, or even break DeepSeek Harness startup. Read its README and install it into the profile it targets.',\n envMissing: 'One small component is needed before installing plugins',\n envFix: 'Set up automatically',\n envFixing: 'Setting up…',\n envFixFail: 'Automatic setup failed — please use \"Export log\" and send us the file',\n loading: 'Loading the catalog…',\n backTop: 'Back to top',\n confirm: 'Confirm',\n cmdDetails: 'Install command',\n catsMore: 'More',\n catsLess: 'Less',\n sortHot: 'Top',\n sortNew: 'New',\n marketUpdate: 'Market update available — upgrade',\n updateAll: 'Update all',\n tabThemes: 'Themes',\n themeApply: 'Use',\n themeActive: 'Active',\n themeEmpty: 'No more theme plugins in the catalog yet — stay tuned',\n progressHint: 'First installs download and resolve dependencies — large plugins can take 1-3 minutes',\n toastReady: 'installed and live',\n toastTheme: 'is now active. Switch any time in Settings → Plugin Market → Themes',\n gotIt: 'Got it',\n}\n","/**\n * Response shapes of the /dsh-market/* host routes plus the pure helpers the\n * Market UI shares between its section and toast components.\n */\n\n/** Localized text keyed by language ('zh' / 'en'). */\nexport type LocalizedText = Record<string, string | undefined>\n\n/** One registry entry from /dsh-market/registry. */\nexport interface RegistryPlugin {\n name: string\n owner: string\n url: string\n npm?: string\n category: string\n description?: LocalizedText\n stars?: number\n added?: string\n install?: string\n}\n\n/** The catalog payload under `registry` in /dsh-market/registry. */\nexport interface Registry {\n count: number\n categories: Record<string, LocalizedText>\n plugins: RegistryPlugin[]\n}\n\n/** Profile dependency map: package name → install spec. */\nexport type InstalledMap = Record<string, string>\n\n/** Per-package update status from /dsh-market/updates. */\nexport interface UpdateStatus {\n updateAvailable?: boolean\n version?: string\n kind?: string\n}\n\n/** Poll payload from /dsh-market/status. */\nexport interface MarketStatus {\n active?: boolean\n lastLine?: string\n seconds?: number\n installed?: InstalledMap\n pnpm?: boolean\n boot?: string\n}\n\n/** Registered theme definition surfaced by the theme service snapshot. */\nexport interface ThemeDef {\n id: string\n colorScheme?: string\n tokens?: Record<string, string | undefined>\n}\n\n/** Theme service snapshot; null when the composition has no theme service. */\nexport interface ThemeSnapshot {\n preference: string\n themes: ThemeDef[]\n}\n\n/** Bound locale translator for the dsh-market namespace. */\nexport type Translate = (key: string) => string\n\nexport function avatarColor(name: string): string {\n let hash = 0\n for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0\n return 'hsl(' + (((hash % 360) + 360) % 360) + ' 55% 52%)'\n}\n\nexport function repoOf(url: string): string | null {\n // Plain repo urls plus /tree/<branch>/<subpath> monorepo links.\n const m = /^https:\\/\\/github\\.com\\/([^/]+\\/[^/]+?)(?:\\/tree\\/.+)?\\/?$/.exec(url)\n return m ? m[1]! : null\n}\n\nexport function readSession(key: string): any {\n try { return JSON.parse(sessionStorage.getItem(key) || 'null') } catch { return null }\n}\n\n/** Heuristic: plugins that target a terminal surface rather than the web UI. */\nexport function looksTerminal(plugin: RegistryPlugin, lang: string): boolean {\n const desc = (plugin.description && (plugin.description[lang] || plugin.description.en)) || ''\n return /\\b(tui|cli|tty|terminal)\\b|终端|命令行/i.test(plugin.name + ' ' + desc)\n}\n\n/** A registry plugin counts as installed when its package name, npm name, or GitHub spec appears in the profile dependencies. */\nexport function isInstalled(plugin: RegistryPlugin, installed: InstalledMap): boolean {\n if (installed[plugin.name] !== undefined) return true\n if (plugin.npm && installed[plugin.npm] !== undefined) return true\n const repo = repoOf(plugin.url)\n if (repo === null) return false\n const needle = ('github:' + repo).toLowerCase()\n return Object.values(installed).some(spec => String(spec).toLowerCase().includes(needle))\n}\n\n/**\n * The brand mark (assets/logo.svg — shared block-grid mark with\n * awesome-dsh-plugin), inlined so the header needs no extra request.\n */\nexport const LOGO_URI = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 128 128\"><rect width=\"128\" height=\"128\" rx=\"28\" fill=\"#f6f2ea\"/><g transform=\"translate(15.7 16.7) scale(0.3) translate(-112 -78)\"><g fill=\"#2b2620\"><rect x=\"112\" y=\"112\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"112\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"112\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"112\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"312\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"312\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/></g><rect x=\"346\" y=\"78\" width=\"88\" height=\"88\" rx=\"14\" fill=\"#c0392b\" transform=\"rotate(9 390 122)\"/></g></svg>')\n\n/** Four representative colors for a theme card's preview strip. */\nexport function themeSwatch(def: ThemeDef): string[] {\n const tk = def.tokens || {}\n const pick = (names: string[]) => { for (const n of names) { if (tk[n]) return tk[n]! } return null }\n const dark = def.colorScheme === 'dark'\n return [\n pick(['--dsw-alias-bg-base', '--dsw-alias-bg-layer-1']) || (dark ? '#0f1115' : '#ffffff'),\n pick(['--dsw-alias-bg-layer-2', '--dsw-alias-bg-overlay']) || (dark ? '#1a1d23' : '#f3f4f6'),\n pick(['--dsw-alias-brand-primary']) || '#4f6ef7',\n pick(['--dsw-alias-label-primary']) || (dark ? '#e5e7eb' : '#1f2328'),\n ]\n}\n","/**\n * Post-reload confirmation via the official Toast primitive: shown once after\n * the refresh that follows a hot install or theme switch, so the user lands\n * back in their flow with visible proof.\n */\nimport { useState } from 'react'\nimport { Toast } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { readSession } from './market-data.ts'\nimport type { Translate } from './market-data.ts'\n\nexport function InstallToast(props: { t: Translate }) {\n const t = props.t\n const [mode] = useState(() => {\n const value = sessionStorage.getItem('dshm-toast-mode')\n sessionStorage.removeItem('dshm-toast-mode')\n return value\n })\n const [names, setNames] = useState<string[]>(() => {\n const value = readSession('dshm-toast')\n sessionStorage.removeItem('dshm-toast')\n return Array.isArray(value) ? value : []\n })\n if (names.length === 0) return null\n return (\n <Toast\n text={names.join(', ') + ' ' + t(mode === 'theme' ? 'toastTheme' : 'toastReady')}\n icon={<span>✨</span>}\n onDone={() => setNames([])}\n />\n )\n}\n","/**\n * The Market settings section: Discover / Themes / Installed tabs over the\n * /dsh-market/* host routes, with install/update/uninstall flows and the\n * pending-restart bookkeeping in sessionStorage.\n */\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'\nimport { Button, IconChevronDownOutline14, IconChevronUpOutline14, IconSearchOutline16, Input, Modal, Pill, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'\nimport css from './Market.module.css'\nimport {\n avatarColor, isInstalled, LOGO_URI, looksTerminal, readSession, repoOf, themeSwatch,\n} from './market-data.ts'\nimport type {\n InstalledMap, Registry, RegistryPlugin, ThemeSnapshot, Translate, UpdateStatus,\n} from './market-data.ts'\n\n/**\n * Card avatar: the plugin owner's GitHub avatar (no API, browser-cached),\n * falling back to the initial-letter tile when it can't load.\n */\nfunction OwnerAvatar({ name, owner }: { name: string; owner: string }) {\n const [failed, setFailed] = useState(false)\n if (failed || owner === '') {\n return (\n <div className={css.av} style={{ background: avatarColor(name) }}>\n {name.replace(/^dsh[-_]/i, '').charAt(0).toUpperCase() || 'P'}\n </div>\n )\n }\n return (\n <img\n className={css.av}\n src={`https://github.com/${encodeURIComponent(owner)}.png?size=96`}\n alt=\"\"\n loading=\"lazy\"\n onError={() => setFailed(true)}\n />\n )\n}\n\nexport interface MarketSectionProps {\n t: Translate\n locale: {\n subscribe(callback: () => void): () => void\n getSnapshot(): { active: string }\n }\n theme: { setTheme(id: string): void }\n themeStore: {\n subscribe(callback: () => void): () => void\n getSnapshot(): ThemeSnapshot | null\n }\n}\n\nexport function MarketSection(props: MarketSectionProps) {\n const t = props.t\n const localeSnap = useSyncExternalStore(\n cb => props.locale.subscribe(cb),\n () => props.locale.getSnapshot(),\n )\n const lang = String(localeSnap.active).toLowerCase().startsWith('zh') ? 'zh' : 'en'\n // null when the composition has no theme service — the Themes tab hides.\n const themeSnap = useSyncExternalStore(\n props.themeStore.subscribe,\n props.themeStore.getSnapshot,\n )\n const [data, setData] = useState<Registry | null>(null)\n const [loadError, setLoadError] = useState(false)\n const [installed, setInstalled] = useState<InstalledMap>({})\n const [skins, setSkins] = useState<string[]>([])\n const [tab, setTab] = useState(() => {\n const saved = sessionStorage.getItem('dshm-tab')\n if (saved !== null) sessionStorage.removeItem('dshm-tab')\n return saved || 'discover'\n })\n const [q, setQ] = useState('')\n const [cat, setCat] = useState('all')\n const [confirming, setConfirming] = useState<RegistryPlugin | null>(null)\n const [busyUrl, setBusyUrl] = useState<string | null>(null)\n const [doneUrls, setDoneUrls] = useState<string[]>([])\n const [installError, setInstallError] = useState<string | null>(null)\n const [updates, setUpdates] = useState<Record<string, UpdateStatus>>({})\n const [updatingName, setUpdatingName] = useState<string | null>(null)\n const [updatingAll, setUpdatingAll] = useState(false)\n const [updatedNames, setUpdatedNames] = useState<string[]>([])\n const [hotUrls, setHotUrls] = useState<string[]>([])\n const [hotNames, setHotNames] = useState<string[]>([])\n const [progressLine, setProgressLine] = useState<string | null>(null)\n const [removeArmed, setRemoveArmed] = useState<string | null>(null)\n const [removingName, setRemovingName] = useState<string | null>(null)\n const [removedCount, setRemovedCount] = useState(0)\n const [envReady, setEnvReady] = useState(true)\n const [envFixing, setEnvFixing] = useState(false)\n const [envFailed, setEnvFailed] = useState(false)\n const [bootId, setBootId] = useState<string | null>(null)\n const [showTop, setShowTop] = useState(false)\n const bodyRef = useRef<HTMLDivElement | null>(null)\n const [sort, setSort] = useState('hot')\n const [catsOpen, setCatsOpen] = useState(false)\n // How many category pills fit in the two collapsed rows (measured once —\n // the settings panel width is fixed); null = measuring render with all\n // pills clamped, then slice so the chevron flows inline after the last one.\n const [visibleCats, setVisibleCats] = useState<number | null>(null)\n const catsWrapRef = useRef<HTMLDivElement | null>(null)\n\n const refreshInstalled = useCallback((force?: boolean) => {\n fetch('/dsh-market/installed', { cache: 'no-store' })\n .then(res => res.json())\n .then(body => {\n setInstalled(body.installed || {})\n setSkins(body.live || [])\n })\n .catch(() => {})\n fetch('/dsh-market/updates' + (force === true ? '?force=1' : ''), { cache: 'no-store' })\n .then(res => res.json())\n .then(body => setUpdates(body.updates || {}))\n .catch(() => {})\n }, [])\n\n useEffect(() => {\n fetch('/dsh-market/registry', { cache: 'no-store' })\n .then(res => { if (!res.ok) throw new Error('HTTP ' + res.status); return res.json() })\n .then(body => setData(body.registry))\n .catch(() => setLoadError(true))\n fetch('/dsh-market/status', { cache: 'no-store' })\n .then(res => res.json())\n .then(status => {\n setEnvReady(status.pnpm !== false)\n if (typeof status.boot === 'string') setBootId(status.boot)\n })\n .catch(() => {})\n refreshInstalled()\n }, [refreshInstalled])\n\n // Pending-restart flags survive tab switches and page reloads, scoped to\n // one host process: a different boot id means the restart happened and the\n // stale banner must not resurrect.\n useEffect(() => {\n if (bootId === null) return\n const saved = readSession('dshm-restart')\n if (saved === null) return\n if (saved.boot !== bootId) {\n sessionStorage.removeItem('dshm-restart')\n return\n }\n if (Array.isArray(saved.doneUrls) && saved.doneUrls.length > 0) setDoneUrls(saved.doneUrls)\n if (Array.isArray(saved.updated) && saved.updated.length > 0) setUpdatedNames(saved.updated)\n if (typeof saved.removed === 'number' && saved.removed > 0) setRemovedCount(saved.removed)\n }, [bootId])\n\n useEffect(() => {\n if (bootId === null) return\n if (doneUrls.length === 0 && updatedNames.length === 0 && removedCount === 0) return\n sessionStorage.setItem('dshm-restart', JSON.stringify({\n boot: bootId,\n doneUrls,\n updated: updatedNames,\n removed: removedCount,\n }))\n }, [bootId, doneUrls, updatedNames, removedCount])\n\n const fixEnv = useCallback(() => {\n setEnvFixing(true)\n setEnvFailed(false)\n fetch('/dsh-market/setup-pnpm', { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' })\n .then(res => res.json())\n .then(body => {\n if (body.ok) setEnvReady(true)\n else setEnvFailed(true)\n })\n .catch(() => setEnvFailed(true))\n .finally(() => setEnvFixing(false))\n }, [])\n\n // Recover an install whose HTTP response was lost (page navigated away or\n // the connection dropped): the pending marker survives in sessionStorage and\n // the poll below converges the button state from the host's ground truth.\n useEffect(() => {\n const pending = readSession('dshm-pending')\n if (pending !== null && typeof pending.url === 'string') setBusyUrl(pending.url)\n }, [])\n\n useEffect(() => {\n if (busyUrl === null && updatingName === null) {\n setProgressLine(null)\n return\n }\n const timer = setInterval(() => {\n fetch('/dsh-market/status', { cache: 'no-store' })\n .then(res => res.json())\n .then(status => {\n if (status.active) {\n setProgressLine((status.lastLine || '…') + ' (' + status.seconds + 's)')\n } else {\n setProgressLine(null)\n setInstalled(status.installed || {})\n const pending = readSession('dshm-pending')\n if (pending !== null && busyUrl !== null) {\n const nowInstalled = data !== null && data.plugins.some(p =>\n p.url === busyUrl && isInstalled(p, status.installed || {}))\n if (nowInstalled) {\n sessionStorage.removeItem('dshm-pending')\n setDoneUrls(urls => urls.includes(busyUrl) ? urls : urls.concat(busyUrl))\n setBusyUrl(null)\n }\n }\n }\n })\n .catch(() => {})\n }, 2000)\n return () => clearInterval(timer)\n }, [busyUrl, updatingName, data])\n\n const plugins = useMemo(() => {\n if (data === null) return []\n const query = q.trim().toLowerCase()\n const list = data.plugins.filter(p => {\n if (cat !== 'all' && p.category !== cat) return false\n if (query === '') return true\n const desc = (p.description && (p.description[lang] || p.description.en)) || ''\n return p.name.toLowerCase().includes(query)\n || p.owner.toLowerCase().includes(query)\n || desc.toLowerCase().includes(query)\n })\n if (sort === 'hot') {\n return [...list].sort((a, b) => (b.stars ?? -1) - (a.stars ?? -1))\n }\n if (sort === 'new') {\n return [...list].sort((a, b) => String(b.added).localeCompare(String(a.added)))\n }\n return list\n }, [data, q, cat, lang, sort])\n\n const doInstall = useCallback((plugin: RegistryPlugin) => {\n setConfirming(null)\n setInstallError(null)\n setBusyUrl(plugin.url)\n sessionStorage.setItem('dshm-pending', JSON.stringify({ url: plugin.url }))\n fetch('/dsh-market/install', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ url: plugin.url }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n sessionStorage.removeItem('dshm-pending')\n if (status === 200 && body.ok && body.hot && plugin.category === 'theme') {\n // Themes auto-activate on install; reload straight into the Themes\n // tab so the new look is on screen immediately.\n sessionStorage.setItem('dshm-toast', JSON.stringify([plugin.name]))\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n return\n }\n if (status === 200 && body.ok) {\n sessionStorage.setItem('dshm-tab', 'installed')\n if (body.hot) {\n setHotUrls(urls => urls.includes(plugin.url) ? urls : urls.concat(plugin.url))\n setHotNames(names => names.includes(plugin.name) ? names : names.concat(plugin.name))\n } else {\n setDoneUrls(urls => urls.includes(plugin.url) ? urls : urls.concat(plugin.url))\n }\n refreshInstalled()\n } else {\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n const detail = text(body.error) || text(body.stderr) || text(body.stdout) || ('exit ' + body.exitCode)\n setInstallError(t('installFail') + ': ' + plugin.name + ' — ' + detail.trim().slice(-600))\n }\n })\n .catch(error => {\n sessionStorage.removeItem('dshm-pending')\n setInstallError(t('installFail') + ': ' + String(error))\n })\n .finally(() => setBusyUrl(null))\n }, [refreshInstalled, t])\n\n const doUpdate = useCallback((name: string) => {\n setInstallError(null)\n setUpdatingName(name)\n return fetch('/dsh-market/update', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n setUpdatedNames(names => names.concat(name))\n refreshInstalled()\n } else {\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n const detail = text(body.error) || text(body.stderr) || text(body.stdout) || ('exit ' + body.exitCode)\n setInstallError(t('updateFail') + ': ' + name + ' — ' + detail.trim().slice(-600))\n }\n })\n .catch(error => setInstallError(t('updateFail') + ': ' + String(error)))\n .finally(() => setUpdatingName(null))\n }, [refreshInstalled, t])\n\n const doUseSkin = useCallback((name: string) => {\n setInstallError(null)\n fetch('/dsh-market/use-skin', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n sessionStorage.setItem('dshm-toast', JSON.stringify([name]))\n sessionStorage.setItem('dshm-toast-mode', 'theme')\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n } else {\n setInstallError(String(body.error || 'failed'))\n }\n })\n .catch(error => setInstallError(String(error)))\n }, [])\n\n const doUninstall = useCallback((name: string) => {\n setRemoveArmed(null)\n setInstallError(null)\n setRemovingName(name)\n return fetch('/dsh-market/uninstall', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n if (!body.hot) setRemovedCount(n => n + 1)\n refreshInstalled()\n } else {\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n setInstallError((text(body.error) || text(body.stderr) || 'error').trim().slice(-600))\n }\n })\n .catch(error => setInstallError(String(error)))\n .finally(() => setRemovingName(null))\n }, [refreshInstalled])\n\n // The market itself stays out of the batch: its update reloads this page\n // mid-run, which would strand the remaining items.\n const selfName = installed['dshmarket'] !== undefined ? 'dshmarket' : 'dsh-market'\n const updatableNames = Object.keys(installed).filter(\n name => name !== selfName && !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable,\n )\n\n const doUpdateAll = useCallback(() => {\n const names = updatableNames.slice()\n setUpdatingAll(true)\n const next = () => {\n const name = names.shift()\n if (name === undefined) {\n setUpdatingAll(false)\n return\n }\n doUpdate(name).then(next, next)\n }\n next()\n }, [updatableNames, doUpdate])\n\n const pendingRestart = doneUrls.length + updatedNames.length + removedCount\n const hasUpdates = Object.keys(installed).some(\n name => !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable,\n )\n\n const themePlugins = data === null ? [] : data.plugins\n .filter(p => p.category === 'theme')\n .sort((a, b) => (b.stars || 0) - (a.stars || 0))\n\n const pluginCard = (p: RegistryPlugin) => {\n const desc = (p.description && (p.description[lang] || p.description.en)) || ''\n const done = doneUrls.includes(p.url) || hotUrls.includes(p.url)\n const already = isInstalled(p, installed)\n const busy = busyUrl === p.url\n return (\n <div key={p.url} className={css.card}>\n <div className={css.row1}>\n <OwnerAvatar name={p.name} owner={p.owner || ''} />\n <div style={{ minWidth: 0 }}>\n <div className={css.nm}>{p.name}</div>\n <div className={css.owner}>\n {p.owner}\n {typeof p.stars === 'number' && <span className={css.star}>{' · ★ ' + p.stars}</span>}\n </div>\n </div>\n <span className={css.grow} />\n <a className={css.src} href={p.url} target=\"_blank\" rel=\"noreferrer\" style={{ alignSelf: 'flex-start', flexShrink: 0 }}>{t('viewSource')}</a>\n </div>\n <div className={css.desc}>{desc}</div>\n <div className={css.foot}>\n <span className={css.tag}>\n {(data!.categories[p.category] && (data!.categories[p.category]![lang] || data!.categories[p.category]!.en)) || p.category}\n </span>\n <span className={css.grow} />\n {done\n ? <span className={css.okState}>{t('installedBadge')}</span>\n : already\n ? <span className={css.okState}>{t('alreadyInstalled')}</span>\n : busy\n ? <Button variant=\"primary\" size=\"sm\" disabled>{t('installing')}</Button>\n : (\n <Button\n variant=\"primary\"\n size=\"sm\"\n disabled={busyUrl !== null || !envReady}\n onClick={() => setConfirming(p)}\n >{t('install')}</Button>\n )}\n </div>\n {busy && (\n <div className={css.progress}>\n <span className={css.spin} />\n <code className={css.grow}>{progressLine || t('progressHint')}</code>\n </div>\n )}\n </div>\n )\n }\n\n const installedNameOf = (p: RegistryPlugin) => {\n if (installed[p.name] !== undefined) return p.name\n const repo = repoOf(p.url)\n if (repo === null) return null\n const needle = ('github:' + repo).toLowerCase()\n for (const [name, spec] of Object.entries(installed)) {\n if (String(spec).toLowerCase().includes(needle)) return name\n }\n return null\n }\n\n // Plugins loaded at boot (bundle-layer skins) aren't in the shim list but\n // are just as live; the boot manifest is the page's own record of them.\n const bootEntries = (typeof window !== 'undefined' && window.__DSH_BOOT__ && Array.isArray(window.__DSH_BOOT__.entries))\n ? window.__DSH_BOOT__.entries\n : []\n\n // Unified card for the Themes tab: install → use/in-use → uninstall.\n const themePluginCard = (p: RegistryPlugin) => {\n const instName = installedNameOf(p)\n if (instName === null) return pluginCard(p)\n const mounted = skins.includes(instName) || bootEntries.some(e => e.id === instName)\n const desc = (p.description && (p.description[lang] || p.description.en)) || ''\n return (\n <div key={p.url} className={css.card}>\n <div className={css.row1}>\n <OwnerAvatar name={p.name} owner={p.owner || ''} />\n <div style={{ minWidth: 0 }}>\n <div className={css.nm}>{p.name}</div>\n <div className={css.owner}>\n {p.owner}\n {typeof p.stars === 'number' && <span className={css.star}>{' · ★ ' + p.stars}</span>}\n </div>\n </div>\n <span className={css.grow} />\n <a className={css.src} href={p.url} target=\"_blank\" rel=\"noreferrer\" style={{ alignSelf: 'flex-start', flexShrink: 0 }}>{t('viewSource')}</a>\n </div>\n <div className={css.desc}>{desc}</div>\n <div className={css.foot}>\n <span className={css.grow} />\n {removingName === instName\n ? <Button variant=\"outline\" size=\"sm\" className={css.dangerBtn} disabled>{t('uninstalling')}</Button>\n : removeArmed === instName\n ? (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.dangerArmed}\n onClick={() => doUninstall(instName).then(() => {\n if (mounted) {\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n }\n })}\n >{t('confirmRemove')}</Button>\n )\n : <Button variant=\"outline\" size=\"sm\" className={css.dangerBtn} onClick={() => setRemoveArmed(instName)}>{t('uninstall')}</Button>}\n {mounted\n ? <span className={css.okState}>{t('themeActive')}</span>\n : <Button variant=\"primary\" size=\"sm\" onClick={() => doUseSkin(instName)}>{t('themeApply')}</Button>}\n </div>\n </div>\n )\n }\n\n const themeCard = (id: string, label: string, swatch: string[]) => {\n const active = themeSnap !== null && themeSnap.preference === id\n return (\n <div key={'th-' + id} className={css.card}>\n <div className={css.swatches}>{swatch.map((c, i) => <i key={i} style={{ background: c }} />)}</div>\n <div className={css.foot}>\n <span className={css.nm}>{label}</span>\n <span className={css.grow} />\n {active\n ? <span className={css.okState}>{t('themeActive')}</span>\n : (\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={() => { try { props.theme.setTheme(id) } catch (error) { setInstallError(String(error)) } }}\n >{t('themeApply')}</Button>\n )}\n </div>\n </div>\n )\n }\n\n const categories = data === null ? [] : Object.keys(data.categories)\n\n useLayoutEffect(() => { setVisibleCats(null) }, [lang, categories.length])\n useLayoutEffect(() => {\n if (catsOpen || visibleCats !== null) return\n const el = catsWrapRef.current\n if (el === null) return\n const chips = [...el.children].filter((c): c is HTMLElement => (c as HTMLElement).dataset?.chip === '1')\n if (chips.length === 0) return\n const first = chips[0]!\n const rowThreeTop = first.offsetTop + (first.offsetHeight + 6) * 2 - 3\n let fits = 0\n for (const chip of chips) { if (chip.offsetTop < rowThreeTop) fits += 1 }\n // Reserve the tail slot of row two for the chevron itself.\n setVisibleCats(fits >= chips.length ? fits : Math.max(1, fits - 1))\n }, [catsOpen, visibleCats, data])\n\n return (\n <div className={css.root}>\n <div className={css.head}>\n <div className={css.titleRow}>\n <img src={LOGO_URI} width={22} height={22} alt=\"\" style={{ borderRadius: '5px', flexShrink: 0 }} />\n <h2 className={css.title}>{t('nav')}</h2>\n {(() => {\n const self = installed['dshmarket'] !== undefined ? 'dshmarket' : 'dsh-market'\n return updates[self] && updates[self].updateAvailable && !updatedNames.includes(self)\n && (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.warnBtn}\n disabled={updatingName !== null || busyUrl !== null}\n onClick={() => { setTab('installed'); doUpdate(self) }}\n >{updatingName === self ? t('updating') : t('marketUpdate')}</Button>\n )\n })()}\n {updatableNames.length >= 2 && (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.warnBtn}\n disabled={updatingAll || updatingName !== null || busyUrl !== null || removingName !== null}\n onClick={() => { setTab('installed'); doUpdateAll() }}\n >{updatingAll ? t('updating') : t('updateAll') + ' (' + updatableNames.length + ')'}</Button>\n )}\n </div>\n <div className={css.sub}>\n {t('subtitle') + (data ? ' · ' + data.count : '') + ' · '}\n <a className={css.src} href=\"/dsh-market/logs\" download=\"dsh-market-log.txt\">{t('exportLog')}</a>\n </div>\n <div className={css.tabs}>\n <button className={tab === 'discover' ? `${css.tab} ${css.on}` : css.tab} onClick={() => setTab('discover')}>{t('tabDiscover')}</button>\n {themeSnap !== null && <button className={tab === 'themes' ? `${css.tab} ${css.on}` : css.tab} onClick={() => setTab('themes')}>{t('tabThemes')}</button>}\n <button className={tab === 'installed' ? `${css.tab} ${css.on}` : css.tab} onClick={() => { setTab('installed'); refreshInstalled(true) }}>\n {t('tabInstalled') + (Object.keys(installed).length > 0 ? ' (' + Object.keys(installed).length + ')' : '')}\n {hasUpdates && <StateDot state=\"error\" size={7} className={css.dot} />}\n </button>\n <span className={css.grow} />\n <Input className={css.searchInline} icon={<IconSearchOutline16 size={14} />} placeholder={t('searchPh')} value={q} onChange={e => setQ(e.target.value)} />\n </div>\n {!envReady && (\n <div className={css.restart}>\n <span>🧩</span>\n <span className={css.grow}>{envFailed ? t('envFixFail') : t('envMissing')}</span>\n {!envFailed && (\n <Button variant=\"primary\" size=\"sm\" disabled={envFixing} onClick={fixEnv}>\n {envFixing ? t('envFixing') : t('envFix')}\n </Button>\n )}\n </div>\n )}\n {hotUrls.length > 0 && (\n <div className={css.restart}>\n <span>✨</span>\n <span className={css.grow}><b>{hotUrls.length}</b> {t('hotBanner')}</span>\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={() => {\n sessionStorage.setItem('dshm-toast', JSON.stringify(hotNames))\n sessionStorage.setItem('dshm-tab', 'installed')\n location.reload()\n }}\n >{t('refresh')}</Button>\n </div>\n )}\n {pendingRestart > 0 && (\n <div className={css.restart}>\n <span>🔄</span>\n <span className={css.grow}><b>{pendingRestart}</b> {t('restartBanner')}</span>\n <span title={t('restartHint')}>ℹ️</span>\n </div>\n )}\n </div>\n {installError !== null && <div className={css.err}>{installError}</div>}\n <div\n className={css.body}\n ref={bodyRef}\n onScroll={e => setShowTop(e.currentTarget.scrollTop > 400)}\n >\n {tab === 'discover'\n ? loadError\n ? <div className={css.empty}>{t('loadFail')}</div>\n : data === null\n ? <div className={css.loading}><span className={css.spin} />{t('loading')}</div>\n : (\n <>\n <div className={css.cats}>\n <div className={css.catsRow}>\n <div ref={catsWrapRef} className={catsOpen || visibleCats === null ? `${css.catsWrap} ${css.catsCollapsed}` : css.catsWrap}>\n {(() => {\n // Collapsed, the selected category is pulled to the front so it never hides.\n const ordered = catsOpen || cat === 'all' ? categories : [cat, ...categories.filter(id => id !== cat)]\n const shown = catsOpen || visibleCats === null ? ordered : ordered.slice(0, Math.max(0, visibleCats - 1))\n return (\n <>\n <Pill data-chip=\"1\" active={cat === 'all'} onClick={() => setCat('all')}>{t('all')}</Pill>\n {shown.map(id => (\n <Pill\n key={id}\n data-chip=\"1\"\n active={cat === id}\n onClick={() => setCat(id)}\n >{(data.categories[id] && (data.categories[id]![lang] || data.categories[id]!.en)) || id}</Pill>\n ))}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className={css.catsToggle}\n icon={catsOpen ? <IconChevronUpOutline14 size={14} /> : <IconChevronDownOutline14 size={14} />}\n aria-label={catsOpen ? t('catsLess') : t('catsMore')}\n onClick={() => setCatsOpen(o => !o)}\n />\n </>\n )\n })()}\n </div>\n <div className={css.sort}>\n {['hot', 'new'].map(key => (\n <button\n key={key}\n className={sort === key ? css.on : ''}\n onClick={() => setSort(key)}\n >{t(key === 'hot' ? 'sortHot' : 'sortNew')}</button>\n ))}\n </div>\n </div>\n </div>\n {plugins.length === 0\n ? <div className={css.empty}>{t('empty')}</div>\n : <div className={css.grid}>{plugins.map(pluginCard)}</div>}\n </>\n )\n : tab === 'themes' && themeSnap !== null\n ? (\n <>\n {/* Light/dark/system live in the official Appearance setting; this\n tab only shows what that setting can't: registered third-party\n palettes (none in the wild yet) and installable theme plugins. */}\n {(() => {\n const extra = themeSnap.themes.filter(def => def.id !== 'light' && def.id !== 'dark')\n return extra.length > 0 && (\n <div className={`${css.grid} ${css.themesGrid}`}>\n {extra.map(def => themeCard(def.id, def.id, themeSwatch(def)))}\n </div>\n )\n })()}\n {data === null\n ? <div className={css.loading}><span className={css.spin} />{t('loading')}</div>\n : themePlugins.length === 0\n ? <div className={css.empty}>{t('themeEmpty')}</div>\n : <div className={css.grid}>{themePlugins.map(themePluginCard)}</div>}\n </>\n )\n : Object.keys(installed).length === 0\n ? <div className={css.empty}>{t('installedEmpty')}</div>\n : Object.entries(installed).map(([name, spec]) => {\n const entry = data === null ? undefined : data.plugins.find(p => p.name === name\n || (repoOf(p.url) !== null && String(spec).toLowerCase().includes(('github:' + repoOf(p.url)).toLowerCase())))\n const status = updates[name]\n const version = status && status.version ? 'v' + status.version : ''\n const specText = String(spec)\n const ghSpec = /^github:([A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+?)(?:#|$)/.exec(specText)\n const repoUrl = entry !== undefined ? entry.url : ghSpec !== null ? 'https://github.com/' + ghSpec[1] : null\n return (\n <div key={name} className={css.irow}>\n <div style={{ minWidth: 0 }}>\n <div className={css.nm}>{name}{version && <span className={css.owner}>{' ' + version}</span>}</div>\n {repoUrl !== null\n ? <a className={`${css.spec} ${css.src}`} href={repoUrl} target=\"_blank\" rel=\"noreferrer\" style={{ display: 'inline-block' }}>{specText}</a>\n : <div className={css.spec}>{specText}</div>}\n {entry !== undefined && (\n <div className={`${css.desc} ${css.descTight}`}>\n {(entry.description && (entry.description[lang] || entry.description.en)) || ''}\n </div>\n )}\n {updatingName === name && (\n <div className={css.progress}>\n <span className={css.spin} />\n <code className={css.grow}>{progressLine || t('progressHint')}</code>\n </div>\n )}\n </div>\n <span className={css.grow} />\n {repoUrl !== null && <a className={css.src} href={repoUrl + '#readme'} target=\"_blank\" rel=\"noreferrer\">{t('readme')}</a>}\n {updatedNames.includes(name)\n ? <span className={css.okState}>{t('updated')}</span>\n : updatingName === name\n ? <Button variant=\"primary\" size=\"sm\" className={css.warnBtn} disabled>{t('updating')}</Button>\n : status && status.updateAvailable\n ? (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.warnBtn}\n disabled={updatingName !== null}\n onClick={() => doUpdate(name)}\n >{t('update')}</Button>\n )\n : status && status.kind === 'linked'\n ? <span className={css.owner}>{t('linkedDev')}</span>\n : <span className={css.owner}>{t('upToDate')}</span>}\n {name !== 'dsh-market' && name !== 'dshmarket' && (\n removingName === name\n ? <Button variant=\"outline\" size=\"sm\" className={css.dangerBtn} disabled>{t('uninstalling')}</Button>\n : removeArmed === name\n ? (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.dangerArmed}\n onClick={() => doUninstall(name)}\n onMouseLeave={() => setRemoveArmed(null)}\n >{t('confirmRemove')}</Button>\n )\n : (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={css.dangerBtn}\n disabled={removingName !== null || busyUrl !== null || updatingName !== null}\n onClick={() => setRemoveArmed(name)}\n >{t('uninstall')}</Button>\n )\n )}\n </div>\n )\n })}\n </div>\n {showTop && (\n <button\n className={css.top}\n title={t('backTop')}\n onClick={() => { const el = bodyRef.current; if (el) el.scrollTo({ top: 0, behavior: 'smooth' }) }}\n >↑</button>\n )}\n {confirming !== null && (\n <Modal\n open\n onClose={() => setConfirming(null)}\n title={t('confirmTitle') + ' ' + confirming.name + '?'}\n description={(confirming.description && (confirming.description[lang] || confirming.description.en)) || ''}\n footer={(\n <>\n <Button variant=\"ghost\" onClick={() => setConfirming(null)}>{t('cancel')}</Button>\n <Button variant=\"primary\" onClick={() => doInstall(confirming)}>{t('confirm')}</Button>\n </>\n )}\n >\n <details className={css.cmdDetails}>\n <summary className={css.cmdSummary}>{t('cmdDetails')}</summary>\n <div className={css.cmd}>{confirming.install}</div>\n </details>\n {looksTerminal(confirming, lang) && (\n <p className={css.warnLine}>\n {'🖥️ ' + t('terminalWarn') + ' '}\n <a className={css.src} href={confirming.url + '#readme'} target=\"_blank\" rel=\"noreferrer\">{t('readme')}</a>\n </p>\n )}\n <p className={css.modalNote}>{'⚠️ ' + t('confirmWarn')}</p>\n </Modal>\n )}\n </div>\n )\n}\n","/**\n * dsh-market client: registers a \"Market\" settings section rendering the\n * plugin market UI, plus the post-install toast in the shell overlay layer.\n * Built by tsdown into the __ModuleLoader__ factory bundle at\n * client/client.js; the only externals are the loader module table's react\n * entries.\n */\nimport { createElement as h } from 'react'\nimport { en, zh } from './locales.ts'\nimport { InstallToast } from './InstallToast.tsx'\nimport { MarketSection } from './MarketSection.tsx'\nimport type { ThemeSnapshot, Translate } from './market-data.ts'\n\nconst NS = 'dsh-market'\n\n/** The subset of the theme service this plugin touches. */\ninterface ThemeService {\n getTheme(): ThemeSnapshot | null\n setTheme(id: string): void\n}\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): Translate\n subscribe(callback: () => void): () => void\n getSnapshot(): { active: string }\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: the\n * host provides the real Context; typing the touched surface keeps this\n * external package free of monorepo-internal type dependencies). */\ninterface MarketClientContext {\n effect(callback: () => unknown, label?: string): void\n on(event: string, callback: () => void): () => void\n locale: LocaleService\n slots: SlotsService\n theme: ThemeService\n}\n\nexport const name = 'dsh-market'\n// 'theme' is safe to require: ui-layout (mandatory in every web composition)\n// already hard-depends on it. This cordis's object-form inject means\n// intercept config, NOT {required,optional} — do not use it here.\nexport const inject = ['slots', 'locale', 'theme']\nexport function apply(ctx: MarketClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-market: dictionaries')\n const t = ctx.locale.bind(NS)\n\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'market',\n order: 40,\n label: () => t('nav'),\n locale: NS,\n inject: () => ({ t }),\n }, () => h(MarketSection, {\n t,\n locale: ctx.locale,\n theme: ctx.theme,\n themeStore: {\n subscribe: (cb: () => void) => ctx.on('theme/change', cb),\n getSnapshot: () => ctx.theme.getTheme(),\n },\n })))\n\n const Toast = () => h(InstallToast, { t })\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'dsh-market-toast',\n label: () => 'dsh-market',\n }, Toast))\n}\n"],"mappings":";;;;;;;;;;;EAEA,MAAa,KAAK;GAChB,KAAK;GACL,UAAU;GACV,UAAU;GACV,aAAa;GACb,cAAc;GACd,KAAK;GACL,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,kBAAkB;GAClB,eAAe;GACf,WAAW;GACX,eAAe;GACf,cAAc;GACd,aAAa;GACb,cAAc;GACd,aAAa;GACb,QAAQ;GACR,OAAO;GACP,gBAAgB;GAChB,UAAU;GACV,aAAa;GACb,YAAY;GACZ,WAAW;GACX,SAAS;GACT,QAAQ;GACR,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAU;GACV,WAAW;GACX,WAAW;GACX,QAAQ;GACR,cAAc;GACd,YAAY;GACZ,QAAQ;GACR,WAAW;GACX,YAAY;GACZ,SAAS;GACT,SAAS;GACT,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU;GACV,SAAS;GACT,SAAS;GACT,cAAc;GACd,WAAW;GACX,WAAW;GACX,YAAY;GACZ,aAAa;GACb,YAAY;GACZ,cAAc;GACd,YAAY;GACZ,YAAY;GACZ,OAAO;EACT;EAKA,MAAa,KAAgC;GAC3C,KAAK;GACL,UAAU;GACV,UAAU;GACV,aAAa;GACb,cAAc;GACd,KAAK;GACL,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,kBAAkB;GAClB,eAAe;GACf,WAAW;GACX,eAAe;GACf,cAAc;GACd,aAAa;GACb,cAAc;GACd,aAAa;GACb,QAAQ;GACR,OAAO;GACP,gBAAgB;GAChB,UAAU;GACV,aAAa;GACb,YAAY;GACZ,WAAW;GACX,SAAS;GACT,QAAQ;GACR,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAU;GACV,WAAW;GACX,WAAW;GACX,QAAQ;GACR,cAAc;GACd,YAAY;GACZ,QAAQ;GACR,WAAW;GACX,YAAY;GACZ,SAAS;GACT,SAAS;GACT,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU;GACV,SAAS;GACT,SAAS;GACT,cAAc;GACd,WAAW;GACX,WAAW;GACX,YAAY;GACZ,aAAa;GACb,YAAY;GACZ,cAAc;GACd,YAAY;GACZ,YAAY;GACZ,OAAO;EACT;;;ECzDA,SAAgB,YAAY,MAAsB;GAChD,IAAI,OAAO;GACX,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,OAAQ,OAAO,KAAK,KAAK,WAAW,CAAC,IAAK;GAChF,OAAO,UAAY,OAAO,MAAO,OAAO,MAAO;EACjD;EAEA,SAAgB,OAAO,KAA4B;GAEjD,MAAM,IAAI,6DAA6D,KAAK,GAAG;GAC/E,OAAO,IAAI,EAAE,KAAM;EACrB;EAEA,SAAgB,YAAY,KAAkB;GAC5C,IAAI;IAAE,OAAO,KAAK,MAAM,eAAe,QAAQ,GAAG,KAAK,MAAM;GAAE,QAAQ;IAAE,OAAO;GAAK;EACvF;;EAGA,SAAgB,cAAc,QAAwB,MAAuB;GAC3E,MAAM,OAAQ,OAAO,gBAAgB,OAAO,YAAY,SAAS,OAAO,YAAY,OAAQ;GAC5F,OAAO,qCAAqC,KAAK,OAAO,OAAO,MAAM,IAAI;EAC3E;;EAGA,SAAgB,YAAY,QAAwB,WAAkC;GACpF,IAAI,UAAU,OAAO,UAAU,KAAA,GAAW,OAAO;GACjD,IAAI,OAAO,OAAO,UAAU,OAAO,SAAS,KAAA,GAAW,OAAO;GAC9D,MAAM,OAAO,OAAO,OAAO,GAAG;GAC9B,IAAI,SAAS,MAAM,OAAO;GAC1B,MAAM,UAAU,YAAY,KAAA,CAAM,YAAY;GAC9C,OAAO,OAAO,OAAO,SAAS,CAAC,CAAC,MAAK,SAAQ,OAAO,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC;EAC1F;;;;;EAMA,MAAa,WAAW,wBAAwB,mBAAmB,01BAA4uB;;EAG/yB,SAAgB,YAAY,KAAyB;GACnD,MAAM,KAAK,IAAI,UAAU,CAAC;GAC1B,MAAM,QAAQ,UAAoB;IAAE,KAAK,MAAM,KAAK,OAAS,IAAI,GAAG,IAAI,OAAO,GAAG;IAAM,OAAO;GAAK;GACpG,MAAM,OAAO,IAAI,gBAAgB;GACjC,OAAO;IACL,KAAK,CAAC,uBAAuB,wBAAwB,CAAC,MAAM,OAAO,YAAY;IAC/E,KAAK,CAAC,0BAA0B,wBAAwB,CAAC,MAAM,OAAO,YAAY;IAClF,KAAK,CAAC,2BAA2B,CAAC,KAAK;IACvC,KAAK,CAAC,2BAA2B,CAAC,MAAM,OAAO,YAAY;GAC7D;EACF;;;;;;;;ECvGA,SAAgB,aAAa,OAAyB;GACpD,MAAM,IAAI,MAAM;GAChB,MAAM,CAAC,SAAA,GAAQA,MAAAA,SAAAA,OAAe;IAC5B,MAAM,QAAQ,eAAe,QAAQ,iBAAiB;IACtD,eAAe,WAAW,iBAAiB;IAC3C,OAAO;GACT,CAAC;GACD,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,OAAyB;IACjD,MAAM,QAAQ,YAAY,YAAY;IACtC,eAAe,WAAW,YAAY;IACtC,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;GACzC,CAAC;GACD,IAAI,MAAM,WAAW,GAAG,OAAO;GAC/B,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,OAAD;IACE,MAAM,MAAM,KAAK,IAAI,IAAI,MAAM,EAAE,SAAS,UAAU,eAAe,YAAY;IAC/E,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAM,IAAO,CAAA;IACnB,cAAc,SAAS,CAAC,CAAC;GAC1B,CAAA;EAEL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECXA,SAAS,YAAY,EAAE,MAAM,SAA0C;GACrE,MAAM,CAAC,QAAQ,cAAA,GAAaC,MAAAA,SAAAA,CAAS,KAAK;GAC1C,IAAI,UAAU,UAAU,IACtB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAWC,0BAAI;IAAI,OAAO,EAAE,YAAY,YAAY,IAAI,EAAE;IAC5D,UAAA,KAAK,QAAQ,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,KAAK;GACvD,CAAA;GAGT,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IACE,WAAWA,0BAAI;IACf,KAAK,sBAAsB,mBAAmB,KAAK,EAAE;IACrD,KAAI;IACJ,SAAQ;IACR,eAAe,UAAU,IAAI;GAC9B,CAAA;EAEL;EAeA,SAAgB,cAAc,OAA2B;GACvD,MAAM,IAAI,MAAM;GAChB,MAAM,cAAA,GAAaC,MAAAA,qBAAAA,EACjB,OAAM,MAAM,OAAO,UAAU,EAAE,SACzB,MAAM,OAAO,YAAY,CACjC;GACA,MAAM,OAAO,OAAO,WAAW,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,WAAW,IAAI,IAAI,OAAO;GAE/E,MAAM,aAAA,GAAYA,MAAAA,qBAAAA,CAChB,MAAM,WAAW,WACjB,MAAM,WAAW,WACnB;GACA,MAAM,CAAC,MAAM,YAAA,GAAWF,MAAAA,SAAAA,CAA0B,IAAI;GACtD,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAS,KAAK;GAChD,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAuB,CAAC,CAAC;GAC3D,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAmB,CAAC,CAAC;GAC/C,MAAM,CAAC,KAAK,WAAA,GAAUA,MAAAA,SAAAA,OAAe;IACnC,MAAM,QAAQ,eAAe,QAAQ,UAAU;IAC/C,IAAI,UAAU,MAAM,eAAe,WAAW,UAAU;IACxD,OAAO,SAAS;GAClB,CAAC;GACD,MAAM,CAAC,GAAG,SAAA,GAAQA,MAAAA,SAAAA,CAAS,EAAE;GAC7B,MAAM,CAAC,KAAK,WAAA,GAAUA,MAAAA,SAAAA,CAAS,KAAK;GACpC,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAgC,IAAI;GACxE,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAwB,IAAI;GAC1D,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAmB,CAAC,CAAC;GACrD,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAwB,IAAI;GACpE,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAuC,CAAC,CAAC;GACvE,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAwB,IAAI;GACpE,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAAS,KAAK;GACpD,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAmB,CAAC,CAAC;GAC7D,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAmB,CAAC,CAAC;GACnD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAmB,CAAC,CAAC;GACrD,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAwB,IAAI;GACpE,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAAwB,IAAI;GAClE,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAwB,IAAI;GACpE,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAS,CAAC;GAClD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,IAAI;GAC7C,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAS,KAAK;GAChD,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAS,KAAK;GAChD,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAwB,IAAI;GACxD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAC5C,MAAM,WAAA,GAAUG,MAAAA,OAAAA,CAA8B,IAAI;GAClD,MAAM,CAAC,MAAM,YAAA,GAAWH,MAAAA,SAAAA,CAAS,KAAK;GACtC,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,KAAK;GAI9C,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAAwB,IAAI;GAClE,MAAM,eAAA,GAAcG,MAAAA,OAAAA,CAA8B,IAAI;GAEtD,MAAM,oBAAA,GAAmBC,MAAAA,YAAAA,EAAa,UAAoB;IACxD,MAAM,yBAAyB,EAAE,OAAO,WAAW,CAAC,CAAC,CAClD,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,CACvB,MAAK,SAAQ;KACZ,aAAa,KAAK,aAAa,CAAC,CAAC;KACjC,SAAS,KAAK,QAAQ,CAAC,CAAC;IAC1B,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;IACjB,MAAM,yBAAyB,UAAU,OAAO,aAAa,KAAK,EAAE,OAAO,WAAW,CAAC,CAAC,CACrF,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,CACvB,MAAK,SAAQ,WAAW,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAC5C,YAAY,CAAC,CAAC;GACnB,GAAG,CAAC,CAAC;GAEL,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,wBAAwB,EAAE,OAAO,WAAW,CAAC,CAAC,CACjD,MAAK,QAAO;KAAE,IAAI,CAAC,IAAI,IAAI,MAAM,IAAI,MAAM,UAAU,IAAI,MAAM;KAAG,OAAO,IAAI,KAAK;IAAE,CAAC,CAAC,CACtF,MAAK,SAAQ,QAAQ,KAAK,QAAQ,CAAC,CAAC,CACpC,YAAY,aAAa,IAAI,CAAC;IACjC,MAAM,sBAAsB,EAAE,OAAO,WAAW,CAAC,CAAC,CAC/C,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,CACvB,MAAK,WAAU;KACd,YAAY,OAAO,SAAS,KAAK;KACjC,IAAI,OAAO,OAAO,SAAS,UAAU,UAAU,OAAO,IAAI;IAC5D,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;IACjB,iBAAiB;GACnB,GAAG,CAAC,gBAAgB,CAAC;GAKrB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,WAAW,MAAM;IACrB,MAAM,QAAQ,YAAY,cAAc;IACxC,IAAI,UAAU,MAAM;IACpB,IAAI,MAAM,SAAS,QAAQ;KACzB,eAAe,WAAW,cAAc;KACxC;IACF;IACA,IAAI,MAAM,QAAQ,MAAM,QAAQ,KAAK,MAAM,SAAS,SAAS,GAAG,YAAY,MAAM,QAAQ;IAC1F,IAAI,MAAM,QAAQ,MAAM,OAAO,KAAK,MAAM,QAAQ,SAAS,GAAG,gBAAgB,MAAM,OAAO;IAC3F,IAAI,OAAO,MAAM,YAAY,YAAY,MAAM,UAAU,GAAG,gBAAgB,MAAM,OAAO;GAC3F,GAAG,CAAC,MAAM,CAAC;GAEX,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,WAAW,MAAM;IACrB,IAAI,SAAS,WAAW,KAAK,aAAa,WAAW,KAAK,iBAAiB,GAAG;IAC9E,eAAe,QAAQ,gBAAgB,KAAK,UAAU;KACpD,MAAM;KACN;KACA,SAAS;KACT,SAAS;IACX,CAAC,CAAC;GACJ,GAAG;IAAC;IAAQ;IAAU;IAAc;GAAY,CAAC;GAEjD,MAAM,UAAA,GAASA,MAAAA,YAAAA,OAAkB;IAC/B,aAAa,IAAI;IACjB,aAAa,KAAK;IAClB,MAAM,0BAA0B;KAAE,QAAQ;KAAQ,SAAS,EAAE,gBAAgB,mBAAmB;KAAG,MAAM;IAAK,CAAC,CAAC,CAC7G,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,CACvB,MAAK,SAAQ;KACZ,IAAI,KAAK,IAAI,YAAY,IAAI;UACxB,aAAa,IAAI;IACxB,CAAC,CAAC,CACD,YAAY,aAAa,IAAI,CAAC,CAAC,CAC/B,cAAc,aAAa,KAAK,CAAC;GACtC,GAAG,CAAC,CAAC;GAKL,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,UAAU,YAAY,cAAc;IAC1C,IAAI,YAAY,QAAQ,OAAO,QAAQ,QAAQ,UAAU,WAAW,QAAQ,GAAG;GACjF,GAAG,CAAC,CAAC;GAEL,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,YAAY,QAAQ,iBAAiB,MAAM;KAC7C,gBAAgB,IAAI;KACpB;IACF;IACA,MAAM,QAAQ,kBAAkB;KAC9B,MAAM,sBAAsB,EAAE,OAAO,WAAW,CAAC,CAAC,CAC/C,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,CACvB,MAAK,WAAU;MACd,IAAI,OAAO,QACT,iBAAiB,OAAO,YAAY,OAAO,QAAQ,OAAO,UAAU,IAAI;WACnE;OACL,gBAAgB,IAAI;OACpB,aAAa,OAAO,aAAa,CAAC,CAAC;OAEnC,IADgB,YAAY,cAClB,MAAM,QAAQ,YAAY,MACb;YAAA,SAAS,QAAQ,KAAK,QAAQ,MAAK,MACtD,EAAE,QAAQ,WAAW,YAAY,GAAG,OAAO,aAAa,CAAC,CAAC,CAAC,GAC3C;SAChB,eAAe,WAAW,cAAc;SACxC,aAAY,SAAQ,KAAK,SAAS,OAAO,IAAI,OAAO,KAAK,OAAO,OAAO,CAAC;SACxE,WAAW,IAAI;QACjB;;MAEJ;KACF,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;IACnB,GAAG,GAAI;IACP,aAAa,cAAc,KAAK;GAClC,GAAG;IAAC;IAAS;IAAc;GAAI,CAAC;GAEhC,MAAM,WAAA,GAAUC,MAAAA,QAAAA,OAAc;IAC5B,IAAI,SAAS,MAAM,OAAO,CAAC;IAC3B,MAAM,QAAQ,EAAE,KAAK,CAAC,CAAC,YAAY;IACnC,MAAM,OAAO,KAAK,QAAQ,QAAO,MAAK;KACpC,IAAI,QAAQ,SAAS,EAAE,aAAa,KAAK,OAAO;KAChD,IAAI,UAAU,IAAI,OAAO;KACzB,MAAM,OAAQ,EAAE,gBAAgB,EAAE,YAAY,SAAS,EAAE,YAAY,OAAQ;KAC7E,OAAO,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,KAAK,KACrC,EAAE,MAAM,YAAY,CAAC,CAAC,SAAS,KAAK,KACpC,KAAK,YAAY,CAAC,CAAC,SAAS,KAAK;IACxC,CAAC;IACD,IAAI,SAAS,OACX,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,OAAO,EAAE,SAAS,OAAO,EAAE,SAAS,GAAG;IAEnE,IAAI,SAAS,OACX,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,OAAO,EAAE,KAAK,CAAC,CAAC,cAAc,OAAO,EAAE,KAAK,CAAC,CAAC;IAEhF,OAAO;GACT,GAAG;IAAC;IAAM;IAAG;IAAK;IAAM;GAAI,CAAC;GAE7B,MAAM,aAAA,GAAYD,MAAAA,YAAAA,EAAa,WAA2B;IACxD,cAAc,IAAI;IAClB,gBAAgB,IAAI;IACpB,WAAW,OAAO,GAAG;IACrB,eAAe,QAAQ,gBAAgB,KAAK,UAAU,EAAE,KAAK,OAAO,IAAI,CAAC,CAAC;IAC1E,MAAM,uBAAuB;KAC3B,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C,MAAM,KAAK,UAAU,EAAE,KAAK,OAAO,IAAI,CAAC;IAC1C,CAAC,CAAC,CACC,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,MAAK,UAAS;KAAE,QAAQ,IAAI;KAAQ;IAAK,EAAE,CAAC,CAAC,CACpE,MAAM,EAAE,QAAQ,WAAW;KAC1B,eAAe,WAAW,cAAc;KACxC,IAAI,WAAW,OAAO,KAAK,MAAM,KAAK,OAAO,OAAO,aAAa,SAAS;MAGxE,eAAe,QAAQ,cAAc,KAAK,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;MAClE,eAAe,QAAQ,YAAY,QAAQ;MAC3C,SAAS,OAAO;MAChB;KACF;KACA,IAAI,WAAW,OAAO,KAAK,IAAI;MAC7B,eAAe,QAAQ,YAAY,WAAW;MAC9C,IAAI,KAAK,KAAK;OACZ,YAAW,SAAQ,KAAK,SAAS,OAAO,GAAG,IAAI,OAAO,KAAK,OAAO,OAAO,GAAG,CAAC;OAC7E,aAAY,UAAS,MAAM,SAAS,OAAO,IAAI,IAAI,QAAQ,MAAM,OAAO,OAAO,IAAI,CAAC;MACtF,OACE,aAAY,SAAQ,KAAK,SAAS,OAAO,GAAG,IAAI,OAAO,KAAK,OAAO,OAAO,GAAG,CAAC;MAEhF,iBAAiB;KACnB,OAAO;MACL,MAAM,QAAQ,MAAe,OAAO,MAAM,WAAW,IAAK,KAAK,OAAQ,EAAU,SAAS,WAAa,EAAU,OAAO,KAAK,OAAO,KAAK,KAAK,UAAU,CAAC;MACzJ,MAAM,SAAS,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,MAAM,KAAM,UAAU,KAAK;MAC7F,gBAAgB,EAAE,aAAa,IAAI,OAAO,OAAO,OAAO,QAAQ,OAAO,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC;KAC3F;IACF,CAAC,CAAC,CACD,OAAM,UAAS;KACd,eAAe,WAAW,cAAc;KACxC,gBAAgB,EAAE,aAAa,IAAI,OAAO,OAAO,KAAK,CAAC;IACzD,CAAC,CAAC,CACD,cAAc,WAAW,IAAI,CAAC;GACnC,GAAG,CAAC,kBAAkB,CAAC,CAAC;GAExB,MAAM,YAAA,GAAWA,MAAAA,YAAAA,EAAa,SAAiB;IAC7C,gBAAgB,IAAI;IACpB,gBAAgB,IAAI;IACpB,OAAO,MAAM,sBAAsB;KACjC,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;IAC/B,CAAC,CAAC,CACC,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,MAAK,UAAS;KAAE,QAAQ,IAAI;KAAQ;IAAK,EAAE,CAAC,CAAC,CACpE,MAAM,EAAE,QAAQ,WAAW;KAC1B,IAAI,WAAW,OAAO,KAAK,IAAI;MAC7B,iBAAgB,UAAS,MAAM,OAAO,IAAI,CAAC;MAC3C,iBAAiB;KACnB,OAAO;MACL,MAAM,QAAQ,MAAe,OAAO,MAAM,WAAW,IAAK,KAAK,OAAQ,EAAU,SAAS,WAAa,EAAU,OAAO,KAAK,OAAO,KAAK,KAAK,UAAU,CAAC;MACzJ,MAAM,SAAS,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,MAAM,KAAM,UAAU,KAAK;MAC7F,gBAAgB,EAAE,YAAY,IAAI,OAAO,OAAO,QAAQ,OAAO,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC;KACnF;IACF,CAAC,CAAC,CACD,OAAM,UAAS,gBAAgB,EAAE,YAAY,IAAI,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,CACvE,cAAc,gBAAgB,IAAI,CAAC;GACxC,GAAG,CAAC,kBAAkB,CAAC,CAAC;GAExB,MAAM,aAAA,GAAYA,MAAAA,YAAAA,EAAa,SAAiB;IAC9C,gBAAgB,IAAI;IACpB,MAAM,wBAAwB;KAC5B,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;IAC/B,CAAC,CAAC,CACC,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,MAAK,UAAS;KAAE,QAAQ,IAAI;KAAQ;IAAK,EAAE,CAAC,CAAC,CACpE,MAAM,EAAE,QAAQ,WAAW;KAC1B,IAAI,WAAW,OAAO,KAAK,IAAI;MAC7B,eAAe,QAAQ,cAAc,KAAK,UAAU,CAAC,IAAI,CAAC,CAAC;MAC3D,eAAe,QAAQ,mBAAmB,OAAO;MACjD,eAAe,QAAQ,YAAY,QAAQ;MAC3C,SAAS,OAAO;KAClB,OACE,gBAAgB,OAAO,KAAK,SAAS,QAAQ,CAAC;IAElD,CAAC,CAAC,CACD,OAAM,UAAS,gBAAgB,OAAO,KAAK,CAAC,CAAC;GAClD,GAAG,CAAC,CAAC;GAEL,MAAM,eAAA,GAAcA,MAAAA,YAAAA,EAAa,SAAiB;IAChD,eAAe,IAAI;IACnB,gBAAgB,IAAI;IACpB,gBAAgB,IAAI;IACpB,OAAO,MAAM,yBAAyB;KACpC,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;IAC/B,CAAC,CAAC,CACC,MAAK,QAAO,IAAI,KAAK,CAAC,CAAC,MAAK,UAAS;KAAE,QAAQ,IAAI;KAAQ;IAAK,EAAE,CAAC,CAAC,CACpE,MAAM,EAAE,QAAQ,WAAW;KAC1B,IAAI,WAAW,OAAO,KAAK,IAAI;MAC7B,IAAI,CAAC,KAAK,KAAK,iBAAgB,MAAK,IAAI,CAAC;MACzC,iBAAiB;KACnB,OAAO;MACL,MAAM,QAAQ,MAAe,OAAO,MAAM,WAAW,IAAK,KAAK,OAAQ,EAAU,SAAS,WAAa,EAAU,OAAO,KAAK,OAAO,KAAK,KAAK,UAAU,CAAC;MACzJ,iBAAiB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,QAAA,CAAS,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC;KACvF;IACF,CAAC,CAAC,CACD,OAAM,UAAS,gBAAgB,OAAO,KAAK,CAAC,CAAC,CAAC,CAC9C,cAAc,gBAAgB,IAAI,CAAC;GACxC,GAAG,CAAC,gBAAgB,CAAC;GAIrB,MAAM,WAAW,UAAU,iBAAiB,KAAA,IAAY,cAAc;GACtE,MAAM,iBAAiB,OAAO,KAAK,SAAS,CAAC,CAAC,QAC5C,SAAQ,SAAS,YAAY,CAAC,aAAa,SAAS,IAAI,KAAK,QAAQ,SAAS,QAAQ,KAAK,CAAC,eAC9F;GAEA,MAAM,eAAA,GAAcA,MAAAA,YAAAA,OAAkB;IACpC,MAAM,QAAQ,eAAe,MAAM;IACnC,eAAe,IAAI;IACnB,MAAM,aAAa;KACjB,MAAM,OAAO,MAAM,MAAM;KACzB,IAAI,SAAS,KAAA,GAAW;MACtB,eAAe,KAAK;MACpB;KACF;KACA,SAAS,IAAI,CAAC,CAAC,KAAK,MAAM,IAAI;IAChC;IACA,KAAK;GACP,GAAG,CAAC,gBAAgB,QAAQ,CAAC;GAE7B,MAAM,iBAAiB,SAAS,SAAS,aAAa,SAAS;GAC/D,MAAM,aAAa,OAAO,KAAK,SAAS,CAAC,CAAC,MACxC,SAAQ,CAAC,aAAa,SAAS,IAAI,KAAK,QAAQ,SAAS,QAAQ,KAAK,CAAC,eACzE;GAEA,MAAM,eAAe,SAAS,OAAO,CAAC,IAAI,KAAK,QAC5C,QAAO,MAAK,EAAE,aAAa,OAAO,CAAC,CACnC,MAAM,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,SAAS,EAAE;GAEjD,MAAM,cAAc,MAAsB;IACxC,MAAM,OAAQ,EAAE,gBAAgB,EAAE,YAAY,SAAS,EAAE,YAAY,OAAQ;IAC7E,MAAM,OAAO,SAAS,SAAS,EAAE,GAAG,KAAK,QAAQ,SAAS,EAAE,GAAG;IAC/D,MAAM,UAAU,YAAY,GAAG,SAAS;IACxC,MAAM,OAAO,YAAY,EAAE;IAC3B,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAiB,WAAWH,0BAAI;KAAhC,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAApB,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;SAAa,MAAM,EAAE;SAAM,OAAO,EAAE,SAAS;QAAK,CAAA;QAClD,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,OAAO,EAAE,UAAU,EAAE;SAA1B,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;UAAK,WAAWA,0BAAI;UAAK,UAAA,EAAE;SAAU,CAAA,GACrC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;UAAK,WAAWA,0BAAI;UAApB,UAAA,CACG,EAAE,OACF,OAAO,EAAE,UAAU,YAAY,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,0BAAI;WAAO,UAAA,UAAU,EAAE;UAAY,CAAA,CACjF;SACF,CAAA,CAAA;;QACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA;QAC5B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;SAAG,WAAWA,0BAAI;SAAK,MAAM,EAAE;SAAK,QAAO;SAAS,KAAI;SAAa,OAAO;UAAE,WAAW;UAAc,YAAY;SAAE;SAAI,UAAA,EAAE,YAAY;QAAK,CAAA;OACzI;;MACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAAO,UAAA;MAAU,CAAA;MACrC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAApB,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAWA,0BAAI;SACjB,UAAA,KAAM,WAAW,EAAE,cAAc,KAAM,WAAW,EAAE,SAAS,CAAE,SAAS,KAAM,WAAW,EAAE,SAAS,CAAE,OAAQ,EAAE;QAC9G,CAAA;QACN,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA;QAC3B,OACG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAWA,0BAAI;SAAU,UAAA,EAAE,gBAAgB;QAAQ,CAAA,IACzD,UACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAWA,0BAAI;SAAU,UAAA,EAAE,kBAAkB;QAAQ,CAAA,IAC3D,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;SAAQ,SAAQ;SAAU,MAAK;SAAK,UAAA;SAAU,UAAA,EAAE,YAAY;QAAU,CAAA,IAEpE,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;SACE,SAAQ;SACR,MAAK;SACL,UAAU,YAAY,QAAQ,CAAC;SAC/B,eAAe,cAAc,CAAC;SAC9B,UAAA,EAAE,SAAS;QAAU,CAAA;OAE9B;;MACJ,QACC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWL,0BAAI;OAApB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA,GAC5B,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,0BAAI;QAAO,UAAA,gBAAgB,EAAE,cAAc;OAAQ,CAAA,CACjE;;KAEJ;IAxCK,GAAA,EAAE,GAwCP;GAET;GAEA,MAAM,mBAAmB,MAAsB;IAC7C,IAAI,UAAU,EAAE,UAAU,KAAA,GAAW,OAAO,EAAE;IAC9C,MAAM,OAAO,OAAO,EAAE,GAAG;IACzB,IAAI,SAAS,MAAM,OAAO;IAC1B,MAAM,UAAU,YAAY,KAAA,CAAM,YAAY;IAC9C,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,SAAS,GACjD,IAAI,OAAO,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,OAAO;IAE1D,OAAO;GACT;GAIA,MAAM,cAAe,OAAO,WAAW,eAAe,OAAO,gBAAgB,MAAM,QAAQ,OAAO,aAAa,OAAO,IAClH,OAAO,aAAa,UACpB,CAAC;GAGL,MAAM,mBAAmB,MAAsB;IAC7C,MAAM,WAAW,gBAAgB,CAAC;IAClC,IAAI,aAAa,MAAM,OAAO,WAAW,CAAC;IAC1C,MAAM,UAAU,MAAM,SAAS,QAAQ,KAAK,YAAY,MAAK,MAAK,EAAE,OAAO,QAAQ;IACnF,MAAM,OAAQ,EAAE,gBAAgB,EAAE,YAAY,SAAS,EAAE,YAAY,OAAQ;IAC7E,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAiB,WAAWA,0BAAI;KAAhC,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAApB,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;SAAa,MAAM,EAAE;SAAM,OAAO,EAAE,SAAS;QAAK,CAAA;QAClD,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,OAAO,EAAE,UAAU,EAAE;SAA1B,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;UAAK,WAAWA,0BAAI;UAAK,UAAA,EAAE;SAAU,CAAA,GACrC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;UAAK,WAAWA,0BAAI;UAApB,UAAA,CACG,EAAE,OACF,OAAO,EAAE,UAAU,YAAY,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,0BAAI;WAAO,UAAA,UAAU,EAAE;UAAY,CAAA,CACjF;SACF,CAAA,CAAA;;QACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA;QAC5B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;SAAG,WAAWA,0BAAI;SAAK,MAAM,EAAE;SAAK,QAAO;SAAS,KAAI;SAAa,OAAO;UAAE,WAAW;UAAc,YAAY;SAAE;SAAI,UAAA,EAAE,YAAY;QAAK,CAAA;OACzI;;MACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAAO,UAAA;MAAU,CAAA;MACrC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAApB,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA;QAC3B,iBAAiB,WACd,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;SAAQ,SAAQ;SAAU,MAAK;SAAK,WAAWL,0BAAI;SAAW,UAAA;SAAU,UAAA,EAAE,cAAc;QAAU,CAAA,IAClG,gBAAgB,WAEZ,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;SACE,SAAQ;SACR,MAAK;SACL,WAAWL,0BAAI;SACf,eAAe,YAAY,QAAQ,CAAC,CAAC,WAAW;UAC9C,IAAI,SAAS;WACX,eAAe,QAAQ,YAAY,QAAQ;WAC3C,SAAS,OAAO;UAClB;SACF,CAAC;SACD,UAAA,EAAE,eAAe;QAAU,CAAA,IAE/B,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;SAAQ,SAAQ;SAAU,MAAK;SAAK,WAAWL,0BAAI;SAAW,eAAe,eAAe,QAAQ;SAAI,UAAA,EAAE,WAAW;QAAU,CAAA;QACpI,UACG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAWA,0BAAI;SAAU,UAAA,EAAE,aAAa;QAAQ,CAAA,IACtD,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;SAAQ,SAAQ;SAAU,MAAK;SAAK,eAAe,UAAU,QAAQ;SAAI,UAAA,EAAE,YAAY;QAAU,CAAA;OAClG;;KACF;IArCK,GAAA,EAAE,GAqCP;GAET;GAEA,MAAM,aAAa,IAAY,OAAe,WAAqB;IACjE,MAAM,SAAS,cAAc,QAAQ,UAAU,eAAe;IAC9D,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAsB,WAAWL,0BAAI;KAArC,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAWA,0BAAI;MAAW,UAAA,OAAO,KAAK,GAAG,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAW,OAAO,EAAE,YAAY,EAAE,EAAI,GAA9B,CAA8B,CAAC;KAAO,CAAA,GAClG,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAWA,0BAAI;MAApB,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,0BAAI;QAAK,UAAA;OAAY,CAAA;OACtC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA;OAC3B,SACG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,0BAAI;QAAU,UAAA,EAAE,aAAa;OAAQ,CAAA,IAEpD,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;QACE,SAAQ;QACR,MAAK;QACL,eAAe;SAAE,IAAI;UAAE,MAAM,MAAM,SAAS,EAAE;SAAE,SAAS,OAAO;UAAE,gBAAgB,OAAO,KAAK,CAAC;SAAE;QAAE;QACnG,UAAA,EAAE,YAAY;OAAU,CAAA;MAE7B;KACF,CAAA,CAAA;IAfK,GAAA,QAAQ,EAeb;GAET;GAEA,MAAM,aAAa,SAAS,OAAO,CAAC,IAAI,OAAO,KAAK,KAAK,UAAU;GAEnE,CAAA,GAAA,MAAA,gBAAA,OAAsB;IAAE,eAAe,IAAI;GAAE,GAAG,CAAC,MAAM,WAAW,MAAM,CAAC;GACzE,CAAA,GAAA,MAAA,gBAAA,OAAsB;IACpB,IAAI,YAAY,gBAAgB,MAAM;IACtC,MAAM,KAAK,YAAY;IACvB,IAAI,OAAO,MAAM;IACjB,MAAM,QAAQ,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC,QAAQ,MAAyB,EAAkB,SAAS,SAAS,GAAG;IACvG,IAAI,MAAM,WAAW,GAAG;IACxB,MAAM,QAAQ,MAAM;IACpB,MAAM,cAAc,MAAM,aAAa,MAAM,eAAe,KAAK,IAAI;IACrE,IAAI,OAAO;IACX,KAAK,MAAM,QAAQ,OAAS,IAAI,KAAK,YAAY,aAAa,QAAQ;IAEtE,eAAe,QAAQ,MAAM,SAAS,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,CAAC;GACpE,GAAG;IAAC;IAAU;IAAa;GAAI,CAAC;GAEhC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAWL,0BAAI;IAApB,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAWA,0BAAI;MAApB,UAAA;OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,0BAAI;QAApB,UAAA;SACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;UAAK,KAAK;UAAU,OAAO;UAAI,QAAQ;UAAI,KAAI;UAAG,OAAO;WAAE,cAAc;WAAO,YAAY;UAAE;SAAI,CAAA;SAClG,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;UAAI,WAAWA,0BAAI;UAAQ,UAAA,EAAE,KAAK;SAAM,CAAA;gBAChC;UACN,MAAM,OAAO,UAAU,iBAAiB,KAAA,IAAY,cAAc;UAClE,OAAO,QAAQ,SAAS,QAAQ,KAAK,CAAC,mBAAmB,CAAC,aAAa,SAAS,IAAI,KAEhF,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;WACE,SAAQ;WACR,MAAK;WACL,WAAWL,0BAAI;WACf,UAAU,iBAAiB,QAAQ,YAAY;WAC/C,eAAe;YAAE,OAAO,WAAW;YAAG,SAAS,IAAI;WAAE;WACrD,UAAA,iBAAiB,OAAO,EAAE,UAAU,IAAI,EAAE,cAAc;UAAU,CAAA;SAE1E,EAAA,CAAG;SACF,eAAe,UAAU,KACxB,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;UACE,SAAQ;UACR,MAAK;UACL,WAAWL,0BAAI;UACf,UAAU,eAAe,iBAAiB,QAAQ,YAAY,QAAQ,iBAAiB;UACvF,eAAe;WAAE,OAAO,WAAW;WAAG,YAAY;UAAE;UACpD,UAAA,cAAc,EAAE,UAAU,IAAI,EAAE,WAAW,IAAI,OAAO,eAAe,SAAS;SAAY,CAAA;QAE3F;;OACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,0BAAI;QAApB,UAAA,CACG,EAAE,UAAU,KAAK,OAAO,QAAQ,KAAK,QAAQ,MAAM,OACpD,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;SAAG,WAAWA,0BAAI;SAAK,MAAK;SAAmB,UAAS;SAAsB,UAAA,EAAE,WAAW;QAAK,CAAA,CAC7F;;OACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,0BAAI;QAApB,UAAA;SACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,WAAW,QAAQ,aAAa,GAAGA,0BAAI,IAAI,GAAGA,0BAAI,OAAOA,0BAAI;UAAK,eAAe,OAAO,UAAU;UAAI,UAAA,EAAE,aAAa;SAAU,CAAA;SACtI,cAAc,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,WAAW,QAAQ,WAAW,GAAGA,0BAAI,IAAI,GAAGA,0BAAI,OAAOA,0BAAI;UAAK,eAAe,OAAO,QAAQ;UAAI,UAAA,EAAE,WAAW;SAAU,CAAA;SACxJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;UAAQ,WAAW,QAAQ,cAAc,GAAGA,0BAAI,IAAI,GAAGA,0BAAI,OAAOA,0BAAI;UAAK,eAAe;WAAE,OAAO,WAAW;WAAG,iBAAiB,IAAI;UAAE;UAAxI,UAAA,CACG,EAAE,cAAc,KAAK,OAAO,KAAK,SAAS,CAAC,CAAC,SAAS,IAAI,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,KACtG,cAAc,iBAAA,GAAA,kBAAA,IAAA,CAACM,sCAAAA,UAAD;WAAU,OAAM;WAAQ,MAAM;WAAG,WAAWN,0BAAI;UAAM,CAAA,CAC/D;;SACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA;SAC5B,iBAAA,GAAA,kBAAA,IAAA,CAACO,sCAAAA,OAAD;UAAO,WAAWP,0BAAI;UAAc,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAACQ,sCAAAA,qBAAD,EAAqB,MAAM,GAAK,CAAA;UAAG,aAAa,EAAE,UAAU;UAAG,OAAO;UAAG,WAAU,MAAK,KAAK,EAAE,OAAO,KAAK;SAAI,CAAA;QACtJ;;OACJ,CAAC,YACA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWR,0BAAI;QAApB,UAAA;SACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAM,KAAQ,CAAA;SACd,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UAAM,WAAWA,0BAAI;UAAO,UAAA,YAAY,EAAE,YAAY,IAAI,EAAE,YAAY;SAAQ,CAAA;SAC/E,CAAC,aACA,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;UAAQ,SAAQ;UAAU,MAAK;UAAK,UAAU;UAAW,SAAS;UAC/D,UAAA,YAAY,EAAE,WAAW,IAAI,EAAE,QAAQ;SAClC,CAAA;QAEP;;OAEN,QAAQ,SAAS,KAChB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWL,0BAAI;QAApB,UAAA;SACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAM,IAAO,CAAA;SACb,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,0BAAI;UAArB,UAAA;WAA2B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UAAI,QAAQ,OAAU,CAAA;WAAC;WAAE,EAAE,WAAW;UAAQ;;SACzE,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;UACE,SAAQ;UACR,MAAK;UACL,eAAe;WACb,eAAe,QAAQ,cAAc,KAAK,UAAU,QAAQ,CAAC;WAC7D,eAAe,QAAQ,YAAY,WAAW;WAC9C,SAAS,OAAO;UAClB;UACA,UAAA,EAAE,SAAS;SAAU,CAAA;QACpB;;OAEN,iBAAiB,KAChB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWL,0BAAI;QAApB,UAAA;SACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAM,KAAQ,CAAA;SACd,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAWA,0BAAI;UAArB,UAAA;WAA2B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UAAI,eAAkB,CAAA;WAAC;WAAE,EAAE,eAAe;UAAQ;;SAC7E,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UAAM,OAAO,EAAE,aAAa;UAAG,UAAA;SAAQ,CAAA;QACpC;;MAEJ;;KACJ,iBAAiB,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAWA,0BAAI;MAAM,UAAA;KAAkB,CAAA;KACtE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MACE,WAAWA,0BAAI;MACf,KAAK;MACL,WAAU,MAAK,WAAW,EAAE,cAAc,YAAY,GAAG;MAExD,UAAA,QAAQ,aACL,YACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAAQ,UAAA,EAAE,UAAU;MAAO,CAAA,IAC/C,SAAS,OACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAApB,UAAA,CAA6B,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA,GAAE,EAAE,SAAS,CAAO;MAE3E,CAAA,IAAA,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAClB,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,0BAAI;QAApB,UAAA,CACA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,KAAK;SAAa,WAAW,YAAY,gBAAgB,OAAO,GAAGA,0BAAI,SAAS,GAAGA,0BAAI,kBAAkBA,0BAAI;SACxG,iBAAA;UAEN,MAAM,UAAU,YAAY,QAAQ,QAAQ,aAAa,CAAC,KAAK,GAAG,WAAW,QAAO,OAAM,OAAO,GAAG,CAAC;UACrG,MAAM,QAAQ,YAAY,gBAAgB,OAAO,UAAU,QAAQ,MAAM,GAAG,KAAK,IAAI,GAAG,cAAc,CAAC,CAAC;UACxG,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;WACE,iBAAA,GAAA,kBAAA,IAAA,CAACS,sCAAAA,MAAD;YAAM,aAAU;YAAI,QAAQ,QAAQ;YAAO,eAAe,OAAO,KAAK;YAAI,UAAA,EAAE,KAAK;WAAQ,CAAA;WACxF,MAAM,KAAI,OACT,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,MAAD;YAEE,aAAU;YACV,QAAQ,QAAQ;YAChB,eAAe,OAAO,EAAE;YACvB,UAAA,KAAK,WAAW,QAAQ,KAAK,WAAW,GAAG,CAAE,SAAS,KAAK,WAAW,GAAG,CAAE,OAAQ;WAAS,GAJxF,EAIwF,CAChG;WACD,iBAAA,GAAA,kBAAA,IAAA,CAACJ,sCAAAA,QAAD;YACE,SAAQ;YACR,MAAK;YACL,WAAWL,0BAAI;YACf,MAAM,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAACU,sCAAAA,wBAAD,EAAwB,MAAM,GAAK,CAAA,IAAI,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,0BAAD,EAA0B,MAAM,GAAK,CAAA;YAC7F,cAAY,WAAW,EAAE,UAAU,IAAI,EAAE,UAAU;YACnD,eAAe,aAAY,MAAK,CAAC,CAAC;WACnC,CAAA;UACD,EAAA,CAAA;SAEN,EAAA,CAAG;QACA,CAAA,GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,WAAWX,0BAAI;SACjB,UAAA,CAAC,OAAO,KAAK,CAAC,CAAC,KAAI,QAClB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAEE,WAAW,SAAS,MAAMA,0BAAI,KAAK;UACnC,eAAe,QAAQ,GAAG;UAC1B,UAAA,EAAE,QAAQ,QAAQ,YAAY,SAAS;SAAU,GAH5C,GAG4C,CACpD;QACE,CAAA,CACA;;MACF,CAAA,GACJ,QAAQ,WAAW,IAChB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAAQ,UAAA,EAAE,OAAO;MAAO,CAAA,IAC5C,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAAO,UAAA,QAAQ,IAAI,UAAU;MAAO,CAAA,CAC5D,EAAA,CAAA,IAER,QAAQ,YAAY,cAAc,OAE9B,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,QAIU;OACN,MAAM,QAAQ,UAAU,OAAO,QAAO,QAAO,IAAI,OAAO,WAAW,IAAI,OAAO,MAAM;OACpF,OAAO,MAAM,SAAS,KACpB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAW,GAAGA,0BAAI,KAAK,GAAGA,0BAAI;QAChC,UAAA,MAAM,KAAI,QAAO,UAAU,IAAI,IAAI,IAAI,IAAI,YAAY,GAAG,CAAC,CAAC;OAC1D,CAAA;MAET,EAAA,CAAG,GACF,SAAS,OACN,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAApB,UAAA,CAA6B,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA,GAAE,EAAE,SAAS,CAAO;MAC7E,CAAA,IAAA,aAAa,WAAW,IACtB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAAQ,UAAA,EAAE,YAAY;MAAO,CAAA,IACjD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAAO,UAAA,aAAa,IAAI,eAAe;MAAO,CAAA,CACxE,EAAA,CAAA,IAEJ,OAAO,KAAK,SAAS,CAAC,CAAC,WAAW,IAChC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAWA,0BAAI;OAAQ,UAAA,EAAE,gBAAgB;MAAO,CAAA,IACrD,OAAO,QAAQ,SAAS,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU;OAC9C,MAAM,QAAQ,SAAS,OAAO,KAAA,IAAY,KAAK,QAAQ,MAAK,MAAK,EAAE,SAAS,QACtE,OAAO,EAAE,GAAG,MAAM,QAAQ,OAAO,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,UAAU,YAAY,OAAO,EAAE,GAAG,EAAA,CAAG,YAAY,CAAC,CAAE;OAC/G,MAAM,SAAS,QAAQ;OACvB,MAAM,UAAU,UAAU,OAAO,UAAU,MAAM,OAAO,UAAU;OAClE,MAAM,WAAW,OAAO,IAAI;OAC5B,MAAM,SAAS,qDAAqD,KAAK,QAAQ;OACjF,MAAM,UAAU,UAAU,KAAA,IAAY,MAAM,MAAM,WAAW,OAAO,wBAAwB,OAAO,KAAK;OACxG,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAgB,WAAWA,0BAAI;QAA/B,UAAA;SACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;UAAK,OAAO,EAAE,UAAU,EAAE;UAA1B,UAAA;WACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;YAAK,WAAWA,0BAAI;YAApB,UAAA,CAAyB,MAAM,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;aAAM,WAAWA,0BAAI;aAAQ,UAAA,MAAM;YAAc,CAAA,CAAO;;WACjG,YAAY,OACT,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;YAAG,WAAW,GAAGA,0BAAI,KAAK,GAAGA,0BAAI;YAAO,MAAM;YAAS,QAAO;YAAS,KAAI;YAAa,OAAO,EAAE,SAAS,eAAe;YAAI,UAAA;WAAY,CAAA,IACzI,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;YAAK,WAAWA,0BAAI;YAAO,UAAA;WAAc,CAAA;WAC5C,UAAU,KAAA,KACT,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;YAAK,WAAW,GAAGA,0BAAI,KAAK,GAAGA,0BAAI;YAC/B,UAAA,MAAM,gBAAgB,MAAM,YAAY,SAAS,MAAM,YAAY,OAAQ;WAC1E,CAAA;WAEN,iBAAiB,QAChB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;YAAK,WAAWA,0BAAI;YAApB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA,GAC5B,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;aAAM,WAAWA,0BAAI;aAAO,UAAA,gBAAgB,EAAE,cAAc;YAAQ,CAAA,CACjE;;UAEJ;;SACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAWA,0BAAI,KAAO,CAAA;SAC3B,YAAY,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;UAAG,WAAWA,0BAAI;UAAK,MAAM,UAAU;UAAW,QAAO;UAAS,KAAI;UAAc,UAAA,EAAE,QAAQ;SAAK,CAAA;SACvH,aAAa,SAAS,IAAI,IACvB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UAAM,WAAWA,0BAAI;UAAU,UAAA,EAAE,SAAS;SAAQ,CAAA,IAClD,iBAAiB,OACf,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;UAAQ,SAAQ;UAAU,MAAK;UAAK,WAAWL,0BAAI;UAAS,UAAA;UAAU,UAAA,EAAE,UAAU;SAAU,CAAA,IAC5F,UAAU,OAAO,kBAEb,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;UACE,SAAQ;UACR,MAAK;UACL,WAAWL,0BAAI;UACf,UAAU,iBAAiB;UAC3B,eAAe,SAAS,IAAI;UAC5B,UAAA,EAAE,QAAQ;SAAU,CAAA,IAExB,UAAU,OAAO,SAAS,WACxB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UAAM,WAAWA,0BAAI;UAAQ,UAAA,EAAE,WAAW;SAAQ,CAAA,IAClD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UAAM,WAAWA,0BAAI;UAAQ,UAAA,EAAE,UAAU;SAAQ,CAAA;SAC1D,SAAS,gBAAgB,SAAS,gBACjC,iBAAiB,OACb,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;UAAQ,SAAQ;UAAU,MAAK;UAAK,WAAWL,0BAAI;UAAW,UAAA;UAAU,UAAA,EAAE,cAAc;SAAU,CAAA,IAClG,gBAAgB,OAEZ,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;UACE,SAAQ;UACR,MAAK;UACL,WAAWL,0BAAI;UACf,eAAe,YAAY,IAAI;UAC/B,oBAAoB,eAAe,IAAI;UACvC,UAAA,EAAE,eAAe;SAAU,CAAA,IAG7B,iBAAA,GAAA,kBAAA,IAAA,CAACK,sCAAAA,QAAD;UACE,SAAQ;UACR,MAAK;UACL,WAAWL,0BAAI;UACf,UAAU,iBAAiB,QAAQ,YAAY,QAAQ,iBAAiB;UACxE,eAAe,eAAe,IAAI;UAClC,UAAA,EAAE,WAAW;SAAU,CAAA;QAGhC;OA5DK,GAAA,IA4DL;MAET,CAAC;KACN,CAAA;KACJ,WACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MACE,WAAWA,0BAAI;MACf,OAAO,EAAE,SAAS;MAClB,eAAe;OAAE,MAAM,KAAK,QAAQ;OAAS,IAAI,IAAI,GAAG,SAAS;QAAE,KAAK;QAAG,UAAU;OAAS,CAAC;MAAE;MAClG,UAAA;KAAS,CAAA;KAEX,eAAe,QACd,iBAAA,GAAA,kBAAA,KAAA,CAACY,sCAAAA,OAAD;MACE,MAAA;MACA,eAAe,cAAc,IAAI;MACjC,OAAO,EAAE,cAAc,IAAI,MAAM,WAAW,OAAO;MACnD,aAAc,WAAW,gBAAgB,WAAW,YAAY,SAAS,WAAW,YAAY,OAAQ;MACxG,QACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAACP,sCAAAA,QAAD;OAAQ,SAAQ;OAAQ,eAAe,cAAc,IAAI;OAAI,UAAA,EAAE,QAAQ;MAAU,CAAA,GACjF,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;OAAQ,SAAQ;OAAU,eAAe,UAAU,UAAU;OAAI,UAAA,EAAE,SAAS;MAAU,CAAA,CACtF,EAAA,CAAA;MATN,UAAA;OAYE,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;QAAS,WAAWL,0BAAI;QAAxB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;SAAS,WAAWA,0BAAI;SAAa,UAAA,EAAE,YAAY;QAAW,CAAA,GAC9D,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,WAAWA,0BAAI;SAAM,UAAA,WAAW;QAAa,CAAA,CAC3C;;OACR,cAAc,YAAY,IAAI,KAC7B,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD;QAAG,WAAWA,0BAAI;QAAlB,UAAA,CACG,SAAS,EAAE,cAAc,IAAI,KAC9B,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;SAAG,WAAWA,0BAAI;SAAK,MAAM,WAAW,MAAM;SAAW,QAAO;SAAS,KAAI;SAAc,UAAA,EAAE,QAAQ;QAAK,CAAA,CACzG;;OAEL,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;QAAG,WAAWA,0BAAI;QAAY,UAAA,QAAQ,EAAE,aAAa;OAAK,CAAA;MACrD;;IAEN;;EAET;;;;;;;;;;EC3wBA,MAAM,KAAK;EAiCX,MAAa,OAAO;EAIpB,MAAa,SAAS;GAAC;GAAS;GAAU;EAAO;EACjD,SAAgB,MAAM,KAAgC;GACpD,IAAI,aAAa,IAAI,OAAO,SAAS,IAAI;IAAE;IAAI;GAAG,CAAC,GAAG,0BAA0B;GAChF,MAAM,IAAI,IAAI,OAAO,KAAK,EAAE;GAE5B,IAAI,MAAM,OAAO,0BAA0B,IAAI,MAAM,SAAS;IAC5D,MAAM;IACN,IAAI;IACJ,OAAO;IACP,aAAa,EAAE,KAAK;IACpB,QAAQ;IACR,eAAe,EAAE,EAAE;GACrB,UAAA,GAASa,MAAAA,cAAAA,CAAE,eAAe;IACxB;IACA,QAAQ,IAAI;IACZ,OAAO,IAAI;IACX,YAAY;KACV,YAAY,OAAmB,IAAI,GAAG,gBAAgB,EAAE;KACxD,mBAAmB,IAAI,MAAM,SAAS;IACxC;GACF,CAAC,CAAC,CAAC;GAEH,MAAM,eAAA,GAAcA,MAAAA,cAAAA,CAAE,cAAc,EAAE,EAAE,CAAC;GACzC,IAAI,MAAM,OAAO,uBAAuB,IAAI,MAAM,SAAS;IACzD,MAAM;IACN,IAAI;IACJ,aAAa;GACf,GAAG,KAAK,CAAC;EACX"}
1
+ {"version":3,"file":"client.js","names":["useState","Toast","useState","css","useSyncExternalStore","useRef","useCallback","useMemo","Button","StateDot","Input","IconSearchOutline16","Pill","IconChevronUpOutline14","IconChevronDownOutline14","Modal","h"],"sources":["../src/client/locales.ts","../src/client/market-data.ts","../src/client/InstallToast.tsx","../src/client/MarketSection.tsx","../src/client/index.ts"],"sourcesContent":["/** zh/en dictionaries for the Market settings section and install toast. */\n\nexport const zh = {\n nav: '插件市场',\n subtitle: '发现社区为 DeepSeek Harness 打造的能力',\n searchPh: '搜索插件,比如:通知、终端、记忆…',\n tabDiscover: '发现',\n tabInstalled: '已安装',\n all: '全部',\n install: '安装',\n installing: '安装中…',\n installedBadge: '✓ 已装好',\n alreadyInstalled: '✓ 已安装',\n restartBanner: '项变更完成,重启 DeepSeek Harness 后生效',\n uninstall: '卸载',\n confirmRemove: '确认卸载?',\n uninstalling: '卸载中…',\n restartHint: '重启方式:关闭当前 dsh 进程后重新运行(例如 dsh web)',\n confirmTitle: '安装',\n confirmWarn: '插件是社区第三方代码。安装即表示你信任该来源;构建脚本默认被禁止执行。',\n cancel: '取消',\n empty: '没有匹配的插件',\n installedEmpty: '还没有装过社区插件,去「发现」页逛逛吧',\n loadFail: '插件目录加载失败,请稍后重试',\n installFail: '安装失败',\n viewSource: '源码',\n hotBanner: '个新插件已装好,刷新页面即可使用',\n refresh: '刷新页面',\n update: '更新',\n updating: '更新中…',\n updated: '✓ 已更新,重启后生效',\n updateFail: '更新失败',\n upToDate: '已是最新',\n linkedDev: '本地开发链接',\n exportLog: '导出日志',\n readme: '使用说明',\n terminalWarn: '这看起来是终端/命令行插件:装进网页版可能无效,甚至导致 DeepSeek Harness 无法启动。建议先看它的使用说明,按说明装进对应的 profile。',\n envMissing: '还差一个小组件才能安装插件',\n envFix: '自动装好',\n envFixing: '正在准备…',\n envFixFail: '自动准备没成功,请点\"导出日志\"把文件发给我们反馈',\n loading: '正在加载插件目录…',\n backTop: '回到顶部',\n confirm: '确认',\n cmdDetails: '安装命令',\n catsMore: '更多分类',\n catsLess: '收起',\n sortHot: '最热',\n sortNew: '最新',\n marketUpdate: '市场有新版本,升级',\n updateAll: '全部更新',\n tabThemes: '主题',\n themeApply: '使用',\n themeActive: '使用中',\n themeEmpty: '目录里暂时还没有主题,敬请期待',\n progressHint: '首次安装需要下载与解析依赖,大插件可能要 1-3 分钟',\n toastReady: '已装好并已生效',\n toastTheme: '已启用。到 设置 → 插件市场 → 主题 可随时切换',\n gotIt: '知道了',\n}\n\n/** A dictionary key; zh is the source of truth, en mirrors it 1:1. */\nexport type MarketKey = keyof typeof zh\n\nexport const en: Record<MarketKey, string> = {\n nav: 'Plugin Market',\n subtitle: 'Discover community plugins for DeepSeek Harness',\n searchPh: 'Search plugins: notify, terminal, memory…',\n tabDiscover: 'Discover',\n tabInstalled: 'Installed',\n all: 'All',\n install: 'Install',\n installing: 'Installing…',\n installedBadge: '✓ Installed',\n alreadyInstalled: '✓ Installed',\n restartBanner: 'change(s) done — restart DeepSeek Harness to apply',\n uninstall: 'Uninstall',\n confirmRemove: 'Confirm?',\n uninstalling: 'Removing…',\n restartHint: 'To restart: stop the current dsh process and run it again (e.g. dsh web)',\n confirmTitle: 'Install',\n confirmWarn: 'Plugins are third-party community code. Installing means you trust this source; build scripts are blocked by default.',\n cancel: 'Cancel',\n empty: 'No plugins match',\n installedEmpty: 'No community plugins yet — browse the Discover tab',\n loadFail: 'Failed to load the plugin catalog, please retry later',\n installFail: 'Install failed',\n viewSource: 'Source',\n hotBanner: 'new plugin(s) ready — refresh the page to use them',\n refresh: 'Refresh',\n update: 'Update',\n updating: 'Updating…',\n updated: '✓ Updated — restart to apply',\n updateFail: 'Update failed',\n upToDate: 'Up to date',\n linkedDev: 'linked (dev)',\n exportLog: 'Export log',\n readme: 'README',\n terminalWarn: 'This looks like a terminal/CLI plugin: installing it into the web profile may do nothing, or even break DeepSeek Harness startup. Read its README and install it into the profile it targets.',\n envMissing: 'One small component is needed before installing plugins',\n envFix: 'Set up automatically',\n envFixing: 'Setting up…',\n envFixFail: 'Automatic setup failed — please use \"Export log\" and send us the file',\n loading: 'Loading the catalog…',\n backTop: 'Back to top',\n confirm: 'Confirm',\n cmdDetails: 'Install command',\n catsMore: 'More',\n catsLess: 'Less',\n sortHot: 'Top',\n sortNew: 'New',\n marketUpdate: 'Market update available — upgrade',\n updateAll: 'Update all',\n tabThemes: 'Themes',\n themeApply: 'Use',\n themeActive: 'Active',\n themeEmpty: 'No more theme plugins in the catalog yet — stay tuned',\n progressHint: 'First installs download and resolve dependencies — large plugins can take 1-3 minutes',\n toastReady: 'installed and live',\n toastTheme: 'is now active. Switch any time in Settings → Plugin Market → Themes',\n gotIt: 'Got it',\n}\n","/**\n * Response shapes of the /dsh-market/* host routes plus the pure helpers the\n * Market UI shares between its section and toast components.\n */\n\n/** Localized text keyed by language ('zh' / 'en'). */\nexport type LocalizedText = Record<string, string | undefined>\n\n/** One registry entry from /dsh-market/registry. */\nexport interface RegistryPlugin {\n name: string\n owner: string\n url: string\n npm?: string\n category: string\n description?: LocalizedText\n stars?: number\n added?: string\n install?: string\n}\n\n/** The catalog payload under `registry` in /dsh-market/registry. */\nexport interface Registry {\n count: number\n categories: Record<string, LocalizedText>\n plugins: RegistryPlugin[]\n}\n\n/** Profile dependency map: package name → install spec. */\nexport type InstalledMap = Record<string, string>\n\n/** Per-package update status from /dsh-market/updates. */\nexport interface UpdateStatus {\n updateAvailable?: boolean\n version?: string\n kind?: string\n}\n\n/** Poll payload from /dsh-market/status. */\nexport interface MarketStatus {\n active?: boolean\n lastLine?: string\n seconds?: number\n installed?: InstalledMap\n pnpm?: boolean\n boot?: string\n}\n\n/** Registered theme definition surfaced by the theme service snapshot. */\nexport interface ThemeDef {\n id: string\n colorScheme?: string\n tokens?: Record<string, string | undefined>\n}\n\n/** Theme service snapshot; null when the composition has no theme service. */\nexport interface ThemeSnapshot {\n preference: string\n themes: ThemeDef[]\n}\n\n/** Bound locale translator for the dsh-market namespace. */\nexport type Translate = (key: string) => string\n\nexport function avatarColor(name: string): string {\n let hash = 0\n for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0\n return 'hsl(' + (((hash % 360) + 360) % 360) + ' 55% 52%)'\n}\n\nexport function repoOf(url: string): string | null {\n // Plain repo urls plus /tree/<branch>/<subpath> monorepo links.\n const m = /^https:\\/\\/github\\.com\\/([^/]+\\/[^/]+?)(?:\\/tree\\/.+)?\\/?$/.exec(url)\n return m ? m[1]! : null\n}\n\nexport function readSession(key: string): any {\n try { return JSON.parse(sessionStorage.getItem(key) || 'null') } catch { return null }\n}\n\n/** Heuristic: plugins that target a terminal surface rather than the web UI. */\nexport function looksTerminal(plugin: RegistryPlugin, lang: string): boolean {\n const desc = (plugin.description && (plugin.description[lang] || plugin.description.en)) || ''\n return /\\b(tui|cli|tty|terminal)\\b|终端|命令行/i.test(plugin.name + ' ' + desc)\n}\n\n/**\n * Unified installed-state matching (#15): both sides collapse to lowercase\n * identity sets — the registry entry contributes its bare name, npm name and\n * owner/repo; the dependency contributes its key and the repo inside its\n * spec — and any exact intersection counts. Exact equality, not substrings,\n * so prefix-related repo names cannot cross-match.\n */\nfunction entryIdentities(plugin: RegistryPlugin): Set<string> {\n const ids = new Set<string>([plugin.name.toLowerCase()])\n if (plugin.npm) ids.add(plugin.npm.toLowerCase())\n const repo = repoOf(plugin.url)\n if (repo !== null) ids.add(repo.toLowerCase())\n return ids\n}\n\nfunction depIdentities(name: string, spec: string): Set<string> {\n const ids = new Set<string>([name.toLowerCase()])\n // A scoped npm key usually mirrors owner/repo — expose that identity so an\n // npm-installed plugin still matches an entry whose npm field is unset.\n const scoped = /^@([^/]+)\\/(.+)$/.exec(name)\n if (scoped !== null) ids.add(`${scoped[1]!.toLowerCase()}/${scoped[2]!.toLowerCase()}`)\n const match = /github:([A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+)/i.exec(spec)\n if (match !== null) ids.add(match[1]!.toLowerCase())\n return ids\n}\n\n/** The installed dependency name a registry entry corresponds to, or null. */\nexport function matchInstalledName(plugin: RegistryPlugin, installed: InstalledMap): string | null {\n const ids = entryIdentities(plugin)\n for (const [name, spec] of Object.entries(installed)) {\n for (const id of depIdentities(name, String(spec))) {\n if (ids.has(id)) return name\n }\n }\n return null\n}\n\n/** The registry entry an installed dependency corresponds to, or undefined. */\nexport function entryForDep(plugins: RegistryPlugin[], name: string, spec: string): RegistryPlugin | undefined {\n const ids = depIdentities(name, String(spec))\n return plugins.find((plugin) => {\n for (const id of entryIdentities(plugin)) if (ids.has(id)) return true\n return false\n })\n}\n\nexport function isInstalled(plugin: RegistryPlugin, installed: InstalledMap): boolean {\n return matchInstalledName(plugin, installed) !== null\n}\n\n/**\n * The brand mark (assets/logo.svg — shared block-grid mark with\n * awesome-dsh-plugin), inlined so the header needs no extra request.\n */\nexport const LOGO_URI = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 128 128\"><rect width=\"128\" height=\"128\" rx=\"28\" fill=\"#f6f2ea\"/><g transform=\"translate(15.7 16.7) scale(0.3) translate(-112 -78)\"><g fill=\"#2b2620\"><rect x=\"112\" y=\"112\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"112\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"112\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"112\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"312\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"312\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/></g><rect x=\"346\" y=\"78\" width=\"88\" height=\"88\" rx=\"14\" fill=\"#c0392b\" transform=\"rotate(9 390 122)\"/></g></svg>')\n\n/** Four representative colors for a theme card's preview strip. */\nexport function themeSwatch(def: ThemeDef): string[] {\n const tk = def.tokens || {}\n const pick = (names: string[]) => { for (const n of names) { if (tk[n]) return tk[n]! } return null }\n const dark = def.colorScheme === 'dark'\n return [\n pick(['--dsw-alias-bg-base', '--dsw-alias-bg-layer-1']) || (dark ? '#0f1115' : '#ffffff'),\n pick(['--dsw-alias-bg-layer-2', '--dsw-alias-bg-overlay']) || (dark ? '#1a1d23' : '#f3f4f6'),\n pick(['--dsw-alias-brand-primary']) || '#4f6ef7',\n pick(['--dsw-alias-label-primary']) || (dark ? '#e5e7eb' : '#1f2328'),\n ]\n}\n","/**\n * Post-reload confirmation via the official Toast primitive: shown once after\n * the refresh that follows a hot install or theme switch, so the user lands\n * back in their flow with visible proof.\n */\nimport { useState } from 'react'\nimport { Toast } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { readSession } from './market-data.ts'\nimport type { Translate } from './market-data.ts'\n\nexport function InstallToast(props: { t: Translate }) {\n const t = props.t\n const [mode] = useState(() => {\n const value = sessionStorage.getItem('dshm-toast-mode')\n sessionStorage.removeItem('dshm-toast-mode')\n return value\n })\n const [names, setNames] = useState<string[]>(() => {\n const value = readSession('dshm-toast')\n sessionStorage.removeItem('dshm-toast')\n return Array.isArray(value) ? value : []\n })\n if (names.length === 0) return null\n return (\n <Toast\n text={names.join(', ') + ' ' + t(mode === 'theme' ? 'toastTheme' : 'toastReady')}\n icon={<span>✨</span>}\n onDone={() => setNames([])}\n />\n )\n}\n","/**\n * The Market settings section: Discover / Themes / Installed tabs over the\n * /dsh-market/* host routes, with install/update/uninstall flows and the\n * pending-restart bookkeeping in sessionStorage.\n */\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'\nimport { Button, IconChevronDownOutline14, IconChevronUpOutline14, IconSearchOutline16, Input, Modal, Pill, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'\nimport css from './Market.module.css'\nimport {\n avatarColor, entryForDep, isInstalled, LOGO_URI, looksTerminal, matchInstalledName, readSession, repoOf, themeSwatch,\n} from './market-data.ts'\nimport type {\n InstalledMap, Registry, RegistryPlugin, ThemeSnapshot, Translate, UpdateStatus,\n} from './market-data.ts'\n\n/**\n * Card avatar: the plugin owner's GitHub avatar (no API, browser-cached),\n * falling back to the initial-letter tile when it can't load.\n */\nfunction OwnerAvatar({ name, owner }: { name: string; owner: string }) {\n const [failed, setFailed] = useState(false)\n if (failed || owner === '') {\n return (\n <div className={css.av} style={{ background: avatarColor(name) }}>\n {name.replace(/^dsh[-_]/i, '').charAt(0).toUpperCase() || 'P'}\n </div>\n )\n }\n return (\n <img\n className={css.av}\n src={`https://github.com/${encodeURIComponent(owner)}.png?size=96`}\n alt=\"\"\n loading=\"lazy\"\n onError={() => setFailed(true)}\n />\n )\n}\n\nexport interface MarketSectionProps {\n t: Translate\n locale: {\n subscribe(callback: () => void): () => void\n getSnapshot(): { active: string }\n }\n theme: { setTheme(id: string): void }\n themeStore: {\n subscribe(callback: () => void): () => void\n getSnapshot(): ThemeSnapshot | null\n }\n}\n\nexport function MarketSection(props: MarketSectionProps) {\n const t = props.t\n const localeSnap = useSyncExternalStore(\n cb => props.locale.subscribe(cb),\n () => props.locale.getSnapshot(),\n )\n const lang = String(localeSnap.active).toLowerCase().startsWith('zh') ? 'zh' : 'en'\n // null when the composition has no theme service — the Themes tab hides.\n const themeSnap = useSyncExternalStore(\n props.themeStore.subscribe,\n props.themeStore.getSnapshot,\n )\n const [data, setData] = useState<Registry | null>(null)\n const [loadError, setLoadError] = useState(false)\n const [installed, setInstalled] = useState<InstalledMap>({})\n const [skins, setSkins] = useState<string[]>([])\n const [tab, setTab] = useState(() => {\n const saved = sessionStorage.getItem('dshm-tab')\n if (saved !== null) sessionStorage.removeItem('dshm-tab')\n return saved || 'discover'\n })\n const [q, setQ] = useState('')\n const [cat, setCat] = useState('all')\n const [confirming, setConfirming] = useState<RegistryPlugin | null>(null)\n const [busyUrl, setBusyUrl] = useState<string | null>(null)\n const [doneUrls, setDoneUrls] = useState<string[]>([])\n const [installError, setInstallError] = useState<string | null>(null)\n const [updates, setUpdates] = useState<Record<string, UpdateStatus>>({})\n const [updatingName, setUpdatingName] = useState<string | null>(null)\n const [updatingAll, setUpdatingAll] = useState(false)\n const [updatedNames, setUpdatedNames] = useState<string[]>([])\n const [hotUrls, setHotUrls] = useState<string[]>([])\n const [hotNames, setHotNames] = useState<string[]>([])\n const [progressLine, setProgressLine] = useState<string | null>(null)\n const [removeArmed, setRemoveArmed] = useState<string | null>(null)\n const [removingName, setRemovingName] = useState<string | null>(null)\n const [removedCount, setRemovedCount] = useState(0)\n const [envReady, setEnvReady] = useState(true)\n const [envFixing, setEnvFixing] = useState(false)\n const [envFailed, setEnvFailed] = useState(false)\n const [bootId, setBootId] = useState<string | null>(null)\n const [showTop, setShowTop] = useState(false)\n const bodyRef = useRef<HTMLDivElement | null>(null)\n const [sort, setSort] = useState('hot')\n const [catsOpen, setCatsOpen] = useState(false)\n // How many category pills fit in the two collapsed rows (measured once —\n // the settings panel width is fixed); null = measuring render with all\n // pills clamped, then slice so the chevron flows inline after the last one.\n const [visibleCats, setVisibleCats] = useState<number | null>(null)\n const catsWrapRef = useRef<HTMLDivElement | null>(null)\n\n const refreshInstalled = useCallback((force?: boolean) => {\n fetch('/dsh-market/installed', { cache: 'no-store' })\n .then(res => res.json())\n .then(body => {\n setInstalled(body.installed || {})\n setSkins(body.live || [])\n })\n .catch(() => {})\n fetch('/dsh-market/updates' + (force === true ? '?force=1' : ''), { cache: 'no-store' })\n .then(res => res.json())\n .then(body => setUpdates(body.updates || {}))\n .catch(() => {})\n }, [])\n\n useEffect(() => {\n fetch('/dsh-market/registry', { cache: 'no-store' })\n .then(res => { if (!res.ok) throw new Error('HTTP ' + res.status); return res.json() })\n .then(body => setData(body.registry))\n .catch(() => setLoadError(true))\n fetch('/dsh-market/status', { cache: 'no-store' })\n .then(res => res.json())\n .then(status => {\n setEnvReady(status.pnpm !== false)\n if (typeof status.boot === 'string') setBootId(status.boot)\n })\n .catch(() => {})\n refreshInstalled()\n }, [refreshInstalled])\n\n // Pending-restart flags survive tab switches and page reloads, scoped to\n // one host process: a different boot id means the restart happened and the\n // stale banner must not resurrect.\n useEffect(() => {\n if (bootId === null) return\n const saved = readSession('dshm-restart')\n if (saved === null) return\n if (saved.boot !== bootId) {\n sessionStorage.removeItem('dshm-restart')\n return\n }\n if (Array.isArray(saved.doneUrls) && saved.doneUrls.length > 0) setDoneUrls(saved.doneUrls)\n if (Array.isArray(saved.updated) && saved.updated.length > 0) setUpdatedNames(saved.updated)\n if (typeof saved.removed === 'number' && saved.removed > 0) setRemovedCount(saved.removed)\n }, [bootId])\n\n useEffect(() => {\n if (bootId === null) return\n if (doneUrls.length === 0 && updatedNames.length === 0 && removedCount === 0) return\n sessionStorage.setItem('dshm-restart', JSON.stringify({\n boot: bootId,\n doneUrls,\n updated: updatedNames,\n removed: removedCount,\n }))\n }, [bootId, doneUrls, updatedNames, removedCount])\n\n const fixEnv = useCallback(() => {\n setEnvFixing(true)\n setEnvFailed(false)\n fetch('/dsh-market/setup-pnpm', { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' })\n .then(res => res.json())\n .then(body => {\n if (body.ok) setEnvReady(true)\n else setEnvFailed(true)\n })\n .catch(() => setEnvFailed(true))\n .finally(() => setEnvFixing(false))\n }, [])\n\n // Recover an install whose HTTP response was lost (page navigated away or\n // the connection dropped): the pending marker survives in sessionStorage and\n // the poll below converges the button state from the host's ground truth.\n useEffect(() => {\n const pending = readSession('dshm-pending')\n if (pending !== null && typeof pending.url === 'string') setBusyUrl(pending.url)\n }, [])\n\n useEffect(() => {\n if (busyUrl === null && updatingName === null) {\n setProgressLine(null)\n return\n }\n const timer = setInterval(() => {\n fetch('/dsh-market/status', { cache: 'no-store' })\n .then(res => res.json())\n .then(status => {\n if (status.active) {\n setProgressLine((status.lastLine || '…') + ' (' + status.seconds + 's)')\n } else {\n setProgressLine(null)\n setInstalled(status.installed || {})\n const pending = readSession('dshm-pending')\n if (pending !== null && busyUrl !== null) {\n const nowInstalled = data !== null && data.plugins.some(p =>\n p.url === busyUrl && isInstalled(p, status.installed || {}))\n if (nowInstalled) {\n sessionStorage.removeItem('dshm-pending')\n setDoneUrls(urls => urls.includes(busyUrl) ? urls : urls.concat(busyUrl))\n setBusyUrl(null)\n }\n }\n }\n })\n .catch(() => {})\n }, 2000)\n return () => clearInterval(timer)\n }, [busyUrl, updatingName, data])\n\n const plugins = useMemo(() => {\n if (data === null) return []\n const query = q.trim().toLowerCase()\n const list = data.plugins.filter(p => {\n if (cat !== 'all' && p.category !== cat) return false\n if (query === '') return true\n const desc = (p.description && (p.description[lang] || p.description.en)) || ''\n return p.name.toLowerCase().includes(query)\n || p.owner.toLowerCase().includes(query)\n || desc.toLowerCase().includes(query)\n })\n if (sort === 'hot') {\n return [...list].sort((a, b) => (b.stars ?? -1) - (a.stars ?? -1))\n }\n if (sort === 'new') {\n return [...list].sort((a, b) => String(b.added).localeCompare(String(a.added)))\n }\n return list\n }, [data, q, cat, lang, sort])\n\n const doInstall = useCallback((plugin: RegistryPlugin) => {\n setConfirming(null)\n setInstallError(null)\n setBusyUrl(plugin.url)\n sessionStorage.setItem('dshm-pending', JSON.stringify({ url: plugin.url }))\n fetch('/dsh-market/install', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ url: plugin.url }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n sessionStorage.removeItem('dshm-pending')\n if (status === 200 && body.ok && body.hot && plugin.category === 'theme') {\n // Themes auto-activate on install; reload straight into the Themes\n // tab so the new look is on screen immediately.\n sessionStorage.setItem('dshm-toast', JSON.stringify([plugin.name]))\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n return\n }\n if (status === 200 && body.ok) {\n sessionStorage.setItem('dshm-tab', 'installed')\n if (body.hot) {\n setHotUrls(urls => urls.includes(plugin.url) ? urls : urls.concat(plugin.url))\n setHotNames(names => names.includes(plugin.name) ? names : names.concat(plugin.name))\n } else {\n setDoneUrls(urls => urls.includes(plugin.url) ? urls : urls.concat(plugin.url))\n }\n refreshInstalled()\n } else {\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n const detail = text(body.error) || [text(body.stderr), text(body.stdout)].filter(Boolean).join('\\n').trim() || ('exit ' + body.exitCode)\n setInstallError(t('installFail') + ': ' + plugin.name + ' — ' + detail.trim().slice(-600))\n }\n })\n .catch(error => {\n sessionStorage.removeItem('dshm-pending')\n setInstallError(t('installFail') + ': ' + String(error))\n })\n .finally(() => setBusyUrl(null))\n }, [refreshInstalled, t])\n\n const doUpdate = useCallback((name: string) => {\n setInstallError(null)\n setUpdatingName(name)\n return fetch('/dsh-market/update', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n setUpdatedNames(names => names.concat(name))\n refreshInstalled()\n } else {\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n const detail = text(body.error) || [text(body.stderr), text(body.stdout)].filter(Boolean).join('\\n').trim() || ('exit ' + body.exitCode)\n setInstallError(t('updateFail') + ': ' + name + ' — ' + detail.trim().slice(-600))\n }\n })\n .catch(error => setInstallError(t('updateFail') + ': ' + String(error)))\n .finally(() => setUpdatingName(null))\n }, [refreshInstalled, t])\n\n const doUseSkin = useCallback((name: string) => {\n setInstallError(null)\n fetch('/dsh-market/use-skin', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n sessionStorage.setItem('dshm-toast', JSON.stringify([name]))\n sessionStorage.setItem('dshm-toast-mode', 'theme')\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n } else {\n setInstallError(String(body.error || 'failed'))\n }\n })\n .catch(error => setInstallError(String(error)))\n }, [])\n\n const doUninstall = useCallback((name: string) => {\n setRemoveArmed(null)\n setInstallError(null)\n setRemovingName(name)\n return fetch('/dsh-market/uninstall', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n if (!body.hot) setRemovedCount(n => n + 1)\n refreshInstalled()\n } else {\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n setInstallError((text(body.error) || text(body.stderr) || 'error').trim().slice(-600))\n }\n })\n .catch(error => setInstallError(String(error)))\n .finally(() => setRemovingName(null))\n }, [refreshInstalled])\n\n // The market itself stays out of the batch: its update reloads this page\n // mid-run, which would strand the remaining items.\n const selfName = installed['dshmarket'] !== undefined ? 'dshmarket' : 'dsh-market'\n const updatableNames = Object.keys(installed).filter(\n name => name !== selfName && !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable,\n )\n\n const doUpdateAll = useCallback(() => {\n const names = updatableNames.slice()\n setUpdatingAll(true)\n const next = () => {\n const name = names.shift()\n if (name === undefined) {\n setUpdatingAll(false)\n return\n }\n doUpdate(name).then(next, next)\n }\n next()\n }, [updatableNames, doUpdate])\n\n const pendingRestart = doneUrls.length + updatedNames.length + removedCount\n const hasUpdates = Object.keys(installed).some(\n name => !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable,\n )\n\n const themePlugins = data === null ? [] : data.plugins\n .filter(p => p.category === 'theme')\n .sort((a, b) => (b.stars || 0) - (a.stars || 0))\n\n const pluginCard = (p: RegistryPlugin) => {\n const desc = (p.description && (p.description[lang] || p.description.en)) || ''\n const done = doneUrls.includes(p.url) || hotUrls.includes(p.url)\n const already = isInstalled(p, installed)\n const busy = busyUrl === p.url\n return (\n <div key={p.url} className={css.card}>\n <div className={css.row1}>\n <OwnerAvatar name={p.name} owner={p.owner || ''} />\n <div style={{ minWidth: 0 }}>\n <div className={css.nm}>{p.name}</div>\n <div className={css.owner}>\n {p.owner}\n {typeof p.stars === 'number' && <span className={css.star}>{' · ★ ' + p.stars}</span>}\n </div>\n </div>\n <span className={css.grow} />\n <a className={css.src} href={p.url} target=\"_blank\" rel=\"noreferrer\" style={{ alignSelf: 'flex-start', flexShrink: 0 }}>{t('viewSource')}</a>\n </div>\n <div className={css.desc}>{desc}</div>\n <div className={css.foot}>\n <span className={css.tag}>\n {(data!.categories[p.category] && (data!.categories[p.category]![lang] || data!.categories[p.category]!.en)) || p.category}\n </span>\n <span className={css.grow} />\n {done\n ? <span className={css.okState}>{t('installedBadge')}</span>\n : already\n ? <span className={css.okState}>{t('alreadyInstalled')}</span>\n : busy\n ? <Button variant=\"primary\" size=\"sm\" disabled>{t('installing')}</Button>\n : (\n <Button\n variant=\"primary\"\n size=\"sm\"\n disabled={busyUrl !== null || !envReady}\n onClick={() => setConfirming(p)}\n >{t('install')}</Button>\n )}\n </div>\n {busy && (\n <div className={css.progress}>\n <span className={css.spin} />\n <code className={css.grow}>{progressLine || t('progressHint')}</code>\n </div>\n )}\n </div>\n )\n }\n\n const installedNameOf = (p: RegistryPlugin) => matchInstalledName(p, installed)\n\n // Plugins loaded at boot (bundle-layer skins) aren't in the shim list but\n // are just as live; the boot manifest is the page's own record of them.\n const bootEntries = (typeof window !== 'undefined' && window.__DSH_BOOT__ && Array.isArray(window.__DSH_BOOT__.entries))\n ? window.__DSH_BOOT__.entries\n : []\n\n // Unified card for the Themes tab: install → use/in-use → uninstall.\n const themePluginCard = (p: RegistryPlugin) => {\n const instName = installedNameOf(p)\n if (instName === null) return pluginCard(p)\n const mounted = skins.includes(instName) || bootEntries.some(e => e.id === instName)\n const desc = (p.description && (p.description[lang] || p.description.en)) || ''\n return (\n <div key={p.url} className={css.card}>\n <div className={css.row1}>\n <OwnerAvatar name={p.name} owner={p.owner || ''} />\n <div style={{ minWidth: 0 }}>\n <div className={css.nm}>{p.name}</div>\n <div className={css.owner}>\n {p.owner}\n {typeof p.stars === 'number' && <span className={css.star}>{' · ★ ' + p.stars}</span>}\n </div>\n </div>\n <span className={css.grow} />\n <a className={css.src} href={p.url} target=\"_blank\" rel=\"noreferrer\" style={{ alignSelf: 'flex-start', flexShrink: 0 }}>{t('viewSource')}</a>\n </div>\n <div className={css.desc}>{desc}</div>\n <div className={css.foot}>\n <span className={css.grow} />\n {removingName === instName\n ? <Button variant=\"outline\" size=\"sm\" className={css.dangerBtn} disabled>{t('uninstalling')}</Button>\n : removeArmed === instName\n ? (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.dangerArmed}\n onClick={() => doUninstall(instName).then(() => {\n if (mounted) {\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n }\n })}\n >{t('confirmRemove')}</Button>\n )\n : <Button variant=\"outline\" size=\"sm\" className={css.dangerBtn} onClick={() => setRemoveArmed(instName)}>{t('uninstall')}</Button>}\n {mounted\n ? <span className={css.okState}>{t('themeActive')}</span>\n : <Button variant=\"primary\" size=\"sm\" onClick={() => doUseSkin(instName)}>{t('themeApply')}</Button>}\n </div>\n </div>\n )\n }\n\n const themeCard = (id: string, label: string, swatch: string[]) => {\n const active = themeSnap !== null && themeSnap.preference === id\n return (\n <div key={'th-' + id} className={css.card}>\n <div className={css.swatches}>{swatch.map((c, i) => <i key={i} style={{ background: c }} />)}</div>\n <div className={css.foot}>\n <span className={css.nm}>{label}</span>\n <span className={css.grow} />\n {active\n ? <span className={css.okState}>{t('themeActive')}</span>\n : (\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={() => { try { props.theme.setTheme(id) } catch (error) { setInstallError(String(error)) } }}\n >{t('themeApply')}</Button>\n )}\n </div>\n </div>\n )\n }\n\n const categories = data === null ? [] : Object.keys(data.categories)\n\n useLayoutEffect(() => { setVisibleCats(null) }, [lang, categories.length])\n useLayoutEffect(() => {\n if (catsOpen || visibleCats !== null) return\n const el = catsWrapRef.current\n if (el === null) return\n const chips = [...el.children].filter((c): c is HTMLElement => (c as HTMLElement).dataset?.chip === '1')\n if (chips.length === 0) return\n const first = chips[0]!\n const rowThreeTop = first.offsetTop + (first.offsetHeight + 6) * 2 - 3\n let fits = 0\n for (const chip of chips) { if (chip.offsetTop < rowThreeTop) fits += 1 }\n // Reserve the tail slot of row two for the chevron itself.\n setVisibleCats(fits >= chips.length ? fits : Math.max(1, fits - 1))\n }, [catsOpen, visibleCats, data])\n\n return (\n <div className={css.root}>\n <div className={css.head}>\n <div className={css.titleRow}>\n <img src={LOGO_URI} width={22} height={22} alt=\"\" style={{ borderRadius: '5px', flexShrink: 0 }} />\n <h2 className={css.title}>{t('nav')}</h2>\n {(() => {\n const self = installed['dshmarket'] !== undefined ? 'dshmarket' : 'dsh-market'\n return updates[self] && updates[self].updateAvailable && !updatedNames.includes(self)\n && (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.warnBtn}\n disabled={updatingName !== null || busyUrl !== null}\n onClick={() => { setTab('installed'); doUpdate(self) }}\n >{updatingName === self ? t('updating') : t('marketUpdate')}</Button>\n )\n })()}\n {updatableNames.length >= 2 && (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.warnBtn}\n disabled={updatingAll || updatingName !== null || busyUrl !== null || removingName !== null}\n onClick={() => { setTab('installed'); doUpdateAll() }}\n >{updatingAll ? t('updating') : t('updateAll') + ' (' + updatableNames.length + ')'}</Button>\n )}\n </div>\n <div className={css.sub}>\n {t('subtitle') + (data ? ' · ' + data.count : '') + ' · '}\n <a className={css.src} href=\"/dsh-market/logs\" download=\"dsh-market-log.txt\">{t('exportLog')}</a>\n </div>\n <div className={css.tabs}>\n <button className={tab === 'discover' ? `${css.tab} ${css.on}` : css.tab} onClick={() => setTab('discover')}>{t('tabDiscover')}</button>\n {themeSnap !== null && <button className={tab === 'themes' ? `${css.tab} ${css.on}` : css.tab} onClick={() => setTab('themes')}>{t('tabThemes')}</button>}\n <button className={tab === 'installed' ? `${css.tab} ${css.on}` : css.tab} onClick={() => { setTab('installed'); refreshInstalled(true) }}>\n {t('tabInstalled') + (Object.keys(installed).length > 0 ? ' (' + Object.keys(installed).length + ')' : '')}\n {hasUpdates && <StateDot state=\"error\" size={7} className={css.dot} />}\n </button>\n <span className={css.grow} />\n <Input className={css.searchInline} icon={<IconSearchOutline16 size={14} />} placeholder={t('searchPh')} value={q} onChange={e => setQ(e.target.value)} />\n </div>\n {!envReady && (\n <div className={css.restart}>\n <span>🧩</span>\n <span className={css.grow}>{envFailed ? t('envFixFail') : t('envMissing')}</span>\n {!envFailed && (\n <Button variant=\"primary\" size=\"sm\" disabled={envFixing} onClick={fixEnv}>\n {envFixing ? t('envFixing') : t('envFix')}\n </Button>\n )}\n </div>\n )}\n {hotUrls.length > 0 && (\n <div className={css.restart}>\n <span>✨</span>\n <span className={css.grow}><b>{hotUrls.length}</b> {t('hotBanner')}</span>\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={() => {\n sessionStorage.setItem('dshm-toast', JSON.stringify(hotNames))\n sessionStorage.setItem('dshm-tab', 'installed')\n location.reload()\n }}\n >{t('refresh')}</Button>\n </div>\n )}\n {pendingRestart > 0 && (\n <div className={css.restart}>\n <span>🔄</span>\n <span className={css.grow}><b>{pendingRestart}</b> {t('restartBanner')}</span>\n <span title={t('restartHint')}>ℹ️</span>\n </div>\n )}\n </div>\n {installError !== null && <div className={css.err}>{installError}</div>}\n <div\n className={css.body}\n ref={bodyRef}\n onScroll={e => setShowTop(e.currentTarget.scrollTop > 400)}\n >\n {tab === 'discover'\n ? loadError\n ? <div className={css.empty}>{t('loadFail')}</div>\n : data === null\n ? <div className={css.loading}><span className={css.spin} />{t('loading')}</div>\n : (\n <>\n <div className={css.cats}>\n <div className={css.catsRow}>\n <div ref={catsWrapRef} className={catsOpen || visibleCats === null ? `${css.catsWrap} ${css.catsCollapsed}` : css.catsWrap}>\n {(() => {\n // Collapsed, the selected category is pulled to the front so it never hides.\n const ordered = catsOpen || cat === 'all' ? categories : [cat, ...categories.filter(id => id !== cat)]\n const shown = catsOpen || visibleCats === null ? ordered : ordered.slice(0, Math.max(0, visibleCats - 1))\n return (\n <>\n <Pill data-chip=\"1\" active={cat === 'all'} onClick={() => setCat('all')}>{t('all')}</Pill>\n {shown.map(id => (\n <Pill\n key={id}\n data-chip=\"1\"\n active={cat === id}\n onClick={() => setCat(id)}\n >{(data.categories[id] && (data.categories[id]![lang] || data.categories[id]!.en)) || id}</Pill>\n ))}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className={css.catsToggle}\n icon={catsOpen ? <IconChevronUpOutline14 size={14} /> : <IconChevronDownOutline14 size={14} />}\n aria-label={catsOpen ? t('catsLess') : t('catsMore')}\n onClick={() => setCatsOpen(o => !o)}\n />\n </>\n )\n })()}\n </div>\n <div className={css.sort}>\n {['hot', 'new'].map(key => (\n <button\n key={key}\n className={sort === key ? css.on : ''}\n onClick={() => setSort(key)}\n >{t(key === 'hot' ? 'sortHot' : 'sortNew')}</button>\n ))}\n </div>\n </div>\n </div>\n {plugins.length === 0\n ? <div className={css.empty}>{t('empty')}</div>\n : <div className={css.grid}>{plugins.map(pluginCard)}</div>}\n </>\n )\n : tab === 'themes' && themeSnap !== null\n ? (\n <>\n {/* Light/dark/system live in the official Appearance setting; this\n tab only shows what that setting can't: registered third-party\n palettes (none in the wild yet) and installable theme plugins. */}\n {(() => {\n const extra = themeSnap.themes.filter(def => def.id !== 'light' && def.id !== 'dark')\n return extra.length > 0 && (\n <div className={`${css.grid} ${css.themesGrid}`}>\n {extra.map(def => themeCard(def.id, def.id, themeSwatch(def)))}\n </div>\n )\n })()}\n {data === null\n ? <div className={css.loading}><span className={css.spin} />{t('loading')}</div>\n : themePlugins.length === 0\n ? <div className={css.empty}>{t('themeEmpty')}</div>\n : <div className={css.grid}>{themePlugins.map(themePluginCard)}</div>}\n </>\n )\n : Object.keys(installed).length === 0\n ? <div className={css.empty}>{t('installedEmpty')}</div>\n : Object.entries(installed).map(([name, spec]) => {\n const entry = data === null ? undefined : entryForDep(data.plugins, name, String(spec))\n const status = updates[name]\n const version = status && status.version ? 'v' + status.version : ''\n const specText = String(spec)\n const ghSpec = /^github:([A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+?)(?:#|$)/.exec(specText)\n const repoUrl = entry !== undefined ? entry.url : ghSpec !== null ? 'https://github.com/' + ghSpec[1] : null\n return (\n <div key={name} className={css.irow}>\n <div style={{ minWidth: 0 }}>\n <div className={css.nm}>{name}{version && <span className={css.owner}>{' ' + version}</span>}</div>\n {repoUrl !== null\n ? <a className={`${css.spec} ${css.src}`} href={repoUrl} target=\"_blank\" rel=\"noreferrer\" style={{ display: 'inline-block' }}>{specText}</a>\n : <div className={css.spec}>{specText}</div>}\n {entry !== undefined && (\n <div className={`${css.desc} ${css.descTight}`}>\n {(entry.description && (entry.description[lang] || entry.description.en)) || ''}\n </div>\n )}\n {updatingName === name && (\n <div className={css.progress}>\n <span className={css.spin} />\n <code className={css.grow}>{progressLine || t('progressHint')}</code>\n </div>\n )}\n </div>\n <span className={css.grow} />\n {repoUrl !== null && <a className={css.src} href={repoUrl + '#readme'} target=\"_blank\" rel=\"noreferrer\">{t('readme')}</a>}\n {updatedNames.includes(name)\n ? <span className={css.okState}>{t('updated')}</span>\n : updatingName === name\n ? <Button variant=\"primary\" size=\"sm\" className={css.warnBtn} disabled>{t('updating')}</Button>\n : status && status.updateAvailable\n ? (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.warnBtn}\n disabled={updatingName !== null}\n onClick={() => doUpdate(name)}\n >{t('update')}</Button>\n )\n : status && status.kind === 'linked'\n ? <span className={css.owner}>{t('linkedDev')}</span>\n : <span className={css.owner}>{t('upToDate')}</span>}\n {name !== 'dsh-market' && name !== 'dshmarket' && (\n removingName === name\n ? <Button variant=\"outline\" size=\"sm\" className={css.dangerBtn} disabled>{t('uninstalling')}</Button>\n : removeArmed === name\n ? (\n <Button\n variant=\"primary\"\n size=\"sm\"\n className={css.dangerArmed}\n onClick={() => doUninstall(name)}\n onMouseLeave={() => setRemoveArmed(null)}\n >{t('confirmRemove')}</Button>\n )\n : (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={css.dangerBtn}\n disabled={removingName !== null || busyUrl !== null || updatingName !== null}\n onClick={() => setRemoveArmed(name)}\n >{t('uninstall')}</Button>\n )\n )}\n </div>\n )\n })}\n </div>\n {showTop && (\n <button\n className={css.top}\n title={t('backTop')}\n onClick={() => { const el = bodyRef.current; if (el) el.scrollTo({ top: 0, behavior: 'smooth' }) }}\n >↑</button>\n )}\n {confirming !== null && (\n <Modal\n open\n onClose={() => setConfirming(null)}\n title={t('confirmTitle') + ' ' + confirming.name + '?'}\n description={(confirming.description && (confirming.description[lang] || confirming.description.en)) || ''}\n footer={(\n <>\n <Button variant=\"ghost\" onClick={() => setConfirming(null)}>{t('cancel')}</Button>\n <Button variant=\"primary\" onClick={() => doInstall(confirming)}>{t('confirm')}</Button>\n </>\n )}\n >\n <details className={css.cmdDetails}>\n <summary className={css.cmdSummary}>{t('cmdDetails')}</summary>\n <div className={css.cmd}>{confirming.install}</div>\n </details>\n {looksTerminal(confirming, lang) && (\n <p className={css.warnLine}>\n {'🖥️ ' + t('terminalWarn') + ' '}\n <a className={css.src} href={confirming.url + '#readme'} target=\"_blank\" rel=\"noreferrer\">{t('readme')}</a>\n </p>\n )}\n <p className={css.modalNote}>{'⚠️ ' + t('confirmWarn')}</p>\n </Modal>\n )}\n </div>\n )\n}\n","/**\n * dsh-market client: registers a \"Market\" settings section rendering the\n * plugin market UI, plus the post-install toast in the shell overlay layer.\n * Built by tsdown into the __ModuleLoader__ factory bundle at\n * client/client.js; the only externals are the loader module table's react\n * entries.\n */\nimport { createElement as h } from 'react'\nimport { en, zh } from './locales.ts'\nimport { InstallToast } from './InstallToast.tsx'\nimport { MarketSection } from './MarketSection.tsx'\nimport type { ThemeSnapshot, Translate } from './market-data.ts'\n\nconst NS = 'dsh-market'\n\n/** The subset of the theme service this plugin touches. */\ninterface ThemeService {\n getTheme(): ThemeSnapshot | null\n setTheme(id: string): void\n}\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): Translate\n subscribe(callback: () => void): () => void\n getSnapshot(): { active: string }\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: the\n * host provides the real Context; typing the touched surface keeps this\n * external package free of monorepo-internal type dependencies). */\ninterface MarketClientContext {\n effect(callback: () => unknown, label?: string): void\n on(event: string, callback: () => void): () => void\n locale: LocaleService\n slots: SlotsService\n theme: ThemeService\n}\n\nexport const name = 'dsh-market'\n// 'theme' is safe to require: ui-layout (mandatory in every web composition)\n// already hard-depends on it. This cordis's object-form inject means\n// intercept config, NOT {required,optional} — do not use it here.\nexport const inject = ['slots', 'locale', 'theme']\nexport function apply(ctx: MarketClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-market: dictionaries')\n const t = ctx.locale.bind(NS)\n\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'market',\n order: 40,\n label: () => t('nav'),\n locale: NS,\n inject: () => ({ t }),\n }, () => h(MarketSection, {\n t,\n locale: ctx.locale,\n theme: ctx.theme,\n themeStore: {\n subscribe: (cb: () => void) => ctx.on('theme/change', cb),\n getSnapshot: () => ctx.theme.getTheme(),\n },\n })))\n\n const Toast = () => h(InstallToast, { t })\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'dsh-market-toast',\n label: () => 'dsh-market',\n }, Toast))\n}\n"],"mappings":"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"}
package/lib/routes.js CHANGED
@@ -116,6 +116,11 @@ function trackProgress(chunk) {
116
116
  const TARGET_RE = /^[A-Za-z0-9@:./_#+-]+$/;
117
117
  function runDshPlugin(profile, pluginArgs) {
118
118
  const { file, args, cwd, viaShell } = dshArgv();
119
+ // pnpm 9 refuses to add at a workspace root without -w (#17); pnpm 10/11
120
+ // accept the flag as a no-op there, so it is safe to pass always.
121
+ if (pluginArgs[0] === 'add' || pluginArgs[0] === 'remove') {
122
+ pluginArgs = [pluginArgs[0], '-w', ...pluginArgs.slice(1)];
123
+ }
119
124
  const target = pluginArgs[pluginArgs.length - 1] ?? '';
120
125
  if (!TARGET_RE.test(target)) {
121
126
  logEvent('error', 'install', `unsafe plugin target rejected: ${JSON.stringify(target)}`);
@@ -251,43 +256,107 @@ function readLockCommits(profile) {
251
256
  * each plugin subdirectory through pnpm's `#path:` selector.
252
257
  * @returns overall success (true when nothing needed retargeting).
253
258
  */
259
+ /**
260
+ * True when the package's declared entry artifact actually exists — github
261
+ * source checkouts of build-required plugins ship no lib/, and promoting one
262
+ * into the bundle layer bricks the next boot (ERR_MODULE_NOT_FOUND kills the
263
+ * whole profile, #18).
264
+ */
265
+ function entryArtifactExists(dir) {
266
+ try {
267
+ const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
268
+ const candidates = [];
269
+ if (typeof manifest.main === 'string')
270
+ candidates.push(manifest.main);
271
+ const rootExport = typeof manifest.exports === 'string'
272
+ ? manifest.exports
273
+ : manifest.exports?.['.'];
274
+ if (typeof rootExport === 'string')
275
+ candidates.push(rootExport);
276
+ else if (rootExport !== null && typeof rootExport === 'object') {
277
+ for (const value of Object.values(rootExport))
278
+ if (typeof value === 'string')
279
+ candidates.push(value);
280
+ }
281
+ if (candidates.length === 0)
282
+ candidates.push('index.js');
283
+ return candidates.some(rel => existsSync(join(dir, rel)));
284
+ }
285
+ catch {
286
+ return false;
287
+ }
288
+ }
289
+ /** True when the installed package's manifest declares a dsh plugin surface. */
290
+ function hasDshManifest(dir) {
291
+ try {
292
+ const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
293
+ return manifest.dsh !== undefined;
294
+ }
295
+ catch {
296
+ return false;
297
+ }
298
+ }
299
+ /** Plugin subdirectories (depth 2) of a collection checkout, as relative paths. */
300
+ function pluginSubdirs(root) {
301
+ const found = [];
302
+ let level1 = [];
303
+ try {
304
+ level1 = readdirSync(root, { withFileTypes: true })
305
+ .filter(dirent => dirent.isDirectory() && /^[A-Za-z0-9_.-]+$/.test(dirent.name) && dirent.name !== 'node_modules')
306
+ .map(dirent => dirent.name);
307
+ }
308
+ catch {
309
+ return found;
310
+ }
311
+ for (const sub of level1) {
312
+ if (hasDshManifest(join(root, sub))) {
313
+ found.push(sub);
314
+ continue;
315
+ }
316
+ try {
317
+ for (const inner of readdirSync(join(root, sub), { withFileTypes: true })) {
318
+ if (!inner.isDirectory() || !/^[A-Za-z0-9_.-]+$/.test(inner.name) || inner.name === 'node_modules')
319
+ continue;
320
+ if (hasDshManifest(join(root, sub, inner.name)))
321
+ found.push(`${sub}/${inner.name}`);
322
+ }
323
+ }
324
+ catch { /* unreadable level — skip */ }
325
+ if (found.length >= 8)
326
+ break;
327
+ }
328
+ return found.slice(0, 8);
329
+ }
254
330
  async function retargetCollections(profile, before, target) {
255
331
  if (!target.startsWith('github:'))
256
332
  return true;
257
- const junk = Object.keys(readInstalled(profile)).filter(name => !before.has(name)
258
- && !existsSync(join(profileDir(profile), 'node_modules', name, 'package.json')));
333
+ // A collection checkout: no root package.json at all, or a root manifest
334
+ // that declares no dsh surface (workspace roots are usually also private
335
+ // with no entry point — #18).
336
+ const junk = Object.keys(readInstalled(profile)).filter((name) => {
337
+ if (before.has(name))
338
+ return false;
339
+ const root = join(profileDir(profile), 'node_modules', name);
340
+ if (!existsSync(join(root, 'package.json')))
341
+ return true;
342
+ return !hasDshManifest(root);
343
+ });
259
344
  let allOk = true;
260
345
  for (const name of junk) {
261
346
  const root = join(profileDir(profile), 'node_modules', name);
262
- let candidates = [];
263
- try {
264
- candidates = readdirSync(root, { withFileTypes: true })
265
- .filter(dirent => dirent.isDirectory() && /^[A-Za-z0-9_.-]+$/.test(dirent.name))
266
- .filter((dirent) => {
267
- try {
268
- const manifest = JSON.parse(readFileSync(join(root, dirent.name, 'package.json'), 'utf8'));
269
- return manifest.dsh !== undefined;
270
- }
271
- catch {
272
- return false;
273
- }
274
- })
275
- .map(dirent => dirent.name)
276
- .slice(0, 5);
277
- }
278
- catch {
279
- candidates = [];
280
- }
281
- logEvent('info', 'install', `${name}: collection repo (no root package.json); plugins inside: ${candidates.join(', ') || 'none'}`);
347
+ const candidates = pluginSubdirs(root);
348
+ logEvent('info', 'install', `${name}: collection repo (root declares no dsh manifest); plugins inside: ${candidates.join(', ') || 'none'}`);
282
349
  await runDshPlugin(profile, ['remove', name]);
283
350
  if (candidates.length === 0) {
284
351
  allOk = false;
285
352
  continue;
286
353
  }
287
354
  for (const sub of candidates) {
288
- const result = await runDshPlugin(profile, ['add', `${target}#path:${sub}`]);
289
- if (result.exitCode !== 0 || result.timedOut)
355
+ const result = await runDshPlugin(profile, ['add', `${target}#path:/${sub}`]);
356
+ if (result.exitCode !== 0 || result.timedOut) {
290
357
  allOk = false;
358
+ logEvent('error', 'install', `${target}#path:/${sub}: exit=${String(result.exitCode)}${result.timedOut ? ' TIMEOUT' : ''} — ${(result.stderr || result.stdout).slice(-220)}`);
359
+ }
291
360
  }
292
361
  }
293
362
  return allOk;
@@ -822,6 +891,40 @@ export function mountMarketRoutes(host, config) {
822
891
  // plugin subdirectories via pnpm's #path: selector.
823
892
  ok = await retargetCollections(config.profile, before, target);
824
893
  }
894
+ // Fake-success guard (#18): a clean exit that added nothing
895
+ // installable must not read as success — and a plugin whose entry
896
+ // artifact is missing (source-only checkout, build blocked) would
897
+ // brick the next boot, so it is removed on the spot.
898
+ let notAPlugin = false;
899
+ let removedBroken = [];
900
+ // Runs even when retargeting partially failed — a broken piece
901
+ // that slipped in must never survive to brick the next boot.
902
+ if (result.exitCode === 0 && !result.timedOut) {
903
+ const addedNow = Object.keys(readInstalled(config.profile)).filter(n => !before.has(n));
904
+ const keep = [];
905
+ for (const n of addedNow) {
906
+ const dir = join(profileDir(config.profile), 'node_modules', n);
907
+ if (hasDshManifest(dir) && entryArtifactExists(dir)) {
908
+ keep.push(n);
909
+ }
910
+ else {
911
+ removedBroken.push(n);
912
+ await runDshPlugin(config.profile, ['remove', n]);
913
+ }
914
+ }
915
+ if (removedBroken.length > 0) {
916
+ logEvent('warn', 'install', `${target}: removed uninstallable pieces (no dsh manifest or missing build artifacts): ${removedBroken.join(', ')}`);
917
+ }
918
+ if (keep.length === 0) {
919
+ ok = false;
920
+ notAPlugin = true;
921
+ logEvent('error', 'install', `${target}: nothing installable survived validation`);
922
+ }
923
+ else {
924
+ // Partial success across a collection still counts as success.
925
+ ok = true;
926
+ }
927
+ }
825
928
  const installed = readInstalled(config.profile);
826
929
  let hot = false;
827
930
  if (ok) {
@@ -843,6 +946,7 @@ export function mountMarketRoutes(host, config) {
843
946
  sendJson(response, ok ? 200 : 502, {
844
947
  ok,
845
948
  hot,
949
+ error: notAPlugin ? 'nothing installable: the plugin(s) need a build step (blocked by default, see allowBuilds) or ship no prebuilt artifacts / 没有可安装的内容:插件需要构建授权(allowBuilds,默认拦截)或未附带构建产物,详见导出日志' : undefined,
846
950
  exitCode: result.exitCode,
847
951
  timedOut: result.timedOut,
848
952
  stdout: result.stdout,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dshmarket",
3
3
  "description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
4
- "version": "1.2.0",
4
+ "version": "1.2.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -7,7 +7,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, use
7
7
  import { Button, IconChevronDownOutline14, IconChevronUpOutline14, IconSearchOutline16, Input, Modal, Pill, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'
8
8
  import css from './Market.module.css'
9
9
  import {
10
- avatarColor, isInstalled, LOGO_URI, looksTerminal, readSession, repoOf, themeSwatch,
10
+ avatarColor, entryForDep, isInstalled, LOGO_URI, looksTerminal, matchInstalledName, readSession, repoOf, themeSwatch,
11
11
  } from './market-data.ts'
12
12
  import type {
13
13
  InstalledMap, Registry, RegistryPlugin, ThemeSnapshot, Translate, UpdateStatus,
@@ -261,7 +261,7 @@ export function MarketSection(props: MarketSectionProps) {
261
261
  refreshInstalled()
262
262
  } else {
263
263
  const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)
264
- const detail = text(body.error) || text(body.stderr) || text(body.stdout) || ('exit ' + body.exitCode)
264
+ const detail = text(body.error) || [text(body.stderr), text(body.stdout)].filter(Boolean).join('\n').trim() || ('exit ' + body.exitCode)
265
265
  setInstallError(t('installFail') + ': ' + plugin.name + ' — ' + detail.trim().slice(-600))
266
266
  }
267
267
  })
@@ -287,7 +287,7 @@ export function MarketSection(props: MarketSectionProps) {
287
287
  refreshInstalled()
288
288
  } else {
289
289
  const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)
290
- const detail = text(body.error) || text(body.stderr) || text(body.stdout) || ('exit ' + body.exitCode)
290
+ const detail = text(body.error) || [text(body.stderr), text(body.stdout)].filter(Boolean).join('\n').trim() || ('exit ' + body.exitCode)
291
291
  setInstallError(t('updateFail') + ': ' + name + ' — ' + detail.trim().slice(-600))
292
292
  }
293
293
  })
@@ -419,16 +419,7 @@ export function MarketSection(props: MarketSectionProps) {
419
419
  )
420
420
  }
421
421
 
422
- const installedNameOf = (p: RegistryPlugin) => {
423
- if (installed[p.name] !== undefined) return p.name
424
- const repo = repoOf(p.url)
425
- if (repo === null) return null
426
- const needle = ('github:' + repo).toLowerCase()
427
- for (const [name, spec] of Object.entries(installed)) {
428
- if (String(spec).toLowerCase().includes(needle)) return name
429
- }
430
- return null
431
- }
422
+ const installedNameOf = (p: RegistryPlugin) => matchInstalledName(p, installed)
432
423
 
433
424
  // Plugins loaded at boot (bundle-layer skins) aren't in the shim list but
434
425
  // are just as live; the boot manifest is the page's own record of them.
@@ -683,8 +674,7 @@ export function MarketSection(props: MarketSectionProps) {
683
674
  : Object.keys(installed).length === 0
684
675
  ? <div className={css.empty}>{t('installedEmpty')}</div>
685
676
  : Object.entries(installed).map(([name, spec]) => {
686
- const entry = data === null ? undefined : data.plugins.find(p => p.name === name
687
- || (repoOf(p.url) !== null && String(spec).toLowerCase().includes(('github:' + repoOf(p.url)).toLowerCase())))
677
+ const entry = data === null ? undefined : entryForDep(data.plugins, name, String(spec))
688
678
  const status = updates[name]
689
679
  const version = status && status.version ? 'v' + status.version : ''
690
680
  const specText = String(spec)
@@ -84,14 +84,54 @@ export function looksTerminal(plugin: RegistryPlugin, lang: string): boolean {
84
84
  return /\b(tui|cli|tty|terminal)\b|终端|命令行/i.test(plugin.name + ' ' + desc)
85
85
  }
86
86
 
87
- /** A registry plugin counts as installed when its package name, npm name, or GitHub spec appears in the profile dependencies. */
88
- export function isInstalled(plugin: RegistryPlugin, installed: InstalledMap): boolean {
89
- if (installed[plugin.name] !== undefined) return true
90
- if (plugin.npm && installed[plugin.npm] !== undefined) return true
87
+ /**
88
+ * Unified installed-state matching (#15): both sides collapse to lowercase
89
+ * identity sets — the registry entry contributes its bare name, npm name and
90
+ * owner/repo; the dependency contributes its key and the repo inside its
91
+ * spec — and any exact intersection counts. Exact equality, not substrings,
92
+ * so prefix-related repo names cannot cross-match.
93
+ */
94
+ function entryIdentities(plugin: RegistryPlugin): Set<string> {
95
+ const ids = new Set<string>([plugin.name.toLowerCase()])
96
+ if (plugin.npm) ids.add(plugin.npm.toLowerCase())
91
97
  const repo = repoOf(plugin.url)
92
- if (repo === null) return false
93
- const needle = ('github:' + repo).toLowerCase()
94
- return Object.values(installed).some(spec => String(spec).toLowerCase().includes(needle))
98
+ if (repo !== null) ids.add(repo.toLowerCase())
99
+ return ids
100
+ }
101
+
102
+ function depIdentities(name: string, spec: string): Set<string> {
103
+ const ids = new Set<string>([name.toLowerCase()])
104
+ // A scoped npm key usually mirrors owner/repo — expose that identity so an
105
+ // npm-installed plugin still matches an entry whose npm field is unset.
106
+ const scoped = /^@([^/]+)\/(.+)$/.exec(name)
107
+ if (scoped !== null) ids.add(`${scoped[1]!.toLowerCase()}/${scoped[2]!.toLowerCase()}`)
108
+ const match = /github:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)/i.exec(spec)
109
+ if (match !== null) ids.add(match[1]!.toLowerCase())
110
+ return ids
111
+ }
112
+
113
+ /** The installed dependency name a registry entry corresponds to, or null. */
114
+ export function matchInstalledName(plugin: RegistryPlugin, installed: InstalledMap): string | null {
115
+ const ids = entryIdentities(plugin)
116
+ for (const [name, spec] of Object.entries(installed)) {
117
+ for (const id of depIdentities(name, String(spec))) {
118
+ if (ids.has(id)) return name
119
+ }
120
+ }
121
+ return null
122
+ }
123
+
124
+ /** The registry entry an installed dependency corresponds to, or undefined. */
125
+ export function entryForDep(plugins: RegistryPlugin[], name: string, spec: string): RegistryPlugin | undefined {
126
+ const ids = depIdentities(name, String(spec))
127
+ return plugins.find((plugin) => {
128
+ for (const id of entryIdentities(plugin)) if (ids.has(id)) return true
129
+ return false
130
+ })
131
+ }
132
+
133
+ export function isInstalled(plugin: RegistryPlugin, installed: InstalledMap): boolean {
134
+ return matchInstalledName(plugin, installed) !== null
95
135
  }
96
136
 
97
137
  /**
package/src/routes.ts CHANGED
@@ -174,6 +174,11 @@ const TARGET_RE = /^[A-Za-z0-9@:./_#+-]+$/
174
174
 
175
175
  function runDshPlugin(profile: string, pluginArgs: string[]): Promise<InstallResult> {
176
176
  const { file, args, cwd, viaShell } = dshArgv()
177
+ // pnpm 9 refuses to add at a workspace root without -w (#17); pnpm 10/11
178
+ // accept the flag as a no-op there, so it is safe to pass always.
179
+ if (pluginArgs[0] === 'add' || pluginArgs[0] === 'remove') {
180
+ pluginArgs = [pluginArgs[0], '-w', ...pluginArgs.slice(1)]
181
+ }
177
182
  const target = pluginArgs[pluginArgs.length - 1] ?? ''
178
183
  if (!TARGET_RE.test(target)) {
179
184
  logEvent('error', 'install', `unsafe plugin target rejected: ${JSON.stringify(target)}`)
@@ -312,39 +317,99 @@ function readLockCommits(profile: string): Map<string, string> {
312
317
  * each plugin subdirectory through pnpm's `#path:` selector.
313
318
  * @returns overall success (true when nothing needed retargeting).
314
319
  */
320
+ /**
321
+ * True when the package's declared entry artifact actually exists — github
322
+ * source checkouts of build-required plugins ship no lib/, and promoting one
323
+ * into the bundle layer bricks the next boot (ERR_MODULE_NOT_FOUND kills the
324
+ * whole profile, #18).
325
+ */
326
+ function entryArtifactExists(dir: string): boolean {
327
+ try {
328
+ const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as {
329
+ main?: string
330
+ exports?: Record<string, unknown> | string
331
+ }
332
+ const candidates: string[] = []
333
+ if (typeof manifest.main === 'string') candidates.push(manifest.main)
334
+ const rootExport = typeof manifest.exports === 'string'
335
+ ? manifest.exports
336
+ : (manifest.exports as Record<string, unknown> | undefined)?.['.']
337
+ if (typeof rootExport === 'string') candidates.push(rootExport)
338
+ else if (rootExport !== null && typeof rootExport === 'object') {
339
+ for (const value of Object.values(rootExport)) if (typeof value === 'string') candidates.push(value)
340
+ }
341
+ if (candidates.length === 0) candidates.push('index.js')
342
+ return candidates.some(rel => existsSync(join(dir, rel)))
343
+ } catch {
344
+ return false
345
+ }
346
+ }
347
+
348
+ /** True when the installed package's manifest declares a dsh plugin surface. */
349
+ function hasDshManifest(dir: string): boolean {
350
+ try {
351
+ const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as { dsh?: unknown }
352
+ return manifest.dsh !== undefined
353
+ } catch {
354
+ return false
355
+ }
356
+ }
357
+
358
+ /** Plugin subdirectories (depth 2) of a collection checkout, as relative paths. */
359
+ function pluginSubdirs(root: string): string[] {
360
+ const found: string[] = []
361
+ let level1: string[] = []
362
+ try {
363
+ level1 = readdirSync(root, { withFileTypes: true })
364
+ .filter(dirent => dirent.isDirectory() && /^[A-Za-z0-9_.-]+$/.test(dirent.name) && dirent.name !== 'node_modules')
365
+ .map(dirent => dirent.name)
366
+ } catch {
367
+ return found
368
+ }
369
+ for (const sub of level1) {
370
+ if (hasDshManifest(join(root, sub))) {
371
+ found.push(sub)
372
+ continue
373
+ }
374
+ try {
375
+ for (const inner of readdirSync(join(root, sub), { withFileTypes: true })) {
376
+ if (!inner.isDirectory() || !/^[A-Za-z0-9_.-]+$/.test(inner.name) || inner.name === 'node_modules') continue
377
+ if (hasDshManifest(join(root, sub, inner.name))) found.push(`${sub}/${inner.name}`)
378
+ }
379
+ } catch { /* unreadable level — skip */ }
380
+ if (found.length >= 8) break
381
+ }
382
+ return found.slice(0, 8)
383
+ }
384
+
315
385
  async function retargetCollections(profile: string, before: Set<string>, target: string): Promise<boolean> {
316
386
  if (!target.startsWith('github:')) return true
317
- const junk = Object.keys(readInstalled(profile)).filter(name => !before.has(name)
318
- && !existsSync(join(profileDir(profile), 'node_modules', name, 'package.json')))
387
+ // A collection checkout: no root package.json at all, or a root manifest
388
+ // that declares no dsh surface (workspace roots are usually also private
389
+ // with no entry point — #18).
390
+ const junk = Object.keys(readInstalled(profile)).filter((name) => {
391
+ if (before.has(name)) return false
392
+ const root = join(profileDir(profile), 'node_modules', name)
393
+ if (!existsSync(join(root, 'package.json'))) return true
394
+ return !hasDshManifest(root)
395
+ })
319
396
  let allOk = true
320
397
  for (const name of junk) {
321
398
  const root = join(profileDir(profile), 'node_modules', name)
322
- let candidates: string[] = []
323
- try {
324
- candidates = readdirSync(root, { withFileTypes: true })
325
- .filter(dirent => dirent.isDirectory() && /^[A-Za-z0-9_.-]+$/.test(dirent.name))
326
- .filter((dirent) => {
327
- try {
328
- const manifest = JSON.parse(readFileSync(join(root, dirent.name, 'package.json'), 'utf8')) as { dsh?: unknown }
329
- return manifest.dsh !== undefined
330
- } catch {
331
- return false
332
- }
333
- })
334
- .map(dirent => dirent.name)
335
- .slice(0, 5)
336
- } catch {
337
- candidates = []
338
- }
339
- logEvent('info', 'install', `${name}: collection repo (no root package.json); plugins inside: ${candidates.join(', ') || 'none'}`)
399
+ const candidates = pluginSubdirs(root)
400
+ logEvent('info', 'install', `${name}: collection repo (root declares no dsh manifest); plugins inside: ${candidates.join(', ') || 'none'}`)
340
401
  await runDshPlugin(profile, ['remove', name])
341
402
  if (candidates.length === 0) {
342
403
  allOk = false
343
404
  continue
344
405
  }
345
406
  for (const sub of candidates) {
346
- const result = await runDshPlugin(profile, ['add', `${target}#path:${sub}`])
347
- if (result.exitCode !== 0 || result.timedOut) allOk = false
407
+ const result = await runDshPlugin(profile, ['add', `${target}#path:/${sub}`])
408
+ if (result.exitCode !== 0 || result.timedOut) {
409
+ allOk = false
410
+ logEvent('error', 'install',
411
+ `${target}#path:/${sub}: exit=${String(result.exitCode)}${result.timedOut ? ' TIMEOUT' : ''} — ${(result.stderr || result.stdout).slice(-220)}`)
412
+ }
348
413
  }
349
414
  }
350
415
  return allOk
@@ -884,6 +949,38 @@ export function mountMarketRoutes(host: MarketHost, config: MarketConfig): () =>
884
949
  // plugin subdirectories via pnpm's #path: selector.
885
950
  ok = await retargetCollections(config.profile, before, target)
886
951
  }
952
+ // Fake-success guard (#18): a clean exit that added nothing
953
+ // installable must not read as success — and a plugin whose entry
954
+ // artifact is missing (source-only checkout, build blocked) would
955
+ // brick the next boot, so it is removed on the spot.
956
+ let notAPlugin = false
957
+ let removedBroken: string[] = []
958
+ // Runs even when retargeting partially failed — a broken piece
959
+ // that slipped in must never survive to brick the next boot.
960
+ if (result.exitCode === 0 && !result.timedOut) {
961
+ const addedNow = Object.keys(readInstalled(config.profile)).filter(n => !before.has(n))
962
+ const keep: string[] = []
963
+ for (const n of addedNow) {
964
+ const dir = join(profileDir(config.profile), 'node_modules', n)
965
+ if (hasDshManifest(dir) && entryArtifactExists(dir)) {
966
+ keep.push(n)
967
+ } else {
968
+ removedBroken.push(n)
969
+ await runDshPlugin(config.profile, ['remove', n])
970
+ }
971
+ }
972
+ if (removedBroken.length > 0) {
973
+ logEvent('warn', 'install', `${target}: removed uninstallable pieces (no dsh manifest or missing build artifacts): ${removedBroken.join(', ')}`)
974
+ }
975
+ if (keep.length === 0) {
976
+ ok = false
977
+ notAPlugin = true
978
+ logEvent('error', 'install', `${target}: nothing installable survived validation`)
979
+ } else {
980
+ // Partial success across a collection still counts as success.
981
+ ok = true
982
+ }
983
+ }
887
984
  const installed = readInstalled(config.profile)
888
985
  let hot = false
889
986
  if (ok) {
@@ -905,6 +1002,7 @@ export function mountMarketRoutes(host: MarketHost, config: MarketConfig): () =>
905
1002
  sendJson(response, ok ? 200 : 502, {
906
1003
  ok,
907
1004
  hot,
1005
+ error: notAPlugin ? 'nothing installable: the plugin(s) need a build step (blocked by default, see allowBuilds) or ship no prebuilt artifacts / 没有可安装的内容:插件需要构建授权(allowBuilds,默认拦截)或未附带构建产物,详见导出日志' : undefined,
908
1006
  exitCode: result.exitCode,
909
1007
  timedOut: result.timedOut,
910
1008
  stdout: result.stdout,