vitepress-allyouneed 0.3.7 → 0.3.8

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
@@ -2,6 +2,18 @@
2
2
 
3
3
  本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/);版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.3.8] - 2026-05-21
6
+
7
+ ### Fixed
8
+ - **wikilink 锚点匹配过严**:用户表格里大量写 `[[X#7.2]]` 想匹配 `## 7.2 Antike — Vorsokratiker`、`[[X#11.2 Kepler]]` 想匹配 `## 11.2 Die drei Kepler'schen Gesetze`、`[[X#4.2 Cavendish]]` 想匹配 `## 4.2 Cavendish-Experiment (8. Klasse, 1798)` 等。老 resolver 只做 exact text / slug match,以上全部归为 "unmatched-anchor"——页面能加载、锚点不跳。现在加两层 fallback:
9
+ 1. **prefix-with-boundary**:`headingPart` 是 heading text 的前缀,且下一字符是 whitespace 或字符串末尾。处理 `#7.2` → `7.2 Antike...`,且**不**误匹配 `7.21 Andere ...`(下一字符不是空白)。
10
+ 2. **token match**:`headingPart` 按空白拆 token,所有 token 都(忽略大小写)出现在 heading text 中。多个 candidate 取最短 text(最精确那条)。处理 `#11.2 Kepler` → `11.2 Die drei Kepler'schen Gesetze`(同时含 "11.2" 和 "kepler",H3 "1. Kepler'sches Gesetz" 不含 "11.2" 被排除)。
11
+
12
+ 这一改更贴近 Obsidian 用户在表格里"section-number-only"或"number + keyword"的实际书写习惯。
13
+
14
+ ### Tests
15
+ - 新增 `tests/v038-anchor-fuzzy.test.ts`,覆盖 exact / prefix / token / 误匹配防护 / unmatched fallback 等 7 个用例。
16
+
5
17
  ## [0.3.6] - 2026-05-20
6
18
 
7
19
  针对用户反馈"Perspectives 有时不加载 / sidebar 有时错"的间歇性问题做了系统排查,5 个真 bug。
package/dist/index.cjs CHANGED
@@ -683,9 +683,7 @@ function resolveWikilink(rawTarget, index, options, kind = "page", currentSource
683
683
  let url = entry.url;
684
684
  let hasUnmatchedAnchor = false;
685
685
  if (headingPart) {
686
- const heading = entry.headings.find(
687
- (h) => h.text === headingPart || h.slug === headingPart || h.slug === options.slugify(headingPart)
688
- );
686
+ const heading = matchHeading(entry, headingPart, options.slugify);
689
687
  if (heading) {
690
688
  url = entry.url + "#" + heading.slug;
691
689
  } else {
@@ -765,6 +763,29 @@ function lookupEntry(target, index, options, currentSourcePath, forcePathStyle =
765
763
  return void 0;
766
764
  }
767
765
  }
766
+ function matchHeading(entry, headingPart, slugify) {
767
+ const exact = entry.headings.find(
768
+ (h) => h.text === headingPart || h.slug === headingPart || h.slug === slugify(headingPart)
769
+ );
770
+ if (exact) return exact;
771
+ const lc = headingPart.toLowerCase();
772
+ const prefix = entry.headings.find((h) => {
773
+ const t = h.text.toLowerCase();
774
+ if (!t.startsWith(lc)) return false;
775
+ if (t.length === lc.length) return true;
776
+ return /\s/.test(h.text[lc.length]);
777
+ });
778
+ if (prefix) return prefix;
779
+ const tokens = headingPart.split(/\s+/).map((t) => t.trim()).filter(Boolean);
780
+ if (tokens.length === 0) return void 0;
781
+ const candidates = entry.headings.filter((h) => {
782
+ const lct = h.text.toLowerCase();
783
+ return tokens.every((tok) => lct.includes(tok.toLowerCase()));
784
+ });
785
+ if (candidates.length === 0) return void 0;
786
+ if (candidates.length === 1) return candidates[0];
787
+ return [...candidates].sort((a, b) => a.text.length - b.text.length)[0];
788
+ }
768
789
  function findFirstFileInFolder(folderPath, index) {
769
790
  const prefix = folderPath + "/";
770
791
  const candidates = [];