dsh-m 0.9.14 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -36,7 +36,7 @@ The default registry includes DSH Skins, ModSearch, the Lark / QQ / Weixin / WeC
36
36
 
37
37
  | View | Capabilities |
38
38
  |---|---|
39
- | **Market** | **Zoned** (0.7.0): **Community** (default landing, the 4,000+ full catalog) and **Curated** (hand-picked primary registry, single page) as two independent zones — each keeps its own category/search/sort/pagination state across tab switches; plus a **Favorites** zone (browser-local bookmarks with delisted-entry detection and one-click cleanup). Community zone: two-row collapsible category chips with sticky collapse, sort switch (downloads/stars/added × asc/desc), windowed pagination (24/48/96 per page), card byline (owner/downloads/stars); clicking a card opens a **detail modal** (superset of the card: download window, screenshot lightbox, capabilities collapsed by default, install command — the row is hidden on desktop and for installed entries since 0.9.14); npm sources pin the latest exact version, GitHub sources pin the release/tag commit |
39
+ | **Market** | **Zoned** (0.7.0): **Community** (default landing, the 4,000+ full catalog) and **Curated** (hand-picked primary registry, single page) as two independent zones — each keeps its own category/search/sort/pagination state across tab switches; plus a **Favorites** zone (browser-local bookmarks with delisted-entry detection and one-click cleanup). Community zone: two-row collapsible category chips with sticky collapse, sort switch (downloads/stars/added × asc/desc), windowed pagination (24/48/96 per page), card byline (owner/downloads/stars); clicking a card opens a **detail modal** (superset of the card: download window, screenshot lightbox, capabilities collapsed by default, install command — the row is hidden on desktop and for installed entries since 0.9.14; install progress and result shown in-modal since 0.9.15); npm sources pin the latest exact version, GitHub sources pin the release/tag commit |
40
40
  | **Operations** | Install/upgrade/uninstall/toggle all flow through a global operation log (0.7.0): state lives off cards, surviving paging/searching/tab switches; persisted to localStorage and resumed after host reloads (each restored op re-validated "still applies" before executing); benignly-invalidated ops show as a neutral "skipped"; the Installed page gains "Update all (N)" batch queueing |
41
41
  | **Installed** | What the current profile (web / desktop, 0.9.0+) actually has, annotated "via market / non-market"; update badges, upgrade, two-step confirm uninstall; 📖 README preview (64KB cap); **live phase badge** (●active / ●failed / ○pending) and a **one-click toggle** (0.4.0: delegates to the official pluginManager service for live application; falls back to file-level edits + restart notice when absent; dsh-m itself and official host lifelines are locked) |
42
42
  | **Settings** | Registry address draft with "Force refresh / Validate & apply / Restore default / Download default registry.json"; configured vs active address and status at a glance (shown only when abnormal); community catalog toggle; dsh-m self-update |
@@ -48,6 +48,11 @@ The default registry includes DSH Skins, ModSearch, the Lark / QQ / Weixin / WeC
48
48
 
49
49
  The restart chain below applies to **Web only** (Desktop takes effect via the official app lifecycle: after installing, quit and reopen the Desktop app — see the 0.9.0 section). After any mutation, the already-open Market and Installed views refresh the profile state together, keeping badges and cards in sync without closing and reopening the marketplace; a "⚡ Restart" banner appears — under systemd, the DSH launcher's `appExit` hook hands the restart back to a unit configured with `Restart=on-failure` or `Restart=always`, avoiding a `systemctl` helper inside the unit cgroup that is about to stop; if `appExit` is unavailable, the fallback uses a manager-owned transient `systemd-run` service and only then a detached helper. The client confirms the replacement by boot id and dismisses the banner, leaving DSH Web's own background connection recovery in control; it does not force a full-page reload during the auth/route handoff. The restart chain has been live-verified on the current DSH Web `0.2.0-rc.2` (2026-10-01): dsh-m probes the unit's `Restart=` policy, hands the stop back via `appExit`, the `status=75/TEMPFAIL` exit is caught by the Restart policy and auto-started, and after recovery the page reconnects in the background with the panel fully functional. Historical scope: the `0.1.5-rc.1` era covered live `/dshm` ping and authenticated `303 → 200` checks; `0.1.2-rc.1` is covered by contract checks plus runtime shape-detection fallback (see the verified array) — with the deployment moved on, no live E2E is chased on retired generations; the transient `systemd-run` fallback only applies to hosts without `appExit` (all supported generations provide it) and stays a documented fallback rather than a release gate; repeated install/uninstall is covered by the profile-transaction test surface (compensation, post-install guard, 813 cases) plus the 0.4.0→0.4.2 consecutive-release evidence. Installs stream live pnpm progress (resolve → download → link → build).
50
50
 
51
+ ### Changed in 0.9.15 — install progress/result now live inside the detail modal
52
+
53
+ - **Scenario**: clicking Install in the detail modal rendered all install feedback (the pnpm phase line, the "changes applied" banner/toast) on the panel layer beneath, half-visible through the modal mask — inside the modal the button just spun, with no sense of progress (live screenshot feedback, 2026-10-02). Uninstall never had this problem: no modal is involved and status already lives on the installed card.
54
+ - **Change**: while an install runs, the modal hosts the progress line itself (same host-status polling: phase / bar / current package); terminal states render an inline result row — success with version + build-script notes and a restart hint (desktop gets the official app lifecycle guide), failure / guard block with the reason and a re-enabled retry button, "skipped" presented neutrally; while the modal is open the underlying twin line steps aside (and returns once the modal closes). Everything still derives from the global operation log and the install result — the "state lives off cards" ownership model is unchanged (DESIGN §2.6).
55
+
51
56
  ### Changed in 0.9.14 (instant-open quartet + context-aware "Install command" row)
52
57
 
53
58
  - **Instant-open quartet** — a fix combo for the marketplace spinner that used to show on every panel open:
package/README.md CHANGED
@@ -36,7 +36,7 @@
36
36
 
37
37
  | 视图 | 能力 |
38
38
  |---|---|
39
- | **市场** | **分区制**(0.7.0):**社区**(默认落地页,4,000+ 条全量目录)与**精选**(手工策展主清单,单页直出)两个独立分区,各有自己的分类/搜索/排序/分页状态(切 tab 互不重置);另有**收藏**分区(浏览器本地书签,自动检测下架条目并一键清理)。社区区:分类 chips 两行折叠 + 吸顶收缩、排序(下载量/Star/收录日期 × 升降)、页码窗口化分页(24/48/96 每页)、卡片 byline(作者/下载量/Star);卡片点开**详情 Modal**(卡片信息超集:下载量窗口三要素、截图灯箱、能力披露默认收起、安装命令(0.9.14 起 desktop 上下文/已安装条目隐藏该折叠行));npm 源锁定最新精确版本,GitHub 源锁定 release/tag 指向的 commit |
39
+ | **市场** | **分区制**(0.7.0):**社区**(默认落地页,4,000+ 条全量目录)与**精选**(手工策展主清单,单页直出)两个独立分区,各有自己的分类/搜索/排序/分页状态(切 tab 互不重置);另有**收藏**分区(浏览器本地书签,自动检测下架条目并一键清理)。社区区:分类 chips 两行折叠 + 吸顶收缩、排序(下载量/Star/收录日期 × 升降)、页码窗口化分页(24/48/96 每页)、卡片 byline(作者/下载量/Star);卡片点开**详情 Modal**(卡片信息超集:下载量窗口三要素、截图灯箱、能力披露默认收起、安装命令(0.9.14 起 desktop 上下文/已安装条目隐藏该折叠行)、安装进度与结果就地显示(0.9.15 起));npm 源锁定最新精确版本,GitHub 源锁定 release/tag 指向的 commit |
40
40
  | **操作记录** | 安装/升级/卸载/开关全部走全局操作记录(0.7.0):状态不挂卡片,翻页/搜索/切 tab 不丢;localStorage 持久化,宿主重载后自动恢复未完成操作(逐条校验「此刻仍成立才执行」);良性前提消失以中性「已跳过」呈现;已装页支持「全部更新 (N)」批量入队 |
41
41
  | **已装** | 当前 profile(web / desktop,0.9.0 起)实装列表,标注「市场安装 / 非市场安装」;可升级徽标、升级、两段式确认卸载;📖 README 预览(64KB 截断);**运行相位徽标**(●active / ●failed / ○pending)与**一键开关**(0.4.0:委派官方 pluginManager 服务活体生效,服务缺席时文件级编辑 + 重启提示;dsh-m 自身与官方宿主命脉锁定不可开关) |
42
42
  | **设置** | registry 地址草稿 +「强制刷新 / 校验并应用 / 恢复默认 / 下载默认 registry.json」;配置地址、生效来源与状态一目了然(仅异常时提示);社区目录开关;dsh-m 自更新 |
@@ -48,6 +48,11 @@
48
48
 
49
49
  以下重启链路仅适用于 **Web**(Desktop 的生效走官方应用生命周期:安装完成后退出并重新打开 Desktop 应用即可,见 0.9.0 节)。安装 / 卸载 / 升级完成后,当前已打开的市场页与已装页会一起重新读取 profile 状态并同步徽标/卡片,不需要关闭后重新打开插件市场;随后出现「⚡ 一键重启」横幅——受 systemd 管理时通过 DSH launcher 的 `appExit` 交给服务的 `Restart` 策略,避免在待停止 unit 的 cgroup 内启动 `systemctl` helper;无 `appExit` 的 systemd 兜底改用 manager-owned transient `systemd-run`,最后才退回 detached-helper。客户端按 boot id 确认新进程已恢复后关闭横幅,交由 DSH Web 自身的后台连接重试恢复页面,不强制整页刷新,避免认证/路由切换期间白屏。重启链路已在当前 DSH Web `0.2.0-rc.2` 实机核验(2026-10-01):dsh-m 探测 unit `Restart=` 策略后经 `appExit` 交还 systemd,`status=75/TEMPFAIL` 退出由 Restart 策略接住自动拉起,服务恢复后页面后台重连、面板全功能可用。历史口径:`0.1.5-rc.1` 时代已核验 `/dshm` ping 与带认证 `303 → 200`;`0.1.2-rc.1` 按契约核对 + 形态探测兜底收录(verified 数组),部署面演进后不再追旧代 live E2E;transient `systemd-run` 兜底仅适用于无 `appExit` 的宿主(受支持代际均提供 `appExit`),保持设计兜底而非发布门槛;连续安装/卸载由 profile 事务测试面(补偿事务、装后守卫等 813 用例)与 0.4.0→0.4.2 连续发布实证覆盖。安装过程实时显示 pnpm 进度(解析 → 下载 → 链接 → 构建)。
50
50
 
51
+ ### 0.9.15 变更:安装进度/终态就地进详情 Modal
52
+
53
+ - **场景**:从详情 Modal 点「安装」后,安装信息(pnpm 阶段进度行、「变更完成」横幅/toast)都渲染在面板底层,隔着 Modal 遮罩半透明透出——弹窗内只有按钮转圈,看不出装到哪一步(实机截图反馈 2026-10-02)。卸载无此问题:不弹 Modal,状态本来就挂在已装卡片上。
54
+ - **变更**:安装进行中,Modal 内直接挂进度行(复用 host status 轮询:阶段/进度条/当前包);终态在 Modal 内就地显示结果行——成功附版本与构建脚本说明 + 重启提示(desktop 给官方应用生命周期指引),失败/守卫拦截附原因且按钮回到可重试,「已跳过」中性呈现;Modal 打开时底层同源进度行让位(关闭 Modal 后照常回归)。信息全部派生自全局操作记录与安装结果,「状态不挂卡片」的所有权模型不变(DESIGN §2.6)。
55
+
51
56
  ### 0.9.14 变更(市场秒开四件套 + 详情 Modal 安装命令显隐)
52
57
 
53
58
  - **市场秒开四件套**:针对「每次打开市场必现『加载收录清单中…』」的加速组合拳——
package/docs/DESIGN.md CHANGED
@@ -89,7 +89,7 @@
89
89
  - 分页:页码窗口化(`1 … n-1 n n+1 … total`,≤7 页全显)+ 页大小 24/48/96 + 筛选变化重置页 1 + 翻页回顶;仅 prev/next 的旧分页退役。
90
90
  - 搜索:相关性加权管线——NFKC 归一化 + 中西文边界插空格 + 字段权重(name/npm > owner > 描述 > 分类)+ 命中类型加分 + 按条目缓存归一化结果;输入 250ms debounce + IME composition 全程处理 + draft/已提交 query 分离。
91
91
  - 描述 5 行钳制(真实溢出才显示展开钮);**数据层双语**——描述按 UI 语言取 zh/en(数据现成),UI 完整双语字典列 backlog。
92
- - **详情 = Modal 且为卡片超集**(「detail 显示少于摘要就是倒退」):byline / 分类 / 收录日期 / 下载量窗口三要素(计数 + 窗口 + 核对时间)/ 描述全文 / 截图灯箱(←→/Esc、**禁自动轮播**)/ 能力披露 + 红线(**默认收起**)/ 安装命令折叠行(0.9.14 起按上下文显隐:desktop 上下文与已安装条目整行隐藏——命令两来源恒为 `--profile web` 语义,desktop 装机走弹窗内安装按钮)/ deprecated 替代链接;精选条目超集另加 verified 与 tags。**安装确认走 Modal**。卡片瘦身:能力披露、截图、安装命令全部迁出卡片(Q44 精神「只在详情层、默认收起」不变,载体升级)。
92
+ - **详情 = Modal 且为卡片超集**(「detail 显示少于摘要就是倒退」):byline / 分类 / 收录日期 / 下载量窗口三要素(计数 + 窗口 + 核对时间)/ 描述全文 / 截图灯箱(←→/Esc、**禁自动轮播**)/ 能力披露 + 红线(**默认收起**)/ 安装命令折叠行(0.9.14 起按上下文显隐:desktop 上下文与已安装条目整行隐藏——命令两来源恒为 `--profile web` 语义,desktop 装机走弹窗内安装按钮)/ deprecated 替代链接;精选条目超集另加 verified 与 tags。**安装确认走 Modal**。安装信息就地显示(0.9.15):进行中进度行与终态结果行挂 Modal 内、底层同源进度行让位——展示派生自全局操作记录,§2.6「状态不挂卡片」所有权模型不变。卡片瘦身:能力披露、截图、安装命令全部迁出卡片(Q44 精神「只在详情层、默认收起」不变,载体升级)。
93
93
  - **全局操作记录**:每个变更操作一条 record(`queued / running / input=冲突待决 / done / warned / failed`),**状态不挂卡片**——翻页、搜索、切 tab 不丢;**localStorage 持久化队列**,宿主重载恢复时逐条校验「此刻仍成立才执行,否则报告」;覆盖安装、升级、卸载、开关。已装侧跟随一致化:「全部更新 (N)」批量入口 + tab 更新红点 + 卡片视觉与发现侧同体系;组管理、个人备注列 backlog。
94
94
  - **收藏**:浏览器 localStorage 本地收藏(不进 profile、不进服务端);收藏区 stale 条目(目录中已下架)单独提示 + 一键清理。
95
95
  - **agent/CLI 契约(允许破坏性变更)**:`dshm_search` 参数改为 `query / category / source('primary'|'community'|'all',默认 all,与 GUI 分区对齐) / limit(默认 10,clamp 1–80) / offset(真翻页)`;`primary_only` 删除。输出补 `community` 标记与 `downloads/stars`;社区分类直出中文标签(不再回退英文 slug);工具描述整体重写(真实规模 + 翻页语义,删「registry is curated & small」)。CLI 对齐 `--source / --offset / --limit`,默认 10 条。
package/lib/client.js CHANGED
@@ -1739,6 +1739,7 @@ var CSS = `
1739
1739
  .dshm-body{flex:1;overflow:auto;padding:14px;display:flex;flex-direction:column;gap:12px}
1740
1740
  .dshm-hint{color:var(--dsw-alias-label-caption,#6b7280);font-size:12px;line-height:18px;margin:0}
1741
1741
  .dshm-err{color:var(--dsw-alias-state-error-primary,#b91c1c);font-size:12px;line-height:18px}
1742
+ .dshm-ok{color:var(--dsw-alias-state-success-primary,#047857);font-size:12px;line-height:18px;word-break:break-word}
1742
1743
  .dshm-btn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-primary,inherit);border-radius:8px;padding:5px 12px;font:inherit;font-size:12px;cursor:pointer;white-space:nowrap}
1743
1744
  .dshm-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
1744
1745
  .dshm-btn:disabled{opacity:.5;cursor:default}
@@ -2388,7 +2389,7 @@ function useModalDepth(active) {
2388
2389
  };
2389
2390
  }, [active]);
2390
2391
  }
2391
- function DetailModal({ it, labels, busy, onClose, onInstall, profileKind }) {
2392
+ function DetailModal({ it, labels, busy, onClose, onInstall, profileKind, installRec, installNote }) {
2392
2393
  useModalDepth(true);
2393
2394
  const shots = it.community === true ? safeScreenshots(it) : [];
2394
2395
  const [lb, setLb] = useState(null);
@@ -2490,6 +2491,13 @@ function DetailModal({ it, labels, busy, onClose, onInstall, profileKind }) {
2490
2491
  h("button", { className: "dshm-btn sm", onClick: copyCmd }, copied ? lookup("modal.copied") : lookup("modal.copy"))
2491
2492
  )
2492
2493
  ) : null,
2494
+ // 0.9.15:安装信息就地进 Modal(主人反馈 2026-10-02:进度/终态原先只隔遮罩在底层透出)。
2495
+ // 进度行复用 ProgressLine(自轮询 host status);底层同源行由 MarketTab 在本 Modal 打开时让位。
2496
+ // installRec 派生自全局操作记录(DESIGN §2.6「状态不挂卡片」的所有权模型不变,此处只是展示):
2497
+ // 仅 queued/running 挂行,input(兼容待决)走上层 CompatDialog;installNote 为本次终态摘要。
2498
+ installRec && (installRec.status === "running" || installRec.status === "queued") ? h(ProgressLine, { key: "opprog" }) : null,
2499
+ installNote ? h("div", { key: "opnote", className: installNote.kind === "err" ? "dshm-err" : installNote.kind === "hint" ? "dshm-hint" : "dshm-ok" }, installNote.text) : null,
2500
+ installNote && installNote.kind === "ok" ? h("div", { key: "opnote-rh", className: "dshm-hint" }, lookup(profileKind === "desktop" ? "profile.restartHint" : "banner.done")) : null,
2493
2501
  h(
2494
2502
  "div",
2495
2503
  { className: "dsvm-modalactions" },
@@ -2523,7 +2531,9 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2523
2531
  }, [filterOpen]);
2524
2532
  const [favDetailItem, setFavDetailItem] = useState(null);
2525
2533
  const [pageJump, setPageJump] = useState("");
2526
- const activeInstallTarget = (ops.records.find((r) => r.kind === "install" && (r.status === "running" || r.status === "queued" || r.status === "input")) || {}).target || null;
2534
+ const activeInstallRec = ops.records.find((r) => r.kind === "install" && (r.status === "running" || r.status === "queued" || r.status === "input")) || null;
2535
+ const activeInstallTarget = activeInstallRec ? activeInstallRec.target : null;
2536
+ const [installNote, setInstallNote] = useState(null);
2527
2537
  const items = data && data.items || [];
2528
2538
  const total = data && data.total || 0;
2529
2539
  const limit = data && data.limit || DEFAULT_PAGE_SIZE2;
@@ -2571,14 +2581,13 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2571
2581
  h(SearchBox, { key: zone, placeholder: lookup("search.ph"), initial: query.query, onCommit: (v) => updateQuery({ query: v }) })
2572
2582
  ) : null;
2573
2583
  const zoneBar = zoneChips3;
2574
- const installDone = (res) => {
2575
- notify({
2576
- kind: "ok",
2577
- needsRestart: true,
2578
- text: lookup("notify.installed", { pkg: res.pkg, version: res.version ? ` v${res.version}` : "" }) + (res.buildApprovals && res.buildApprovals.length ? lookup("notify.builds", { names: res.buildApprovals.join(", ") }) : res.fallbackAllBuilds ? lookup("notify.builds.fallback") : "") + (res.bundleWarning === "no-patch-layer" ? lookup("notify.bundlewarning") : "")
2579
- });
2584
+ const installResultText = (res) => lookup("notify.installed", { pkg: res.pkg, version: res.version ? ` v${res.version}` : "" }) + (res.buildApprovals && res.buildApprovals.length ? lookup("notify.builds", { names: res.buildApprovals.join(", ") }) : res.fallbackAllBuilds ? lookup("notify.builds.fallback") : "") + (res.bundleWarning === "no-patch-layer" ? lookup("notify.bundlewarning") : "");
2585
+ const installDone = (it2, res) => {
2586
+ notify({ kind: "ok", needsRestart: true, text: installResultText(res) });
2587
+ setInstallNote({ id: it2.id, kind: "ok", text: installResultText(res) });
2580
2588
  };
2581
2589
  const doInstall = async (it, version, forceIncompatible, reuseOpId) => {
2590
+ setInstallNote(null);
2582
2591
  try {
2583
2592
  const res = await ops.runOp(
2584
2593
  "install",
@@ -2589,26 +2598,28 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2589
2598
  reuseOpId
2590
2599
  // 审计 #12:确认/重试复用同一记录,不再新开
2591
2600
  );
2592
- installDone(res);
2601
+ installDone(it, res);
2593
2602
  await (onMutation ? onMutation() : reload(false));
2594
2603
  } catch (e) {
2595
2604
  if (e && e.opSuperseded) {
2596
2605
  notify({ kind: "ok", text: lookup("op.superseded.note", { target: it.name }) });
2606
+ setInstallNote({ id: it.id, kind: "hint", text: lookup("op.superseded.note", { target: it.name }) });
2597
2607
  } else if (e && e.guard) {
2598
- notify({
2599
- kind: "err",
2600
- text: [
2601
- lookup("guard.blocked"),
2602
- `${lookup("guard.compstatus")}: ${e.guard.compensation?.status || "\u2014"}\uFF08${e.guard.compensation?.note || ""}\uFF09`,
2603
- e.guard.repairBasis ? `${lookup("guard.repairbasis")}: ${e.guard.repairBasis}` : null,
2604
- lookup("guard.noforce"),
2605
- e.guard.restartSafe ? lookup("guard.restartsafenow") : lookup("guard.restartunsafe")
2606
- ].filter(Boolean).join(" | ")
2607
- });
2608
+ const guardText = [
2609
+ lookup("guard.blocked"),
2610
+ `${lookup("guard.compstatus")}: ${e.guard.compensation?.status || "\u2014"}\uFF08${e.guard.compensation?.note || ""}\uFF09`,
2611
+ e.guard.repairBasis ? `${lookup("guard.repairbasis")}: ${e.guard.repairBasis}` : null,
2612
+ lookup("guard.noforce"),
2613
+ e.guard.restartSafe ? lookup("guard.restartsafenow") : lookup("guard.restartunsafe")
2614
+ ].filter(Boolean).join(" | ");
2615
+ notify({ kind: "err", text: guardText });
2616
+ setInstallNote({ id: it.id, kind: "err", text: guardText });
2608
2617
  } else if (e && e.issue) {
2609
2618
  setCompatConfirm({ it, version, issue: e.issue, opId: e.opId });
2610
2619
  } else {
2611
- notify({ kind: "err", text: lookup("failed.install", { err: e && e.message || e }) });
2620
+ const failText = lookup("failed.install", { err: e && e.message || e });
2621
+ notify({ kind: "err", text: failText });
2622
+ setInstallNote({ id: it.id, kind: "err", text: failText });
2612
2623
  }
2613
2624
  }
2614
2625
  };
@@ -2683,7 +2694,9 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2683
2694
  busy: activeInstallTarget === favDetailItem.id,
2684
2695
  onClose: () => setFavDetailItem(null),
2685
2696
  onInstall: (it2) => doInstall(it2),
2686
- profileKind
2697
+ profileKind,
2698
+ installRec: activeInstallRec && activeInstallRec.target === favDetailItem.id ? activeInstallRec : null,
2699
+ installNote: installNote && installNote.id === favDetailItem.id ? installNote : null
2687
2700
  }) : null
2688
2701
  );
2689
2702
  }
@@ -2747,7 +2760,9 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2747
2760
  onPick: (id) => updateQuery({ category: id, offset: 0 }),
2748
2761
  trailing: filterTrigger
2749
2762
  }),
2750
- activeInstallTarget ? h(ProgressLine, { key: "prog" }) : null,
2763
+ // 0.9.15:安装目标条目的详情 Modal 打开时,进度行入 Modal、底层行让位(避免隔着遮罩双重透出);
2764
+ // Modal 关闭后底层行照常回归(关闭弹窗的安装仍可见)
2765
+ activeInstallTarget && !(detailItem && detailItem.id === activeInstallTarget) ? h(ProgressLine, { key: "prog" }) : null,
2751
2766
  loading && !data ? h("div", { className: "dshm-empty" }, lookup("market.loading"), Spin()) : error ? h("div", { className: "dshm-err" }, lookup("failed.load", { err: error })) : items.length === 0 ? h("div", { className: "dshm-empty" }, lookup("market.empty")) : h(
2752
2767
  React.Fragment,
2753
2768
  null,
@@ -2841,7 +2856,9 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2841
2856
  busy: activeInstallTarget === detailItem.id,
2842
2857
  onClose: () => setDetailId(null),
2843
2858
  onInstall: (it2) => doInstall(it2),
2844
- profileKind
2859
+ profileKind,
2860
+ installRec: activeInstallRec && activeInstallRec.target === detailItem.id ? activeInstallRec : null,
2861
+ installNote: installNote && installNote.id === detailItem.id ? installNote : null
2845
2862
  }) : null
2846
2863
  );
2847
2864
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.9.14",
3
+ "version": "0.9.15",
4
4
  "description": "可自定义收录清单的 DSH 插件市场:收录、安装、卸载、升级一站完成,附 agent 工具与 dshm CLI。",
5
5
  "license": "MIT",
6
6
  "type": "module",