dsh-m 0.9.46 → 0.9.47

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,14 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
8
8
 
9
9
  ## 中文
10
10
 
11
+ ### 0.9.47 变更:README 预览内嵌 HTML 子集渲染(GitHub 风 README 不再满屏标签)
12
+
13
+ - **问题**:市场/收藏/已装详情 Modal 的 README 折叠页,遇到 GitHub 风重度内嵌 HTML 的 README(`<p align="center">` 徽章墙、`<img>` Logo、`<details>` 折叠等——npm packument 兜底与本地读取都常见)会把标签按字面整屏显示,观感如「乱码」。根因是渲染端自研 markdown 渲染器此前完全不解析 HTML 标签(原始内容本身没有问题,GitHub 上渲染正常)。
14
+ - **渲染子集(`src/client/markdown.js`)**:块级包裹标签(`p/div/center/blockquote/details/ul/ol/li/table/thead/tbody/tr/td/th/h1-6/figure/section/dl` 等)剥壳后按完整 markdown 递归渲染,内部标题/列表/表格/围栏结构全保留;行内标签(`a/img/strong/em/del/code/kbd/mark/sub/sup/u/small/span/br/hr` 等)token 化构造元素;`align` 属性映射 `textAlign`,`<img width/height>` 映射显式尺寸并放开徽章 20px 高度帽(无 width 的徽章仍走小图帽);`<details>/<summary>` 原生折叠;文本含标签的普通段落/标题/表格单元格也走行内 token 流(`Press <kbd>Ctrl</kbd>` 类可渲染)。
15
+ - **安全模型(不变式)**:绝不向 DOM 注入原始 HTML 字符串——只解析出白名单标签与白名单属性,全部经 `h()` 构造 React 元素,URL 一律过 `safeUrl` 闸门(`javascript:` 等归 `#`);未知标签剥壳留文本,`script/style/svg/iframe` 等容器连同内容整体丢弃;递归深度护栏(>8 层降级纯文本)。无 `dangerouslySetInnerHTML`,不存在 XSS 注入面。
16
+ - **已知边界**:npm packument 兜底的 README 不携带仓库基址,相对路径图片(如 `docs/assets/logo.svg`)按 safeUrl 归 `#` 后由 onError 隐藏(徽章等外链图片不受影响);`<picture><source>` 的 source 分支丢弃、保 `img` 回退。
17
+ - **测试**:`client-markdown.test.mjs` 净增 10 例(块级剥壳递归、徽章行连排、行内混排、script 丢弃与 javascript: 归化、未知标签剥壳、details/summary、整篇 div 包裹、HTML 表格、HTML 标题与行内元素、未闭合标签降级与深度护栏);真实 dsh-TUI README(25926 字符)渲染验证零标签泄漏、64 个徽章链接正常构造;全量测试 1175/1175,typecheck 零错误。纯客户端变更,刷新页面即生效。
18
+
11
19
  ### 0.9.46 变更:双语 README 精简重构+实拍 WebP 截图集(文档版)
12
20
 
13
21
  - **README×2 重构**:中文 17.5KB → 8.3KB、英文 18.1KB → 9.4KB——以用户任务动线重组(30 秒上手 → 浏览/搜索/安装 → 已装与设置 → Agent 工具与 CLI → 兼容与 FAQ);获取线路、缓存语义等实现细节不再展开,统一移交 [`docs/DESIGN.md`](docs/DESIGN.md) 与 ADR 承接;运行相位/生效时机表述与激活分桶语义对齐(不硬承诺「一定重启/一定刷新」,以操作返回为准)。
@@ -349,6 +357,14 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
349
357
 
350
358
  ## English
351
359
 
360
+ ### Added in 0.9.47 — embedded-HTML subset rendering for the README preview (GitHub-style READMEs no longer show raw tags)
361
+
362
+ - **Problem**: the README fold in the detail Modal (market/curated/installed) showed GitHub-style HTML-heavy READMEs (`<p align="center">` badge walls, `<img>` logos, `<details>` blocks — common in both the npm packument fallback and local reads) with every tag rendered literally, looking like mojibake. Root cause: the in-house markdown renderer never parsed HTML tags at all. The source content itself was fine — GitHub renders it normally.
363
+ - **Rendering subset (`src/client/markdown.js`)**: block-level wrapper tags (`p/div/center/blockquote/details/ul/ol/li/table/thead/tbody/tr/td/th/h1-6/figure/section/dl` etc.) are unwrapped and their inner content re-rendered as full markdown, preserving headings/lists/tables/fences; inline tags (`a/img/strong/em/del/code/kbd/mark/sub/sup/u/small/span/br/hr` etc.) are tokenized into React elements; `align` maps to `textAlign`; `<img width/height>` maps to explicit size and lifts the 20px badge height cap (badges without width keep the small-image cap); `<details>/<summary>` render as native disclosure; ordinary paragraphs/headings/table cells whose text contains tags also go through the inline token stream (`Press <kbd>Ctrl</kbd>` renders properly).
364
+ - **Security model (invariant)**: raw HTML is never injected into the DOM — only whitelisted tags with whitelisted attributes are parsed, everything is built via `h()` React elements, and URLs pass the `safeUrl` gate (`javascript:` etc. collapse to `#`); unknown tags are unwrapped keeping their text, `script/style/svg/iframe` containers are dropped together with their content, and a recursion depth guard (>8) degrades to plain text. No `dangerouslySetInnerHTML` — no XSS surface.
365
+ - **Known limits**: the npm packument fallback carries no repository base URL, so relative-path images (e.g. `docs/assets/logo.svg`) collapse to `#` via safeUrl and hide on error (externally hosted badge images are unaffected); inside `<picture><source>` the source branch is dropped and the `img` fallback kept.
366
+ - **Testing**: 10 new cases in `client-markdown.test.mjs` (block unwrap recursion, badge rows, inline mix, script dropping + `javascript:` neutralization, unknown-tag unwrap, details/summary, whole-document div wrapper, HTML tables, HTML headings + inline elements, unclosed-tag degradation + depth guard); the real dsh-TUI README (25,926 chars) renders with zero tag leaks and 64 badge links constructed; full suite 1175/1175, typecheck clean. Client-only change — a page refresh takes effect.
367
+
352
368
  ### Added in 0.9.46 — bilingual README restructure + live-captured WebP screenshot set (docs release)
353
369
 
354
370
  - **README×2 restructured**: zh-CN 17.5KB → 8.3KB, en 18.1KB → 9.4KB — reorganized around the user's task flow (quick start → browse/search/install → installed & settings → agent tools & CLI → compatibility & FAQ). Fetch-chain and cache-semantics details no longer live here; they are delegated to [`docs/DESIGN.md`](docs/DESIGN.md) and the ADRs. Runtime-phase / takes-effect wording now matches the activation-bucket semantics (no hard promises of "always restart" or "always refresh" — follow the operation result).
package/docs/DESIGN.md CHANGED
@@ -182,6 +182,8 @@ npm 安装 / GitHub 安装 / 升级 / 自升级 / 卸载是**同一个事务模
182
182
  技术:`src/client.js` 经 **esbuild** 打包为 `lib/client.js`;`window.__ModuleLoader__.load({ id: "dsh-m", factory })` 注册;**React 从 module loader require**(零额外运行时依赖);manifest 注入 `@deepseek-ai/dsh-client-runtime`、`@deepseek-ai/dsh-client-ui-slots`、`@deepseek-ai/dsh-client-ui-settings`。
183
183
  图标:GitHub 来源自动用 `https://github.com/<owner>.png?size=64`;`icon` 字段可覆盖;npm-only 条目首字母色块回退。
184
184
 
185
+ **README 预览渲染(`src/client/markdown.js`,0.9.47 起)**:自研轻量渲染器,安全模型 = 「白名单标签 + 白名单属性 + safeUrl 闸门 → 全部经 `h()` 构造 React 元素」,**绝不注入原始 HTML 字符串**(无 `dangerouslySetInnerHTML`,文本节点 React 转义兜底)。markdown 侧支持标题/列表/表格/围栏/引用/徽章链接等;HTML 侧支持 GitHub 风 README 常见内嵌子集——块级包裹标签(`p/div/center/details/table/tr/td/li/ul/ol/h1-6` 等)剥壳后按 markdown 递归(保内部结构),行内标签(`a/img/strong/em/code/kbd/mark/br/sub/sup` 等)token 化渲染,`align` 属性映射 `textAlign`、`<img width/height>` 映射显式尺寸(放开徽章 20px 高度帽);未知标签剥壳留文本,`script/style/svg/iframe` 等容器连同内容丢弃,`javascript:` 等危险 URL 归 `#`。已知边界:npm packument 兜底的 README 不携带仓库基址,相对路径图片(如 `docs/assets/x.svg`)按 safeUrl 归 `#` 后由 onError 隐藏;行内文本含标签的解析粒度到段落级(`Press <kbd>Ctrl</kbd>` 类可渲染)。数据源:已装条目读 profile 本地 `README.md`(≤64KB 截断);未装条目回源 npm packument 顶层 `readme` 字段(0.9.45 U10b)。
186
+
185
187
  ## 5. Agent 工具(host)与 CLI
186
188
 
187
189
  - 工具前缀 `dshm_`,共 7 个:`dshm_search` / `dshm_list` / `dshm_install` / `dshm_uninstall` / `dshm_outdated` / `dshm_upgrade` / `dshm_restart`。Agent tools 运行于 Host 进程,与 Web GUI **共用 `host` namespace 缓存与 active config**;`dshm_search` 走服务端过滤(metadata-only,`withLatest=false`、limit ≤80),返回不含本地路径的短 summary。
package/lib/client.js CHANGED
@@ -407,6 +407,50 @@ function safeUrl(u) {
407
407
  if (/^[/#]/.test(t)) return t;
408
408
  return "#";
409
409
  }
410
+ function parseHtmlAttrs(s) {
411
+ const attrs = {};
412
+ if (!s) return attrs;
413
+ const re = /([a-zA-Z][a-zA-Z0-9-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
414
+ let m;
415
+ while (m = re.exec(s)) attrs[m[1].toLowerCase()] = m[2] !== void 0 ? m[2] : m[3] !== void 0 ? m[3] : m[4] !== void 0 ? m[4] : "";
416
+ return attrs;
417
+ }
418
+ function htmlAlignStyle(attrs) {
419
+ const a = String(attrs.align || "").toLowerCase();
420
+ return a === "center" || a === "left" || a === "right" || a === "justify" ? { textAlign: a } : void 0;
421
+ }
422
+ function htmlImgStyle(attrs) {
423
+ const size = (v) => /^\d+(\.\d+)?$/.test(v) ? Number(v) : /^\d+(\.\d+)?%$/.test(v) ? v : null;
424
+ const st = {};
425
+ const w = size(String(attrs.width || ""));
426
+ if (w !== null) {
427
+ st.width = w;
428
+ st.maxWidth = "100%";
429
+ st.height = "auto";
430
+ st.maxHeight = "none";
431
+ }
432
+ const hgt = size(String(attrs.height || ""));
433
+ if (hgt !== null) st.height = hgt;
434
+ return Object.keys(st).length ? st : void 0;
435
+ }
436
+ function htmlBlockEnd(lines, i) {
437
+ const om = /<([a-zA-Z][a-zA-Z0-9-]*)/.exec(lines[i]);
438
+ const tag = om ? om[1].toLowerCase() : "";
439
+ if (!tag || HTML_VOID_TAGS.has(tag)) return i;
440
+ if (new RegExp(`</${tag}\\s*>`, "i").test(lines[i].slice(om.index))) return i;
441
+ const closeRe = new RegExp(`</${tag}\\s*>`, "i");
442
+ let lastNonBlank = i;
443
+ for (let j = i + 1; j < lines.length; j++) {
444
+ if (!lines[j].trim()) {
445
+ if (!HTML_SPAN_BLANK.has(tag) || j - i > 400) return lastNonBlank;
446
+ continue;
447
+ }
448
+ lastNonBlank = j;
449
+ if (closeRe.test(lines[j])) return j;
450
+ if (j - i > 400) return j;
451
+ }
452
+ return lastNonBlank;
453
+ }
410
454
  function createMarkdown(h2) {
411
455
  function ExtLink2({ href, className, children }) {
412
456
  return h2(
@@ -421,11 +465,13 @@ function createMarkdown(h2) {
421
465
  children
422
466
  );
423
467
  }
424
- function MdImg2({ src, alt }) {
468
+ function MdImg2({ src, alt, style, title }) {
425
469
  return h2("img", {
426
470
  className: "dshm-md-img",
427
471
  src: safeUrl(src),
428
472
  alt: alt || "",
473
+ title: title || void 0,
474
+ style: style || void 0,
429
475
  referrerPolicy: "no-referrer",
430
476
  onError: (e) => {
431
477
  e.currentTarget.style.display = "none";
@@ -474,7 +520,7 @@ function createMarkdown(h2) {
474
520
  if (last < src.length) nodes.push(src.slice(last));
475
521
  return nodes;
476
522
  }
477
- function mdBlocks(lines, kb) {
523
+ function mdBlocks(lines, kb, depth = 0) {
478
524
  const out = [];
479
525
  let i = 0;
480
526
  let n = 0;
@@ -485,6 +531,8 @@ function createMarkdown(h2) {
485
531
  const isUl = (s) => /^\s*[-*+]\s+/.test(s);
486
532
  const isOl = (s) => /^\s*\d+[.)]\s+/.test(s);
487
533
  const isTableRow = (s) => s.includes("|") && /^\s*\|/.test(s);
534
+ const isHtmlBlock = (s) => /^\s*<\/?[a-zA-Z][a-zA-Z0-9-]*(?:\s[^>]*?)?\/?>/.test(s);
535
+ const inlineOf = (s, key) => /<[a-zA-Z]/.test(s) ? mdHtmlInline(s, key) : mdInline(s, key);
488
536
  while (i < lines.length) {
489
537
  const line = lines[i];
490
538
  if (!line.trim()) {
@@ -492,6 +540,12 @@ function createMarkdown(h2) {
492
540
  continue;
493
541
  }
494
542
  const k = `${kb}-b${n++}`;
543
+ if (isHtmlBlock(line)) {
544
+ const end = htmlBlockEnd(lines, i);
545
+ out.push(...mdHtmlBlock(lines.slice(i, end + 1).join("\n"), `${k}H`, depth));
546
+ i = end + 1;
547
+ continue;
548
+ }
495
549
  if (isFence(line)) {
496
550
  const buf2 = [];
497
551
  i++;
@@ -502,7 +556,7 @@ function createMarkdown(h2) {
502
556
  }
503
557
  if (isHeading(line)) {
504
558
  const hm = /^(#{1,6})\s+(.*)$/.exec(line);
505
- out.push(h2(`h${hm[1].length}`, { key: k }, mdInline(hm[2], k)));
559
+ out.push(h2(`h${hm[1].length}`, { key: k }, inlineOf(hm[2], k)));
506
560
  i++;
507
561
  continue;
508
562
  }
@@ -514,7 +568,7 @@ function createMarkdown(h2) {
514
568
  if (isQuote(line)) {
515
569
  const buf2 = [];
516
570
  while (i < lines.length && isQuote(lines[i])) buf2.push(lines[i++].replace(/^\s*>\s?/, ""));
517
- out.push(h2("blockquote", { key: k }, mdBlocks(buf2, k)));
571
+ out.push(h2("blockquote", { key: k }, mdBlocks(buf2, k, depth)));
518
572
  continue;
519
573
  }
520
574
  if (isUl(line) || isOl(line)) {
@@ -522,7 +576,7 @@ function createMarkdown(h2) {
522
576
  const re = ordered ? /^\s*\d+[.)]\s+(.*)$/ : /^\s*[-*+]\s+(.*)$/;
523
577
  const items = [];
524
578
  while (i < lines.length && (ordered ? isOl(lines[i]) : isUl(lines[i]))) {
525
- items.push(h2("li", { key: `li${items.length}` }, mdInline(re.exec(lines[i])[1], `${k}-${items.length}`)));
579
+ items.push(h2("li", { key: `li${items.length}` }, inlineOf(re.exec(lines[i])[1], `${k}-${items.length}`)));
526
580
  i++;
527
581
  }
528
582
  out.push(h2(ordered ? "ol" : "ul", { key: k }, items));
@@ -541,30 +595,138 @@ function createMarkdown(h2) {
541
595
  h2(
542
596
  "table",
543
597
  { key: k },
544
- h2("thead", null, h2("tr", null, head.map((c, x) => h2("th", { key: x }, mdInline(c, `${k}h${x}`))))),
545
- h2("tbody", null, rows.map((r, y) => h2("tr", { key: y }, r.map((c, x) => h2("td", { key: x }, mdInline(c, `${k}${y}x${x}`))))))
598
+ h2("thead", null, h2("tr", null, head.map((c, x) => h2("th", { key: x }, inlineOf(c, `${k}h${x}`))))),
599
+ h2("tbody", null, rows.map((r, y) => h2("tr", { key: y }, r.map((c, x) => h2("td", { key: x }, inlineOf(c, `${k}${y}x${x}`))))))
546
600
  )
547
601
  );
548
602
  continue;
549
603
  }
550
604
  const buf = [line];
551
605
  i++;
552
- while (i < lines.length && lines[i].trim() && !isFence(lines[i]) && !isHeading(lines[i]) && !isHr(lines[i]) && !isQuote(lines[i]) && !isUl(lines[i]) && !isOl(lines[i])) {
606
+ while (i < lines.length && lines[i].trim() && !isFence(lines[i]) && !isHeading(lines[i]) && !isHr(lines[i]) && !isQuote(lines[i]) && !isUl(lines[i]) && !isOl(lines[i]) && !isHtmlBlock(lines[i])) {
553
607
  buf.push(lines[i]);
554
608
  i++;
555
609
  }
556
- out.push(h2("p", { key: k }, mdInline(buf.join(" "), k)));
610
+ out.push(h2("p", { key: k }, inlineOf(buf.join(" "), k)));
557
611
  }
558
612
  return out;
559
613
  }
560
- function renderMarkdown2(src) {
561
- return mdBlocks(String(src || "").replace(/\r\n?/g, "\n").split("\n"), "md");
614
+ function mdHtmlInline(src, kb) {
615
+ const out = [];
616
+ const stack = [];
617
+ let last = 0;
618
+ let n = 0;
619
+ let m;
620
+ const cur = () => stack.length ? stack[stack.length - 1].kids : out;
621
+ const pushText = (t) => {
622
+ if (!t) return;
623
+ if (!String(t).trim()) {
624
+ cur().push(" ");
625
+ return;
626
+ }
627
+ const parts = String(t).split(/\n[ \t]*\n+/);
628
+ parts.forEach((part, idx) => {
629
+ const nodes = mdInline(part.replace(/\s+/g, " "), `${kb}t${n++}`);
630
+ if (!nodes.length) return;
631
+ if (idx === 0 || stack.length) cur().push(...nodes);
632
+ else out.push(h2("p", { key: `${kb}tp${n++}` }, ...nodes));
633
+ });
634
+ };
635
+ const closeTop = () => {
636
+ const f = stack.pop();
637
+ cur().push(f.build(f.kids));
638
+ };
639
+ const sepNode = (name, key) => {
640
+ const sep = HTML_UNWRAP_SEP[name];
641
+ if (sep === "br") cur().push(h2("br", { key }));
642
+ else if (sep === "sp") cur().push(" ");
643
+ };
644
+ HTML_TAG_RE.lastIndex = 0;
645
+ while (m = HTML_TAG_RE.exec(src)) {
646
+ if (m.index > last) pushText(src.slice(last, m.index));
647
+ const full = m[0];
648
+ const name = m[2].toLowerCase();
649
+ const key = `${kb}h${n++}`;
650
+ if (m[1]) {
651
+ const idx = stack.map((f) => f.name).lastIndexOf(name);
652
+ if (idx >= 0) {
653
+ while (stack.length > idx + 1) closeTop();
654
+ closeTop();
655
+ } else sepNode(name, key);
656
+ } else if (HTML_SKIP_TAGS.has(name)) {
657
+ const cm = new RegExp(`</${name}\\s*>`, "i").exec(src.slice(m.index + full.length));
658
+ HTML_TAG_RE.lastIndex = cm ? m.index + full.length + cm.index + cm[0].length : src.length;
659
+ last = HTML_TAG_RE.lastIndex;
660
+ continue;
661
+ } else if (name === "img") {
662
+ const attrs = parseHtmlAttrs(m[3]);
663
+ cur().push(h2(MdImg2, { key, src: attrs.src, alt: attrs.alt, title: attrs.title, style: htmlImgStyle(attrs) }));
664
+ } else if (name === "br" || name === "hr") {
665
+ cur().push(h2(name, { key }));
666
+ } else if (HTML_VOID_TAGS.has(name) || m[4]) {
667
+ } else if (HTML_INLINE_MAP[name]) {
668
+ const attrs = parseHtmlAttrs(m[3]);
669
+ if (name === "a") {
670
+ stack.push({ name, kids: [], build: (kids) => h2(ExtLink2, { key, href: attrs.href, title: attrs.title }, kids) });
671
+ } else {
672
+ const type = HTML_INLINE_MAP[name];
673
+ const style = htmlAlignStyle(attrs);
674
+ stack.push({ name, kids: [], build: (kids) => h2(type, style ? { key, style } : { key }, kids) });
675
+ }
676
+ } else if (/^h[1-6]$/.test(name)) {
677
+ const style = htmlAlignStyle(parseHtmlAttrs(m[3]));
678
+ const lvl = name;
679
+ stack.push({ name, kids: [], build: (kids) => h2(lvl, style ? { key, style } : { key }, kids) });
680
+ } else {
681
+ sepNode(name, key);
682
+ }
683
+ last = m.index + full.length;
684
+ }
685
+ if (last < src.length) pushText(src.slice(last));
686
+ while (stack.length) closeTop();
687
+ return out;
688
+ }
689
+ function mdHtmlBlock(raw, kb, depth) {
690
+ const s = String(raw).trim();
691
+ if (depth > 8) return [s];
692
+ const om = /^<([a-zA-Z][a-zA-Z0-9-]*)((?:\s[^>]*)?)>/.exec(s);
693
+ if (!om) return mdHtmlInline(s, kb);
694
+ const tag = om[1].toLowerCase();
695
+ if (!HTML_BLOCK_WRAP.has(tag)) return mdHtmlInline(s, kb);
696
+ const cm = new RegExp(`</${tag}\\s*>\\s*$`, "i").exec(s.slice(om[0].length));
697
+ if (!cm) return mdHtmlInline(s, kb);
698
+ let inner = s.slice(om[0].length, om[0].length + cm.index);
699
+ const attrs = parseHtmlAttrs(om[2] || "");
700
+ const style = htmlAlignStyle(attrs);
701
+ if (tag === "details") {
702
+ const sm = /<summary[^>]*>([\s\S]*?)<\/summary>/i.exec(inner);
703
+ let sumNode = null;
704
+ if (sm) {
705
+ inner = inner.slice(0, sm.index) + inner.slice(sm.index + sm[0].length);
706
+ sumNode = h2("summary", { key: `${kb}sum` }, ...mdHtmlInline(sm[1], `${kb}sm`));
707
+ }
708
+ const kids = sumNode ? [sumNode, ...renderMarkdown2(inner, depth + 1)] : renderMarkdown2(inner, depth + 1);
709
+ return [h2("details", attrs.open !== void 0 ? { key: kb, open: true } : { key: kb }, ...kids)];
710
+ }
711
+ if (/^h[1-6]$/.test(tag)) return [h2(tag, style ? { key: kb, style } : { key: kb }, ...mdHtmlInline(inner, kb))];
712
+ return [h2(tag, style ? { key: kb, style } : { key: kb }, ...renderMarkdown2(inner, depth + 1))];
713
+ }
714
+ function renderMarkdown2(src, depth = 0) {
715
+ return mdBlocks(String(src || "").replace(/\r\n?/g, "\n").split("\n"), "md", depth);
562
716
  }
563
717
  return { ExtLink: ExtLink2, MdImg: MdImg2, renderMarkdown: renderMarkdown2 };
564
718
  }
719
+ var HTML_TAG_RE, HTML_VOID_TAGS, HTML_SKIP_TAGS, HTML_BLOCK_WRAP, HTML_SPAN_BLANK, HTML_INLINE_MAP, HTML_UNWRAP_SEP;
565
720
  var init_markdown = __esm({
566
721
  "src/client/markdown.js"() {
567
722
  "use strict";
723
+ HTML_TAG_RE = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)((?:\s+[a-zA-Z][a-zA-Z0-9-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*(\/?)>/g;
724
+ HTML_VOID_TAGS = /* @__PURE__ */ new Set(["br", "hr", "img", "source", "input", "wbr", "area", "col", "link", "meta"]);
725
+ HTML_SKIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "iframe", "object", "embed", "noscript", "svg", "title", "textarea", "select", "video", "audio", "canvas", "template", "form", "button", "head"]);
726
+ HTML_BLOCK_WRAP = /* @__PURE__ */ new Set(["p", "div", "center", "blockquote", "details", "ul", "ol", "li", "table", "thead", "tbody", "tr", "td", "th", "figure", "figcaption", "section", "article", "main", "header", "footer", "nav", "aside", "dl", "dt", "dd"]);
727
+ HTML_SPAN_BLANK = /* @__PURE__ */ new Set(["details", "div", "blockquote", "table", "thead", "tbody", "tr", "ul", "ol", "dl", "figure", "section", "article", "td", "th", "li"]);
728
+ HTML_INLINE_MAP = { a: "a", strong: "strong", b: "strong", em: "em", i: "em", del: "del", s: "del", strike: "del", code: "code", kbd: "kbd", sub: "sub", sup: "sup", u: "u", small: "small", mark: "mark", span: "span" };
729
+ HTML_UNWRAP_SEP = { tr: "br", li: "br", p: "br", h1: "br", h2: "br", h3: "br", h4: "br", h5: "br", h6: "br", table: "br", ul: "br", ol: "br", td: "sp", th: "sp" };
568
730
  }
569
731
  });
570
732
 
@@ -2031,6 +2193,12 @@ button.dshm-badge:hover{filter:brightness(.95)}
2031
2193
  .dshm-readme.md th{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.1))}
2032
2194
  .dshm-readme.md hr{border:0;border-top:1px solid var(--dsw-alias-border-l2,#cbd5e1);margin:8px 0}
2033
2195
  .dshm-readme.md .dshm-md-note{margin-top:8px;padding-top:6px;border-top:1px dashed var(--dsw-alias-border-l2,#cbd5e1);color:var(--dsw-alias-label-caption,#9ca3af);font-size:11px}
2196
+ /* 0.9.47 HTML \u5B50\u96C6\uFF1Adetails/summary/kbd/mark \u4E0E li\xB7td \u5185\u5C42 p \u95F4\u8DDD\u6536\u655B */
2197
+ .dshm-readme.md details{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;padding:4px 10px;margin:6px 0}
2198
+ .dshm-readme.md summary{cursor:pointer;font-weight:600;margin:2px 0}
2199
+ .dshm-readme.md kbd{background:rgba(127,127,127,.16);border:1px solid var(--dsw-alias-border-l2,#cbd5e1);border-bottom-width:2px;border-radius:4px;padding:0 4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}
2200
+ .dshm-readme.md mark{background:rgba(255,213,0,.35);color:inherit;border-radius:2px;padding:0 2px}
2201
+ .dshm-readme.md li>p,.dshm-readme.md td>p,.dshm-readme.md th>p,.dshm-readme.md summary>p,.dshm-readme.md p>p{margin:2px 0}
2034
2202
  .dshm-links{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:7px;padding-top:6px;border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);font-size:11px;color:var(--dsw-alias-label-caption,#6b7280)}
2035
2203
  .dshm-links-k,.dshm-links-sep{color:var(--dsw-alias-label-caption,#9ca3af)}
2036
2204
  .dshm-links a{color:var(--dsw-alias-state-business-primary,#4d6bfe);text-decoration:none;font-weight:500}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.9.46",
3
+ "version": "0.9.47",
4
4
  "description": "可自定义收录清单的 DSH 插件市场:收录、安装、卸载、升级一站完成,附 agent 工具与 dshm CLI。",
5
5
  "license": "MIT",
6
6
  "type": "module",