Skip to main content

Overview

lpd-mdx-preview is a VS Code extension that renders .mdx and .md files in a side panel with governed Mintlify built-ins, Livepeer custom component renderers, and Mermaid diagrams. It is the primary local authoring preview tool for this repo, but runtime-dependent surfaces still need verification in a Mintlify dev session. Keybinding: Cmd+Shift+V (Mac) / Ctrl+Shift+V (Windows/Linux) Location: tools/editor-extensions/lpd-mdx-preview/

Repo Context

tools/editor-extensions/lpd-mdx-preview
extension.js
package.json
README.md
lpd-mdx-preview-0.0.2.vsix

Installation

Works in VS Code, Cursor, and Windsurf (all VS Code forks). The same .vsix file installs in all three. All editors at once (recommended):
Detects which editors are installed (~/.vscode, ~/.cursor, ~/.windsurf) and deploys to each. The installer verifies that the checked-in .vsix matches source before installing. If the package is stale, the install fails and prints the exact rebuild command. Manual install: Extensions sidebar → ... menu → Install from VSIX → select tools/editor-extensions/lpd-mdx-preview/lpd-mdx-preview-0.0.2.vsix. Rebuild after source changes:
Then re-run install.sh or reinstall manually.

Usage

Open any .mdx or .md file, then press Cmd+Shift+V (Mac) or Ctrl+Shift+V (Windows/Linux).The preview opens in a side panel and updates automatically as you type (300ms debounce).
The preview detects your VS Code colour theme automatically (auto mode). You can override this in settings:
Options: auto | light | dark
Frontmatter is stripped from the preview body and rendered as a header bar showing: title, pageType, audience, status, purpose, and lastVerified.
Fenced code blocks tagged with mermaid are rendered using the bundled Mermaid.js with Livepeer theme colours. Diagrams update live as you edit.

Component Rendering Tiers

Custom components (your own project .jsx files) fall into Tier 3 — they render as labelled placeholders with their children content visible. Most Mintlify built-ins and the Livepeer component library render as styled HTML, but runtime-heavy built-ins such as OpenAPI remain approximate.

Component Reference

Tier 1 — Mintlify Built-ins

Purpose: Callout boxes for supplementary information.
Purpose: Generic callout with a custom Font Awesome icon.
Purpose: Linked or unlinked content card with optional icon and arrow.
Purpose: Responsive grid wrapper for <Card> components.
Purpose: Interactive tabbed content sections.
Tab props:
Purpose: Collapsible content sections. Rendered as <details>/<summary> in the preview.
Accordion props:
Purpose: Numbered step-by-step walkthrough.
Step props:
Purpose: Bordered container for isolating content visually. No props — content as children.
Purpose: Two-column layout grid. No props.
Purpose: Show/hide toggle for secondary content.
Purpose: Code block container. Rendered as plain <pre><code> in the preview without syntax highlighting.
Purpose: Inline Font Awesome icon.
Purpose: Changelog or versioned annotation with a label.
Purpose: API response field documentation row.
Purpose: API parameter documentation row.
Purpose: High-severity callout variant.
Purpose: Inline pill label with accent styling.
Purpose: Group multiple code blocks in one bordered container.
Purpose: Repo-style file tree rendering using canonical Mintlify syntax.
Tree.Folder props:Tree.File props:
Purpose: API reference embed. The preview renders a descriptive placeholder rather than the full interactive Mintlify API experience.
Purpose: Styled HTML table with header and body rows.
TableRow props:TableCell props:

Tier 2 — Livepeer Custom Components

Spacing

Purpose: Horizontal rule with optional centred label text.Location: snippets/components/elements/spacing/Divider.jsx
Purpose: Explicit vertical whitespace block.
Purpose: Plain <hr> divider. No props.

Containers

Purpose: Constrains content width and centres it horizontally.Location: snippets/components/wrappers/containers/Containers.jsx
Purpose: Card-like container with a visible border.
Purpose: Forces content to full available width. No props.
Purpose: Flexbox layout wrapper.
Purpose: CSS Grid layout wrapper.
Purpose: Scrollable container with a fixed maximum height.

Diagrams

Purpose: Horizontally scrollable wrapper for Mermaid or wide diagram content.Location: snippets/components/displays/diagrams/ScrollableDiagram.jsx
Purpose: Inline text link with suffix.Location: snippets/components/elements/links/Links.jsx
Purpose: Card that links to a destination.

Text & Headings

Purpose: Styled italic accent text. Used below page titles. No props.
Purpose: Blockquote with Livepeer accent border. FrameQuote adds an outer border frame. No props.
Purpose: Heading components with optional Font Awesome icon prefix. Used on FrameMode pages.
Purpose: Explicit paragraph wrapper. Used on FrameMode pages. No props.
Purpose: Inline code-styled text value.
Purpose: Composable title elements with suffix. No props — content as children.
Purpose: Centred page title and subtitle block. Used on portal/landing pages.

Cards

Purpose: Standalone content card with title and icon.
Purpose: Card constrained to a max width and centred.
Purpose: Card with an image on the left and content on the right.
Purpose: Card with a heading and freeform children content.
Purpose: Horizontally scrollable row of cards. No props.

Callout Banners

Purpose: Styled callout with a custom icon (emoji or character).
Purpose: Tip callout with a 💡 icon. No props — content as children.
Purpose: Status banners indicating a feature is pending, in preview, or under review. No props.

Media

Purpose: Image with optional caption.
Purpose: Clickable image that links to a URL.
Purpose: Embedded YouTube player (16:9 aspect ratio).
Purpose: Video or media block with a heading label.

Steps

Purpose: Custom step list with numbered indicators and a connecting line.
StyledStep props:
Purpose: Alias for StyledSteps. Same rendering, same props.

Grids & Layouts

Purpose: Two-column grid (2×n). No props.
Purpose: Flex column wrappers for custom accordion arrangements. No props.

Tables

Purpose: Scrollable table wrapper. No props — pass raw HTML table elements as children.
Purpose: Filterable table — live text search and category dropdown. Fully interactive in deployed Mintlify (uses useState). Renders as a static mock in the preview: non-functional search bar + category dropdown chrome + full table body. An ⚡ interactive — static in preview badge is shown to set expectations.
SEO note: Custom components render client-side only — table rows are not in the initial HTML. Acceptable trade-off for reference/glossary pages.

Hero & Portal Scaffolding

Purpose: Structural wrappers for full-width portal and hero layouts on FrameMode pages. No props on any of these.

Icons & Indicators

Purpose: Inline Livepeer brand indicator badge. No props.
Purpose: Animated icon badge (animation runs in Mintlify; static badge in preview).
Purpose: Inline download link styled as a button.

Embeds & Social

Purpose: Bordered container for embedded markdown content. No props.
Purpose: Framed container for external or imported content with a title header.

Math

Purpose: Mathematical expression display. Rendered as styled <code> in preview (not LaTeX-rendered).

Tier 3 — Placeholder Rendering

Any component not in Tier 1 or Tier 2 renders as:
Props are shown truncated to 40 characters. Children render inside so content remains readable. To promote a component from Tier 3 to Tier 2, add a renderer function to lib/component-map.js under the livepeerComponents object.

Interactive components

Some Tier 2 components use React hooks (useState, useEffect) and are fully interactive in deployed Mintlify but cannot run in the VS Code webview (Node.js environment, no React runtime). These render as static mocks — they show the correct UI chrome but do not respond to input. Currently affected: SearchTable, SocialLinks, MarkdownEmbed, and OpenAPI. These components display an ⚡ interactive — static in preview badge so authors know to verify their behaviour in a Mintlify dev session, not just the local preview. Data-feed integrators (ShowcaseCards, CoinGeckoExchanges, LumaEvents, DiscordAnnouncements, TwitterTimeline) fall to Tier 3 placeholders for the same reason — they fetch live data and cannot be meaningfully mocked offline.

Configuration

Architecture

Registers two commands (livepeer.openMdxPreview, livepeer.openMdxPreviewToSide), the Cmd+Shift+V keybinding, and two event listeners (document change → debounced re-render; theme change → full re-render). Manages a Map of open panels keyed by document URI.
Parses raw MDX text into a flat array of typed segments: frontmatter, import, markdown, mermaid, codeblock, jsx, jsx-expression. Regex-based extraction — not a full MDX compiler, for robustness with non-standard MDX.
Functions for the governed Mintlify built-ins used by this repo, including canonical Tree.Folder / Tree.File rendering and descriptive placeholders where full runtime parity is not practical in the preview. Each function signature: (props, childrenHtml) => htmlString.
Livepeer custom component renderers. Merges with Tier 1 into COMPONENT_MAP. Exports renderSegments() which dispatches each segment to its renderer or falls through to renderPlaceholder().
Builds the full webview HTML document including CSP headers, Font Awesome CDN (fa 6.5.1), local CSS/JS vscode-resource: URIs, and the rendered body.
Runs inside the webview. Initialises Mermaid.js with Livepeer theme vars, processes .lpd-md-raw blocks with markdown-it, and handles tab switching interactions.
CSS custom properties for light and dark themes using the governed Livepeer accent variables from style.css. Styles for all Tier 1 and Tier 2 component classes.

Future Features

renderSegments() returns { html, hasMermaid }. The webview template conditionally injects <script src="${mermaidUri}"> only when hasMermaid is true. Pages without Mermaid diagrams skip the 2.45 MB bundle entirely.
Any component that falls through to the placeholder renderer can be promoted to Tier 2 by adding a renderer function to lib/component-map.js under livepeerComponents. The function signature is (props, childrenHtml) => htmlString.Priority candidates based on usage frequency in the repo:
  • Snippet (used in composable page patterns)
  • data-feed integrators that could be represented by stable static mocks
  • additional low-use repo components that currently fall through to generic placeholders
Code blocks are currently rendered as plain <pre><code> without syntax colouring. Adding highlight.js (vendored, ~50 KB minified) as a third media asset and calling hljs.highlightAll() in preview.js would add language-aware colouring for all fenced blocks.
Sync the scroll position of the preview panel to the cursor position in the editor. VS Code’s built-in Markdown preview uses editor/scroll messages via the webview messaging API. The same pattern can be applied here: on onDidChangeTextEditorVisibleRanges, post a message to the webview to scroll to the corresponding rendered element.
onDidSaveTextDocument watches for any .jsx save in the workspace and re-renders all open preview panels. Component source changes are reflected immediately without touching the .mdx file.
Multi-line {/* ... */} comment blocks — typically template instructions or reviewer flags — are rendered as collapsed <details> elements instead of leaking into the preview as visible text. Single-line {/* ... */} comments are still silently stripped. The collapsible uses a dashed border and muted palette to distinguish it from content.Parser: lib/mdx-parser.js detects {/* without a same-line */} and collects lines until the closing */}, emitting a jsx-comment segment.Renderer: lib/component-map.js handles jsx-comment as <details class="lpd-comment"> with a 💬 comment summary and pre-formatted body.

References

Extension source

Entry point and command registration.

Component map

Tier 2 renderers and dispatch logic.

Authoring tools

Other VS Code tools in this repo.
Last modified on May 22, 2026