From a1db08f32e3240099b99ade4763a284deb7c7f9e Mon Sep 17 00:00:00 2001 From: Tolaria Date: Wed, 3 Jun 2026 20:38:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20wiki=20links=20now=20root-relative=20?= =?UTF-8?q?=E2=80=94=20handles=20Tolaria=20[[docs/path/to/page]]=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strips docs/ prefix from Tolaria-generated links, collapses /index suffixes, removes .md extensions, and produces root-relative URLs (/page/path/) that work from any page regardless of depth. Images get root-relative asset paths. --- _extensions/wikilinks.py | 76 ++++++++++++++++++++++++++--------- docs/contributing/markdown.md | 2 +- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/_extensions/wikilinks.py b/_extensions/wikilinks.py index e3f2821..896a053 100644 --- a/_extensions/wikilinks.py +++ b/_extensions/wikilinks.py @@ -3,15 +3,19 @@ Python-Markdown extension: converts [[Wikilinks]] to standard Markdown links. Mirrors the behavior of mkdocs-ezlinks-plugin's WikiLinkScanner, but as a pure Markdown preprocessor (no MkDocs plugin API needed). Zensical handles -the resulting .md links natively. +the resulting links natively. Syntax: - [[Page Name]] → [Page Name](page-name.md) - [[Page Name|Display Text]] → [Display Text](page-name.md) - [[Page Name#anchor]] → [Page Name](page-name.md#anchor) - [[Page Name#anchor|Text]] → [Text](page-name.md#anchor) - ![[image.png]] → ![image.png](image.png) - [[folder/Page Name]] → [Page Name](folder/page-name.md) + [[Page Name]] → [Page Name](/page-name/) + [[Page Name|Display Text]] → [Display Text](/page-name/) + [[Page Name#anchor]] → [Page Name](/page-name/#anchor) + [[Page Name#anchor|Text]] → [Text](/page-name/#anchor) + ![[image.png]] → ![image.png](/image.png) + +Tolaria-compatible: when the vault root is the project root, links like +``[[docs/contributing/index]]`` are stripped of the ``docs/`` prefix and +converted to root-relative URLs (e.g. ``/contributing/``) that work from +any page regardless of depth. Code blocks (fenced and inline) are skipped — wiki links inside them pass through unchanged. @@ -44,6 +48,10 @@ _CODE_RE = re.compile( re.DOTALL, ) +# Prefix to strip from Tolaria-generated links when the vault root is +# the project root (so links look like [[docs/foo/bar]]). +_DOCS_PREFIX = "docs/" + def _slugify(text: str) -> str: """Convert a human page name into a filename slug. @@ -58,8 +66,35 @@ def _slugify(text: str) -> str: return slug +def _url_from_link(link: str) -> str: + """Convert a wiki link target to a root-relative URL. + + Handles Tolaria-style full paths: strips ``docs/`` prefix, + removes ``.md`` extension, collapses ``/index`` suffixes, + slugifies each component, and prepends ``/``. + """ + raw = link.strip() + + # Strip .md extension + raw = re.sub(r"\.md$", "", raw, flags=re.IGNORECASE) + + # Strip docs/ prefix (Tolaria vault root = project root) + if raw.lower().startswith(_DOCS_PREFIX): + raw = raw[len(_DOCS_PREFIX) :] + + # Collapse trailing /index — section index pages resolve to the dir + raw = re.sub(r"/index$", "", raw) + + if not raw: + return "/" + + # Slugify each path component + parts = [_slugify(p) for p in raw.split("/") if p] + return "/" + "/".join(parts) + "/" + + class WikilinksPreprocessor(Preprocessor): - """Preprocessor that converts [[wikilinks]] to [text](slug.md).""" + """Preprocessor that converts [[wikilinks]] to root-relative [text](/url/).""" def run(self, lines: list[str]) -> list[str]: text = "\n".join(lines) @@ -69,15 +104,12 @@ class WikilinksPreprocessor(Preprocessor): result: list[str] = [] cursor = 0 for m in _CODE_RE.finditer(text): - # Transform the non-code region before this match before = text[cursor : m.start()] if before: result.append(_WIKILINK_RE.sub(self._replace, before)) - # Pass through the code region unchanged result.append(m.group(0)) cursor = m.end() - # Transform any remaining text after the last code region tail = text[cursor:] if tail: result.append(_WIKILINK_RE.sub(self._replace, tail)) @@ -94,17 +126,25 @@ class WikilinksPreprocessor(Preprocessor): if not (link or text or anchor): return match.group(0) - slug = _slugify(link) if link else "" - href = slug + # Build the href: root-relative URL for pages; relative for images + if is_image: + # Images: keep filename, strip docs/ prefix, make root-relative + raw = link.strip() + if raw.lower().startswith(_DOCS_PREFIX): + raw = raw[len(_DOCS_PREFIX) :] + # Non-image assets might have extensions; keep them + href = raw if "." in raw else _slugify(raw) + if not href.startswith("/"): + href = "/" + href + else: + href = _url_from_link(link) + + # Append anchor if present if anchor: anchor_slug = _slugify(anchor) if anchor_slug: - href = f"{slug}#{anchor_slug}" if slug else f"#{anchor_slug}" + href = href.rstrip("/") + "#" + anchor_slug - if not href: - return match.group(0) - - # Build standard markdown link / image if is_image: return f"![{text}]({href})" else: diff --git a/docs/contributing/markdown.md b/docs/contributing/markdown.md index aa1d05d..0e89e33 100644 --- a/docs/contributing/markdown.md +++ b/docs/contributing/markdown.md @@ -5,7 +5,7 @@ icon: simple/markdown ## Introduction to Markdown Language -Markdown is a simple language to build web pages. It is easier to read and write than HTML (the language web browsers read) and so we use it to write articles. You can use this reference to help you write posts and pages directly in our repository where the source documents for this wiki are held. See [[Contributing]] for more. +Markdown is a simple language to build web pages. It is easier to read and write than HTML (the language web browsers read) and so we use it to write articles. You can use this reference to help you write posts and pages directly in our repository where the source documents for this wiki are held. See [[docs/contributing/index]] for more. ## Headers