Maintain an Elegant Markdown and Extended Markdown Render Pipeline
Blueprint: How Lossless Renders Markdown and Extended Markdown
1. Content Locations and Sources
- Root content monorepo
- Primary authoring may or may not happen on
- a directory OUTSIDE the current Astro project, thus needing custom routing.
- a directory INSIDE the current Astro project, but not the src/content file.
- a git submodule for easier collaborative editing of content.
- SOME COMBINATION OF THE ABOVE.
-
- Astro content collections wiring
- Once defined in
site/src/content.config.ts. - Uses a helper
resolveContentPath(relativePath)that:- Joins
contentBasePath(from env) with the relative path. - Converts the absolute path into a
file://URL for Astro’sglobloader.
-
- Collections are defined in the loosest way possible, where almost all metadata is optional and the object is a passthrough. This is because there are inconsistent behaviors in maintaining consistent YAML frontmatter, but we don't want to site builds to fail.
-
paths.blueprintsis mapped toresolveContentPath('lost-in-public/blueprints'), so this blueprint is rendered through the same pipeline.
-
- Generated markdown content
- Some markdown lives under
site/src/generated-content/**(e.g. generated essays, prompts, blueprints). -
resolveContentPathis aware of this: if a path already starts with./src/generated-content, it is left as-is and not re-resolved.
-
- MDX pages
- A separate
pagesCollectionis defined formdx-pages, but this blueprint focuses on the markdown → MDAST →AstroMarkdownpipeline. We have not successfully managed to implement MDX pages but only because we have been focused on Extended Markdown with adding various syntax and mapping it to a new component.
-
2. Astro Global Markdown Configuration
- Location :
site/astro.config.mjs→markdownblock. - Remark layer (global)
-
syntaxHighlight: falseto disable Astro’s built-in Shiki; highlighting is handled separately. -
remarkPlugins: []– all remark processing for articles has been moved to layout-level processing (primarilyOneArticle.astro).
-
- Rehype layer (global)
-
rehypeRaw(must come first):- Allows raw HTML inside markdown (
node.type === "html"), which is later rendered byAstroMarkdownor passed through.
-
-
rehypeAutolinkHeadings:- Appends
#anchor links to headings with CSS classes likeheader-anchor/header-anchor-symbol.
-
-
rehypeMermaid:- Handles mermaid diagrams with an
img-svgstrategy and dark mode.
-
-
- Takeaway
- Global markdown config handles HTML safety , heading anchors , and mermaid .
- All semantic markdown and extended-markdown behavior is defined in the layout + component pipeline below.
-
3. Primary Article Rendering Pipeline
3.1 Overview
The main “Lossless article” path for markdown is:
- Content collections (from
content.config.ts) provide a markdown string (body) and frontmatter (data). - A page/route uses
OneArticle.astroas its layout, passingcontentanddata. -
OneArticle.astrousesunified+ remark plugins to produce a transformed MDAST . -
OneArticleOnPage.astroreceives the transformed MDAST and frontmatter, splits out the table of contents, and passes the rest toAstroMarkdown.astro. -
AstroMarkdown.astrorecursively renders the MDAST nodes into HTML and Astro components, including all extended markdown features.
3.2 OneArticle.astro – Layout-Level Markdown Processing
- Location :
site/src/layouts/OneArticle.astro. - Inputs (props):
-
Component: usuallyOneArticleOnPage.astroor compatible article component. -
title: article heading. -
data: frontmatter / content metadata. -
content: raw markdown string. -
markdownFile?: optional path for debugging.
-
- Unified pipeline
- Builds a remark processor:
-
remarkParse– parse markdown into MDAST. -
remarkGfm– GitHub-flavored markdown support (tables, task lists, etc.). -
remarkBacklinks– custom plugin to handle Obsidian-style backlinks and internal links. -
remarkImages– custom plugin for image normalization and path handling. -
remarkDirective– parses directive syntax, creatingleafDirective,containerDirective,textDirectivenodes. -
remarkDirectiveToComponent– Lossless plugin that:- Validates directive names.
- Preserves directive nodes in the MDAST for downstream handling (no immediate HTML transform).
-
-
remarkCitations– citations and references processing. -
remarkTableOfContents– injects atableOfContentsnode into the MDAST.
-
- Directive node types (how remarkDirective shapes the AST)
-
textDirective- Inline, used inside paragraphs or headings.
- Example:
This is a :badge[New] inline directive. - Becomes a
textDirectivenode withname,attributes, and inlinechildren.
-
-
leafDirective- Block-level, no nested markdown content (self-contained block).
- Typical for single-line component-style directives.
- Example:
::figma-embed{src="https://www.figma.com/..." width="800"}. - Becomes a
leafDirectivenode withnameandattributes, usually an emptychildrenarray.
-
-
containerDirective- Block-level, with nested markdown children (lists, paragraphs, etc.).
- Used for directives that wrap other markdown, like tool galleries or slides.
- Example:markdown
:::tool-showcase - [[Tooling/AI-Toolkit/Tool Name|Display Name]] - [[vertical-toolkits/Category/Another Tool|Another Tool]] ::: - Becomes a
containerDirectivenode whosechildrencontain lists / listItems / paragraphs.
-
In the Lossless pipeline,remarkDirectiveandremarkDirectiveToComponentare responsible for creating and preserving these directive nodes in the MDAST.AstroMarkdown.astrothen inspectsnode.type(textDirective,leafDirective,containerDirective) andnode.name(e.g.figma-embed,tool-showcase,tooling-gallery,image-gallery,slides) to decide which Astro component to render and how to interpretattributesandchildren. -
- Flow:
-
mdast = processor.parse(content) -
transformedMdast = await processor.run(mdast)
-
-
- Output
- Passes
transformedMdastand normalizeddatainto theComponent(usuallyOneArticleOnPage).
-
3.3 OneArticleOnPage.astro – Layout + TOC + Info Sidebar
- Location :
site/src/components/articles/OneArticleOnPage.astro. - Responsibilities
- Layout for a single article including:
- Main content (
AstroMarkdown). - Optional
InfoSidebar(metadata, tags, authors, dates, semantic version, augmented_with, etc.). - Table of contents (desktop and mobile).
-
- Calculates
effectiveHeadingfromarticleHeadingordata.titleand displays it as the main rendered<h1>with aCopyLinkButton. - Normalizes
databefore passing it down:- Ensures
authorsis an array. - Formats dates.
- Prepares
dataForMarkdownpassed intoAstroMarkdown, optionally strippingtitleto avoid duplicate titles.
-
-
- TOC management
- Accepts
content: Root(MDAST root) fromOneArticle.astro. - Computes:
-
safeContent: always a validrootwithchildren. -
children:safeContent.children(defensive check). -
hProperties: fromsafeContent.data.hProperties || {}.
-
- Derives
tocNodeby searching forchild.type === 'tableOfContents'. - Passes
tocNode.data.maptoTableOfContents.astro. - For main content:
- Calls
AstroMarkdownwith a syntheticroot:-
type: 'root'. -
children: children.filter(child => child?.type !== 'tableOfContents'). -
data: { hProperties }.
-
-
-
3.4 AstroMarkdown.astro – Core Extended Markdown Renderer
- Location :
site/src/components/markdown/AstroMarkdown.astro. - Inputs (props):
-
node: any MDAST node (root, heading, paragraph, link, directive, etc.). -
data: includespath,id, and propagated frontmatter fields.
-
- Core behaviors
- Maintains an explicit list
handled_typesincluding:- Standard nodes:
root,paragraph,text,heading,image,list,listItem,code,inlineCode,table*,strong,emphasis,break,html, etc. - Extended nodes:
citation,citations,citationReference,footnote*,tableOfContents,imageGallery,toolingGallery,thematicBreak. - Directive nodes:
leafDirective,containerDirective,textDirective.
-
- Uses recursive self-calls (
<Astro.self node={child} data={data} />) to walk the MDAST tree. - Enriches
data.dirpath = dirname(data.path)for image/gallery helpers.
-
- Major node handling (high level)
-
root: renders all children via recursion. -
heading: usesextractAllText+slugifyto produce stableids for headings and wraps them withCopyLinkButton. -
list/listItem: renders ordered/unordered lists with a.custom-liclass and nested spacing rules. -
table,tableRow,tableCell: renders semantic tables with a scrollable wrapper. -
link:- Detects YouTube videos / playlists / Shorts by URL patterns and renders:
-
YouTubeEmbed,YouTubePlaylistEmbed, orYouTubeShortsEmbed.
-
- Falls back to a normal
<a>withhPropertiesfor standard links.
-
-
code:- Handles legacy extended syntaxes using code block languages/meta:
-
toolingGalleryandyaml toolingGallery→ rendersToolingGalleryand shows a deprecation warning , encouraging:::tooling-gallerydirectives instead. -
imageGalleryandyaml imageGallery→ rendersImageGallerywith a deprecation warning , encouraging:::image-gallerydirectives. -
slides→ parses an inline config + backlink list and rendersSlidesEmbed.
-
- For all other languages:
- Delegates to
BaseCodeblockusinggetLanguageRoutingStrategyandisSpecialRendererLanguagefromshikiHighlighter.
-
-
-
inlineCode: styles inline code tokens. -
html: injects raw HTML viaset:html, relying onrehypeRawfromastro.config.mjs. -
blockquote: usesArticleCalloutfor styled callouts. -
citations/citation: usesArticleCitationsBlockandArticleCitationcomponents.
-
- Directive handling (extended markdown)There are two primary patterns for directives in the Lossless pipeline:
- Remark-time mapping and preservation (in
remark-directives.ts):- Directives are validated and preserved as directive nodes.
-
remarkDirectiveToComponentleaves them forAstroMarkdownto inspect.
-
- Render-time interpretation in
AstroMarkdown:-
AstroMarkdowninterpretsleafDirectiveandcontainerDirectivenodes and renders the appropriate components.
-
Key directive types handled inAstroMarkdown:-
figma-embed(leaf and container):- Uses props like
src/url,width,height, andauth-user. - Renders a Figma embed iframe with a footer link.
- Full architecture and Figma-specific behavior are documented in the existing blueprint “Maintain Directives in Extended Markdown Render Pipeline” .
-
-
tool-showcase(container):- Parses markdown list items inside the directive.
- Extracts backlink patterns
[[path/to/tool|Display Name]]using helper functions. - Resolves tools via
getCollection('tooling')andToolShowcaseIsland.astro. - Renders an interactive tool carousel.
-
-
tooling-gallery(container):- New directive-based replacement for the YAML
toolingGallerycode block. - Parses list items for backlinks and tag filters.
- Uses
getCollection('tooling')andresolveToolIdfromtoolUtils. - Renders
ToolingGallery.astrowith an optionalsmallvariant and tag filters.
-
-
portfolio-gallery(container):- Similar to
tooling-gallery, but forclient-portfolioscollection. - Parses list items for backlinks and
tag:filters. - Uses
getCollection('client-portfolios')andresolvePortfolioId. - Renders
PortfolioGallery.astrowith normalized portfolio data.
-
-
image-gallery(container):- Parses list items for links or plain text URLs.
- Builds a mini YAML-like code string and passes it to
ImageGallery.astro. - New directive-based replacement for
imageGalleryYAML code blocks.
-
-
slides(planned + partially implemented):- Under the directory & directive blueprints, a
slidesdirective is planned to use the same slide-selection model as theslidescode block andSlidesEmbed.astro. -
SlidesEmbed.astrobuilds an embed URL that targets the/slides/embed/[...slug].astroroute, which renders Reveal.js-based markdown decks.
-
All directive behavior is designed to be lossless across the pipeline:- Micromark (via
remark-parse) parses the directive syntax. -
remark-directiveconverts it into MDAST directive nodes. -
remarkDirectiveToComponentvalidates and preserves the nodes. -
AstroMarkdowninspects directive names and attributes, and renders appropriate components.
-
4. Simple Markdown Renderer (Non-Article Use)
- Location :
site/src/utils/simpleMarkdownRenderer.ts. - Purpose
- Provide a simple way to render markdown to HTML + plain text, primarily for:
- Previews.
- Email bodies.
- Tooling that does not use the full article layout.
-
-
- Pipeline
- Uses the same remark stack as
OneArticle.astro:-
remarkParse,remarkGfm,remarkBacklinks,remarkImages,remarkDirective,remarkDirectiveToComponent,remarkCitations,remarkTableOfContents.
-
- Then:
-
remarkRehype→rehypeStringify.
-
- Returns:
-
html– full rendered HTML string. -
plainText– string with all HTML tags stripped.
-
-
- Important note
- The simple renderer does not run through
AstroMarkdown.astro, so directive nodes will not become rich components here; they will be treated as transformed HTML according to how the plugins behave. - For full extended markdown behavior (directives, galleries, Figma, etc.), use the OneArticle + AstroMarkdown pipeline.
-
5. Debugging and Introspection
- Markdown debugger
-
site/src/utils/markdown/markdownDebugger.tsexposes amarkdownDebuggersingleton used by layout-level code. - Controlled via environment variables:
-
DEBUG_MARKDOWN– enable/disable logging. -
DEBUG_MARKDOWN_VERBOSE– enable detailed logs. -
DEBUG_AST– allow writing debug files viaastDebugger.
-
- Also supports URL query parameters:
-
?debug-markdownand?debug-markdown-verbose.
-
-
- DebugMarkdown component
-
OneArticle.astrocan renderDebugMarkdown.astrowhenmarkdownFileis provided. - Shows raw markdown and debugging aids for a specific source file.
-
- Recommended debugging flow
- Enable
DEBUG_MARKDOWNand optionallyDEBUG_AST. - Inspect
transformedMdaststructure to confirm:- Directives are present as
leafDirective/containerDirective. -
tableOfContentsnode is correctly injected. - Custom nodes like
citations,imageGallery, etc. are present.
-
-
6. How to Safely Extend the Pipeline
When adding new extended markdown features:
- 1. Start at the directive & AST layer
- Decide on a directive syntax (leaf vs container) or a code block language.
- Add validator / preservation logic in
remark-directives.tsif needed.
-
- 2. Add render-time handling in AstroMarkdown
- Extend the
handled_typeslist if introducing a new node type. - In
AstroMarkdown.astro, add a new branch for your directive:- Parse attributes / inner markdown content.
- Render an Astro component or island.
-
-
- 3. Keep layout responsibilities separate
-
OneArticle.astroremains the place for defining remark pipeline order . -
OneArticleOnPage.astromust continue to:- Extract
tableOfContentsinto a separate component. - Pass a clean
rootnode intoAstroMarkdown.
-
-
- 4. Respect content locations
- Add new collections in
src/content.config.tsusingresolveContentPath. - Keep authoring under the root
/contentdirectory unless there is a clear reason to usesite/src/generated-content.
-
- 5. Document changes
- For any substantial new extended markdown feature, create:
- A blueprint under
content/lost-in-public/blueprints. - Optionally, a spec under
content/specsfor deeper implementation details.
-
-
This blueprint ties together the Lossless site’s markdown and extended-markdown pipeline—across content locations, the Astro config, the unified/remark stack, and the
AstroMarkdown renderer—so future work can extend it without breaking existing behavior.