lpd-mdx-preview — VS Code Extension
.
Not present in production builds. Safe no-op in prod. */
body {
padding: 0 !important;
}
/* Colors Used
#3CB540 - Jade Green
#2b9a66 - Light Green
#18794E - Dark Green
Complementary Greens
See https://coolors.co/004225-1a794e-08a045-3cb540-62ba4f
#004225 - Deep Forrest
#1A794E - Turf Green
#08A045 - Medium Jungle
#3CB540 - Jade Green
#6BBF59 - Moss Green
See https://coolors.co/0c0c0c-073b3a-1a794e-08a045-6bbf59
#0C0C0C - Onyx Black
#073B3A - Dark Teal
#1A794E - Turf Green
#08A045 - Medium Jungle
#6BBF59 - Moss Green
See https://coolors.co/fffffa-073b3a-1a794e-08a045-6bbf59
#FFFFFA - Porcelain
#073B3A - Dark Teal
#1A794E - Turf Green
#08A045 - Medium Jungle
#6BBF59 - Moss Green
Pink Offset Colour
See https://coolors.co/073b3a-1a794e-f61067-08a045-6bbf59
#F61067 - Razzmatazz Pink
#073B3A - Dark Teal
#1A794E - Turf Green
#08A045 - Medium Jungle
#6BBF59 - Moss Green
*/
/* ============================================
GLOBAL THEME VARIABLES
Component governance source of truth
============================================ */
:root {
--lp-color-accent: #3cb540;
--lp-color-accent-strong: #18794e;
--lp-color-accent-soft: #6bbf59;
--lp-color-accent-bright: #5dd662;
--lp-color-accent-brightest: #a0f0a5;
--lp-color-arbitrum: #3ea6f8;
--lp-color-text-primary: #181c18;
--lp-color-text-secondary: #717571;
--lp-color-text-muted: #9ca3af;
--lp-color-bg-page: #ffffff;
--lp-color-bg-card: #f9fafb;
--lp-color-bg-elevated: #f3f6f4;
--lp-color-bg-subtle: rgba(24, 28, 24, 0.04);
--lp-color-bg-overlay: rgba(12, 12, 12, 0.5);
--lp-color-border-default: #e5e7eb;
--lp-color-border-strong: rgba(24, 28, 24, 0.18);
--lp-color-border-inverse: rgba(255, 255, 255, 0.5);
--lp-color-on-accent: #ffffff;
--lp-color-link: #18794e;
--lp-color-link-hover: #004225;
--lp-color-brand-discord: #5865f2;
--lp-color-brand-forum: #00aeef;
--lp-color-brand-github: #181c18;
--lp-color-brand-x: #181c18;
--lp-color-brand-globe: #00c0ff;
--lp-color-brand-twitch: #9048ff;
--lp-color-brand-youtube: #ff0034;
--lp-color-brand-instagram: #dc2275;
--lp-color-brand-linkedin: #0189df;
--lp-color-brand-preview: #b636dd;
--lp-color-brand-coming-soon: #ef1a73;
--lp-color-brand-linux: #ff9a0e;
--lp-color-brand-windows: #14bbf7;
--lp-color-brand-macos: #60ba47;
--lp-color-status-good: #22c55e;
--lp-color-status-warn: #fbbf24;
--lp-color-status-bad: #ef4444;
--lp-spacing-1: 0.25rem;
--lp-spacing-2: 0.5rem;
--lp-spacing-3: 0.75rem;
--lp-spacing-4: 1rem;
--lp-spacing-6: 1.5rem;
--lp-spacing-8: 2rem;
--lp-spacing-px-3: 3px;
--lp-spacing-px-4: 4px;
--lp-spacing-px-6: 6px;
--lp-spacing-px-8: 8px;
--lp-spacing-px-12: 12px;
--lp-font-sans: 'Inter', 'Segoe UI', sans-serif;
--lp-font-mono: 'SFMono-Regular', 'SF Mono', 'Menlo', monospace;
--lp-radius-sm: 0.25rem;
--lp-radius-md: 0.5rem;
--lp-radius-lg: 0.75rem;
--lp-shadow-card: 0 8px 24px rgba(24, 28, 24, 0.08);
--lp-z-base: 1;
--lp-z-overlay: 10;
--lp-z-modal: 50;
/* Legacy aliases maintained during migration */
--accent: var(--lp-color-accent);
--accent-dark: var(--lp-color-accent-strong);
--hero-text: var(--lp-color-text-primary);
--text: var(--lp-color-text-secondary);
--text-secondary: var(--lp-color-text-secondary);
--muted-text: var(--lp-color-text-muted);
--background: var(--lp-color-bg-page);
--card-background: var(--lp-color-bg-card);
--background-highlight: var(--lp-color-bg-subtle);
--border: var(--lp-color-border-default);
--button-text: var(--lp-color-on-accent);
--page-header-description-color: var(--lp-color-text-secondary);
--arbitrum: var(--lp-color-arbitrum);
}
.dark {
--lp-color-accent: #2b9a66;
--lp-color-accent-strong: #18794e;
--lp-color-accent-soft: #3cb540;
--lp-color-accent-bright: #5dd662;
--lp-color-accent-brightest: #7fe584;
--lp-color-text-primary: #e0e4e0;
--lp-color-text-secondary: #a0a4a0;
--lp-color-text-muted: #6b7280;
--lp-color-bg-page: #0d0d0d;
--lp-color-bg-card: #1a1a1a;
--lp-color-bg-elevated: #141a16;
--lp-color-bg-subtle: rgba(255, 255, 255, 0.1);
--lp-color-bg-overlay: rgba(0, 0, 0, 0.5);
--lp-color-border-default: #333333;
--lp-color-border-strong: rgba(255, 255, 255, 0.3);
--lp-color-border-inverse: rgba(255, 255, 255, 0.5);
--lp-color-on-accent: #ffffff;
--lp-color-link: #5dd662;
--lp-color-link-hover: #a0f0a5;
--lp-color-brand-github: #f0f0f0;
/* Legacy aliases maintained during migration */
--accent: var(--lp-color-accent);
--accent-dark: var(--lp-color-accent-strong);
--hero-text: var(--lp-color-text-primary);
--text: var(--lp-color-text-secondary);
--text-secondary: var(--lp-color-text-secondary);
--muted-text: var(--lp-color-text-muted);
--background: var(--lp-color-bg-page);
--card-background: var(--lp-color-bg-card);
--background-highlight: var(--lp-color-bg-subtle);
--border: var(--lp-color-border-default);
--button-text: var(--lp-color-on-accent);
--page-header-description-color: var(--lp-color-text-secondary);
--arbitrum: var(--lp-color-arbitrum);
}
/* ============================================ */
/* Code block themes
hiki codeblock themes:
Popular Dark Themes:
github-dark (what you have now)
github-dark-dimmed
github-dark-high-contrast
dracula
dracula-soft
monokai
nord
one-dark-pro
poimandres
rose-pine
everforest-dark
vitesse-dark
Popular Light Themes:
github-light (what you have now)
github-light-high-contrast
solarized-light
rose-pine-dawn
everforest-light
vitesse-light */
/* img[alt="dark logo"],
img[alt="light logo"] {
max-width: 180px;
} */
/* V2 TEST */
/* a.nav-tabs-item[href="/pages/resources/resources_hub.mdx"],
a.nav-tabs-item[href="/pages/08_help/README"] {
color: rgba(255, 90, 90, 0.342) !important;
} */
/* Make the nav-tabs container full width */
.nav-tabs {
width: 100%;
justify-content: flex-start;
}
/* Fix Mintlify content width and centering.
Regular pages: balance padding + widen inner cap.
Portal/frame pages: balance padding (smaller) + widen inner cap for full-width hero. */
@media (min-width: 1024px) {
/* Regular pages */
#content-container:not(:has(.frame-mode-hero-full)):not(
:has(.frame-mode-container)
) {
padding-left: 3rem !important;
padding-right: 3rem !important;
}
#content-container:not(:has(.frame-mode-hero-full)):not(
:has(.frame-mode-container)
)
> .max-w-5xl {
max-width: 72rem !important;
}
/* Portal/frame pages — tighter balanced padding, wider inner cap */
#content-container:has(.frame-mode-hero-full),
#content-container:has(.frame-mode-container) {
padding-left: 2rem !important;
padding-right: 2rem !important;
}
#content-container:has(.frame-mode-hero-full) > .max-w-5xl,
#content-container:has(.frame-mode-container) > .max-w-5xl {
max-width: 80rem !important;
}
}
#navbar > div.z-10.mx-auto.relative > div.hidden.lg\:flex.px-12.h-12 > div {
column-gap: 2rem !important;
}
a.nav-tabs-item[href*='/internal/'] {
margin-left: 1rem;
margin-right: -1rem;
padding-right: 0;
border-bottom-color: transparent !important;
}
/* .gap-x-6 {
column-gap: 2rem !important;
} */
/* .nav-tabs h-full flex text-sm gap-x-6 {
column-gap: 2rem !important;
} */
/* Push Resource HUB to the right and style as outlined button */
a.nav-tabs-item[href$='/resources/redirect'],
a.nav-tabs-item[href$='/resources/portal'],
a.nav-tabs-item[href$='/07_resources/redirect'],
a.nav-tabs-item[href$='/07_resources/portal'] {
margin-left: auto;
background-color: transparent;
border: 1px solid var(--accent) !important;
padding: 4px 8px;
border-radius: 4px;
font-size: 0.7rem;
height: auto !important;
align-self: center;
margin-right: -2rem;
}
/* Color the text */
/* a.nav-tabs-item[href="/v2/resources/resources_hub"] {
color: #2b9a66 !important;
} */
/* Shrink & color the icon */
a.nav-tabs-item[href$='/resources/redirect'] svg,
a.nav-tabs-item[href$='/resources/portal'] svg,
a.nav-tabs-item[href$='/07_resources/redirect'] svg,
a.nav-tabs-item[href$='/07_resources/portal'] svg,
a.nav-tabs-item[href$='/07_resources/resources_hub'] svg {
height: 0.75rem;
width: 0.75rem;
/* background-color: #2b9a66 !important; */
}
/* Hide the underline on the button */
a.nav-tabs-item[href$='/resources/redirect'] > div:last-child,
a.nav-tabs-item[href$='/resources/portal'] > div:last-child,
a.nav-tabs-item[href$='/07_resources/redirect'] > div:last-child,
a.nav-tabs-item[href$='/07_resources/portal'] > div:last-child,
a.nav-tabs-item[href$='/07_resources/resources_hub'] > div:last-child {
display: none;
}
/* Stack footer links vertically */
#footer .flex-col .flex.gap-4 {
flex-direction: column !important;
gap: 0rem !important;
}
/* Reduce footer padding */
#footer > div {
padding-top: 2rem !important;
padding-bottom: 1rem !important;
}
/* Accessibility: prevent hidden assistant sheet from receiving focus */
#chat-assistant-sheet[aria-hidden='true'] {
display: none !important;
}
/* Accessibility: ensure CTA buttons meet minimum target size */
button.text-left.text-gray-600.text-sm.font-medium {
min-height: 24px;
padding-top: 4px;
padding-bottom: 4px;
}
/* #footer > div > div:first-child {
display: flex;
flex-direction: row !important;
color: red !important;
}
#footer > div > div:first-child > div {
display: flex;
flex-direction: row !important;
color: green !important;
} */
/* Fix bad styling of cards with arrows */
[data-component-part='card-content-container'] {
padding-right: 2.5rem; /* Creates space for the arrow */
}
/* Reposition View component dropdown */
/*
To find the correct selector:
1. Open your page with View components in the browser
2. Right-click on the dropdown in the top-right corner
3. Select "Inspect Element"
4. Find the class name or data attribute
5. Replace the selector below with the actual one
*/
/* Common possible selectors - uncomment and adjust the one that works */
/* Option 1: If it has a data attribute */
/* [data-view-dropdown] {
position: relative !important;
top: 60px !important;
right: 20px !important;
} */
/* Option 2: If it's in a fixed container */
/* .fixed [class*="view"] {
position: relative !important;
top: 60px !important;
} */
/* Option 3: Target by position (fixed elements in top-right) */
/* .fixed.top-0.right-0 [class*="select"],
.fixed.top-0.right-0 [class*="dropdown"] {
position: relative !important;
top: 60px !important;
margin-right: 20px !important;
} */
/* Option 4: Move it inline with content instead of fixed position */
/* Replace 'ACTUAL_SELECTOR' with the real class name from browser inspection */
/* ACTUAL_SELECTOR {
position: static !important;
display: inline-block !important;
margin-bottom: 20px !important;
} */
.code-block > div > div > svg {
background-color: #18794e !important;
}
/* Error 404 Styling */
#error-description > span > div > div {
border: 1px solid #18794e !important;
}
body
> div.relative.antialiased.text-gray-500.dark\:text-gray-400
> div.peer-\[\.is-not-custom\]\:lg\:flex.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:\!hidden.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:sm\:\!hidden.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:md\:\!hidden.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:lg\:\!hidden.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:xl\:\!hidden
> div.flex.flex-col.items-center.justify-center.w-full.max-w-lg.overflow-x-hidden.mx-auto.py-48.px-5.text-center.\*\:text-center.gap-y-8.not-found-container
> div {
margin-top: -5rem;
}
#error-description
> span
> div
> div
> div.relative.rounded-xl.overflow-hidden.flex.justify-center
> img {
width: 500px;
aspect-ratio: 4 / 3;
object-fit: cover;
/* border: 1px solid #fff; */
}
/* Step List Color Icons Styling */
/* #content > div.steps > div > div.absolute.ml-\[-13px\].py-2 > div {
background-color: #18794e !important;
} */
/* Step List Color Titles */
#content
> div.steps.ml-3\.5.mt-10.mb-6
> div
> div.w-full.overflow-hidden.pl-8.pr-px
> p {
color: #2b9a66 !important;
}
/* View Dropdown */
/* #radix-_R_5slubt9fen9fdb_ */
/* Turn off bg-white in dark mode for multi-view dropdown (PALM THEME BUG) */
.dark .bg-white\/\[0\.95\].multi-view-dropdown-trigger {
background-color: transparent !important;
background: none !important;
}
/* Sidebar collapse button - bigger and easier to click */
/* #sidebar button.absolute {
min-width: 2.5rem !important;
min-height: 2.5rem !important;
padding: 0.75rem !important;
z-index: 100 !important;
} */
/* Override US flag with UK flag in language selector */
/* Hide the original img and use background-image instead */
/* #localization-select-trigger img[alt="US"],
#localization-select-item-en img[alt="US"],
img[alt="US"][src*="flags/US.svg"] {
opacity: 0 !important;
position: relative !important;
}
#localization-select-trigger img[alt="US"]::before,
#localization-select-item-en img[alt="US"]::before,
img[alt="US"][src*="flags/US.svg"]::before {
content: "" !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background-image: url("/snippets/assets/media/images/site/united-kingdom-flag-icon.svg") !important;
background-size: cover !important;
background-position: center !important;
border-radius: 50% !important;
opacity: 1 !important;
} */
/* Hide the panel on frame mode pages (MINTLIFY SUCKS) */
/* Hide empty table of contents layout only when it's empty */
#table-of-contents-layout:empty,
#content-side-layout:has(#table-of-contents-layout:empty) {
display: none;
}
/* DynamicTable: force fixed layout so columnWidths prop values take effect.
Mintlify's Tailwind prose resets table-layout to auto — !important required. */
[data-docs-dynamic-table] {
table-layout: fixed !important;
}
/* StyledTable should sit flush inside its own border shell.
Mint wraps rendered tables in a scroll container with vertical padding,
which creates a false gap above/below the header row. */
[data-docs-styled-table-shell] > div {
padding-top: 0 !important;
padding-bottom: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* BorderedBox should own its internal spacing.
Trim default block margins on the first/last rendered child so headings
and paragraphs do not add a false gap inside the padded shell. */
[data-docs-bordered-box] > :first-child {
margin-top: 0 !important;
}
[data-docs-bordered-box] > :last-child {
margin-bottom: 0 !important;
}
[data-docs-bordered-box][data-accent-bar]::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 4px;
background-color: var(--accent-bar-color);
border-radius: inherit;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
/* Frame mode container - 80% of #content-container width, centered */
/* Breaks out of #content padding to center in full #content-container */
.frame-mode-container {
width: calc(100% + 96px + 20px); /* 976px */
margin-left: -96px;
margin-right: -20px;
margin-bottom: 2rem;
padding-left: 15%; /* Adjust this for desired content width */
padding-right: 15%; /* Adjust this for desired content width */
box-sizing: border-box;
}
/* Frame mode container inside hero - already broken out, so reset */
.frame-mode-hero-full .frame-mode-container {
width: 100%;
margin-left: 0;
margin-right: 0;
padding-left: 0%;
padding-right: 0%;
}
/* Pagination on frame mode pages ONLY - match container padding */
[data-page-mode='frame'] #pagination {
width: calc(100% + 96px + 20px);
margin-left: -96px;
margin-right: -20px;
padding-left: calc((100% + 96px + 20px) * 0.1 + 96px);
padding-right: calc((100% + 96px + 20px) * 0.1 + 20px);
box-sizing: border-box;
}
/* Hero full width - breaks out of #content padding to fill #content-container */
.frame-mode-hero-full {
width: calc(100% + 96px + 20px);
margin-left: -96px;
margin-right: -20px;
position: relative;
}
@media (max-width: 1023px) {
.frame-mode-container {
width: 100%;
margin-left: 0;
margin-right: 0;
padding-left: 1rem;
padding-right: 1rem;
}
[data-page-mode='frame'] #pagination {
width: 100%;
margin-left: 0;
margin-right: 0;
padding-left: 1rem;
padding-right: 1rem;
}
.frame-mode-hero-full {
width: 100%;
margin-left: 0;
margin-right: 0;
}
}
#starfield {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
/* Target the card content container */
.frame-mode-hero-full [data-component-part='card-content-container'] {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-left: 1rem;
padding-right: 2.5rem; /* Space for arrow icon (0.75rem right + icon width ~1rem + margin) */
}
/* Target the arrow icon */
.frame-mode-hero-full #card-link-arrow-icon {
top: 0.75rem;
right: 0.75rem;
}
/* #content > div.frame-mode-hero-full > div.frame-mode-container > div > div:nth-child(2) > div > div > div:nth-child(4) > a > div {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
#content > div.frame-mode-hero-full > div.frame-mode-container > div > div:nth-child(2) > div > div > div:nth-child(4) > a > div > #card-link-arrow-icon {
top: 0.75rem;
right: 0.75rem;
} */
/* ============================================
ACCESSIBILITY — Focus indicators
============================================ */
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible,
a:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid var(--accent) !important;
outline-offset: 2px;
}
/* ============================================
ACCESSIBILITY — Responsive breakpoints
============================================ */
@media (max-width: 767px) {
.frame-mode-hero-full {
width: 100%;
max-width: 100%;
overflow-x: hidden;
}
}
@media (max-width: 480px) {
#content {
padding-left: 1rem;
padding-right: 1rem;
}
}
/* ============================================
UTILITY CLASSES — inline element styling
Used where components can't replace inline spans
(e.g., inside Mintlify
, components)
============================================ */
.lp-inline-flex {
display: flex;
align-items: center;
}
.lp-text-muted {
color: var(--lp-color-text-secondary);
}
.lp-text-italic-muted {
font-style: italic;
color: var(--lp-color-text-secondary);
}
.lp-inline-flex-gap {
display: flex;
align-items: center;
gap: 0.2rem;
}
.lp-link-underline {
border-bottom: 1.5px solid var(--lp-color-text-primary);
color: var(--lp-color-text-primary);
padding-bottom: 0.25rem;
}
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
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):
bash tools/editor-extensions/install.sh
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:
cd tools/editor-extensions/lpd-mdx-preview
npx @vscode/vsce package --no-dependencies -o lpd-mdx-preview-0.0.2.vsix
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: "livepeer.mdxPreview.theme" : "dark"
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
Note / Tip / Warning / Info
Purpose: Callout boxes for supplementary information.< Note > Standard info callout. </ Note >
< Tip > Helpful suggestion. </ Tip >
< Warning > Something to watch out for. </ Warning >
< Info > Neutral informational note. </ Info >
Prop Type Default Description (none) — — Content passed as children
Purpose: Generic callout with a custom Font Awesome icon.< Callout icon = "rocket" > Custom callout with icon. </ Callout >
Prop Type Default Description iconstring infoFont Awesome icon name (e.g. rocket, star)
Purpose: Linked or unlinked content card with optional icon and arrow.< Card title = "Gateway Setup" icon = "server" href = "/v2/gateways/setup" arrow >
Run your first gateway in minutes.
</ Card >
< Card title = "Horizontal" icon = "code" href = "/v2/developers" arrow horizontal >
Displayed inline.
</ Card >
Prop Type Default Description titlestring — Card heading iconstring — Font Awesome icon name hrefstring — Link target; wraps card in <a> arrowboolean falseAppends → to title horizontalboolean falseInline flex layout
Purpose: Responsive grid wrapper for <Card> components.< CardGroup cols = { 2 } >
< Card title = "One" />
< Card title = "Two" />
</ CardGroup >
Prop Type Default Description cols1 | 2 | 3 | 4 2Number of grid columns
Purpose: Interactive tabbed content sections.< Tabs >
< Tab title = "On-chain" icon = "link" > Content for tab one. </ Tab >
< Tab title = "Off-chain" icon = "cloud" > Content for tab two. </ Tab >
</ Tabs >
Tab props: Prop Type Default Description titlestring TabTab label iconstring — Font Awesome icon name
Accordion / AccordionGroup
Purpose: Collapsible content sections. Rendered as <details>/<summary> in the preview.< AccordionGroup >
< Accordion title = "From a Cloud Background?" icon = "cloud" >
Content here.
</ Accordion >
</ AccordionGroup >
Accordion props: Prop Type Default Description titlestring DetailsSummary/header text iconstring — Font Awesome icon name
Purpose: Numbered step-by-step walkthrough.< Steps >
< Step title = "Install dependencies" > Run ` npm install ` . </ Step >
< Step title = "Configure" > Set your environment variables. </ Step >
</ Steps >
Step props: Prop Type Default Description titlestring — Step heading
Purpose: Bordered container for isolating content visually. No props — content as children.< Frame >
< img src = "/path/to/image.png" />
</ Frame >
Purpose: Two-column layout grid. No props.< Columns >
< div > Left </ div >
< div > Right </ div >
</ Columns >
Purpose: Show/hide toggle for secondary content.< Expandable title = "Advanced options" > Hidden until expanded. </ Expandable >
Prop Type Default Description titlestring Show moreToggle label
Purpose: Code block container. Rendered as plain <pre><code> in the preview without syntax highlighting.< CodeBlock language = "bash" > livepeer -gateway </ CodeBlock >
Prop Type Default Description languagestring — Language identifier (e.g. bash, json)
Purpose: Inline Font Awesome icon.Prop Type Default Description iconstring — Font Awesome icon name namestring — Alias for icon
Purpose: Changelog or versioned annotation with a label.< Update label = "Q4 2025" > Off-chain gateway mode shipped. </ Update >
Prop Type Default Description labelstring — Date or version label datestring — Alias for label
Purpose: API response field documentation row.< ResponseField name = "id" type = "string" >
Unique identifier for the job.
</ ResponseField >
Prop Type Default Description namestring — Field name (rendered in accent colour) typestring — Data type label
Purpose: API parameter documentation row.< ParamField path = "streamId" type = "string" required >
Stream identifier passed in the URL path.
</ ParamField >
Prop Type Default Description pathstring — Path parameter name querystring — Query parameter name bodystring — Request-body field name headerstring — Header name typestring — Data type label requiredboolean falseAdds a required marker
Purpose: High-severity callout variant.< Danger > Do not expose this secret in client-side code. </ Danger >
Purpose: Inline pill label with accent styling.< Badge color = "var(--accent)" > Preview </ Badge >
Prop Type Default Description colorstring var(--accent)Accent colour used for border, text, and background tint textstring — Fallback text when no children are passed
Purpose: Group multiple code blocks in one bordered container.< CodeGroup >
< CodeBlock language = "bash" > npm install </ CodeBlock >
< CodeBlock language = "bash" > npm run dev </ CodeBlock >
</ CodeGroup >
Tree / Tree.Folder / Tree.File
Purpose: Repo-style file tree rendering using canonical Mintlify syntax.< Tree >
< Tree.Folder name = "src" defaultOpen >
< Tree.File name = "index.js" />
</ Tree.Folder >
</ Tree >
Tree.Folder props: Prop Type Default Description namestring folderFolder label defaultOpenboolean falseExpands the folder by default
Tree.File props: Prop Type Default Description namestring fileFile label
Purpose: API reference embed. The preview renders a descriptive placeholder rather than the full interactive Mintlify API experience.< OpenAPI method = "get" path = "/asset/{id}" />
Prop Type Default Description methodstring — HTTP method label pathstring — API path shown in the placeholder openapistring — Alias for path
StyledTable / TableRow / TableCell
Purpose: Styled HTML table with header and body rows.< StyledTable >
< TableRow header >
< TableCell header > Column A </ TableCell >
< TableCell header > Column B </ TableCell >
</ TableRow >
< TableRow >
< TableCell > Value </ TableCell >
< TableCell > Value </ TableCell >
</ TableRow >
</ StyledTable >
TableRow props: Prop Type Default Description headerboolean falseRenders row with header background
TableCell props: Prop Type Default Description headerboolean falseRenders as <th> with bold weight
Tier 2 — Livepeer Custom Components
Spacing
Purpose: Horizontal rule with optional centred label text.Location: snippets/components/elements/spacing/Divider.jsx< CustomDivider />
< CustomDivider middleText = "Role Evolution" />
Prop Type Default Description middleTextstring — Text centred in the divider textstring — Alias for middleText
Purpose: Explicit vertical whitespace block.Prop Type Default Description heightstring 24pxCSS height value sizestring — Alias for height
Purpose: Plain <hr> divider. No props.
Containers
Purpose: Constrains content width and centres it horizontally.Location: snippets/components/wrappers/containers/Containers.jsx< CenteredContainer maxWidth = "960px" >
Content here.
</ CenteredContainer >
Prop Type Default Description maxWidthstring 100%CSS max-width value
Purpose: Card-like container with a visible border.< BorderedBox borderColor = "#2d9a67" padding = "24px" >
Content here.
</ BorderedBox >
Prop Type Default Description borderColorstring var(--border)CSS border colour backgroundColorstring var(--card-bg)CSS background colour paddingstring 16pxCSS padding
Purpose: Forces content to full available width. No props.< FullWidthContainer > Content here. </ FullWidthContainer >
Purpose: Flexbox layout wrapper.< FlexContainer direction = "row" gap = "16px" align = "center" >
< div > Item A </ div >
< div > Item B </ div >
</ FlexContainer >
Prop Type Default Description directionstring — CSS flex-direction (row, column) gapstring 12pxCSS gap alignstring — CSS align-items justifystring — CSS justify-content wrapboolean falseEnables flex-wrap: wrap
Purpose: CSS Grid layout wrapper.< GridContainer columns = "repeat(3, 1fr)" gap = "16px" >
< div > Cell 1 </ div >
< div > Cell 2 </ div >
< div > Cell 3 </ div >
</ GridContainer >
Prop Type Default Description columnsstring — CSS grid-template-columns value gapstring 12pxCSS gap
Diagrams
Links
Purpose: Inline text link with → suffix.Location: snippets/components/elements/links/Links.jsx< LinkArrow href = "/v2/gateways/concepts/capabilities" label = "Gateway Capabilities" newline = { false } />
Prop Type Default Description hrefstring #Link target labelstring — Link text textstring — Alias for label newlineboolean trueIf false, renders inline (no line break)
Purpose: Arrow-prefixed inline link.< GotoLink href = "/v2/gateways" > Go to Gateways </ GotoLink >
Prop Type Default Description hrefstring #Link target labelstring — Override link text (otherwise uses children)
Purpose: Card that links to a destination.< GotoCard href = "/v2/gateways/setup" > Setup content here. </ GotoCard >
Prop Type Default Description hrefstring #Link target
Text & Headings
Purpose: Styled italic accent text. Used below page titles. No props.< Subtitle > Understand how gateways route workloads. </ Subtitle >
Purpose: Blockquote with Livepeer accent border. FrameQuote adds an outer border frame. No props.< Quote > Gateways are the demand side of the network. </ Quote >
< FrameQuote > Framed and quoted. </ FrameQuote >
H1 / H2 / H3 / H4 / H5 / H6
Purpose: Heading components with optional Font Awesome icon prefix. Used on FrameMode pages.< H2 icon = "server" > Gateway Setup </ H2 >
Prop Type Default Description iconstring — Font Awesome icon name shown before heading text
Purpose: Explicit paragraph wrapper. Used on FrameMode pages. No props.< P > Body text content here. </ P >
Purpose: Inline code-styled text value.< CopyText text = "livepeer -gateway -orchAddr 0x..." />
Prop Type Default Description textstring — Text to display as inline code valuestring — Alias for text
CardTitleTextWithArrow / AccordionTitleWithArrow
Purpose: Composable title elements with → suffix. No props — content as children.< CardTitleTextWithArrow > Quick Start </ CardTitleTextWithArrow >
< AccordionTitleWithArrow > Show Details </ AccordionTitleWithArrow >
Cards
Purpose: Standalone content card with title and icon.< DisplayCard title = "Run a Gateway" icon = "server" >
Step-by-step instructions.
</ DisplayCard >
Prop Type Default Description titlestring — Card heading iconstring — Font Awesome icon name
Purpose: Card constrained to a max width and centred.< WidthCard width = "600px" > Constrained content. </ WidthCard >
Prop Type Default Description widthstring 100%CSS max-width maxWidthstring — Alias for width
Purpose: Card with an image on the left and content on the right.< InlineImageCard src = "/snippets/assets/logos/Livepeer-Logo-Symbol.svg" >
Description text here.
</ InlineImageCard >
Prop Type Default Description srcstring — Image source URL
Purpose: Card with a heading and freeform children content.< InteractiveCard title = "Configuration" > Content here. </ InteractiveCard >
Prop Type Default Description titlestring — Card heading
Purpose: Horizontally scrollable row of cards. No props.< CardCarousel >
< Card title = "A" />
< Card title = "B" />
</ CardCarousel >
Callout Banners
Purpose: Styled callout with a custom icon (emoji or character).< CustomCallout icon = "🔧" > Manual configuration required. </ CustomCallout >
Prop Type Default Description iconstring ℹ️Icon character or emoji
Purpose: Tip callout with a 💡 icon. No props — content as children.< TipWithArrow > Use the remote signer to skip ETH setup. </ TipWithArrow >
ComingSoonCallout / PreviewCallout / ReviewCallout
Purpose: Status banners indicating a feature is pending, in preview, or under review. No props.< ComingSoonCallout > Dashboard analytics </ ComingSoonCallout >
< PreviewCallout > Python SDK support </ PreviewCallout >
< ReviewCallout > Pricing model — subject to change </ ReviewCallout >
Purpose: Image with optional caption.< Image src = "/snippets/assets/diagrams/gateway-flow.png" alt = "Gateway flow" caption = "Traffic routing through the gateway layer" />
Prop Type Default Description srcstring — Image URL altstring — Alt text captionstring — Caption displayed below image
Purpose: Clickable image that links to a URL.< LinkImage src = "/snippets/assets/logos/Livepeer-Logo-Symbol.svg" href = "https://livepeer.org" alt = "Livepeer" />
Prop Type Default Description srcstring — Image URL hrefstring #Link target altstring — Alt text
Purpose: Embedded YouTube player (16:9 aspect ratio).< YouTubeVideo id = "dQw4w9WgXcQ" />
Prop Type Default Description idstring — YouTube video ID videoIdstring — Alias for id
Purpose: Video or media block with a heading label.< TitledVideo title = "Gateway Demo" >
< video src = "/assets/demo.mp4" controls />
</ TitledVideo >
Prop Type Default Description titlestring — Heading displayed above the media
Steps
Purpose: Custom step list with numbered indicators and a connecting line.< StyledSteps >
< StyledStep title = "Configure your node" > Edit the config file. </ StyledStep >
< StyledStep title = "Start the service" > Run ` systemctl start livepeer ` . </ StyledStep >
</ StyledSteps >
StyledStep props: Prop Type Default Description titlestring — Step heading
Purpose: Alias for StyledSteps. Same rendering, same props.< ListSteps >
< StyledStep title = "Step one" > Content </ StyledStep >
</ ListSteps >
Grids & Layouts
Purpose: Two-column grid (2×n). No props.< QuadGrid >
< Card title = "A" />
< Card title = "B" />
</ QuadGrid >
AccordionLayout / AccordionGroupList
Purpose: Flex column wrappers for custom accordion arrangements. No props.< AccordionLayout >
< Accordion title = "Item" > Content </ Accordion >
</ AccordionLayout >
Tables
Purpose: Scrollable table wrapper. No props — pass raw HTML table elements as children.< DynamicTable >
< tr >< th > Column </ th ></ tr >
</ DynamicTable >
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.< SearchTable searchPlaceholder = "Search terms…" categoryLabel = "All categories" >
< tr >< th > Term </ th >< th > Definition </ th ></ tr >
< tr >< td > Example </ td >< td > Description </ td ></ tr >
</ SearchTable >
Prop Type Default Purpose searchPlaceholderstring Search…Placeholder text in the search input categoryLabelstring All categoriesLabel for the category dropdown searchColumnsarray — Columns to include in search (passed to SearchTable.jsx)
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
HeroSectionContainer / HeroContentContainer / PortalHeroContent / PortalContentContainer
Purpose: Structural wrappers for full-width portal and hero layouts on FrameMode pages. No props on any of these.< HeroSectionContainer >
< HeroContentContainer >
< PortalHeroContent > Hero copy here. </ PortalHeroContent >
</ HeroContentContainer >
</ HeroSectionContainer >
Icons & Indicators
Purpose: Inline Livepeer brand indicator badge. No props.
Purpose: Animated icon badge (animation runs in Mintlify; static badge in preview).< BlinkingIcon icon = "terminal" />
Prop Type Default Description iconstring terminalFont Awesome icon name
Embeds & Social
Purpose: Bordered container for embedded markdown content. No props.< MarkdownEmbed > Embedded content here. </ MarkdownEmbed >
Purpose: Framed container for external or imported content with a title header.< ExternalContent title = "API Response" > Response body here. </ ExternalContent >
Prop Type Default Description titlestring External ContentHeader label
Purpose: Social media link row. Fully interactive in Mintlify; renders as static note in preview. No props.
Math
Purpose: Mathematical expression display. Rendered as styled <code> in preview (not LaTeX-rendered).< MathInline expression = "E = mc^2" />
< MathBlock expression = "\sum_{i=1}^{n} x_i" />
Prop Type Default Description expressionstring — Mathematical expression string mathstring — Alias for expression
Tier 3 — Placeholder Rendering
Any component not in Tier 1 or Tier 2 renders as:
┌ - - - - - - - - - - - - - - ┐
<ComponentName prop="value">
[children rendered here]
└ - - - - - - - - - - - - - - ┘
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
Setting Type Default Options Description livepeer.mdxPreview.themestring autoauto | light | darkPreview colour theme. auto matches VS Code.
Architecture
extension.js — Entry point
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.
lib/mdx-parser.js — Segment parser
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.
lib/mintlify-components.js — Tier 1 renderers
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.
lib/component-map.js — Tier 2 renderers + dispatch
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().
lib/webview-template.js — HTML shell
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.
media/preview.js — Client-side logic
Runs inside the webview. Initialises Mermaid.js with Livepeer theme vars, processes .lpd-md-raw blocks with markdown-it, and handles tab switching interactions.
media/preview.css — Theme & component styles
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
Lazy-load Mermaid.js ✓ Implemented
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.
Promote Tier 3 components on demand
Syntax highlighting for code blocks
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.
Custom component hot-reload ✓ Implemented
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.
Collapsible JSX comment blocks ✓ Implemented (v0.0.2)
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 April 8, 2026