You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Markdown renderer externalizes in-document anchors (target="_blank") and emits headings without ids, so TOC links never scroll #57971
The shared markdown base renderer wraps every link — including in-document
anchors like [Install](#install) — in <a href="…" class="external-link" target="_blank" rel="noopener noreferrer">
(packages/ui/src/context/marked-base.tsx:6-10), and headings come out of marked's
default renderer with no id attribute. Nothing in packages/session-ui or packages/app handles #… clicks or resolves heading slugs (a search for startsWith("#") / anchor scrolling in those packages only matches theme/color
code), and packages/session-ui/src/components/markdown.tsx adds no ids in
post-processing either.
Consequence: a table of contents inside rendered markdown (session timeline, and
anywhere the markdown worker's output is shown) is decorative. Clicking an entry
opens a useless new tab at the app URL plus the hash on web, and on desktop the
popup is denied or produces an empty window; even after navigating, no element
matches the hash because headings carry no ids.
Repro
In any session, render markdown containing a TOC, e.g. an assistant answer or a
displayed document starting with:
Expected: the view scrolls to the ## Install heading (the link resolves inside the
document).
Actual: a new tab/window opens at the app origin with #install appended (web) or
nothing happens (desktop); the target heading has no anchor in either case.
Root cause
packages/ui/src/context/marked-base.tsx:9 — target="_blank" rel="noopener noreferrer" is applied unconditionally, with no branch for href values that
start with #.
Marked's default heading output has no id; no slug pass exists downstream
(packages/session-ui/src/components/markdown.tsx post-processes code links and
favicons, not headings).
No click delegation for in-document anchors on any markdown surface, so there is
no fallback resolution either.
Proposed fix
In createMarkdownBase's link renderer, skip target/rel when href.trim().startsWith("#") so anchors stay in-document.
Emit GitHub-style slug ids on headings in the renderer (lowercase, drop
non-word/non-space punctuation, spaces to dashes, dedupe per document), giving
anchors a real target.
Where markdown renders inside a scroll container, delegate clicks on #… links
to scroll that container, with a slug-of-heading-text fallback for documents
rendered before ids existed.
Downstream impact
The same contract was inherited by downstream apps built around OpenCode's markdown
pipeline: e.g. omnirush-ai/omnirush-gui renders every link with target="_blank"
and no heading ids, and its file-preview surface shipped with dead TOC links until
it added local click handling plus slug lookup
(vernikr/omnirush-gui@947f078). Fixing the renderer contract here would let every
surface — and downstream ports that follow it — inherit working anchors instead of
each re-implementing click delegation.
Thanks for the detailed write-up, @vernikr-personal. You're asking for links inside a document, like a table of contents with [Install](#install), to scroll to the matching heading in rendered markdown. Right now that can't work for two reasons:
The shared renderer gives every link target="_blank" and treats it as external.
Headings are rendered without ids, so there's nothing for the link to land on.
That matches how the renderer works today: it was built to treat every link as external and was never meant to support in-document navigation. So I've filed this as a feature request rather than a bug. Your proposal (keeping # links in the page, adding slug ids to headings and scrolling the containing view when one is clicked) is a clear outline of what it would take. Maintainers will consider it.
Thanks for the fast triage — the feature-request framing is fine; the outcome (working in-document navigation) matters more than the label. A few additions that may help prioritization:
A working reference implementation exists in a shipped app. OmniRush.ai (desktop app built on OpenCode, markdown pipeline following the same contract) hit the same dead-TOC behaviour in its file-preview surface and shipped a fix in vernikr/omnirush-gui@947f078 (release v3.7.4): click delegation on the preview's scroll container plus a GitHub-style slug-of-heading-text fallback for headings without ids (~60 lines, no renderer changes). So the "scroll the containing view" half is proven in production; the renderer half (slug ids on headings + not externalizing # hrefs) is what would make anchors work everywhere, including the session timeline.
Agent-generated documents make this routine. Sessions routinely produce long markdown with a TOC (plans, proposals, READMEs); without in-document anchors every such TOC is decorative in all rendered surfaces. Same motivation as related Desktop: file:// markdown links are not clickable #44902 (file:// markdown links on Desktop).
Happy to contribute a PR for the three steps from the issue body (skip target/rel for # hrefs in createMarkdownBase, slug ids with per-document dedupe in the heading renderer, a small click-delegation helper for scroll containers) — behind a flag if a gradual rollout is preferred.
I checked #46550, #57211 and #44902: none of them covers anchor resolution or heading ids, so this can stay as the umbrella for in-document navigation.
Activity
opencode-agent commentedon Oct 22, 2026
Thanks for the detailed write-up, @vernikr-personal. You're asking for links inside a document, like a table of contents with
[Install](#install), to scroll to the matching heading in rendered markdown. Right now that can't work for two reasons:target="_blank"and treats it as external.ids, so there's nothing for the link to land on.That matches how the renderer works today: it was built to treat every link as external and was never meant to support in-document navigation. So I've filed this as a feature request rather than a bug. Your proposal (keeping
#links in the page, adding slug ids to headings and scrolling the containing view when one is clicked) is a clear outline of what it would take. Maintainers will consider it.Related (not duplicates):
target="_blank"links as externalfile://markdown links not being clickable on Desktopvernikr-personal commentedon Oct 22, 2026
Thanks for the fast triage — the feature-request framing is fine; the outcome (working in-document navigation) matters more than the label. A few additions that may help prioritization:
vernikr/omnirush-gui@947f078(release v3.7.4): click delegation on the preview's scroll container plus a GitHub-style slug-of-heading-text fallback for headings without ids (~60 lines, no renderer changes). So the "scroll the containing view" half is proven in production; the renderer half (slug ids on headings + not externalizing#hrefs) is what would make anchors work everywhere, including the session timeline.file://markdown links on Desktop).target/relfor#hrefs increateMarkdownBase, slug ids with per-document dedupe in the heading renderer, a small click-delegation helper for scroll containers) — behind a flag if a gradual rollout is preferred.I checked #46550, #57211 and #44902: none of them covers anchor resolution or heading ids, so this can stay as the umbrella for in-document navigation.