Skip to content

fix: MantisHub page rendering — wiki-links, callouts, lists, embeds, and CSS - #118

Merged
vboctor merged 15 commits into
mainfrom
mantishub-page-render-fixes
Apr 27, 2026
Merged

vboctor merged 15 commits into
mainfrom
mantishub-page-render-fixes

Conversation

@vboctor

@vboctor vboctor commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Fixes a batch of MantisHub page-rendering bugs reported across HTML and markdown views. Each commit addresses one issue.

Closes

Test plan

  • bun run ci (markdown lint, typecheck, all bun + vitest test suites, UI build, worker build)
  • 37 mantishub theme tests cover each fix (image, nested lists, hr, callouts, video embeds, etc.)
  • 33 UI tests cover the markdown viewer's callout / video / wikilink rendering

🤖 Generated with Claude Code

vboctor and others added 15 commits April 26, 2026 22:14
Closes #103. Previously, [[page-name]] / [[/project/page-name]] links
whose targets did not exist in the current collection were emitted as
the literal text "[[page-name]]" in HTML output. Now they render as
<span class="mt-page-link mt-page-missing"> styled with a dimmed,
dashed underline so authors can spot missing references at a glance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #104. When the page content begins with a leading "# Heading"
matching the stored title, strip it from the body and use it as the
rendered page heading in both the HTML view (a single <h1 class="mt-title">)
and the markdown view (a single "## Title" line). This avoids the
visual duplication of two stacked headings that previously occurred
in both views.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #105. Adds an optional lookupEntityTitle resolver to
ThemeRenderContext so themes can label cross-references with the
target's display title rather than its slug. The MantisHub theme now
emits "[[page-name]]" links (in both HTML and markdown output) using
the resolved page title when one is available, falling back to the
page-name only when no title is known. The resolver is wired through
the sync engine and the clone, generate, prepare, and serve commands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #106. Replace the flat unordered-list regex with a recursive
parser that builds a nested <ul>/<li> tree based on the leading
indentation of each bullet line. Tab indents are normalized to two
spaces. The outer list keeps the mt-md-list class; nested lists are
plain <ul> elements rendered inside their parent <li>.

Also restore the default list padding inside .mt-description, which
the global "* { padding: 0 }" reset would otherwise strip — without
this, browsers render the list with no visible bullets or
indentation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #107. Recognize > [!NOTE] / [!TIP] / [!IMPORTANT] / [!WARNING] /
[!CAUTION] markers at the start of a blockquote and emit a styled
mt-md-callout div with a labelled title block instead of leaking the
raw "[!TYPE]" marker into a generic <blockquote>.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #108. Detect "- [ ]" and "- [x]" list items inside the nested-
list builder and emit a disabled <input type="checkbox"> plus
mt-md-task / mt-md-task-done classes so they render as a proper
checklist instead of leaking the literal bracket syntax.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #109. Replace separate ordered/unordered list regexes with a
single nested-list parser that handles both styles, supports
arbitrary nesting depth via indentation, and tolerates a single blank
line between sibling items so an outer numbered list is no longer
split into multiple <ol>s each restarting at 1. Numbered sub-items
now render as a nested <ol> inside their parent <li>, and a list that
does not start at 1 emits an <ol start="N"> attribute.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously "![alt](url)" leaked through as the literal "!" followed
by a plain link to the URL with the alt text as label. Now it renders
as <img class="mt-md-image"> with max-width styling so embedded
screenshots actually appear in the page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lines

Allow any characters (not just a leading space) after ">" so a stray
">." typo line in a multi-line quote no longer splits the block into
two separate <blockquote> elements. The leading "[ \t.]" run is
stripped from each line before joining.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Recognise GFM "> [!NOTE]" callouts (and friends) when the line is
indented with leading whitespace before the ">", and trim trailing
whitespace from the marker line before matching, so existing
admonitions in the corpus that happen to be formatted loosely render
as styled callouts instead of plain blockquotes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Detect markdown thematic-break syntax and emit <hr class="mt-md-hr">
with a styled border so section dividers used throughout the page
corpus render correctly instead of leaking through as literal "---".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a list item is followed by an indented non-marker line (the
"bold title then explanation paragraph" pattern), the continuation
text now becomes part of the same <li> separated by <br> instead of
falling out of the list and rendering as an unrelated text line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The global "* { margin: 0 }" reset zeroes the default <p> margins,
which made adjacent paragraphs in a page body run together with no
visual separation. Add explicit margin rules scoped to .mt-description
so HTML page bodies match the spacing seen in the markdown view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #116. Detect video-sharing URLs that appear as markdown image
syntax — "![alt](https://...)" — and render an embedded player in
both views. Loom, YouTube, and Vimeo use their public iframe-embed
URLs; Komodo (which has no public iframe-embed URL on its free tier)
renders as a clickable play-card link that opens the recording in a
new tab.

The HTML view emits the iframe / play-card directly. The markdown
view leaves the source ![alt](url) untouched on disk and detects
videos inside the React `img` component, so the same source produces
the same player in both views without injecting raw HTML into the
generated .md file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes #117. The previous admonition rendering used a thin coloured
left border with a monochrome bold title and emoji icons — visually
almost indistinguishable from a regular blockquote. Replace with a
GitHub-style design in both the HTML and markdown views: a 4px
coloured left border, a colour-mix tinted background, an inline
Octicon-style SVG icon, and a coloured bold title in the type's hue.
Each of the five GFM types (NOTE / TIP / IMPORTANT / WARNING /
CAUTION) gets its own colour and icon, and the existing Obsidian
types (info / danger / link / git) inherit sensible mappings.

Also accept the shorthand "[Note]" / "[Warning]" markers without the
GFM "!" prefix, default the title to the type label when no inline
title is supplied, and fix a regex bug where "\s*" after the marker
swallowed the trailing newline so the body text was captured as the
title.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vboctor
vboctor merged commit 5d2aefe into main Apr 27, 2026
1 check passed
@vboctor
vboctor deleted the mantishub-page-render-fixes branch April 27, 2026 05:18
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.

1 participant