dsh-life-pack 0.3.54 → 0.3.56

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/dist/client.js CHANGED
@@ -471,14 +471,14 @@ window.__ModuleLoader__.load({
471
471
  path: "/api/ilife-manager"
472
472
  };
473
473
  //#endregion
474
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/config.js
474
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/config.js
475
475
  function assertPrefix(value, role) {
476
476
  if (typeof value !== "string" || value.length === 0) throw new Error("[dsh-plugin-update] " + role + " 非法:须为非空字符串(收到 " + JSON.stringify(value) + ")");
477
477
  if (value.includes(".") || value.includes("/") || value.includes("\\") || /\s/.test(value)) throw new Error("[dsh-plugin-update] " + role + " 非法:不得含有点、路径分隔符或空白(收到 " + JSON.stringify(value) + ")");
478
478
  return value;
479
479
  }
480
480
  //#endregion
481
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/redaction.js
481
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/redaction.js
482
482
  const COPY_BUDGET_CHARS = 1500;
483
483
  const REDACTED_PATH = "<路径>";
484
484
  const REDACTED_SECRET = "<脱敏>";
@@ -542,7 +542,7 @@ window.__ModuleLoader__.load({
542
542
  return truncateForCopy(scrubWithoutBudget(flat));
543
543
  }
544
544
  //#endregion
545
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/service.js
545
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/service.js
546
546
  function updateError(code) {
547
547
  return Object.assign(new Error(code), { code });
548
548
  }
@@ -629,7 +629,7 @@ window.__ModuleLoader__.load({
629
629
  return 0;
630
630
  }
631
631
  //#endregion
632
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/lang.js
632
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/lang.js
633
633
  function normalizeLangTag(tag) {
634
634
  if (typeof tag !== "string") return "zh";
635
635
  const s = tag.trim().toLowerCase().replace(/_/g, "-");
@@ -660,20 +660,41 @@ window.__ModuleLoader__.load({
660
660
  return [];
661
661
  }
662
662
  }
663
+ function overrideTagOf(override) {
664
+ if (override !== null && typeof override === "object") {
665
+ try {
666
+ const fn = override.getActive;
667
+ if (typeof fn === "function") {
668
+ const v = fn.call(override);
669
+ if (typeof v === "string" && v.trim()) return v;
670
+ }
671
+ } catch {}
672
+ return null;
673
+ }
674
+ if (typeof override === "string" && override.trim()) return override;
675
+ return null;
676
+ }
677
+ function langSignalSource(override) {
678
+ if (overrideTagOf(override) !== null) return "override";
679
+ if (readDocumentLang() !== null) return "dom";
680
+ if (readNavigatorLangs().length > 0) return "navigator";
681
+ return "default";
682
+ }
663
683
  function resolveLang(override) {
664
- if (override !== null && typeof override === "object") try {
665
- const fn = override.getActive;
666
- if (typeof fn === "function") {
667
- const v = fn.call(override);
668
- if (typeof v === "string" && v.trim()) return normalizeLangTag(v);
684
+ switch (langSignalSource(override)) {
685
+ case "override": {
686
+ const tag = overrideTagOf(override);
687
+ return tag === null ? "zh" : normalizeLangTag(tag);
669
688
  }
670
- } catch {}
671
- else if (typeof override === "string" && override.trim()) return normalizeLangTag(override);
672
- const docLang = readDocumentLang();
673
- if (docLang !== null) return normalizeLangTag(docLang);
674
- const navs = readNavigatorLangs();
675
- for (const c of navs) return normalizeLangTag(c);
676
- return "zh";
689
+ case "dom": {
690
+ const docLang = readDocumentLang();
691
+ return docLang === null ? "zh" : normalizeLangTag(docLang);
692
+ }
693
+ case "navigator":
694
+ for (const c of readNavigatorLangs()) return normalizeLangTag(c);
695
+ return "zh";
696
+ default: return "zh";
697
+ }
677
698
  }
678
699
  const entries = /* @__PURE__ */ new Set();
679
700
  let mo = null;
@@ -750,7 +771,7 @@ window.__ModuleLoader__.load({
750
771
  };
751
772
  }
752
773
  //#endregion
753
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/bilingual.js
774
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/bilingual.js
754
775
  const BILINGUAL_STRINGS = {
755
776
  "entry.label.idle": {
756
777
  en: "Check for updates",
@@ -1147,6 +1168,11 @@ window.__ModuleLoader__.load({
1147
1168
  zh: "已是最新,无需更新。",
1148
1169
  draft: true
1149
1170
  },
1171
+ "panel.banner.not-installed": {
1172
+ en: "Not installed",
1173
+ zh: "未安装",
1174
+ draft: true
1175
+ },
1150
1176
  "panel.toast.checking": {
1151
1177
  en: "Checking for updates…",
1152
1178
  zh: "正在查询版本信息…",
@@ -1543,8 +1569,8 @@ window.__ModuleLoader__.load({
1543
1569
  draft: true
1544
1570
  },
1545
1571
  "panel.error.log-hint": {
1546
- en: "Check logs: filter by plugin ID {pluginId} for events {e1}, {e2}, {e3}.",
1547
- zh: "请查看日志:按插件标识 {pluginId} 筛选 {e1}、{e2}、{e3} 事件。",
1572
+ en: "Check logs: {dir} (one directory per plugin, no overlap). Read them with {cmd}; filter by plugin ID {pluginId} for events {e1}, {e2}, {e3}.",
1573
+ zh: "请查看日志:{dir}(一个插件一棵目录,互不覆盖)。一条命令看:{cmd};按插件标识 {pluginId} 筛选 {e1}、{e2}、{e3} 事件。",
1548
1574
  draft: true
1549
1575
  },
1550
1576
  "panel.error.log-follow": {
@@ -1807,6 +1833,11 @@ window.__ModuleLoader__.load({
1807
1833
  zh: "本次未能查询",
1808
1834
  draft: true
1809
1835
  },
1836
+ "batch.row.not-installed": {
1837
+ en: "Not installed",
1838
+ zh: "未安装",
1839
+ draft: true
1840
+ },
1810
1841
  "batch.notice.auto-resumed": {
1811
1842
  en: "Auto-continued the unfinished batch ({count} left).",
1812
1843
  zh: "已自动继续上次未完成的更新(剩余 {count} 个)。",
@@ -2242,6 +2273,16 @@ window.__ModuleLoader__.load({
2242
2273
  zh: "共 {m} 条,仅显示前 {n} 条",
2243
2274
  draft: true
2244
2275
  },
2276
+ "changelog.truncated.file": {
2277
+ en: "This log is larger than 64 KiB; only the first part is rendered.",
2278
+ zh: "日志过长,超出上限的部分没有渲染。",
2279
+ draft: true
2280
+ },
2281
+ "changelog.truncated.versions": {
2282
+ en: "{n} more versions are hidden.",
2283
+ zh: "其后还有 {n} 个版本未显示。",
2284
+ draft: true
2285
+ },
2245
2286
  "changelog.yanked.banner": {
2246
2287
  en: "Version {version} was yanked by the author. Install is not affected. Please confirm before proceeding.",
2247
2288
  zh: "目标版本 {version} 已被作者撤回(yanked),安装不受影响,继续前请确认。",
@@ -2372,7 +2413,7 @@ window.__ModuleLoader__.load({
2372
2413
  }
2373
2414
  const BILINGUAL_CSS = [".dsh-upd-bi{display:inline;overflow-wrap:anywhere}", ".dsh-upd-bi [lang]{overflow-wrap:anywhere}"].join("\n");
2374
2415
  //#endregion
2375
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/changelog.js
2416
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/changelog.js
2376
2417
  const CHANGELOG_MAX_CHARS = 65536;
2377
2418
  const CHANGELOG_ALL_CATEGORIES = [
2378
2419
  "Added",
@@ -2397,6 +2438,27 @@ window.__ModuleLoader__.load({
2397
2438
  Removed: "移除",
2398
2439
  Security: "安全"
2399
2440
  };
2441
+ const CHANGELOG_CATEGORY_ALIASES = {
2442
+ "bug fixes": "Fixed",
2443
+ fixes: "Fixed",
2444
+ fix: "Fixed",
2445
+ "bug fix": "Fixed",
2446
+ features: "Added",
2447
+ feature: "Added",
2448
+ feat: "Added",
2449
+ "security fixes": "Security",
2450
+ "security fix": "Security",
2451
+ deprecations: "Deprecated",
2452
+ removals: "Removed",
2453
+ 修复: "Fixed",
2454
+ 新增: "Added",
2455
+ 特性: "Added",
2456
+ 变更: "Changed",
2457
+ 改动: "Changed",
2458
+ 移除: "Removed",
2459
+ 弃用: "Deprecated",
2460
+ 安全: "Security"
2461
+ };
2400
2462
  function emptySections() {
2401
2463
  return {
2402
2464
  Added: [],
@@ -2428,9 +2490,25 @@ window.__ModuleLoader__.load({
2428
2490
  if (t === "deprecated") return "Deprecated";
2429
2491
  if (t === "removed") return "Removed";
2430
2492
  if (t === "security") return "Security";
2493
+ const alias = CHANGELOG_CATEGORY_ALIASES[t];
2494
+ if (alias) return alias;
2431
2495
  for (const k of CHANGELOG_ALL_CATEGORIES) if (t === CHANGELOG_CATEGORY_ZH[k]) return k;
2432
2496
  return null;
2433
2497
  }
2498
+ function normalizeCategoryMulti(raw) {
2499
+ const t = String(raw || "").trim();
2500
+ if (!t) return null;
2501
+ const parts = t.split(/[/|·、,,;;+]/).map((s) => s.trim()).filter((s) => !!s);
2502
+ if (parts.length <= 1) return normalizeCategory(t);
2503
+ let acc = null;
2504
+ for (const part of parts) {
2505
+ const cat = normalizeCategory(part);
2506
+ if (!cat) return null;
2507
+ if (acc === null) acc = cat;
2508
+ else if (acc !== cat) return null;
2509
+ }
2510
+ return acc;
2511
+ }
2434
2512
  function isUnreleasedVersion(v) {
2435
2513
  return typeof v === "string" && v.trim().toLowerCase() === "unreleased";
2436
2514
  }
@@ -2447,7 +2525,7 @@ window.__ModuleLoader__.load({
2447
2525
  date: null,
2448
2526
  yanked
2449
2527
  };
2450
- const vm = t.match(/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/);
2528
+ const vm = t.match(RE_VERSION_TOKEN);
2451
2529
  if (!vm) return {
2452
2530
  version: null,
2453
2531
  date: null,
@@ -2466,28 +2544,110 @@ window.__ModuleLoader__.load({
2466
2544
  if (t.length <= 500) return t;
2467
2545
  return `${t.slice(0, 499)}\u2026`;
2468
2546
  }
2469
- const RE_VERSION_HEADING = /^##(?!#)\s*(.+?)\s*$/;
2470
- const RE_CATEGORY_HEADING = /^###\s*(.+?)\s*$/;
2547
+ function indentLevel(line) {
2548
+ const raw = (String(line || "").match(/^[ \t]*/) || [""])[0];
2549
+ let n = 0;
2550
+ for (const ch of raw) n += ch === " " ? 2 : 1;
2551
+ return Math.min(3, Math.floor(n / 2));
2552
+ }
2553
+ function matchOrderedBody(line) {
2554
+ const m = String(line || "").match(/^\s*\d{1,9}[.)]\s+(.+?)\s*$/);
2555
+ return m ? String(m[1] !== void 0 && m[1] !== null ? m[1] : "") : null;
2556
+ }
2557
+ function truncateEntryLines(lines, ledger) {
2558
+ const out = [];
2559
+ let truncated = false;
2560
+ for (const raw of lines) {
2561
+ const line = String(raw === null || raw === void 0 ? "" : raw).replace(/[ \t]+$/, "");
2562
+ if (!line) continue;
2563
+ if ((out.length > 0 ? out.join("\n") + "\n" + line : line).length <= 500) {
2564
+ out.push(line);
2565
+ continue;
2566
+ }
2567
+ truncated = true;
2568
+ break;
2569
+ }
2570
+ const text = out.join("\n");
2571
+ if (!truncated) return text;
2572
+ if (ledger) ledger.items += 1;
2573
+ if (text.length < 500) return text + "…";
2574
+ return text.slice(0, 499) + "…";
2575
+ }
2576
+ function bufEndsWithItem(lines) {
2577
+ const last = Array.isArray(lines) && lines.length > 0 ? String(lines[lines.length - 1] || "") : "";
2578
+ if (!last) return false;
2579
+ if (RE_FENCE_TOGGLE.test(last)) return true;
2580
+ return /^[ \t]*(?:[-*+]|\d{1,9}[.)])\s+/.test(last);
2581
+ }
2582
+ function canonicalNestedItem(line, level, body) {
2583
+ const m = String(line || "").match(/^[ \t]*(?:([-*+])|(\d{1,9}[.)]))\s+/);
2584
+ const marker = m && m[2] ? m[2] : m && m[1] ? m[1] : "-";
2585
+ return " ".repeat(Math.max(1, level)) + marker + " " + String(body || "").replace(/\s+/g, " ").trim();
2586
+ }
2471
2587
  const RE_BULLET = /^\s*[-*+]\s+(.+?)\s*$/;
2472
2588
  const RE_FENCE_TOGGLE = new RegExp("^\\s*(" + String.fromCharCode(96).repeat(3) + "|~~~)");
2473
- function matchVersionHeading(line) {
2474
- try {
2475
- const m = String(line !== null && line !== void 0 ? line : "").match(RE_VERSION_HEADING);
2476
- if (!m) return null;
2477
- return String(m[1] !== void 0 && m[1] !== null ? m[1] : "").trim();
2478
- } catch {
2479
- return null;
2589
+ const RE_VERSION_NUM = /(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/;
2590
+ const RE_UNRELEASED = /unreleased/i;
2591
+ const RE_VERSION_TOKEN = /(\d+\.\d+(?:\.\d+)*(?:-[0-9A-Za-z.-]+)?)/;
2592
+ function isPlainVersionTitle(text) {
2593
+ const t = String(text || "").trim();
2594
+ if (!t) return false;
2595
+ if (!/(?:^|[^\d.])v?\d+\.\d+(?![\d.])/.test(t)) return false;
2596
+ const core = t.replace(/\[YANKED\]/gi, " ").replace(/\[([^\]]*)\]\([^)]*\)/g, " ").replace(/\([^)]*\)/g, " ").replace(/([^)]*)/g, " ").replace(/\d{4}-\d{2}-\d{2}/g, " ").replace(/[\[\]()()【】]/g, " ").replace(/^[\s#>*_\-\u2014\u2013::|]+/, "").replace(/[\s#>*_\-\u2014\u2013::|]+$/, "").trim();
2597
+ return /^v?\d+\.\d+$/.test(core);
2598
+ }
2599
+ function isVersionIshWithoutNumber(text) {
2600
+ const t = String(text !== null && text !== void 0 ? text : "").trim();
2601
+ if (!t) return false;
2602
+ if (extractVersionDateAndYanked(t).version) return false;
2603
+ return /^\s*v?\d+\s*(?:$|[\u2014\u2013\-((])/.test(t);
2604
+ }
2605
+ function isSetextTextCandidate(line) {
2606
+ const t = String(line === null || line === void 0 ? "" : line);
2607
+ if (!t.trim()) return false;
2608
+ if (/^[ \t]/.test(t)) return false;
2609
+ if (/^\s{0,3}#{1,6}\s/.test(t)) return false;
2610
+ if (/^\s*(?:[-*+]|\d{1,9}[.)])\s+/.test(t)) return false;
2611
+ if (/^\s*>/.test(t)) return false;
2612
+ if (/^\s*\|/.test(t)) return false;
2613
+ if (isFenceToggle(t)) return false;
2614
+ return true;
2615
+ }
2616
+ function normalizeSetextHeadings(lines) {
2617
+ const out = lines.map((l) => String(l === null || l === void 0 ? "" : l));
2618
+ for (let i = 0; i + 1 < out.length; i++) {
2619
+ const m = out[i + 1].match(/^ {0,3}(={3,}|-{3,})\s*$/);
2620
+ if (!m || !isSetextTextCandidate(out[i])) continue;
2621
+ out[i] = (m[1].charAt(0) === "=" ? "# " : "## ") + out[i].trim();
2622
+ out[i + 1] = "";
2480
2623
  }
2624
+ return out;
2625
+ }
2626
+ const RE_ANY_HEADING = /^\s{0,3}(#{1,6})\s*(.+?)\s*$/;
2627
+ function looksLikeVersionHeading(text) {
2628
+ const t = String(text !== null && text !== void 0 ? text : "").trim();
2629
+ if (!t) return false;
2630
+ if (RE_UNRELEASED.test(t)) return true;
2631
+ if (RE_VERSION_NUM.test(t)) return true;
2632
+ return isPlainVersionTitle(t);
2481
2633
  }
2482
- function matchCategoryHeading(line) {
2634
+ function matchAnyHeading(line) {
2483
2635
  try {
2484
- const m = String(line !== null && line !== void 0 ? line : "").match(RE_CATEGORY_HEADING);
2636
+ const m = String(line !== null && line !== void 0 ? line : "").match(RE_ANY_HEADING);
2485
2637
  if (!m) return null;
2486
- return String(m[1] !== void 0 && m[1] !== null ? m[1] : "").trim();
2638
+ return {
2639
+ level: (m[1] || "").length,
2640
+ text: String(m[2] !== void 0 && m[2] !== null ? m[2] : "").trim()
2641
+ };
2487
2642
  } catch {
2488
2643
  return null;
2489
2644
  }
2490
2645
  }
2646
+ function matchVersionHeadingAny(line) {
2647
+ const h = matchAnyHeading(line);
2648
+ if (!h || h.level > 4) return null;
2649
+ return looksLikeVersionHeading(h.text) ? h.text : null;
2650
+ }
2491
2651
  function matchBulletBody(line) {
2492
2652
  try {
2493
2653
  const m = String(line !== null && line !== void 0 ? line : "").match(RE_BULLET);
@@ -2545,85 +2705,247 @@ window.__ModuleLoader__.load({
2545
2705
  return true;
2546
2706
  }
2547
2707
  }
2548
- function parseChangelog(markdown) {
2708
+ function ensureOther(cur, title) {
2709
+ const list = cur.otherSections ?? (cur.otherSections = []);
2710
+ const last = list[list.length - 1];
2711
+ if (title && last && last.title === "" && last.items.length === 0) {
2712
+ last.title = title.slice(0, 120);
2713
+ return last;
2714
+ }
2715
+ const sec = {
2716
+ title: title.slice(0, 120),
2717
+ items: [],
2718
+ count: 0
2719
+ };
2720
+ list.push(sec);
2721
+ return sec;
2722
+ }
2723
+ function countVersionHeadings(tail) {
2724
+ let n = 0;
2725
+ for (const line of String(tail || "").split("\n")) if (matchVersionHeadingAny(line) !== null) n += 1;
2726
+ return n;
2727
+ }
2728
+ function parseChangelogReport(markdown) {
2729
+ const ledger = {
2730
+ chars: false,
2731
+ tailVersions: 0,
2732
+ entries: 0,
2733
+ sections: 0,
2734
+ sectionItems: 0,
2735
+ items: 0
2736
+ };
2549
2737
  try {
2550
- if (typeof markdown !== "string" || !markdown.trim()) return [];
2738
+ if (typeof markdown !== "string" || !markdown.trim()) return {
2739
+ entries: [],
2740
+ truncation: ledger
2741
+ };
2551
2742
  let text = markdown.replace(/\r\n/g, "\n");
2552
- if (text.length > 65536) text = text.slice(0, CHANGELOG_MAX_CHARS);
2553
- const lines = text.split("\n");
2743
+ if (text.length > 65536) {
2744
+ ledger.chars = true;
2745
+ ledger.tailVersions = countVersionHeadings(text.slice(CHANGELOG_MAX_CHARS));
2746
+ text = text.slice(0, CHANGELOG_MAX_CHARS);
2747
+ }
2748
+ const lines = normalizeSetextHeadings(text.split("\n"));
2554
2749
  const entries = [];
2555
2750
  let cur = null;
2556
- let curCat = null;
2751
+ let bucket = null;
2557
2752
  let inFence = false;
2753
+ let buf = null;
2754
+ const bufNow = () => buf;
2755
+ let fenceTo = null;
2756
+ let fenceIndent = 0;
2757
+ let pendingBlank = false;
2758
+ const destOf = () => {
2759
+ if (!bucket) return null;
2760
+ return "cat" in bucket ? { cat: bucket.cat } : { other: bucket.other };
2761
+ };
2762
+ const ensureDest = () => {
2763
+ const d = destOf();
2764
+ if (d) return d;
2765
+ if (!cur) return null;
2766
+ const other = ensureOther(cur, "");
2767
+ bucket = { other };
2768
+ return { other };
2769
+ };
2770
+ const flushBuf = () => {
2771
+ const pending = buf;
2772
+ buf = null;
2773
+ pendingBlank = false;
2774
+ if (!pending || !cur) return;
2775
+ const text2 = truncateEntryLines(pending.lines, ledger);
2776
+ if (!text2) return;
2777
+ const c = cur;
2778
+ if ("cat" in pending.dest) {
2779
+ c.sections[pending.dest.cat].push(text2);
2780
+ try {
2781
+ if (c.counts) c.counts[pending.dest.cat] += 1;
2782
+ } catch {}
2783
+ } else {
2784
+ pending.dest.other.items.push(text2);
2785
+ pending.dest.other.count = (pending.dest.other.count ?? 0) + 1;
2786
+ }
2787
+ };
2788
+ const openBuf = (first, dest) => {
2789
+ buf = {
2790
+ lines: [first],
2791
+ dest
2792
+ };
2793
+ };
2558
2794
  const pushCurrent = () => {
2795
+ flushBuf();
2559
2796
  if (!cur) return;
2560
- if (!CHANGELOG_ALL_CATEGORIES.some((c) => cur.sections[c].length > 0)) return;
2797
+ const e = cur;
2798
+ if (!(CHANGELOG_ALL_CATEGORIES.some((c) => e.sections[c].length > 0) || (e.otherSections ?? []).some((s) => s.items.length > 0))) return;
2561
2799
  for (const c of CHANGELOG_ALL_CATEGORIES) {
2562
- const list = cur.sections[c];
2563
- if (list.length > 200) cur.sections[c] = list.slice(0, 200);
2800
+ const list = e.sections[c];
2801
+ if (list.length > 200) {
2802
+ ledger.sections += 1;
2803
+ ledger.sectionItems += list.length - 200;
2804
+ e.sections[c] = list.slice(0, 200);
2805
+ }
2564
2806
  }
2565
- entries.push(cur);
2807
+ for (const s of e.otherSections ?? []) if (s.items.length > 200) {
2808
+ ledger.sections += 1;
2809
+ ledger.sectionItems += s.items.length - 200;
2810
+ s.items = s.items.slice(0, 200);
2811
+ }
2812
+ entries.push(e);
2566
2813
  };
2567
2814
  for (const rawLine of lines) {
2568
2815
  const line = String(rawLine ?? "");
2569
2816
  if (isFenceToggle(line)) {
2570
- inFence = !inFence;
2817
+ if (inFence) {
2818
+ inFence = false;
2819
+ if (fenceTo) {
2820
+ fenceTo.push("```");
2821
+ fenceTo = null;
2822
+ }
2823
+ continue;
2824
+ }
2825
+ inFence = true;
2826
+ const lead = ((line.match(/^[ \t]*/) || [""])[0] || "").length;
2827
+ fenceIndent = lead;
2828
+ const b0 = bufNow();
2829
+ if (lead > 0 && b0) {
2830
+ fenceTo = b0.lines;
2831
+ fenceTo.push("```" + line.replace(/^[ \t]*(?:`{3,}|~{3,})\s*/, "").trim());
2832
+ continue;
2833
+ }
2834
+ const dest = ensureDest();
2835
+ flushBuf();
2836
+ if (dest) {
2837
+ openBuf("```" + line.replace(/^[ \t]*(?:`{3,}|~{3,})\s*/, "").trim(), dest);
2838
+ const b1 = bufNow();
2839
+ fenceTo = b1 ? b1.lines : null;
2840
+ } else fenceTo = null;
2571
2841
  continue;
2572
2842
  }
2573
- if (inFence) continue;
2574
- const versionTitle = matchVersionHeading(line);
2575
- if (versionTitle !== null) {
2576
- pushCurrent();
2577
- cur = null;
2578
- curCat = null;
2579
- const { version, date, yanked } = extractVersionDateAndYanked(versionTitle);
2580
- if (!version) continue;
2581
- cur = {
2582
- version,
2583
- date,
2584
- yanked,
2585
- sections: emptySections(),
2586
- counts: emptyCounts()
2587
- };
2843
+ if (inFence) {
2844
+ if (fenceTo) {
2845
+ const lead = ((line.match(/^[ \t]*/) || [""])[0] || "").length;
2846
+ fenceTo.push(line.slice(Math.min(fenceIndent, lead)).replace(/[ \t]+$/, ""));
2847
+ }
2588
2848
  continue;
2589
2849
  }
2590
- const catTitle = matchCategoryHeading(line);
2591
- if (catTitle !== null) {
2592
- const cat = normalizeCategory(catTitle);
2593
- curCat = cur && cat && isCategoryName(cat) ? cat : null;
2850
+ const head = matchAnyHeading(line);
2851
+ if (head) {
2852
+ const vTitle = head.level <= 4 && looksLikeVersionHeading(head.text) ? head.text : null;
2853
+ if (vTitle !== null) {
2854
+ pushCurrent();
2855
+ cur = null;
2856
+ bucket = null;
2857
+ const { version, date, yanked } = extractVersionDateAndYanked(vTitle);
2858
+ if (!version) continue;
2859
+ cur = {
2860
+ version,
2861
+ date,
2862
+ yanked,
2863
+ sections: emptySections(),
2864
+ counts: emptyCounts()
2865
+ };
2866
+ continue;
2867
+ }
2868
+ if (isVersionIshWithoutNumber(head.text)) {
2869
+ if (cur) {
2870
+ pushCurrent();
2871
+ cur = null;
2872
+ bucket = null;
2873
+ }
2874
+ continue;
2875
+ }
2876
+ if (cur) {
2877
+ flushBuf();
2878
+ const cat = normalizeCategoryMulti(head.text);
2879
+ if (cat && isCategoryName(cat)) bucket = { cat };
2880
+ else bucket = { other: ensureOther(cur, head.text) };
2881
+ continue;
2882
+ }
2594
2883
  continue;
2595
2884
  }
2596
2885
  const bulletBody = matchBulletBody(line);
2597
- if (bulletBody !== null && cur && curCat) {
2598
- const item = truncateBullet(bulletBody);
2599
- if (item) {
2600
- cur.sections[curCat].push(item);
2601
- try {
2602
- if (cur.counts) cur.counts[curCat] += 1;
2603
- } catch {}
2886
+ const orderedBody = bulletBody === null ? matchOrderedBody(line) : null;
2887
+ const body = bulletBody !== null ? bulletBody : orderedBody;
2888
+ if (body !== null) {
2889
+ const level = indentLevel(line);
2890
+ const b = bufNow();
2891
+ if (level > 0 && b && !(pendingBlank && !bufEndsWithItem(b.lines))) {
2892
+ b.lines.push(canonicalNestedItem(line, level, body));
2893
+ pendingBlank = false;
2894
+ continue;
2895
+ }
2896
+ if (bulletBody !== null) {
2897
+ const b2 = bufNow();
2898
+ if (pendingBlank && b2 && !bufEndsWithItem(b2.lines)) flushBuf();
2899
+ flushBuf();
2900
+ const dest = ensureDest();
2901
+ if (body.replace(/\s+/g, " ").trim().length > 500) ledger.items += 1;
2902
+ const item = truncateBullet(body);
2903
+ if (dest && item) openBuf(item, dest);
2904
+ continue;
2604
2905
  }
2605
- continue;
2606
2906
  }
2607
2907
  const trimmed = line.trim();
2608
- if (trimmed && !trimmed.startsWith("#") && /^\s/.test(line) && cur && curCat) {
2609
- const list = cur.sections[curCat];
2610
- if (list.length > 0) {
2611
- const merged = truncateBullet(`${list[list.length - 1]} ${trimmed}`);
2612
- if (merged) list[list.length - 1] = merged;
2613
- }
2908
+ if (!trimmed) {
2909
+ pendingBlank = true;
2614
2910
  continue;
2615
2911
  }
2912
+ if (trimmed.startsWith("#")) continue;
2913
+ if (cur) {
2914
+ const dest = ensureDest();
2915
+ if (!dest) continue;
2916
+ if (bufNow() && pendingBlank && !/^[ \t]/.test(line)) flushBuf();
2917
+ const text2 = trimmed === ">" || trimmed.startsWith("> ") ? "> " + trimmed.replace(/^>\s?/, "") : /^[ \t]/.test(line) ? " ".repeat(Math.max(1, indentLevel(line))) + trimmed : trimmed.replace(/[ \t]+$/, "");
2918
+ const bp2 = bufNow();
2919
+ if (bp2) bp2.lines.push(text2);
2920
+ else openBuf(text2, dest);
2921
+ pendingBlank = false;
2922
+ }
2616
2923
  }
2617
2924
  pushCurrent();
2618
- return entries.slice(0, 100);
2925
+ if (entries.length > 100) {
2926
+ ledger.entries = entries.length - 100;
2927
+ return {
2928
+ entries: entries.slice(0, 100),
2929
+ truncation: ledger
2930
+ };
2931
+ }
2932
+ return {
2933
+ entries,
2934
+ truncation: ledger
2935
+ };
2619
2936
  } catch {
2620
- return [];
2937
+ return {
2938
+ entries: [],
2939
+ truncation: ledger
2940
+ };
2621
2941
  }
2622
2942
  }
2623
2943
  function hasVisibleSections(entry) {
2624
2944
  if (!entry || typeof entry !== "object") return false;
2625
2945
  try {
2626
- return CHANGELOG_ALL_CATEGORIES.some((c) => Array.isArray(entry.sections?.[c]) && entry.sections[c].length > 0);
2946
+ if (CHANGELOG_ALL_CATEGORIES.some((c) => Array.isArray(entry.sections?.[c]) && entry.sections[c].length > 0)) return true;
2947
+ const other = entry.otherSections;
2948
+ return Array.isArray(other) && other.some((s) => Array.isArray(s?.items) && s.items.length > 0);
2627
2949
  } catch {
2628
2950
  return false;
2629
2951
  }
@@ -2679,6 +3001,170 @@ window.__ModuleLoader__.load({
2679
3001
  function escapeChangelogHtml(text) {
2680
3002
  return String(text ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2681
3003
  }
3004
+ const MD_URL_MAX = 2048;
3005
+ const MD_MAX_DEPTH = 2;
3006
+ function isHttpUrl(u) {
3007
+ return /^https?:\/\//i.test(String(u || ""));
3008
+ }
3009
+ function renderInlineTailAware(s) {
3010
+ let src = s;
3011
+ let out = renderInlineMarkdown(src, 0);
3012
+ if (!s.endsWith("…")) return out;
3013
+ for (let round = 0; round < 3; round++) {
3014
+ let visible = "";
3015
+ try {
3016
+ visible = out.replace(/<code[^>]*>[\s\S]*?<\/code>/g, "").replace(/<[^>]*>/g, "");
3017
+ } catch {
3018
+ break;
3019
+ }
3020
+ const m = visible.match(/\*\*|__|~~|`/);
3021
+ if (!m) break;
3022
+ const marker = m[0];
3023
+ const at = src.lastIndexOf(marker);
3024
+ if (at < 0) break;
3025
+ src = src.slice(0, at) + src.slice(at + marker.length);
3026
+ out = renderInlineMarkdown(src, 0);
3027
+ }
3028
+ return out;
3029
+ }
3030
+ function matchBracketLink(s, openIdx) {
3031
+ try {
3032
+ if (s[openIdx] !== "[") return null;
3033
+ const close = s.indexOf("]", openIdx + 1);
3034
+ if (close < 0 || s[close + 1] !== "(") return null;
3035
+ const urlEnd = s.indexOf(")", close + 2);
3036
+ if (urlEnd < 0) return null;
3037
+ const raw = s.slice(close + 2, urlEnd).trim();
3038
+ if (!raw || raw.length > MD_URL_MAX) return null;
3039
+ const url = raw.split(/\s+/)[0] || "";
3040
+ if (!url) return null;
3041
+ return {
3042
+ text: s.slice(openIdx + 1, close),
3043
+ url,
3044
+ end: urlEnd + 1
3045
+ };
3046
+ } catch {
3047
+ return null;
3048
+ }
3049
+ }
3050
+ function trimBareUrl(raw) {
3051
+ let u = String(raw || "");
3052
+ while (u.length > 0 && /[.,;:!?'"”’、。,;:!?]/.test(u[u.length - 1])) u = u.slice(0, -1);
3053
+ while (u.endsWith(")")) {
3054
+ const open = (u.match(/\(/g) || []).length;
3055
+ if ((u.match(/\)/g) || []).length <= open) break;
3056
+ u = u.slice(0, -1);
3057
+ }
3058
+ return u;
3059
+ }
3060
+ function mdAnchor(url, inner) {
3061
+ return "<a class=\"dsh-upd-md-link\" data-openlink=\"1\" href=\"" + escapeChangelogHtml(url) + "\" target=\"_blank\" rel=\"noopener noreferrer\">" + inner + "</a>";
3062
+ }
3063
+ function renderInlineMarkdown(text, depth) {
3064
+ const s = String(text ?? "");
3065
+ if (!s) return "";
3066
+ if (depth > MD_MAX_DEPTH) return escapeChangelogHtml(s);
3067
+ let out = "";
3068
+ let i = 0;
3069
+ const n = s.length;
3070
+ while (i < n) {
3071
+ const c = s[i];
3072
+ if (c === "\\" && i + 1 < n && /[\\`*_~[\]()!<>#|]/.test(s[i + 1])) {
3073
+ out += escapeChangelogHtml(s[i + 1]);
3074
+ i += 2;
3075
+ continue;
3076
+ }
3077
+ if (c === "`") {
3078
+ let ticks = 1;
3079
+ while (i + ticks < n && s[i + ticks] === "`" && ticks < 3) ticks++;
3080
+ const fence = "`".repeat(ticks);
3081
+ const end = s.indexOf(fence, i + ticks);
3082
+ if (end > i + ticks) {
3083
+ out += "<code class=\"dsh-upd-md-code\">" + escapeChangelogHtml(s.slice(i + ticks, end)) + "</code>";
3084
+ i = end + ticks;
3085
+ continue;
3086
+ }
3087
+ }
3088
+ if (c === "!" && s[i + 1] === "[") {
3089
+ const img = matchBracketLink(s, i + 1);
3090
+ if (img && isHttpUrl(img.url)) {
3091
+ const alt = img.text.trim() || img.url;
3092
+ out += "<span class=\"dsh-upd-md-img\" data-src=\"" + escapeChangelogHtml(img.url) + "\" title=\"" + escapeChangelogHtml(img.url) + "\">" + renderInlineMarkdown(alt, depth + 1) + "</span>";
3093
+ i = img.end;
3094
+ continue;
3095
+ }
3096
+ }
3097
+ if (c === "[") {
3098
+ const lk = matchBracketLink(s, i);
3099
+ if (lk && isHttpUrl(lk.url)) {
3100
+ out += mdAnchor(lk.url, renderInlineMarkdown(lk.text, depth + 1));
3101
+ i = lk.end;
3102
+ continue;
3103
+ }
3104
+ }
3105
+ if (c === "<") {
3106
+ const m = s.slice(i).match(/^<(https?:\/\/[^<>\s]+)>/i);
3107
+ if (m) {
3108
+ out += mdAnchor(m[1], escapeChangelogHtml(m[1]));
3109
+ i += m[0].length;
3110
+ continue;
3111
+ }
3112
+ }
3113
+ if ((c === "h" || c === "H") && /^https?:\/\//i.test(s.slice(i, i + 8))) {
3114
+ const m = s.slice(i).match(/^https?:\/\/[A-Za-z0-9\-._~:/?#@!$&*+,;=%()[\]]+/i);
3115
+ if (m) {
3116
+ const url = trimBareUrl(m[0]);
3117
+ if (url.length > 8) {
3118
+ out += mdAnchor(url, escapeChangelogHtml(url));
3119
+ i += url.length;
3120
+ continue;
3121
+ }
3122
+ }
3123
+ }
3124
+ if (c === "*" || c === "_") {
3125
+ let run = 1;
3126
+ while (i + run < n && s[i + run] === c && run < 2) run++;
3127
+ const marker = c.repeat(run);
3128
+ const prev = i > 0 ? s[i - 1] : "";
3129
+ if (!(c === "_" && /[0-9A-Za-z\u4e00-\u9fff]/.test(prev))) {
3130
+ const end = s.indexOf(marker, i + run);
3131
+ if (end > i + run) {
3132
+ const inner = s.slice(i + run, end);
3133
+ if (inner.trim() === inner && !inner.includes("\n")) {
3134
+ const tag = run === 2 ? "strong" : "em";
3135
+ out += "<" + tag + ">" + renderInlineMarkdown(inner, depth + 1) + "</" + tag + ">";
3136
+ i = end + run;
3137
+ continue;
3138
+ }
3139
+ }
3140
+ }
3141
+ }
3142
+ if (c === "~" && s[i + 1] === "~") {
3143
+ const end = s.indexOf("~~", i + 2);
3144
+ if (end > i + 2) {
3145
+ out += "<del class=\"dsh-upd-md-del\">" + renderInlineMarkdown(s.slice(i + 2, end), depth + 1) + "</del>";
3146
+ i = end + 2;
3147
+ continue;
3148
+ }
3149
+ }
3150
+ out += escapeChangelogHtml(c);
3151
+ i++;
3152
+ }
3153
+ return out;
3154
+ }
3155
+ function renderChangelogInline(text) {
3156
+ try {
3157
+ const s = String(text !== null && text !== void 0 ? text : "");
3158
+ if (!s) return "";
3159
+ return renderInlineTailAware(s);
3160
+ } catch {
3161
+ try {
3162
+ return escapeChangelogHtml(String(text !== null && text !== void 0 ? text : ""));
3163
+ } catch {
3164
+ return "";
3165
+ }
3166
+ }
3167
+ }
2682
3168
  function countsOf(entry) {
2683
3169
  try {
2684
3170
  const out = {
@@ -2733,19 +3219,159 @@ window.__ModuleLoader__.load({
2733
3219
  return "";
2734
3220
  }
2735
3221
  }
2736
- function renderChangelogItem(item, lang) {
3222
+ function matchListItem(line) {
3223
+ const m = String(line || "").match(/^([ \t]*)(?:([-*+])|(\d{1,9}[.)]))[ \t]+(.*)$/);
3224
+ if (!m) return null;
3225
+ return {
3226
+ depth: indentLevel(m[1] || ""),
3227
+ ordered: m[3] !== void 0 && m[3] !== null,
3228
+ text: m[4] || ""
3229
+ };
3230
+ }
3231
+ function renderListItems(items, start, base) {
3232
+ const first = items[start];
3233
+ const tag = first && first.ordered ? "ol" : "ul";
3234
+ const cls = tag === "ol" ? "dsh-upd-md-list-ol" : "dsh-upd-md-list";
3235
+ let html = "<" + tag + " class=\"" + cls + "\">";
3236
+ let i = start;
3237
+ while (i < items.length && items[i].depth >= base) {
3238
+ const it = items[i];
3239
+ if (it.depth > base) {
3240
+ const sub = renderListItems(items, i, it.depth);
3241
+ html += sub.html;
3242
+ i = sub.next;
3243
+ continue;
3244
+ }
3245
+ html += "<li>" + renderChangelogInline(it.text);
3246
+ i += 1;
3247
+ const nextItem = items[i];
3248
+ if (nextItem && nextItem.depth > base) {
3249
+ const sub = renderListItems(items, i, nextItem.depth);
3250
+ html += sub.html;
3251
+ i = sub.next;
3252
+ }
3253
+ html += "</li>";
3254
+ }
3255
+ html += "</" + tag + ">";
3256
+ return {
3257
+ html,
3258
+ next: i
3259
+ };
3260
+ }
3261
+ function isThematicBreak(line) {
3262
+ const t = String(line === null || line === void 0 ? "" : line);
3263
+ return /^ {0,3}([-*_])(?:\s*\1){2,}\s*$/.test(t);
3264
+ }
3265
+ function renderChangelogBlocks(lines) {
3266
+ try {
3267
+ const src = Array.isArray(lines) ? lines.map((x) => String(x === null || x === void 0 ? "" : x)) : [];
3268
+ const out = [];
3269
+ let para = [];
3270
+ const flushPara = () => {
3271
+ if (!para.length) return;
3272
+ out.push("<div class=\"dsh-upd-md-para\">" + renderChangelogInline(para.join(" ")) + "</div>");
3273
+ para = [];
3274
+ };
3275
+ let i = 0;
3276
+ while (i < src.length) {
3277
+ const line = src[i];
3278
+ if (isFenceToggle(line)) {
3279
+ flushPara();
3280
+ const code = [];
3281
+ i += 1;
3282
+ while (i < src.length && !isFenceToggle(src[i])) {
3283
+ code.push(src[i]);
3284
+ i += 1;
3285
+ }
3286
+ if (i < src.length) i += 1;
3287
+ out.push("<pre class=\"dsh-upd-md-pre\"><code class=\"dsh-upd-md-precode\">" + escapeChangelogHtml(code.join(String.fromCharCode(10))) + "</code></pre>");
3288
+ continue;
3289
+ }
3290
+ if (/^[ \t]*>/.test(line)) {
3291
+ flushPara();
3292
+ const quote = [];
3293
+ while (i < src.length && /^[ \t]*>/.test(src[i])) {
3294
+ quote.push(src[i].replace(/^[ \t]*>[ \t]?/, ""));
3295
+ i += 1;
3296
+ }
3297
+ out.push("<blockquote class=\"dsh-upd-md-quote\">" + renderChangelogInline(quote.join(" ")) + "</blockquote>");
3298
+ continue;
3299
+ }
3300
+ if (isThematicBreak(line)) {
3301
+ flushPara();
3302
+ out.push("<hr class=\"dsh-upd-md-hr\">");
3303
+ i += 1;
3304
+ continue;
3305
+ }
3306
+ if (matchListItem(line)) {
3307
+ flushPara();
3308
+ const items = [];
3309
+ while (i < src.length) {
3310
+ const head = matchListItem(src[i]);
3311
+ if (!head) break;
3312
+ let text2 = head.text;
3313
+ i += 1;
3314
+ while (i < src.length && !matchListItem(src[i]) && !isFenceToggle(src[i]) && !/^[ \t]*>/.test(src[i]) && !isThematicBreak(src[i])) {
3315
+ const extra = src[i].trim();
3316
+ if (extra) text2 += " " + extra;
3317
+ i += 1;
3318
+ }
3319
+ items.push({
3320
+ depth: head.depth,
3321
+ ordered: head.ordered,
3322
+ text: text2
3323
+ });
3324
+ }
3325
+ out.push(renderListItems(items, 0, items.length > 0 ? items[0].depth : 0).html);
3326
+ continue;
3327
+ }
3328
+ const text = line.trim();
3329
+ if (text) para.push(text);
3330
+ i += 1;
3331
+ }
3332
+ flushPara();
3333
+ return out.join("");
3334
+ } catch {
3335
+ return "";
3336
+ }
3337
+ }
3338
+ function leadsWithCategoryName(item, cat) {
3339
+ const first = String(item !== null && item !== void 0 ? item : "").split(String.fromCharCode(10))[0].trim();
3340
+ if (!first) return false;
3341
+ const bare = first.replace(/^[*_`~\s]+/, "");
3342
+ for (const name of [String(cat), CHANGELOG_CATEGORY_ZH[cat]]) {
3343
+ if (!name) continue;
3344
+ if (bare.slice(0, name.length).toLowerCase() !== name.toLowerCase()) continue;
3345
+ const next = bare.slice(name.length, name.length + 1);
3346
+ if (!next) return true;
3347
+ if (/[\s·::,,))\]】\-—–、。.;;/||*_`~]/.test(next)) return true;
3348
+ }
3349
+ return false;
3350
+ }
3351
+ function renderChangelogItem(item, lang, cat) {
2737
3352
  try {
2738
3353
  const l = normalizeLangTag(lang ?? "zh");
2739
3354
  const s = String(item !== null && item !== void 0 ? item : "");
2740
3355
  if (!s) return "";
2741
- const split = splitBreakingPrefix(s);
2742
- if (!split) return "<li>" + escapeChangelogHtml(s) + "</li>";
2743
- const head = escapeChangelogHtml(split.head);
2744
- const prefix = escapeChangelogHtml(split.prefix);
2745
- const rest = escapeChangelogHtml(split.rest);
3356
+ const nlIdx = s.indexOf(String.fromCharCode(10));
3357
+ const firstLine = nlIdx === -1 ? s : s.slice(0, nlIdx);
3358
+ if (/^[ \t]*(?:```|~~~|>|[-*+][ \t]|\d{1,9}[.)][ \t]|[-*_]{3,}\s*$)/.test(firstLine)) {
3359
+ const blockLead = cat && isCategoryName(String(cat)) ? " data-cat-block=\"1\"" : "";
3360
+ const blockHtml = renderChangelogBlocks(s.split(String.fromCharCode(10)));
3361
+ return blockHtml ? "<li" + blockLead + ">" + blockHtml + "</li>" : "";
3362
+ }
3363
+ const head = firstLine;
3364
+ const tailHtml = renderChangelogBlocks(nlIdx === -1 ? [] : s.slice(nlIdx + 1).split(String.fromCharCode(10)));
3365
+ const lead = cat && isCategoryName(String(cat)) && leadsWithCategoryName(head, String(cat)) ? " data-cat-lead=\"1\"" : "";
3366
+ if (!head) return tailHtml ? "<li" + lead + ">" + tailHtml + "</li>" : "";
3367
+ const split = splitBreakingPrefix(head);
3368
+ if (!split) return "<li" + lead + ">" + renderChangelogInline(head) + tailHtml + "</li>";
3369
+ const headHtml = renderChangelogInline(split.head);
3370
+ const prefix = renderChangelogInline(split.prefix);
3371
+ const rest = renderChangelogInline(split.rest);
2746
3372
  const badgeText = copyText("changelog.breaking.badge", l);
2747
3373
  const badge = "<span class=\"dsh-upd-breaking-badge\" role=\"img\" aria-label=\"" + escapeChangelogHtml(copyText("changelog.breaking.aria", l)) + "\">" + escapeChangelogHtml(badgeText) + "</span> ";
2748
- return "<li>" + head + badge + "<strong>" + prefix + "</strong>" + rest + "</li>";
3374
+ return "<li" + lead + ">" + headHtml + badge + "<strong>" + prefix + "</strong>" + rest + tailHtml + "</li>";
2749
3375
  } catch {
2750
3376
  return "";
2751
3377
  }
@@ -2777,7 +3403,7 @@ window.__ModuleLoader__.load({
2777
3403
  parts.push("<ul>");
2778
3404
  for (const item of items) {
2779
3405
  if (!item) continue;
2780
- const li = renderChangelogItem(item, l);
3406
+ const li = renderChangelogItem(item, l, cat);
2781
3407
  if (li) parts.push(li);
2782
3408
  }
2783
3409
  parts.push("</ul>");
@@ -2801,11 +3427,12 @@ window.__ModuleLoader__.load({
2801
3427
  parts.push("<ul>");
2802
3428
  for (const item of items) {
2803
3429
  if (!item) continue;
2804
- const li = renderChangelogItem(item, l);
3430
+ const li = renderChangelogItem(item, l, cat);
2805
3431
  if (li) parts.push(li);
2806
3432
  }
2807
3433
  parts.push("</ul></details>");
2808
3434
  }
3435
+ for (const block of renderOtherSections(entry, l)) parts.push(block);
2809
3436
  parts.push("</div>");
2810
3437
  return parts.join(String.fromCharCode(10));
2811
3438
  } catch {
@@ -2819,9 +3446,53 @@ window.__ModuleLoader__.load({
2819
3446
  return "";
2820
3447
  }
2821
3448
  }
3449
+ function renderOtherSections(entry, l) {
3450
+ const out = [];
3451
+ const list = Array.isArray(entry.otherSections) ? entry.otherSections : [];
3452
+ for (const s of list) {
3453
+ const items = Array.isArray(s?.items) ? s.items : [];
3454
+ if (items.length === 0) continue;
3455
+ const title = String(s.title || "").trim();
3456
+ out.push("<div class=\"dsh-upd-changelog-cat\" data-cat=\"other\">");
3457
+ if (title) out.push("<div class=\"dsh-upd-changelog-catname\">" + renderChangelogInline(title) + "</div>");
3458
+ const total = typeof s.count === "number" && s.count > items.length ? s.count : items.length;
3459
+ if (total > items.length) out.push(truncatedNoteHTML(total, items.length, l));
3460
+ out.push("<ul>");
3461
+ for (const item of items) {
3462
+ if (!item) continue;
3463
+ const li = renderChangelogItem(item, l, null);
3464
+ if (li) out.push(li);
3465
+ }
3466
+ out.push("</ul>");
3467
+ out.push("</div>");
3468
+ }
3469
+ return out;
3470
+ }
3471
+ function truncationNoteHTML(tr, lang) {
3472
+ try {
3473
+ const l = normalizeLangTag(lang ?? "zh");
3474
+ if (!tr || typeof tr !== "object") return "";
3475
+ const parts = [];
3476
+ if (tr.chars === true) {
3477
+ parts.push(copyText("changelog.truncated.file", l));
3478
+ const n = Number(tr.tailVersions) || 0;
3479
+ if (n > 0) parts.push(copyText("changelog.truncated.versions", l, { n: String(n) }));
3480
+ }
3481
+ const droppedEntries = Number(tr.entries) || 0;
3482
+ if (droppedEntries > 0) parts.push(copyText("changelog.truncated.count", l, {
3483
+ n: String(100),
3484
+ m: String(100 + droppedEntries)
3485
+ }));
3486
+ if (parts.length === 0) return "";
3487
+ return "<div class=\"dsh-upd-changelog-truncnote\">" + escapeChangelogHtml(parts.join(" ")) + "</div>";
3488
+ } catch {
3489
+ return "";
3490
+ }
3491
+ }
2822
3492
  function renderChangelogHTML(entries, opts) {
2823
3493
  try {
2824
3494
  const l = normalizeLangTag(opts?.lang ?? "zh");
3495
+ const note = truncationNoteHTML(opts?.truncated, l);
2825
3496
  if (!Array.isArray(entries) || entries.length === 0) return renderChangelogNeutral(l);
2826
3497
  const from = opts && typeof opts.from === "string" ? opts.from : null;
2827
3498
  const to = opts && typeof opts.to === "string" ? opts.to : null;
@@ -2835,11 +3506,11 @@ window.__ModuleLoader__.load({
2835
3506
  return false;
2836
3507
  }
2837
3508
  });
2838
- if (ranged.length === 0) return renderChangelogNeutral(l);
3509
+ if (ranged.length === 0) return note + renderChangelogNeutral(l);
2839
3510
  const blocks = ranged.map((e) => renderChangelogSection(e, l)).filter((s) => !!s);
2840
- if (blocks.length === 0) return renderChangelogNeutral(l);
3511
+ if (blocks.length === 0) return note + renderChangelogNeutral(l);
2841
3512
  return `<div class="dsh-upd-changelog">
2842
- ${blocks.join("\n")}
3513
+ ${note}${blocks.join("\n")}
2843
3514
  </div>`;
2844
3515
  } catch {
2845
3516
  try {
@@ -2850,12 +3521,46 @@ ${blocks.join("\n")}
2850
3521
  }
2851
3522
  }
2852
3523
  //#endregion
2853
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/log-events.js
3524
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/log-events.js
2854
3525
  const LOG_EVENT_CALL = "host.call";
2855
3526
  const LOG_EVENT_CALL_FAIL = "host.call.fail";
2856
3527
  const LOG_EVENT_INSTALL_EXEC = "update.install.exec";
2857
3528
  //#endregion
2858
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/panel.js
3529
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/codes.js
3530
+ const BLOCKED_REASONS = [
3531
+ "unknown-profile",
3532
+ "channel-mismatch",
3533
+ "source-install",
3534
+ "invalid-installation",
3535
+ "installation-changed",
3536
+ "pending-restart",
3537
+ "registry-conflict",
3538
+ "incompatible-node",
3539
+ "recovery-required"
3540
+ ];
3541
+ const PHONE_FAILURE_CODES = [
3542
+ "check-failed",
3543
+ "invalid-release",
3544
+ "check-expired",
3545
+ "update-busy",
3546
+ "install-failed"
3547
+ ];
3548
+ const CHECK_SIDE_FAILURE_CODES = [
3549
+ "check-failed",
3550
+ "invalid-release",
3551
+ "check-expired"
3552
+ ];
3553
+ const INTERNAL_CODE = "internal";
3554
+ [...BLOCKED_REASONS, ...PHONE_FAILURE_CODES];
3555
+ [...[...PHONE_FAILURE_CODES, ...BLOCKED_REASONS]];
3556
+ function isOneOf(codes, code) {
3557
+ return typeof code === "string" && codes.includes(code);
3558
+ }
3559
+ function isCheckSideFailureCode(code) {
3560
+ return typeof code === "string" && isOneOf(CHECK_SIDE_FAILURE_CODES, code.trim());
3561
+ }
3562
+ //#endregion
3563
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/panel.js
2859
3564
  function normalizePanelTheme(value) {
2860
3565
  return value === "archive" ? "archive" : "default";
2861
3566
  }
@@ -3035,7 +3740,7 @@ ${blocks.join("\n")}
3035
3740
  title: "panel.failure.install-failed.title",
3036
3741
  action: "panel.failure.install-failed.action"
3037
3742
  },
3038
- internal: {
3743
+ [INTERNAL_CODE]: {
3039
3744
  title: "panel.failure.internal.title",
3040
3745
  action: "panel.failure.internal.action"
3041
3746
  }
@@ -3076,7 +3781,7 @@ ${blocks.join("\n")}
3076
3781
  const err = reply && typeof reply.error === "string" ? reply.error.trim() : "";
3077
3782
  if (err) return err;
3078
3783
  if (typeof fallback === "string" && fallback.trim()) return fallback.trim();
3079
- return "internal";
3784
+ return INTERNAL_CODE;
3080
3785
  }
3081
3786
  function redactForCopy(text) {
3082
3787
  return sanitizeForCopy(text);
@@ -3419,12 +4124,13 @@ ${blocks.join("\n")}
3419
4124
  if (!staleFailure && (failedCode || jobState === "failed" || jobState === "interrupted")) {
3420
4125
  const code = failedCode || "install-failed";
3421
4126
  const copy = failureCopy(code, l);
4127
+ const checkDetail = copy?.zh ?? code;
3422
4128
  return {
3423
4129
  banner: {
3424
4130
  kind: "failed",
3425
4131
  title: copyText("panel.banner.error.title", l, {
3426
4132
  code,
3427
- detail: copy?.zh ?? code
4133
+ detail: snapshot?.installed === false && isCheckSideFailureCode(code) ? copyText("panel.banner.not-installed", l) + ":" + checkDetail : checkDetail
3428
4134
  }),
3429
4135
  action: copy?.act || copyText("panel.banner.error.action-fallback", l)
3430
4136
  },
@@ -3466,19 +4172,35 @@ ${blocks.join("\n")}
3466
4172
  queueNote
3467
4173
  };
3468
4174
  }
3469
- if (snapshot.canInstall && snapshot.latestVersion) return {
4175
+ if (snapshot.canInstall && snapshot.latestVersion) {
4176
+ const running = snapshot.installed === false ? copyText("panel.banner.not-installed", l) : snapshot.runningVersion;
4177
+ return {
4178
+ banner: {
4179
+ kind: "update",
4180
+ title: copyText("panel.banner.update-title", l, {
4181
+ latest: snapshot.latestVersion,
4182
+ running
4183
+ }),
4184
+ action: copyText("panel.banner.update-action", l)
4185
+ },
4186
+ installEnabled: true,
4187
+ installLabel: copyText("panel.action.install-version", l, { latest: snapshot.latestVersion }),
4188
+ skippedLatest: false,
4189
+ showManual: manual ? true : false,
4190
+ showReset: false,
4191
+ queueNote
4192
+ };
4193
+ }
4194
+ if (snapshot?.installed === false) return {
3470
4195
  banner: {
3471
- kind: "update",
3472
- title: copyText("panel.banner.update-title", l, {
3473
- latest: snapshot.latestVersion,
3474
- running: snapshot.runningVersion
3475
- }),
3476
- action: copyText("panel.banner.update-action", l)
4196
+ kind: "idle",
4197
+ title: copyText("panel.banner.not-installed", l),
4198
+ action: copyText("panel.action.check", l)
3477
4199
  },
3478
- installEnabled: true,
3479
- installLabel: copyText("panel.action.install-version", l, { latest: snapshot.latestVersion }),
4200
+ installEnabled: false,
4201
+ installLabel: copyText("panel.action.install", l),
3480
4202
  skippedLatest: false,
3481
- showManual: manual ? true : false,
4203
+ showManual: false,
3482
4204
  showReset: false,
3483
4205
  queueNote
3484
4206
  };
@@ -3528,7 +4250,20 @@ ${blocks.join("\n")}
3528
4250
  ".dsh-upd-changelog-security-more{margin:4px 0 6px}",
3529
4251
  ".dsh-upd-changelog-security-more>summary{cursor:pointer;font-size:var(--dsh-update-panel-small-font-size,12px);opacity:.8}",
3530
4252
  ".dsh-upd-changelog-more-note{font-size:var(--dsh-update-panel-small-font-size,12px);opacity:.7;margin:2px 0 4px}",
4253
+ ".dsh-upd-changelog-truncnote{font-size:var(--dsh-update-panel-small-font-size,12px);opacity:.8;border-left:3px solid var(--dsh-update-border,#d1d5db);padding:2px 0 2px 8px;margin:4px 0 6px}",
3531
4254
  ".dsh-upd-changelog-neutral{color:inherit;opacity:.8}",
4255
+ ".dsh-upd-md-code{font-family:var(--dsh-update-font-mono,Consolas,Menlo,monospace);font-size:.92em;background:var(--dsh-update-bg-soft,#f3f4f6);border:1px solid var(--dsh-update-border,#e5e7eb);border-radius:3px;padding:0 3px;word-break:break-word}",
4256
+ ".dsh-upd-md-link{color:var(--dsh-update-primary,#2563eb);text-decoration:underline;text-underline-offset:2px;cursor:pointer;word-break:break-all}",
4257
+ ".dsh-upd-md-del{opacity:.7}",
4258
+ ".dsh-upd-md-img{display:inline-block;max-width:100%;border:1px dashed var(--dsh-update-border,#d1d5db);border-radius:3px;padding:0 4px;font-size:var(--dsh-update-panel-small-font-size,12px);opacity:.85;word-break:break-all}",
4259
+ ".dsh-upd-md-pre{margin:6px 0;padding:7px 9px;max-width:100%;overflow-x:auto;background:var(--dsh-update-bg-soft,#f3f4f6);border:1px solid var(--dsh-update-border,#e5e7eb);border-radius:4px}",
4260
+ ".dsh-upd-md-precode{display:block;font-family:var(--dsh-update-font-mono,Consolas,Menlo,monospace);font-size:.9em;white-space:pre;background:none;border:0;padding:0}",
4261
+ ".dsh-upd-md-quote{margin:6px 0;padding:1px 0 1px 9px;border-left:3px solid var(--dsh-update-border,#d1d5db);color:var(--dsh-update-text-muted,#4b5563)}",
4262
+ ".dsh-upd-md-list{margin:4px 0;padding-left:18px;list-style:disc}",
4263
+ ".dsh-upd-md-list-ol{margin:4px 0;padding-left:20px;list-style:decimal}",
4264
+ ".dsh-upd-md-list li,.dsh-upd-md-list-ol li{margin:2px 0}",
4265
+ ".dsh-upd-md-para{margin:4px 0}",
4266
+ ".dsh-upd-md-hr{margin:8px 0;border:0;border-top:1px solid var(--dsh-update-border,#e5e7eb)}",
3532
4267
  ".dsh-upd-overlay{position:fixed;inset:0;background:rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:9999}",
3533
4268
  ".dsh-upd-overlay .dsh-upd{background:var(--dsh-update-bg,#ffffff);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}",
3534
4269
  ".dsh-upd-overlay .dsh-upd:has(>.dsh-upd-body)>:not(.dsh-upd-body){flex:none}",
@@ -3693,6 +4428,7 @@ ${blocks.join("\n")}
3693
4428
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-actions{flex-wrap:wrap}",
3694
4429
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-footer{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsh-update-border)}",
3695
4430
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-foot-note{color:var(--dsh-update-text-muted)}",
4431
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-footer button{background:transparent}",
3696
4432
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-head{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;padding-right:120px}",
3697
4433
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-name{font-family:var(--dsh-update-font-serif);font-size:22px;font-weight:700}",
3698
4434
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-meta{width:100%;font-size:var(--dsh-update-panel-small-font-size,12.5px);color:var(--dsh-update-text-muted)}",
@@ -3733,12 +4469,21 @@ ${blocks.join("\n")}
3733
4469
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog ul{margin:6px 0 0 2px;padding-left:18px;font-size:var(--dsh-update-panel-log-font-size,13.5px)}",
3734
4470
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog li{margin:3px 0}",
3735
4471
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-catname{display:none}",
3736
- ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"Added\"] ul li::before{content:\"Added · \";color:var(--dsh-update-primary);font-weight:700}",
3737
- ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"Fixed\"] ul li::before{content:\"Fixed · \";color:var(--dsh-update-primary);font-weight:700}",
3738
- ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"Changed\"] ul li::before{content:\"Changed · \";color:var(--dsh-update-primary);font-weight:700}",
3739
- ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"Security\"] ul li::before{content:\"Security · \";color:var(--dsh-update-primary);font-weight:700}",
3740
- ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-fold[data-cat=\"Deprecated\"] ul li::before{content:\"Deprecated · \";color:var(--dsh-update-text-muted);font-weight:700}",
3741
- ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-fold[data-cat=\"Removed\"] ul li::before{content:\"Removed · \";color:var(--dsh-update-text-muted);font-weight:700}",
4472
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"other\"] .dsh-upd-changelog-catname{display:block;font-size:var(--dsh-update-panel-small-font-size,12.5px);color:var(--dsh-update-primary);font-weight:700}",
4473
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-truncnote{border-left-color:var(--dsh-update-border-strong);opacity:1;color:var(--dsh-update-text-muted)}",
4474
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"Added\"]>ul>li:not([data-cat-lead]):not([data-cat-block])::before{content:\"Added · \";color:var(--dsh-update-primary);font-weight:700}",
4475
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"Fixed\"]>ul>li:not([data-cat-lead]):not([data-cat-block])::before{content:\"Fixed · \";color:var(--dsh-update-primary);font-weight:700}",
4476
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"Changed\"]>ul>li:not([data-cat-lead]):not([data-cat-block])::before{content:\"Changed · \";color:var(--dsh-update-primary);font-weight:700}",
4477
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-cat[data-cat=\"Security\"]>ul>li:not([data-cat-lead]):not([data-cat-block])::before{content:\"Security · \";color:var(--dsh-update-primary);font-weight:700}",
4478
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-fold[data-cat=\"Deprecated\"]>ul>li:not([data-cat-lead]):not([data-cat-block])::before{content:\"Deprecated · \";color:var(--dsh-update-text-muted);font-weight:700}",
4479
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-changelog-fold[data-cat=\"Removed\"]>ul>li:not([data-cat-lead]):not([data-cat-block])::before{content:\"Removed · \";color:var(--dsh-update-text-muted);font-weight:700}",
4480
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-md-code{background:var(--dsh-update-bg-soft);border-color:var(--dsh-update-border);color:var(--dsh-update-primary-deep);border-radius:2px;padding:0 3px}",
4481
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-md-link{color:var(--dsh-update-primary);text-decoration:none;border-bottom:1px solid currentColor}",
4482
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-md-img{border-color:var(--dsh-update-border-strong);color:var(--dsh-update-text-muted)}",
4483
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-md-pre{background:var(--dsh-update-bg-soft);border-color:var(--dsh-update-border);border-radius:2px}",
4484
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-md-quote{border-left-color:var(--dsh-update-border-strong);color:var(--dsh-update-text-muted)}",
4485
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-md-list,.dsh-upd[data-theme=\"archive\"] .dsh-upd-md-list-ol{padding-left:16px}",
4486
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-md-hr{border-top-color:var(--dsh-update-border-strong)}",
3742
4487
  "@media (max-width:640px){.dsh-upd[data-theme=\"archive\"]{padding:10px 12px}.dsh-upd[data-theme=\"archive\"]::before{top:12px;right:12px;width:56px;height:56px;font-size:15px;box-shadow:inset 0 0 0 4px var(--dsh-update-bg),inset 0 0 0 5px currentColor}.dsh-upd[data-theme=\"archive\"] .dsh-upd-masthead-title{font-size:19px}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner{padding-right:16px}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner .dsh-upd-sealmini{min-width:28px;height:28px;font-size:14px;line-height:20px}.dsh-upd[data-theme=\"archive\"] .dsh-upd-banner>div:first-child{white-space:normal}}",
3743
4488
  "@media (prefers-color-scheme: dark){.dsh-upd[data-theme=\"archive\"]{--dsh-update-bg-soft:#141210;--dsh-update-bg:#1e1a15;--dsh-update-text:#ece5d3;--dsh-update-text-muted:#a89c83;",
3744
4489
  "--dsh-update-border:#3a3226;--dsh-update-border-strong:#5c4e3b;--dsh-update-primary:#e0684e;--dsh-update-primary-deep:#f0866b;",
@@ -3794,7 +4539,7 @@ ${blocks.join("\n")}
3794
4539
  const unknown = copyText("panel.meta.unknown", l);
3795
4540
  const cell = (k, v) => `<div><span class="dsh-upd-strip-k">${escapeHtml$2(k)}</span><span class="dsh-upd-strip-v">${escapeHtml$2(v ?? unknown)}</span></div>`;
3796
4541
  if (!snapshot) return "";
3797
- return `<div class="dsh-upd-strip">` + cell(copyText("panel.strip.running", l), snapshot.runningVersion) + cell(copyText("panel.strip.installed", l), snapshot.installedVersion) + cell(copyText("panel.strip.latest", l), snapshot.latestVersion) + `</div>`;
4542
+ return `<div class="dsh-upd-strip">` + cell(copyText("panel.strip.running", l), snapshot.runningVersion) + cell(copyText("panel.strip.installed", l), snapshot.installed === false ? copyText("panel.banner.not-installed", l) : snapshot.installedVersion) + cell(copyText("panel.strip.latest", l), snapshot.latestVersion) + `</div>`;
3798
4543
  }
3799
4544
  function progressBar(snapshot, lang) {
3800
4545
  const job = snapshot?.job;
@@ -3847,14 +4592,18 @@ ${blocks.join("\n")}
3847
4592
  let hasLog = false;
3848
4593
  if (snapshot && snapshot.latestVersion) try {
3849
4594
  const mdText = typeof changelogMarkdown === "string" ? changelogMarkdown : "";
3850
- const entries = parseChangelog(mdText);
4595
+ const report = parseChangelogReport(mdText);
4596
+ const entries = report.entries;
3851
4597
  const ranged = changelogForUpdate(entries, snapshot.runningVersion, snapshot.latestVersion, snapshot.installedVersion);
3852
4598
  if (ranged.length === 0 && mdText.trim() && entries.length === 0) {
3853
- inner = `<div class="dsh-upd-changelog-wrap"><div class="dsh-upd-changelog-neutral">${escapeHtml$2(copyText("panel.changelog.unavailable", l))}</div></div>`;
4599
+ inner = `<div class="dsh-upd-changelog-wrap"><div class="dsh-upd-changelog-neutral">${escapeHtml$2(copyText("panel.changelog.unavailable", l))}</div>${truncationNoteHTML(report.truncation, l)}</div>`;
3854
4600
  hasLog = false;
3855
4601
  } else {
3856
- const changelogHTML = renderChangelogHTML(ranged, { lang: l });
3857
- const fromText = String(snapshot.runningVersion ?? "");
4602
+ const changelogHTML = renderChangelogHTML(ranged, {
4603
+ lang: l,
4604
+ truncated: report.truncation
4605
+ });
4606
+ const fromText = snapshot.installed === false ? copyText("panel.banner.not-installed", l) : String(snapshot.runningVersion ?? "");
3858
4607
  const toText = String(snapshot.latestVersion ?? "");
3859
4608
  const rangeTitle = fromText && toText ? copyText("panel.changelog.heading-range", l, {
3860
4609
  from: fromText,
@@ -3927,7 +4676,13 @@ ${changelogHTML}
3927
4676
  }
3928
4677
  } else errLines.push(`<div class="dsh-upd-changelog-neutral">${escapeHtml$2(copyText("panel.error.no-failure", l))}</div>`);
3929
4678
  if (input.showLogHint !== false) {
4679
+ const logDirInput = input.logDir;
4680
+ const logDirKnown = typeof logDirInput === "string" && logDirInput.trim() ? logDirInput.trim() : null;
4681
+ const logDirShown = logDirKnown ?? "<DSH_HOME>/logs/<derived>";
4682
+ const logCmdShown = logDirKnown ? `npx dsh-log ${logDirKnown}` : "npx dsh-log <log-dir>";
3930
4683
  errLines.push(`<div class="dsh-upd-log">${copyText("panel.error.log-hint", l, {
4684
+ dir: `<code>${escapeHtml$2(logDirShown)}</code>`,
4685
+ cmd: `<code>${escapeHtml$2(logCmdShown)}</code>`,
3931
4686
  pluginId: `<code>${escapeHtml$2(pluginId)}</code>`,
3932
4687
  e1: `<code>${LOG_EVENT_CALL}</code>`,
3933
4688
  e2: `<code>${LOG_EVENT_CALL_FAIL}</code>`,
@@ -3967,7 +4722,7 @@ ${UPDATE_PANEL_ARCHIVE_CSS}` : UPDATE_PANEL_CSS}</style>
3967
4722
  ${body}`;
3968
4723
  }
3969
4724
  //#endregion
3970
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/entry.js
4725
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/entry.js
3971
4726
  const UPDATE_ENTRY_CSS = [
3972
4727
  ".dsh-upd-entry{display:inline-flex;align-items:center;gap:8px;font:13px/1.6 var(--dsh-update-font-sans,system-ui,\"Microsoft YaHei\",sans-serif);font-size:var(--dsh-update-entry-font-size,13px);color:var(--dsh-update-text,#1f2937)}",
3973
4728
  ".dsh-upd-entry-btn{font:inherit;border:1px solid var(--dsh-update-border,#d1d5db);border-radius:var(--dsh-update-entry-border-radius,6px);",
@@ -4081,7 +4836,7 @@ ${body}`;
4081
4836
  }
4082
4837
  }
4083
4838
  //#endregion
4084
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/panel-batch.js
4839
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/panel-batch.js
4085
4840
  const BATCH_PANEL_POLL = {
4086
4841
  defaultMs: 1500,
4087
4842
  minMs: 250
@@ -4153,8 +4908,10 @@ ${body}`;
4153
4908
  if (!isObject$1(item)) return null;
4154
4909
  const e = item;
4155
4910
  const canInstallRaw = e["canInstall"];
4911
+ const installedRaw = e["installed"];
4156
4912
  return {
4157
4913
  lastCheckedAt: typeof e["lastCheckedAt"] === "number" ? e["lastCheckedAt"] : 0,
4914
+ installed: typeof installedRaw === "boolean" ? installedRaw : null,
4158
4915
  installedVersion: typeof e["installedVersion"] === "string" ? e["installedVersion"] : null,
4159
4916
  latestVersion: typeof e["latestVersion"] === "string" ? e["latestVersion"] : null,
4160
4917
  canInstall: typeof canInstallRaw === "boolean" ? canInstallRaw : null,
@@ -4240,7 +4997,7 @@ ${body}`;
4240
4997
  case "installing": return batchText("batch.row.installing", l);
4241
4998
  case "current": return batchText("batch.row.current", l);
4242
4999
  case "done": return row.restartRequired === true ? batchText("batch.row.done-restart", l) : batchText("batch.row.done", l);
4243
- case "failed": return batchText("batch.row.failed-retry", l);
5000
+ case "failed": return isCheckSideFailureCode(jobFailureOf(row)?.code ?? (typeof row.error === "string" && row.error ? row.error : null)) ? batchText("batch.row.failed", l) : batchText("batch.row.failed-retry", l);
4244
5001
  case "skipped": return batchText("batch.row.skipped-idle", l);
4245
5002
  default: return batchText("batch.row.unknown", l);
4246
5003
  }
@@ -4273,7 +5030,9 @@ ${body}`;
4273
5030
  hasUpdate: true
4274
5031
  };
4275
5032
  }
4276
- const current = currentVersionOf(row) ?? (knowledge && typeof knowledge.installedVersion === "string" && knowledge.installedVersion ? knowledge.installedVersion : null);
5033
+ const snapshotCurrent = currentVersionOf(row);
5034
+ const knowledgeCurrent = knowledge && knowledge.installed !== false && typeof knowledge.installedVersion === "string" && knowledge.installedVersion ? knowledge.installedVersion : null;
5035
+ const current = snapshotCurrent ?? knowledgeCurrent;
4277
5036
  const latest = knowledge && typeof knowledge.latestVersion === "string" && knowledge.latestVersion ? knowledge.latestVersion : null;
4278
5037
  if (current && latest && latest !== current) return {
4279
5038
  current,
@@ -4296,10 +5055,11 @@ ${body}`;
4296
5055
  hasUpdate: false
4297
5056
  };
4298
5057
  }
4299
- function batchRowVersionHTML(row, knowledge) {
5058
+ function batchRowVersionHTML(row, knowledge, lang) {
4300
5059
  const parts = batchRowVersionParts(row, knowledge);
4301
5060
  if (parts.hasUpdate && parts.latest) {
4302
- const cur = parts.current ?? "?";
5061
+ const snapshot = isObject$1(row.snapshot) ? row.snapshot : null;
5062
+ const cur = parts.current ?? (snapshot !== null && snapshot["installed"] === false ? batchText("batch.row.not-installed", lang) : "?");
4303
5063
  const newCls = asBatchPhase(row.phase) === "done" ? "dsh-upd-bver-new-ok" : "dsh-upd-bver-new";
4304
5064
  return "<span class=\"dsh-upd-bver\"><span class=\"dsh-upd-bver-cur\">" + escapeHtml$1(cur) + "</span><span class=\"dsh-upd-bver-arrow\" aria-hidden=\"true\"> → </span><span class=\"" + newCls + "\">" + escapeHtml$1(parts.latest) + "</span></span>";
4305
5065
  }
@@ -4405,12 +5165,27 @@ ${body}`;
4405
5165
  const seal = batchSealOf(counts, lang);
4406
5166
  const root = "<div class=\"dsh-upd dsh-upd-batch\" data-mode=\"" + mode + "\" data-seal=\"" + escapeHtml$1(seal.text) + "\" data-seal-tone=\"" + seal.tone + "\"" + attr + tokensAttr + ">\n" + kernel + "\n</div>";
4407
5167
  const body = mode === "dialog" ? "<div class=\"dsh-upd-overlay\" data-mode=\"dialog\">" + root + "</div>" : root;
5168
+ let detailDialog = "";
5169
+ if (expandedKey !== null) {
5170
+ const target = rows.find((row) => row.key === expandedKey) ?? null;
5171
+ if (target !== null) {
5172
+ const rawSkipped = skippedVersions[target.key];
5173
+ const skippedForDialog = typeof rawSkipped === "string" && rawSkipped ? rawSkipped : null;
5174
+ detailDialog = detailDialogHTML(target, {
5175
+ ...ctx,
5176
+ expanded: true,
5177
+ skipped: skippedForDialog,
5178
+ notice: target.key === detailNoticeKey ? notice : null,
5179
+ changelogMarkdown: typeof changelogs[target.key] === "string" ? changelogs[target.key] : null
5180
+ });
5181
+ }
5182
+ }
4408
5183
  return "<style>" + (archive ? [
4409
5184
  UPDATE_PANEL_CSS,
4410
5185
  UPDATE_BATCH_PANEL_CSS,
4411
5186
  UPDATE_PANEL_ARCHIVE_CSS,
4412
5187
  UPDATE_BATCH_PANEL_ARCHIVE_CSS
4413
- ].join(String.fromCharCode(10)) : [UPDATE_PANEL_CSS, UPDATE_BATCH_PANEL_CSS].join(String.fromCharCode(10))) + "</style>\n" + body;
5188
+ ].join(String.fromCharCode(10)) : [UPDATE_PANEL_CSS, UPDATE_BATCH_PANEL_CSS].join(String.fromCharCode(10))) + "</style>\n" + body + (detailDialog ? "\n" + detailDialog : "");
4414
5189
  }
4415
5190
  function batchSummaryText(counts, rows, total, installing, hasError, lang) {
4416
5191
  const l = langOf(lang);
@@ -4486,7 +5261,7 @@ ${body}`;
4486
5261
  const skipped = ctx.skipped;
4487
5262
  const queued = isQueuedRow(row);
4488
5263
  const position = queued ? queuePositionOf(row) : null;
4489
- const versionHTML = batchRowVersionHTML(row, ctx.knowledge);
5264
+ const versionHTML = batchRowVersionHTML(row, ctx.knowledge, ctx.lang);
4490
5265
  const rowStatusText = queued ? batchQueuedStatus(position, ctx.lang) : ctx.worldLatest !== null ? batchText("batch.row.update", ctx.lang, { version: ctx.worldLatest }) : ctx.inRound ? batchRowKnowledgeText(row, ctx.knowledge, ctx.lang) ?? batchRowStatus(row, skipped, ctx.lang) : batchRowPendingStatus(row, ctx.knowledge, ctx.lang) ?? batchRowStatus(row, skipped, ctx.lang);
4491
5266
  const actions = [];
4492
5267
  const off = ctx.inFlight ? " disabled" : "";
@@ -4500,7 +5275,7 @@ ${body}`;
4500
5275
  actions.push("<button type=\"button\" data-act=\"toggle-details\" data-key=\"" + keyAttr + "\">" + escapeHtml$1(ctx.expanded ? batchText("batch.row-action.hide-detail", ctx.lang) : batchText("batch.row-action.show-detail", ctx.lang)) + "</button>");
4501
5276
  const failLine = phase === "failed" && !queued ? (() => {
4502
5277
  const failCode = row.error || "install-failed";
4503
- const failDetail = failureCopy(failCode, ctx.lang)?.zh ?? failureCopy("unknown", ctx.lang)?.zh ?? "";
5278
+ const failDetail = isCheckSideFailureCode(jobFailureOf(row)?.code ?? (typeof row.error === "string" && row.error ? row.error : null)) ? batchText("batch.row.failed", ctx.lang) : failureCopy(failCode, ctx.lang)?.zh ?? failureCopy("unknown", ctx.lang)?.zh ?? "";
4504
5279
  const escaped = escapeHtml$1(batchText("batch.row.error-label", ctx.lang, {
4505
5280
  code: failCode,
4506
5281
  detail: failDetail
@@ -4509,28 +5284,7 @@ ${body}`;
4509
5284
  return "<span class=\"dsh-upd-bfail\">" + (escaped.includes(codeEsc) ? escaped.replace(codeEsc, "<code>" + codeEsc + "</code>") : escaped) + "</span>";
4510
5285
  })() : "";
4511
5286
  const main = "<button type=\"button\" class=\"dsh-upd-brow-main\" data-act=\"toggle-details\" data-key=\"" + keyAttr + "\" aria-expanded=\"" + (ctx.expanded ? "true" : "false") + "\"><span class=\"dsh-upd-updot\" data-tone=\"" + dotToneOf(row, phase, skipped, queued, ctx.worldLatest) + "\"></span><span class=\"dsh-upd-bname\">" + escapeHtml$1(titleOf(row, ctx.titles)) + "</span>" + versionHTML + "<span class=\"dsh-upd-bstat\">" + escapeHtml$1(rowStatusText) + (ctx.inRound && !queued ? "(" + escapeHtml$1(batchText("batch.row.unfinished-tag", ctx.lang)) + ")" : "") + "</span>" + failLine + "</button>";
4512
- const detail = ctx.expanded ? "<div class=\"dsh-upd-bdetail\" data-key=\"" + keyAttr + "\">" + detailActionsHTML(row, ctx) + (ctx.notice !== null ? "<div class=\"dsh-upd-bdetail-notice\" role=\"status\">" + escapeHtml$1(ctx.notice) + "</div>" : "") + detailHTML(row, ctx) + "</div>" : "";
4513
- return "<div class=\"dsh-upd-brow-set\" data-key=\"" + keyAttr + "\"><div class=\"dsh-upd-brow\" data-phase=\"" + escapeHtml$1(phase) + "\" data-key=\"" + keyAttr + "\"" + (queued ? " data-queued=\"1\"" : "") + ">" + main + "<span class=\"dsh-upd-brow-actions\">" + actions.join("") + "</span></div>" + detail + "</div>";
4514
- }
4515
- function detailHTML(row, ctx) {
4516
- return stripActionButtons(renderUpdatePanelHTML({
4517
- snapshot: asSnapshotLike(row.snapshot),
4518
- manual: typeof row.manual === "string" ? row.manual : null,
4519
- changelogMarkdown: typeof ctx.changelogMarkdown === "string" ? ctx.changelogMarkdown : null,
4520
- queue: asQueueLike(row.queue),
4521
- skippedLatest: ctx.skipped !== null,
4522
- lastError: row.error,
4523
- errorKind: row.error,
4524
- mode: "embedded",
4525
- showOthers: false,
4526
- pluginId: pluginIdOf(row),
4527
- copyNotice: null,
4528
- theme: ctx.theme,
4529
- themeTokens: ctx.themeTokens,
4530
- profileName: typeof row.profileName === "string" && row.profileName ? row.profileName : null,
4531
- hostKind: typeof row.hostKind === "string" && row.hostKind ? row.hostKind : null,
4532
- actions: "none"
4533
- }, ctx.lang));
5287
+ return "<div class=\"dsh-upd-brow-set\" data-key=\"" + keyAttr + "\"><div class=\"dsh-upd-brow\" data-phase=\"" + escapeHtml$1(phase) + "\" data-key=\"" + keyAttr + "\"" + (queued ? " data-queued=\"1\"" : "") + ">" + main + "<span class=\"dsh-upd-brow-actions\">" + actions.join("") + "</span></div></div>";
4534
5288
  }
4535
5289
  function detailActionsHTML(row, ctx) {
4536
5290
  const phase = asBatchPhase(row.phase);
@@ -4554,6 +5308,37 @@ ${body}`;
4554
5308
  function stripActionButtons(html) {
4555
5309
  return html.replace(/<button\b[^>]*data-action=[^>]*>[\s\S]*?<\/button>/g, "");
4556
5310
  }
5311
+ function detailFooterHTML(row, ctx) {
5312
+ const keyAttr = escapeHtml$1(row.key);
5313
+ return "<div class=\"dsh-upd-footer\"><span class=\"dsh-upd-foot-note\">" + escapeHtml$1(batchText("panel.footer.note", ctx.lang)) + "</span><button type=\"button\" data-act=\"detail-close\" data-key=\"" + keyAttr + "\" title=\"" + escapeHtml$1(batchText("panel.footer.close-title", ctx.lang)) + "\">" + escapeHtml$1(batchText("panel.footer.close", ctx.lang)) + "</button></div>";
5314
+ }
5315
+ function detailDialogHTML(row, ctx) {
5316
+ const keyAttr = escapeHtml$1(row.key);
5317
+ const withoutStyle = stripActionButtons(renderUpdatePanelHTML({
5318
+ snapshot: asSnapshotLike(row.snapshot),
5319
+ manual: typeof row.manual === "string" ? row.manual : null,
5320
+ changelogMarkdown: typeof ctx.changelogMarkdown === "string" ? ctx.changelogMarkdown : null,
5321
+ queue: asQueueLike(row.queue),
5322
+ skippedLatest: ctx.skipped !== null,
5323
+ lastError: row.error,
5324
+ errorKind: row.error,
5325
+ mode: "dialog",
5326
+ showOthers: false,
5327
+ pluginId: pluginIdOf(row),
5328
+ copyNotice: null,
5329
+ theme: ctx.theme,
5330
+ themeTokens: ctx.themeTokens,
5331
+ profileName: typeof row.profileName === "string" && row.profileName ? row.profileName : null,
5332
+ hostKind: typeof row.hostKind === "string" && row.hostKind ? row.hostKind : null,
5333
+ actions: "none"
5334
+ }, ctx.lang)).replace(/^<style>[\s\S]*?<\/style>\s*/, "");
5335
+ const bar = "<div class=\"dsh-upd-bdetail-dialog-bar\" data-detail-bar=\"" + keyAttr + "\">" + detailActionsHTML(row, ctx) + "</div>" + (ctx.notice !== null ? "<div class=\"dsh-upd-bdetail-notice\" role=\"status\">" + escapeHtml$1(ctx.notice) + "</div>" : "");
5336
+ const withBar = withoutStyle.replace("<div class=\"dsh-upd-overlay\"", "<div class=\"dsh-upd-overlay dsh-upd-bdetail-dialog\" data-detail-key=\"" + keyAttr + "\"").replace("<div class=\"dsh-upd-body\">", bar + "<div class=\"dsh-upd-body\">");
5337
+ const footer = detailFooterHTML(row, ctx);
5338
+ const at = withBar.lastIndexOf("</div></div>");
5339
+ if (at < 0) return withBar + footer;
5340
+ return withBar.slice(0, at) + footer + withBar.slice(at);
5341
+ }
4557
5342
  function dotToneOf(row, phase, skipped, queued, worldLatest = null) {
4558
5343
  if (phase === "failed" && !queued) return "bad";
4559
5344
  if (phase === "installing" || phase === "checking") return "busy";
@@ -4612,12 +5397,10 @@ ${body}`;
4612
5397
  ".dsh-upd-bdetail-actions{margin:0 0 6px}",
4613
5398
  ".dsh-upd .dsh-upd-bdetail-actions button{font-size:12.5px}",
4614
5399
  ".dsh-upd-bdetail-notice{margin:0 0 6px;font-size:var(--dsh-update-panel-small-font-size,12.5px);opacity:.85}",
4615
- "/* 详情复用单插件内核:不许嵌套滚动——中性化 overlay 的 max-height/overflow,自身也不给 overflow。 */",
4616
- ".dsh-upd-bdetail{margin:2px 0 10px;overflow:visible}",
4617
- ".dsh-upd .dsh-upd-bdetail .dsh-upd{max-width:none}",
4618
- ".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd{max-height:none;overflow:visible}",
4619
- ".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd-body{overflow:visible}",
4620
- ".dsh-upd-overlay .dsh-upd-bdetail .dsh-upd-body .dsh-upd-chap-head{position:static}",
5400
+ "/* Detail dialog (#108): reuse kernel dialog scroll (body scroll + sticky heads); batch actions as fixed bar; legacy inline overflow:visible retired with inline removal. */",
5401
+ ".dsh-upd-overlay.dsh-upd-bdetail-dialog{z-index:10000}",
5402
+ ".dsh-upd-bdetail-dialog-bar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;padding:8px 0;margin-bottom:8px;border-bottom:1px dashed var(--dsh-update-border,#e5e7eb)}",
5403
+ ".dsh-upd-bdetail-dialog-bar .dsh-upd-bdetail-actions{margin:0}",
4621
5404
  "@media (prefers-color-scheme: dark){.dsh-upd-batch-title i{color:var(--dsh-update-focus,#93c5fd)}}",
4622
5405
  "@media (prefers-reduced-motion: reduce){.dsh-upd-batch *{transition:none !important;animation:none !important}}",
4623
5406
  ".dsh-upd-batch{overflow-anchor:none}",
@@ -4653,6 +5436,7 @@ ${body}`;
4653
5436
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-brow-actions button{font-size:12px;line-height:1.45;padding:4px 10px;margin:0;min-width:0;background:transparent}",
4654
5437
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-bdetail-actions button{font-size:12px;line-height:1.45;padding:4px 10px;margin:0;min-width:0;background:transparent}",
4655
5438
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-batch-more button{font-size:12px;line-height:1.45;padding:4px 10px;margin:0;min-width:0;background:transparent}",
5439
+ ".dsh-upd[data-theme=\"archive\"] .dsh-upd-batch-prefs button{font-size:13px;line-height:1.45;padding:6px 12px;margin:0;min-width:0;background:transparent}",
4656
5440
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-banner>.dsh-upd-browline{align-self:stretch;display:flex;align-items:center;gap:10px;flex-wrap:wrap}",
4657
5441
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-banner>.dsh-upd-browline .dsh-upd-blist{flex:1 1 auto;min-width:220px;text-align:left;margin:0;color:var(--dsh-update-text-muted)}",
4658
5442
  ".dsh-upd[data-theme=\"archive\"] .dsh-upd-banner>.dsh-upd-browline button{flex:none;margin:0;margin-left:auto;font-size:13px;line-height:1.45;padding:6px 12px;min-width:0;background:transparent}"
@@ -4748,6 +5532,7 @@ ${body}`;
4748
5532
  function currentVersionOf(row) {
4749
5533
  const snapshot = row.snapshot;
4750
5534
  if (!isObject$1(snapshot)) return null;
5535
+ if (snapshot["installed"] === false) return null;
4751
5536
  const running = snapshot["runningVersion"];
4752
5537
  if (typeof running === "string" && running) return running;
4753
5538
  const installed = snapshot["installedVersion"];
@@ -5397,6 +6182,11 @@ ${body}`;
5397
6182
  render();
5398
6183
  maybeAutoRowChangelog(expandedKey);
5399
6184
  return;
6185
+ case "detail-close":
6186
+ if (expandedKey === null) return;
6187
+ expandedKey = null;
6188
+ render();
6189
+ return;
5400
6190
  case "restart":
5401
6191
  try {
5402
6192
  if (typeof onRestartRequested === "function") {
@@ -5424,7 +6214,13 @@ ${body}`;
5424
6214
  function onKeyDown(ev) {
5425
6215
  try {
5426
6216
  const e = ev;
5427
- if (!mounted || mode !== "dialog" || !e || e.key !== "Escape") return;
6217
+ if (!mounted || !e || e.key !== "Escape") return;
6218
+ if (expandedKey !== null) {
6219
+ expandedKey = null;
6220
+ render();
6221
+ return;
6222
+ }
6223
+ if (mode !== "dialog") return;
5428
6224
  requestDialogClose();
5429
6225
  } catch {}
5430
6226
  }
@@ -5559,7 +6355,7 @@ ${body}`;
5559
6355
  };
5560
6356
  }
5561
6357
  //#endregion
5562
- //#region ../../node_modules/.pnpm/dsh-plugin-update@0.13.0/node_modules/dsh-plugin-update/dist/entry-batch.js
6358
+ //#region ../../node_modules/.pnpm/dsh-plugin-update@0.17.1/node_modules/dsh-plugin-update/dist/entry-batch.js
5563
6359
  function isObject(value) {
5564
6360
  return !!value && typeof value === "object" && !Array.isArray(value);
5565
6361
  }
@@ -8458,7 +9254,7 @@ ${body}`;
8458
9254
  }, react.createElement("span", { style: S.morePkg }, row.pkg), react.createElement("span", { style: S.moreDesc }, row.desc), react.createElement("span", { style: S.moreIcon }, react.createElement(ExternalIcon, null)))));
8459
9255
  }
8460
9256
  /** 版本胶囊那一行字:只在这里拼一次,屏上与用例读的是同一串。 */
8461
- const MANAGER_VERSION_TEXT = "dsh-life-pack · 0.3.54";
9257
+ const MANAGER_VERSION_TEXT = "dsh-life-pack · 0.3.56";
8462
9258
  /** 右下角那本书离面板两沿的距离:用户口径「一直处于面板右下角、不随列表滑动消失」
8463
9259
  * ⇒ sticky 吸附(面板相对,视口多大都落在面板角上);INSET=右沿,BOTTOM=底沿。 */
8464
9260
  const MANUAL_ENTRY_INSET = 16;