Skip to content

fix(wiki): portable relative markdown links so wiki navigation works outside Obsidian#1465

Closed
TPAteeq wants to merge 1 commit into
Graphify-Labs:v8from
TPAteeq:fix-wiki-portable-links
Closed

fix(wiki): portable relative markdown links so wiki navigation works outside Obsidian#1465
TPAteeq wants to merge 1 commit into
Graphify-Labs:v8from
TPAteeq:fix-wiki-portable-links

Conversation

@TPAteeq

@TPAteeq TPAteeq commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Closes #1444.

graphify export wiki (to_wiki) wrote articles with Obsidian-style [[Title]]
wikilinks. Two defects made nearly every navigation link dead outside Obsidian:

  1. Convention mismatch. [[Title]] resolves by note title only inside
    Obsidian. Everywhere else — VS Code preview, GitHub, GitLab, a plain browser —
    [[Domain Data Models]] resolves to a literal Domain Data Models.md, but the
    article file is Domain_Data_Models.md (the slug replaces spaces with
    underscores and substitutes reserved characters). The reporter measured one real
    run: index + 38 articles, 206 [[...]] links, 108 of them community/god-node
    navigation links — all broken outside Obsidian.
  2. Dead links. God-node articles linked every neighbor ([[AwsHelper.py]],
    [[.read_object_key()]]), but only communities and god nodes get article files —
    so those ~98 node-level links were dead even inside Obsidian.

Fix

Emit standard relative markdown links [display](slug.md) instead of [[...]],
with the target URL-encoded via urllib.parse.quote so spaces, &, parentheses,
and # survive intact in every CommonMark renderer and in Obsidian (which resolves
standard relative links and still shows them in graph view / backlinks). Links
whose label has no article render as plain text instead of a dangling link.

To make the link target always match the file actually written, to_wiki now
assigns every article its slug in a first pass (building a label -> slug
resolver) before rendering any body — so a link tracks the on-disk filename
including any numeric collision suffix (parser_2.md). Slug-assignment order
(communities, then god nodes) is unchanged, so the case-fold collision dedup keeps
producing identical filenames.

This is the portable-links option from the issue; I left the alternative
--obsidian-style flag out to keep the change focused — standard links already
work in Obsidian, so nothing is lost there, and a flag is easy to add later if
anyone wants [[...]] back.

Testing

  • Updated the existing test_wiki.py cases that asserted the old [[...]] form to
    assert the new [display](slug.md) form.
  • New regression tests: no file contains [[...]]; every inline link target across
    the whole wiki resolves to a real file on disk; display text keeps the human
    label while the target is the underscored filename; special characters (space,
    &, #, parens) round-trip through URL-encoding to the real file; node-level
    links to article-less neighbors are plain text; and links to a collision-suffixed
    article target the suffixed slug. All red on the pre-fix code, green after.
  • Full suite: 2423 passed, 2 skipped.

Out of scope

Two pre-existing label-ambiguity edge cases (two communities with an identical
label collapse to one link target and orphan the second article; a community
literally labelled index collides with index.md) are not introduced or worsened
here — the old [[...]] form was equally ambiguous — so I left them for a separate
change to keep this PR scoped to the reported bug.

…outside Obsidian (Graphify-Labs#1444)

graphify export wiki emitted Obsidian [[Title]] wikilinks. Two defects:

1. [[Title]] resolves by note title only inside Obsidian. Everywhere else
   (VS Code preview, GitHub, GitLab, browsers) [[Domain Data Models]] points at
   a literal "Domain Data Models.md", but the file is "Domain_Data_Models.md"
   (the slug substitutes spaces/reserved chars), so nearly every navigation
   link opened an empty page.
2. God-node articles linked every neighbor ([[AwsHelper.py]], [[.method()]]),
   but only communities and god nodes get article files, so those node-level
   links were dead even inside Obsidian.

Emit standard [display](slug.md) links with the target URL-encoded (spaces, &,
parens, # survive in CommonMark renderers and Obsidian alike). Links whose
target has no article are downgraded to plain text. Each article's slug is
computed in a first pass so a link's target always matches the file actually
written, collision suffix and all.

Cosmetic, no graph/data change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
safishamsi pushed a commit that referenced this pull request Jun 27, 2026
…de Obsidian (#1444)

to_wiki emitted Obsidian `[[Title]]` wikilinks. Outside Obsidian `[[Domain Data
Models]]` resolves to a literal "Domain Data Models.md", but the article file is
the slugged "Domain_Data_Models.md", so nearly every community/god-node link
opened an empty page; god-node articles also linked node-level neighbors that
never get an article file, so those were dead even inside Obsidian.

Links are now standard `[display](slug.md)` with the target URL-encoded (spaces,
&, parentheses, # survive in CommonMark and Obsidian alike); a link whose target
has no article is rendered as plain text instead of left dangling. A label->slug
resolver is built up front so a link points at the real on-disk filename incl. the
case-fold collision suffix.

Ported from PR #1465 by @TPAteeq. Maintainer edit: trimmed the CHANGELOG wording
that overstated the guarantee ("always matches ... collision suffix and all") —
two articles sharing a byte-identical label still keep the first slug (pre-existing
behavior, same as the old [[label]]), so the claim is scoped to the case-fold case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@safishamsi

Copy link
Copy Markdown
Collaborator

Thanks @TPAteeq — this is the correct fix for #1444 (standard [display](slug.md) links, URL-encoded, resolving in every renderer). Landed on v8 in ec5bb33 with your authorship. Minor maintainer edit: trimmed the CHANGELOG wording that said the target "always" matches the written file — two articles with a byte-identical label still keep the first slug (pre-existing, same as the old [[label]]), so I scoped the claim to the case-fold collision case. Verified end-to-end: no [[..]] emitted, links URL-encode and resolve to real files. Closing as merged-by-port.

@safishamsi safishamsi closed this Jun 27, 2026
safishamsi pushed a commit that referenced this pull request Jun 27, 2026
…de Obsidian (#1444)

to_wiki emitted Obsidian `[[Title]]` wikilinks. Outside Obsidian `[[Domain Data
Models]]` resolves to a literal "Domain Data Models.md", but the article file is
the slugged "Domain_Data_Models.md", so nearly every community/god-node link
opened an empty page; god-node articles also linked node-level neighbors that
never get an article file, so those were dead even inside Obsidian.

Links are now standard `[display](slug.md)` with the target URL-encoded (spaces,
&, parentheses, # survive in CommonMark and Obsidian alike); a link whose target
has no article is rendered as plain text instead of left dangling. A label->slug
resolver is built up front so a link points at the real on-disk filename incl. the
case-fold collision suffix.

Ported from PR #1465 by @TPAteeq. Maintainer edit: trimmed the CHANGELOG wording
that overstated the guarantee ("always matches ... collision suffix and all") —
two articles sharing a byte-identical label still keep the first slug (pre-existing
behavior, same as the old [[label]]), so the claim is scoped to the case-fold case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
safishamsi added a commit that referenced this pull request Jul 7, 2026
#1712)

GRAPH_REPORT.md rendered the Community Hubs section as Obsidian wikilinks, but
the `_COMMUNITY_*.md` notes they target are only created by the opt-in
`--obsidian` export — and the report is written at build time, before any export
runs. So on a default run every link dangled: inside an Obsidian vault they
spawned phantom `_COMMUNITY_*` nodes in the graph view, and outside Obsidian they
rendered as literal brackets that navigate nowhere.

`generate()` now takes `obsidian: bool = False`; by default the hubs render as a
plain list, and the wikilink form is emitted only when a caller opts in. The
Obsidian export's own community notes already cross-link each other, so the vault
stays navigable without the report's links. Mirrors the #1444/#1465 portability
fix that was applied to `export wiki`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: graphify export wiki emits Obsidian [[wikilinks]] that break in every non-Obsidian renderer

2 participants