Skip to content

Yaml widget for frontmatter + Disabling KaTeX equation scrollbar until hover - #368

Open
Al3cLee wants to merge 4 commits into
brianhuster:mainfrom
Al3cLee:yaml-widget
Open

Yaml widget for frontmatter + Disabling KaTeX equation scrollbar until hover#368
Al3cLee wants to merge 4 commits into
brianhuster:mainfrom
Al3cLee:yaml-widget

Conversation

@Al3cLee

@Al3cLee Al3cLee commented May 3, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR makes two independent improvements to the markdown preview:

  1. YAML frontmatter is now rendered as a collapsible ▶ Document metadata widget instead of leaking into the document as raw <hr> and paragraph elements.
  2. KaTeX display equations no longer show a spurious, big thick scrollbar when the formula fits within the page width. The scroll bar now only shows upon mouse hover on the equation.

There is a newly introduced file markdown-it-front-matter.min.js of size 2.1 KB.


1. YAML Frontmatter Collapsible Widget

Problem

Currently, the parser markdown-it has no front-matter awareness. Its hr block rule matches --- on any line without a startLine === 0 guard, so a YAML front-matter block at the top of a file was rendered as:

<hr>
<p>title: My Document</p>
<p>author: Jane</p>
<hr>

This becomes ugly when one deals with, e.g., SKILL.md files where the yaml section is quite large.

Approach

The markdown-it-front-matter plugin (v0.2.4) is bundled as a self-contained UMD module. It inserts a new block rule before the table rule in markdown-it's block ruler chain, with an explicit startLine !== 0 guard so it fires only when --- appears at the very first character of the document. The rule produces a token with hidden: true (suppressing any HTML output) and fires a synchronous callback with the raw YAML string.

Files changed

static/markdown/markdown-it-front-matter.min.js (new)
UMD wrapper around the plugin source, exposing window.markdownitFrontMatter.

static/markdown/main.js

  • main.js:22–25: The plugin is registered with a callback that stores the captured YAML into the module-level variable _frontmatter. This variable is reset to null at the top of every livepreview_render call so stale frontmatter from a previous render cannot bleed through.

  • main.js:27–37: buildFrontmatterHtml(yamlText, open) constructs a native HTML <details>/<summary> element. The raw YAML is HTML-escaped and placed inside a fenced <pre><code class="language-yaml"> block so highlight.js applies syntax colouring automatically via the existing hljs.highlightAll() call. The open parameter sets the open attribute on <details> to restore the user's expand/collapse state across re-renders.

  • main.js:39–49: livepreview_render now reads the open property of any existing .frontmatter-block element before replacing innerHTML, then passes it to buildFrontmatterHtml. This ensures that a live edit in Neovim does not collapse the widget if the user had expanded it.

  • main.js:52: The initial textContent read is now .trim()'d before being passed to livepreview_render. The HTML template in template.lua places the markdown body after a literal \n inside the <div class="markdown-body">, so textContent always starts with a newline. The frontmatter plugin's first guard checks state.src.charCodeAt(0) === '-' (char code 45); the leading \n (char code 10) caused the plugin to silently bail out on every initial page load. Live updates via WebSocket were unaffected because Neovim's buffer content (table.concat(lines, "\n") in lua/livepreview/init.lua:64) carries no leading whitespace.

lua/livepreview/template.lua

  • template.lua:67 (the md2html script string): a <script defer> tag for markdown-it-front-matter.min.js is inserted between the KaTeX plugin and main.js. All markdown-it plugin scripts use defer, so they execute in document order after DOM parsing; placing it before main.js guarantees window.markdownitFrontMatter is defined when main.js runs.

  • template.lua:46–52: Seven CSS rules are added to the existing inline <style> block for .frontmatter-block. The / triangle is rendered via summary::before content rather than the browser's native <details> marker (which is suppressed with ::-webkit-details-marker{display:none}), giving consistent appearance across browsers. A @media(prefers-color-scheme:dark) rule adjusts the border colour from GitHub's light-mode #d0d7de to its dark-mode equivalent #30363d.

2. KaTeX Display Scrollbar Fix

Problem

The pre-existing rule in template.lua:36:

.katex-display{margin:1em 0;text-align:center;overflow-x:auto;overflow-y:hidden}

caused a scrollbar to appear on every \begin{equation} or \begin{align} environment, even when the rendered formula was narrower than the page. This is actually the default behavior that one sees at katex.org, but in a document preview, such scroll bars are too eye-catchy; they resemble the horizontal rule --- but are actually not. KaTeX uses em-based internal sizing, and sub-pixel rounding routinely makes scrollWidth 1–2px larger than clientWidth — just enough for overflow-x:auto to activate. Code blocks do not have this problem because white-space:pre gives the browser an exact content width to measure against.

Fix

template.lua:36–41: The .katex-display rule is extended and five additional rules are added, all within the existing inline <style> block:

/* keep overflow-x:auto so genuinely wide equations still scroll */
.katex-display { ... scrollbar-width:thin; scrollbar-color:transparent transparent }

/* reveal on hover — Firefox / standard API */
.katex-display:hover { scrollbar-color:rgba(0,0,0,0.2) transparent }

/* WebKit: keep a 4px track but invisible thumb */
.katex-display::-webkit-scrollbar { height:4px }
.katex-display::-webkit-scrollbar-thumb { background:transparent; border-radius:2px }
.katex-display:hover::-webkit-scrollbar-thumb { background:rgba(128,128,128,0.4) }

/* dark mode thumb colour */
@media(prefers-color-scheme:dark) { ... }

overflow-x:auto is intentionally kept so that a formula genuinely wider than the viewport is still scrollable. The scrollbar is made invisible by default (transparent thumb, zero-opacity track colour) and fades in on hover — matching the overlay-scrollbar behaviour that code blocks already get on macOS via the OS layer, and extending equivalent behaviour to Windows/Linux where scrollbars are always-visible by default.


Files changed

File Type Purpose
static/markdown/markdown-it-front-matter.min.js New Bundled UMD plugin
static/markdown/main.js Modified Plugin registration, widget builder, state persistence, textContent trim fix
lua/livepreview/template.lua Modified Script tag for plugin, frontmatter CSS, KaTeX scrollbar CSS

Al3cLee added 4 commits May 3, 2026 15:33
Use markdown-it-front-matter plugin to intercept frontmatter at parse
time instead of rendering it as <hr> + paragraph text. Wraps the raw
YAML in a <details>/<summary> block with open/closed state preserved
across live updates.
The HTML template wraps the markdown body with a leading newline, so
reading .textContent gives a string starting with \n. The frontmatter
plugin guards on charCodeAt(0) === '-', so the leading \n caused it to
silently skip the frontmatter block on every initial page load. Live
updates via WebSocket were unaffected since buffer content has no
leading whitespace.
KaTeX's em-based sizing causes sub-pixel rounding that makes scrollWidth
1-2px larger than clientWidth, triggering overflow-x:auto to show a
scrollbar even when the equation visually fits. Make the scrollbar thumb
transparent by default and reveal it on hover, matching overlay-scrollbar
behavior for both WebKit and Firefox.
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