dsh-m 0.9.52 → 0.9.53

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,13 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
8
8
 
9
9
  ## 中文
10
10
 
11
+ ### 0.9.53 变更:ZoneChips 方案A「隐身全量测量」——冷挂载分类行错误折叠根除 + 行容量即时重测
12
+
13
+ - **动机(0.9.52 端到端验证发现)**:新浏览器 profile(无 localStorage 快照)首开面板时,市场数据未到 → `fit` 在空列表上测量 → 数据到达后重测面对的是按陈旧预算折叠的 DOM(只渲染 1 颗 + 「+N」)→ 棘轮式冻结在「3 颗 + +20」单行折叠。暖会话(快照命中)首挂载即全量数据、一次测准,故 0.7.0 以来从未暴露。姊妹问题:测量 effect 不监听宽度变化,窗口缩放后 `+N` 与实际容量脱节。完整根因链与无头实证见 `docs/plans/2026-10-08-zonechips-visual-clip-requirements.md`。
14
+ - **方案(方案A「隐身全量测量」,经独立评审三轮 11 条意见收敛,评审记录见计划文档修订记录)**:废除 slice 预算,分类 chips **全量渲染**,折叠改为 `.dshm-chips` 上的 `max-height`(实测行高 × 行数)+ `overflow:hidden` 视觉裁剪;越界 chip 逐颗 `visibility:hidden`(与现状「未渲染不进 tab 序」对齐);`+N`/`⌃` 改右侧 overlay 组(水平并排、钉末可见行右端,gutter `--dshm-clip-gutter:132px` 几何保证与 chip 零相交);筛选触发器折叠态钉 overlay、展开态维持 in-flow;重测钩子 = deps(`chips.length/expanded/zone/stuck/rowHeight`)+ `ResizeObserver`(rAF 节流)+ `document.fonts.ready`,均 feature-detect 降级(先例 `:947`)。测量纯函数(`chipRows/countBeyondRows/clipTopOf/autoExpandDecision`)下沉 `market-state.js`,20 例 Node 单测直测。
15
+ - **语义保持**:吸顶收一行/回滚两行(0.9.52 哨兵链路不动,裁剪宿主为 `.dshm-chips`、严禁 wrap 裁剪——wrap 裁剪会剪掉哨兵致 stuck 永久误判,评审 Q1 阻断项);`+N` 展开/`⌃` 收起;激活分类越界自动展开 + autoRef「手动收起后不弹回」(实现拆分为重置/决策两 effect,评审 Q3);0 计数降透明。行为改进一处:折叠态筛选由「行尾第 3 行 in-flow」改为「末可见行右端 overlay 常驻」。
16
+ - **测试**:新增 `client-zonechips-clip.test.mjs` 20 例 + smoke 全量渲染断言 1 例,全量 1221 例零失败、typecheck 零错误;无头探针 V1–V6 矩阵实证(冷数据到达两行折叠正确 / 三档宽度即时重算 / 精选区不预留 gutter / 吸顶一行重算且回滚恢复 / 筛选与 +N 零遮挡均可点 / 自动展开与 autoRef 不弹回),探针不入库(0.9.52 惯例)。
17
+
11
18
  ### 0.9.52 变更:头部行距统一——吸顶哨兵移出 flex 流,消除搜索框与分类行间 25px 空带
12
19
 
13
20
  - **动机(主人截图发现)**:市场头部「搜索框行 → 分类 chips 行」间距实测 25px(12 gap + 1px 哨兵 + 12 gap),是「分区 chips → 搜索框行」12px 节奏的两倍余,社区/精选两区一致,视觉上是一条全宽无内容死带;搜索态下 ZoneChips 连带哨兵整体卸载、空带自动消失——证明是布局副作用而非设计节奏。
@@ -393,6 +400,13 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
393
400
 
394
401
  ## English
395
402
 
403
+ ### Changed in 0.9.53 — ZoneChips Plan A "invisible full-list measurement": cold-mount mis-collapse eradicated + live row-capacity re-measurement
404
+
405
+ - **Motivation (found during 0.9.52 end-to-end verification)**: with a fresh browser profile (no localStorage snapshot), the market data had not arrived when the panel first mounted, so the old `fit` measurement ran on an EMPTY list; when data arrived the re-measure faced a DOM already collapsed by the stale budget (1 chip + "+N") and ratcheted to a frozen "3 chips + +20" single row. Warm sessions hit the snapshot on first mount and measured correctly — which is why this 0.7.0-era defect never surfaced. Sister issue: the measure effect ignores width changes, so `+N` drifts from real capacity after window resizes. Full causal chain with headless evidence: `docs/plans/2026-10-08-zonechips-visual-clip-requirements.md`.
406
+ - **Change (Plan A "invisible full-list measurement", converged through an independent 3-round review with 11 findings)**: the slice-budget is gone — category chips **always render in full**; collapsing is a `max-height` (measured row-height × rows) + `overflow:hidden` visual clip on `.dshm-chips`; chips beyond the clip get per-chip `visibility:hidden` (parity with today's "unrendered = not in tab order"); `+N`/`⌃` become a right-side overlay group (horizontal pair pinned to the last visible row, gutter `--dshm-clip-gutter:132px` geometrically guarantees zero chip intersection); the filter trigger is pinned as an overlay while collapsed and stays in-flow when expanded; re-measure hooks = deps (`chips.length/expanded/zone/stuck/rowHeight`) + `ResizeObserver` (rAF-throttled) + `document.fonts.ready`, all feature-detected with graceful degradation (precedent `:947`). Measurement pure functions (`chipRows/countBeyondRows/clipTopOf/autoExpandDecision`) moved to `market-state.js` with 20 direct Node unit tests.
407
+ - **Semantics preserved**: sticky collapse to one row / restore on scroll-up (the 0.9.52 sentinel chain untouched; the clip host is `.dshm-chips` and clipping the wrap is forbidden — it would clip the sentinel and permanently mis-set stuck, review blocker Q1); `+N` expand / `⌃` collapse; auto-expand when the active category falls beyond the clip + autoRef "manual collapse is respected" (implemented as split reset/decision effects, review Q3); zero-count dimming. One intentional behavior improvement: while collapsed, the filter trigger moved from "third in-flow row end" to a persistent overlay at the last visible row's right end.
408
+ - **Tests**: new `client-zonechips-clip.test.mjs` (20 cases) + 1 full-render smoke assertion; full suite 1221 cases zero failures, typecheck clean; headless probe matrix V1–V6 verified (cold data arrival collapses correctly at two rows / three-width live recompute / curated zone reserves no gutter / sticky one-row recompute and restore / filter & +N zero-overlap and clickable / auto-expand & autoRef no-bounce); probes not committed (0.9.52 convention).
409
+
396
410
  ### Changed in 0.9.52 — uniform header rhythm: sticky sentinel leaves the flex flow, killing the 25px dead band between search row and category chips
397
411
 
398
412
  - **Motivation (spotted in the owner's screenshots)**: the market header gap between the search row and the category chips row measures 25px (12 gap + 1px sentinel + 12 gap) — more than double the 12px rhythm between zone chips and the search row, identical in both community and curated zones, visually a full-width empty dead band; in search mode ZoneChips (and its sentinel) unmount entirely and the band disappears — proof it is a layout side effect, not designed rhythm.
package/lib/client.js CHANGED
@@ -28,6 +28,10 @@ var market_state_exports = {};
28
28
  __export(market_state_exports, {
29
29
  DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
30
30
  MARKET_PAGE_SIZES: () => MARKET_PAGE_SIZES,
31
+ autoExpandDecision: () => autoExpandDecision,
32
+ chipRows: () => chipRows,
33
+ clipTopOf: () => clipTopOf,
34
+ countBeyondRows: () => countBeyondRows,
31
35
  createZoneState: () => createZoneState,
32
36
  marketNotice: () => marketNotice,
33
37
  mergeLatestFields: () => mergeLatestFields,
@@ -230,6 +234,55 @@ function mergeLatestFields(nextItems, prevItems) {
230
234
  });
231
235
  return touched ? out : nextItems;
232
236
  }
237
+ function chipRows(offsets) {
238
+ if (!Array.isArray(offsets)) return 0;
239
+ let rows = 0;
240
+ let last = null;
241
+ for (const t of offsets) {
242
+ if (typeof t !== "number" || !Number.isFinite(t)) continue;
243
+ if (last === null || t !== last) {
244
+ rows += 1;
245
+ last = t;
246
+ }
247
+ }
248
+ return rows;
249
+ }
250
+ function countBeyondRows(offsets, maxRows) {
251
+ if (!Array.isArray(offsets)) return 0;
252
+ if (!Number.isInteger(maxRows) || maxRows < 1) return 0;
253
+ let beyond = 0;
254
+ let rowCount = 0;
255
+ let last = null;
256
+ for (const t of offsets) {
257
+ if (typeof t !== "number" || !Number.isFinite(t)) continue;
258
+ if (last === null || t !== last) {
259
+ rowCount += 1;
260
+ last = t;
261
+ }
262
+ if (rowCount > maxRows) beyond += 1;
263
+ }
264
+ return beyond;
265
+ }
266
+ function clipTopOf(offsets, maxRows) {
267
+ if (!Array.isArray(offsets)) return Infinity;
268
+ if (!Number.isInteger(maxRows) || maxRows < 1) return Infinity;
269
+ let rowCount = 0;
270
+ let last = null;
271
+ for (const t of offsets) {
272
+ if (typeof t !== "number" || !Number.isFinite(t)) continue;
273
+ if (last === null || t !== last) {
274
+ rowCount += 1;
275
+ last = t;
276
+ if (rowCount === maxRows + 1) return t;
277
+ }
278
+ }
279
+ return Infinity;
280
+ }
281
+ function autoExpandDecision(activeTop, clipTop, autoRefMatched, expanded) {
282
+ const beyond = typeof activeTop === "number" && Number.isFinite(activeTop) && typeof clipTop === "number" && Number.isFinite(clipTop) && activeTop >= clipTop;
283
+ if (!beyond || autoRefMatched) return { record: false, expand: false };
284
+ return { record: true, expand: !expanded };
285
+ }
233
286
  var MARKET_PAGE_SIZES, DEFAULT_PAGE_SIZE, CURATED_ORDER, CURATED_LABELS, CURATED_IDS, SLUG_RE, SORT_FIELDS, sameSort, FALLBACK_REGISTRY_STATE, FALLBACK_COMMUNITY, LATEST_MERGE_FIELDS;
234
287
  var init_market_state = __esm({
235
288
  "src/client/market-state.js"() {
@@ -1505,7 +1558,7 @@ var h = React.createElement;
1505
1558
  var { useState, useEffect, useLayoutEffect, useCallback, useMemo, useRef } = React;
1506
1559
  var PLUGIN_ID = "dsh-m";
1507
1560
  var API = "/dshm";
1508
- 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, mergeLatestFields: mergeLatestFields2 } = (init_market_state(), __toCommonJS(market_state_exports));
1561
+ 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, mergeLatestFields: mergeLatestFields2, chipRows: chipRows2, countBeyondRows: countBeyondRows2, clipTopOf: clipTopOf2, autoExpandDecision: autoExpandDecision2 } = (init_market_state(), __toCommonJS(market_state_exports));
1509
1562
  var { readMarketSnapshot: readMarketSnapshot2, writeMarketSnapshot: writeMarketSnapshot2, isDefaultFirstPageQuery: isDefaultFirstPageQuery2 } = (init_market_snapshot(), __toCommonJS(market_snapshot_exports));
1510
1563
  var { backdropCloseHandlers: backdropCloseHandlers2 } = (init_backdrop(), __toCommonJS(backdrop_exports));
1511
1564
  var { createMarkdown: createMarkdown2 } = (init_markdown(), __toCommonJS(markdown_exports));
@@ -2133,7 +2186,10 @@ var CSS = `
2133
2186
  .dshm-btn.sm{padding:3px 9px;font-size:11px}
2134
2187
  .dshm-input{flex:1;min-width:120px;border:1px solid var(--dsw-alias-border-l2,#c7d2fe);background:var(--dsw-alias-bg-layer-2,transparent);color:var(--dsw-alias-label-primary,inherit);border-radius:999px;padding:7px 14px;font:inherit;font-size:12px;outline:none}
2135
2188
  .dshm-input:focus{border-color:var(--dsw-alias-interactive-bg-selected,#4f46e5)}
2136
- .dshm-chips{display:flex;flex-wrap:wrap;gap:6px}
2189
+ .dshm-chips{display:flex;flex-wrap:wrap;gap:6px;position:relative}
2190
+ .dshm-chips-clip{overflow:hidden}
2191
+ .dshm-chips-gutter{padding-right:var(--dshm-clip-gutter,132px)}
2192
+ .dsvm-chipmore{position:absolute;right:8px;bottom:8px;display:flex;align-items:center;gap:8px;min-height:28px;background:var(--dsw-alias-bg-base,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.18));border-radius:999px;padding:2px 8px;box-sizing:border-box}
2137
2193
  .dsvm-searchrow{display:flex;align-items:center;gap:8px}
2138
2194
  .dsvm-searchrow .dshm-search{flex:1;display:flex}
2139
2195
  .dshm-search{position:relative}
@@ -2656,42 +2712,69 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
2656
2712
  const chips = useMemo(() => zoneChips2(counts, labels, zone), [counts, labels, zone]);
2657
2713
  const [expanded, setExpanded] = useState(false);
2658
2714
  const [stuck, setStuck] = useState(false);
2659
- const [fit, setFit] = useState({ rows2: 99, rows1: 99 });
2715
+ const [geom, setGeom] = useState(null);
2716
+ const [rowHeight, setRowHeight] = useState(0);
2660
2717
  const wrapRef = useRef(null);
2661
2718
  const sentinelRef = useRef(null);
2662
2719
  const autoRef = useRef(null);
2663
- const collapsedBudget = Math.min(stuck ? fit.rows1 : fit.rows2, chips.length);
2720
+ const maxRows = stuck ? 1 : 2;
2664
2721
  const activeIdx = active ? chips.findIndex((c) => c.id === active) : -1;
2722
+ const maxH = maxRows * (rowHeight || 22) + (maxRows - 1) * 6;
2723
+ const measure = useCallback(() => {
2724
+ const el = wrapRef.current;
2725
+ if (!el || expanded) return;
2726
+ const tops = [];
2727
+ let rh = 0;
2728
+ for (const k of el.children) {
2729
+ if (!(k instanceof HTMLElement) || k.getAttribute("data-chip") !== "1") continue;
2730
+ tops.push(k.offsetTop);
2731
+ if (!rh && k.offsetHeight) rh = k.offsetHeight;
2732
+ }
2733
+ if (!tops.length) return;
2734
+ const next = { tops, clipTop: clipTopOf2(tops, maxRows), hiddenCount: countBeyondRows2(tops.slice(1), maxRows) };
2735
+ setGeom((prev) => prev && prev.tops.length === next.tops.length && prev.clipTop === next.clipTop && prev.hiddenCount === next.hiddenCount ? prev : next);
2736
+ setRowHeight((prev) => Math.abs(prev - rh) <= 0.5 ? prev : rh);
2737
+ }, [expanded, maxRows]);
2665
2738
  useEffect(() => {
2666
2739
  if (!active) {
2667
2740
  autoRef.current = null;
2668
2741
  setExpanded(false);
2669
- return;
2670
- }
2671
- if (activeIdx >= collapsedBudget && autoRef.current !== active) {
2672
- autoRef.current = active;
2673
- setExpanded(true);
2674
2742
  }
2675
- }, [active, activeIdx, collapsedBudget]);
2743
+ }, [active]);
2744
+ useEffect(() => {
2745
+ if (!active || !geom) return;
2746
+ const activeTop = geom.tops[activeIdx + 1];
2747
+ const d = autoExpandDecision2(activeTop, geom.clipTop, autoRef.current === active, expanded);
2748
+ if (d.record) autoRef.current = active;
2749
+ if (d.expand) setExpanded(true);
2750
+ }, [active, activeIdx, expanded, geom]);
2676
2751
  useLayoutEffect(() => {
2752
+ if (!expanded) measure();
2753
+ }, [chips.length, expanded, zone, stuck, rowHeight, measure]);
2754
+ useEffect(() => {
2677
2755
  const el = wrapRef.current;
2678
- if (!el || expanded) return;
2679
- let rows2 = 0;
2680
- let rows1 = 0;
2681
- let rowCount = 0;
2682
- let lastTop = null;
2683
- for (const k of el.children) {
2684
- if (!(k instanceof HTMLElement) || k.getAttribute("data-chip") !== "1") continue;
2685
- const t = k.offsetTop;
2686
- if (lastTop === null || t !== lastTop) {
2687
- rowCount += 1;
2688
- lastTop = t;
2689
- }
2690
- if (rowCount <= 2) rows2 += 1;
2691
- if (rowCount <= 1) rows1 += 1;
2692
- }
2693
- setFit({ rows2, rows1 });
2694
- }, [chips.length, expanded, zone]);
2756
+ if (!el || expanded || typeof ResizeObserver === "undefined") return;
2757
+ let raf = 0;
2758
+ const ro = new ResizeObserver(() => {
2759
+ cancelAnimationFrame(raf);
2760
+ raf = requestAnimationFrame(measure);
2761
+ });
2762
+ ro.observe(el);
2763
+ return () => {
2764
+ ro.disconnect();
2765
+ cancelAnimationFrame(raf);
2766
+ };
2767
+ }, [expanded, measure]);
2768
+ useEffect(() => {
2769
+ if (expanded || typeof document === "undefined" || !document.fonts?.ready) return;
2770
+ let cancelled = false;
2771
+ document.fonts.ready.then(() => {
2772
+ if (!cancelled) measure();
2773
+ });
2774
+ return () => {
2775
+ cancelled = true;
2776
+ };
2777
+ }, [expanded, measure]);
2695
2778
  useEffect(() => {
2696
2779
  const s = sentinelRef.current;
2697
2780
  if (!s || typeof IntersectionObserver === "undefined") return;
@@ -2699,41 +2782,62 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
2699
2782
  io.observe(s);
2700
2783
  return () => io.disconnect();
2701
2784
  }, []);
2702
- const budget = expanded ? chips.length : collapsedBudget;
2703
- const shown = chips.slice(0, budget);
2704
- const hidden = chips.length - shown.length;
2705
- const btn = (c) => h(
2706
- "button",
2707
- { key: c.id, "data-chip": "1", className: `dshm-chip${active === c.id ? " on" : ""}${c.count === 0 ? " zero" : ""}`, onClick: () => onPick(active === c.id ? null : c.id) },
2708
- // 0.9.45 U3/U7:0 计数桶显式渲染「0」+ 降透明(社区区 chip 计数恒 >0,行为不变)
2709
- `${c.labelKey ? lookup(c.labelKey) : c.label} ${c.count || 0}`
2710
- );
2785
+ const clip = !expanded;
2786
+ const overlay = clip && (trailing != null || geom && geom.hiddenCount > 0);
2787
+ const clipTop = geom ? geom.clipTop : Infinity;
2788
+ const hiddenCount = geom ? geom.hiddenCount : 0;
2789
+ const btn = (c, i) => {
2790
+ const top = geom ? geom.tops[i + 1] : null;
2791
+ const clipped = clip && geom && typeof top === "number" && top >= clipTop;
2792
+ return h(
2793
+ "button",
2794
+ {
2795
+ key: c.id,
2796
+ "data-chip": "1",
2797
+ style: clipped ? { visibility: "hidden" } : void 0,
2798
+ className: `dshm-chip${active === c.id ? " on" : ""}${c.count === 0 ? " zero" : ""}`,
2799
+ onClick: () => onPick(active === c.id ? null : c.id)
2800
+ },
2801
+ // 0.9.45 U3/U7:0 计数桶显式渲染「0」+ 降透明(社区区 chip 计数恒 >0,行为不变)
2802
+ `${c.labelKey ? lookup(c.labelKey) : c.label} ${c.count || 0}`
2803
+ );
2804
+ };
2711
2805
  return h(
2712
2806
  React.Fragment,
2713
2807
  null,
2714
2808
  h(
2715
2809
  "div",
2716
2810
  { className: "dsvm-chipswrap" },
2717
- // 吸顶检测哨兵(0.9.52):移入 sticky 容器内绝对定位——sticky 本身即定位上下文,
2718
- // 哨兵退出 .dshm-body 的 flex 流,不再多吃一份 12px gap(此前哨兵作为兄弟子项在
2719
- // 搜索框与分类行之间压出 12+1+12=25px 空带,行距节奏破坏)。top:-5px 复刻旧几何
2720
- // (旧兄弟哨兵顶边恰在 wrap 上沿上方 5px),IO 出视口触发点逐像素等价。
2811
+ // 吸顶检测哨兵(0.9.52):wrap 内 top:-5px 绝对定位,出视口 ⇒ stuck。⚠️ 裁剪宿主是
2812
+ // .dshm-chips(D3):wrap 决不可 overflow:hidden,否则哨兵被剪 → IO 恒 false → 永久吸顶态。
2721
2813
  h("div", {
2722
2814
  ref: sentinelRef,
2723
2815
  style: { position: "absolute", top: "-5px", left: 0, width: "1px", height: "1px", pointerEvents: "none" }
2724
2816
  }),
2725
2817
  h(
2726
2818
  "div",
2727
- { ref: wrapRef, className: "dshm-chips", title: wrapTitle || void 0 },
2819
+ {
2820
+ ref: wrapRef,
2821
+ className: `dshm-chips${clip ? " dshm-chips-clip" : ""}${clip && overlay ? " dshm-chips-gutter" : ""}`,
2822
+ title: wrapTitle || void 0,
2823
+ style: { maxHeight: clip ? `${maxH}px` : "none" }
2824
+ },
2728
2825
  h("button", { "data-chip": "1", className: `dshm-chip${active == null ? " on" : ""}`, onClick: () => onPick(null) }, lookup("cat.all")),
2729
- ...shown.map(btn),
2730
- hidden > 0 ? h(
2731
- "button",
2732
- { "data-chip": "1", className: "dshm-chip", onClick: () => setExpanded(!expanded) },
2733
- expanded ? "\u2303" : `+${hidden}`
2734
- ) : null,
2735
- trailing || null
2736
- )
2826
+ ...chips.map((c, i) => btn(c, i)),
2827
+ expanded ? [
2828
+ h("button", { key: "collapse", "data-chip": "1", className: "dshm-chip", onClick: () => setExpanded(false) }, "\u2303"),
2829
+ trailing || null
2830
+ ] : null
2831
+ ),
2832
+ // 折叠态右侧 overlay 组(D5/D6):[筛选][+N] 水平并排、钉末可见行右端;gutter 保证与 chip 零相交。
2833
+ // 吸顶单行态末可见行=第 1 行,两按钮仍同行并排(第④类碰撞结构性消除,R2-N1)。
2834
+ // ⚠️ 展开态不用 overlay(⌃/筛选 in-flow,与现状一致);弹层锚点 .dsvm-filterwrap 随组定位。
2835
+ clip && overlay ? h(
2836
+ "div",
2837
+ { className: "dsvm-chipmore" },
2838
+ trailing || null,
2839
+ hiddenCount > 0 ? h("button", { className: "dshm-chip", onClick: () => setExpanded(true) }, `+${hiddenCount}`) : null
2840
+ ) : null
2737
2841
  )
2738
2842
  );
2739
2843
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.9.52",
3
+ "version": "0.9.53",
4
4
  "description": "可自定义收录清单的 DSH 插件市场:收录、安装、卸载、升级一站完成,附 agent 工具与 dshm CLI。",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/registry.json CHANGED
@@ -87,7 +87,7 @@
87
87
  {
88
88
  "id": "dsh-skip-browser-auth",
89
89
  "name": "DSH Skip Browser Auth",
90
- "description": "替换官方 Connection 跳过 BrowserAuth,免 Token;已适配 0.1.2-rc.1、0.1.5-rc.1/rc.2、0.1.7-rc.1/rc.2 与 0.2.0-rc.1/rc.2。",
90
+ "description": "替换官方 Connection 跳过 BrowserAuth,免 Token;已适配七个已审计精确版本对,详见仓库。仅支持 web profile;desktop profile(存在 dsh-desktop-host 的环境)自动休眠,属设计行为。",
91
91
  "category": "self-dev",
92
92
  "tags": ["BrowserAuth", "连接", "安全", "工具"],
93
93
  "verified": ["0.1.2-rc.1", "0.1.5-rc.1", "0.1.5-rc.2", "0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.1", "0.2.0-rc.2"],