dsh-m 0.9.26 → 0.9.27

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/CHANGELOG.md CHANGED
@@ -8,6 +8,12 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
8
8
 
9
9
  ## 中文
10
10
 
11
+ ### 0.9.27 变更:修复遮罩拖选误关——面板内按下、拖出释放不再关面板
12
+
13
+ - **问题(装机实测 2026-10-03)**:在市场搜索框内左键按下向左拖选(越过面板边界)释放,整个面板被关闭。非浏览器鼠标手势——面板遮罩是裸 `onClick: onClose`,而在面板内容里按下、拖到遮罩上释放时,浏览器把 `click` 派发到按下/释放目标的公共祖先(恰是遮罩),被误判为「点遮罩关面板」。详情 Modal、截图灯箱、兼容确认弹层同属该缺陷类。
14
+ - **修复**:新增纯函数 `backdropCloseHandlers`(src/client/backdrop.js)——仅当 **mousedown 与 click 都落在遮罩自身**时才关闭(其余组合一律不关,click 后按位状态复位);四处遮罩(主面板/详情/灯箱/兼容弹层)统一切换。点遮罩关闭、Esc 关闭、✕ 按钮行为不变。
15
+ - **验证**:新增 `tests/client-backdrop.test.mjs`(关闭/两类拖选不关/状态复位/畸形输入安全,5 组断言);全量 1030 pass / 0 fail。
16
+
11
17
  ### 0.9.26 变更:跨区搜索精选稳定前置——摘要行计数与首页所见一致
12
18
 
13
19
  - **问题(装机实测 2026-10-03)**:搜索「sidebar」摘要行报「⭐ 精选 3 · 社区 315」,首页精选段只见 1 条。非重复计算——`sourceCounts` 不读分类计数(`alsoCategories` 无涉),3 条为真实精选命中;错位根因是摘要行报全局命中数,而相关性排序叠加社区区默认 downloads 降序 tie-break 把弱命中精选压进后页。
@@ -269,6 +275,12 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
269
275
 
270
276
  ## English
271
277
 
278
+ ### Changed in 0.9.27 — fix accidental drag-out close: press inside, release on backdrop no longer closes the panel
279
+
280
+ - **Problem (observed on install, 2026-10-03)**: pressing inside the market search box and dragging left past the panel edge closed the whole panel. Not a browser mouse gesture — the panel backdrop used a bare `onClick: onClose`; when a press inside the panel content drags onto the backdrop and releases, the browser dispatches `click` to the common ancestor of the press/release targets (the backdrop itself), misread as "clicked the backdrop". The detail modal, screenshot lightbox, and compat dialog shared the same defect class.
281
+ - **Fix**: new pure helper `backdropCloseHandlers` (src/client/backdrop.js) — close only when **both mousedown and click land on the backdrop itself** (all other combinations are ignored; the pressed state resets after each click); all four overlays (main panel/detail/lightbox/compat dialog) switched over. Backdrop-click close, Esc, and the ✕ button behave exactly as before.
282
+ - **Verification**: new `tests/client-backdrop.test.mjs` (close / two drag-out scenarios ignored / state reset / malformed input safe — 5 assertions); full suite 1030 pass / 0 fail.
283
+
272
284
  ### Changed in 0.9.26 — curated hits first in cross-zone search: summary counts now match page one
273
285
 
274
286
  - **Problem (observed on install, 2026-10-03)**: searching "sidebar" showed "⭐ Curated 3 · Community 315" but only one curated card on the first page. Not double counting — `sourceCounts` never reads category counts (`alsoCategories` is unrelated) and all 3 were genuine curated hits. The mismatch: the summary line reports global hit counts while the relevance ranking plus the community zone's default downloads-desc tie-break pushed weakly-matching curated entries onto later pages.
package/lib/client.js CHANGED
@@ -348,6 +348,31 @@ var init_market_snapshot = __esm({
348
348
  }
349
349
  });
350
350
 
351
+ // src/client/backdrop.js
352
+ var backdrop_exports = {};
353
+ __export(backdrop_exports, {
354
+ backdropCloseHandlers: () => backdropCloseHandlers
355
+ });
356
+ function backdropCloseHandlers(onClose) {
357
+ let pressedOnBackdrop = false;
358
+ return {
359
+ onMouseDown: (e) => {
360
+ pressedOnBackdrop = Boolean(e) && e.target === e.currentTarget;
361
+ },
362
+ onClick: (e) => {
363
+ const onBackdrop = Boolean(e) && e.target === e.currentTarget;
364
+ const shouldClose = pressedOnBackdrop && onBackdrop;
365
+ pressedOnBackdrop = false;
366
+ if (shouldClose && typeof onClose === "function") onClose();
367
+ }
368
+ };
369
+ }
370
+ var init_backdrop = __esm({
371
+ "src/client/backdrop.js"() {
372
+ "use strict";
373
+ }
374
+ });
375
+
351
376
  // src/client/markdown.js
352
377
  var markdown_exports = {};
353
378
  __export(markdown_exports, {
@@ -1224,6 +1249,7 @@ var PLUGIN_ID = "dsh-m";
1224
1249
  var API = "/dshm";
1225
1250
  var { DEFAULT_PAGE_SIZE: DEFAULT_PAGE_SIZE2, MARKET_PAGE_SIZES: MARKET_PAGE_SIZES2, pageItems: pageItems2, createZoneState: createZoneState2, normalizeMarketQuery: normalizeMarketQuery2, resetPageOnFilterChange: resetPageOnFilterChange2, normalizeMarketResponse: normalizeMarketResponse2, registryNotice: registryNotice2, zoneChips: zoneChips2, marketNotice: marketNotice2, searchSourceOf: searchSourceOf2 } = (init_market_state(), __toCommonJS(market_state_exports));
1226
1251
  var { readMarketSnapshot: readMarketSnapshot2, writeMarketSnapshot: writeMarketSnapshot2, isDefaultFirstPageQuery: isDefaultFirstPageQuery2 } = (init_market_snapshot(), __toCommonJS(market_snapshot_exports));
1252
+ var { backdropCloseHandlers: backdropCloseHandlers2 } = (init_backdrop(), __toCommonJS(backdrop_exports));
1227
1253
  var { createMarkdown: createMarkdown2 } = (init_markdown(), __toCommonJS(markdown_exports));
1228
1254
  var { ExtLink, MdImg, renderMarkdown } = createMarkdown2(h);
1229
1255
  var { installedViewModel: installedViewModel2, registrySourceKey: registrySourceKey2 } = (init_installed_view(), __toCommonJS(installed_view_exports));
@@ -2444,7 +2470,7 @@ function Shot({ src, onClick }) {
2444
2470
  function Lightbox({ shots, index, onNav, onClose }) {
2445
2471
  return h(
2446
2472
  "div",
2447
- { className: "dsvm-lightbox", onClick: onClose },
2473
+ { className: "dsvm-lightbox", ...backdropCloseHandlers2(onClose) },
2448
2474
  h("img", { src: shots[index], alt: "", onClick: (e) => e.stopPropagation(), referrerPolicy: "no-referrer" }),
2449
2475
  h(
2450
2476
  "div",
@@ -2504,7 +2530,7 @@ function DetailModal({ it, labels, busy, onClose, onInstall, profileKind, instal
2504
2530
  const kv = (k, v) => h(React.Fragment, { key: k }, h("dt", null, k), h("dd", null, v));
2505
2531
  return h(
2506
2532
  "div",
2507
- { className: "dsvm-modal", onClick: onClose },
2533
+ { className: "dsvm-modal", ...backdropCloseHandlers2(onClose) },
2508
2534
  h(
2509
2535
  "div",
2510
2536
  { className: "dsvm-modalbox", onClick: (e) => e.stopPropagation() },
@@ -2709,7 +2735,7 @@ function MarketTab({ notify, markets, onMutation, ops, favorites, profileKind })
2709
2735
  };
2710
2736
  const CompatDialog = compatConfirm ? h(
2711
2737
  "div",
2712
- { className: "dshm-compat-overlay", onClick: () => setCompatConfirm(null) },
2738
+ { className: "dshm-compat-overlay", ...backdropCloseHandlers2(() => setCompatConfirm(null)) },
2713
2739
  h(
2714
2740
  "div",
2715
2741
  { className: "dshm-compat-dialog", onClick: (e) => e.stopPropagation() },
@@ -3857,7 +3883,8 @@ function MarketPanel({ onClose }) {
3857
3883
  }, [profile]);
3858
3884
  return h(
3859
3885
  "div",
3860
- { className: full ? "dshm-overlay full" : "dshm-overlay", onClick: onClose },
3886
+ // 0.9.27 防拖拽误关:面板内按下拖选到遮罩释放时,click 落在公共祖先(遮罩)上——旧 onClick: onClose 会误关面板
3887
+ { className: full ? "dshm-overlay full" : "dshm-overlay", ...backdropCloseHandlers2(onClose) },
3861
3888
  h(
3862
3889
  "div",
3863
3890
  { className: full ? "dshm-panel full" : "dshm-panel", onClick: (e) => e.stopPropagation() },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.9.26",
3
+ "version": "0.9.27",
4
4
  "description": "可自定义收录清单的 DSH 插件市场:收录、安装、卸载、升级一站完成,附 agent 工具与 dshm CLI。",
5
5
  "license": "MIT",
6
6
  "type": "module",