npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tobisk/markdown-manuals

v1.3.4

Published

Render a folder of Obsidian-flavored Markdown as an offline HTML manual and PDF.

Readme

Markdown Manual Renderer

@tobisk/markdown-manuals turns a numbered folder of CommonMark, GFM, and Obsidian-flavored Markdown into two outputs from one parsed document model:

  • a searchable, responsive, self-contained offline HTML manual and deterministic ZIP; and
  • a paginated PDF produced with React-PDF, including metadata, bookmarks, internal links, running headers, footers, and page numbers.

It supports headings, paragraphs, emphasis, code, links, local images, tables, nested lists, task lists, blockquotes, Obsidian callouts, [[wikilinks]], environment placeholders, and build-time Mermaid diagrams. Raw Markdown HTML is not executed.

[!IMPORTANT] This project was vibe coded to solve a particular documentation problem in ToskLight. It was built through rapid, AI-assisted iteration against that manual rather than designed from a complete general-purpose publishing specification. The repository includes automated and visual checks, but adopters should review generated documents carefully and expect to adapt hooks and configuration for their own publishing requirements.

Try the example

The repository contains a complete small manual under examples/basic. After installing dependencies, render it with:

npm run example

This writes offline HTML, a ZIP, and an A4 PDF under examples/basic/output/. The example covers folder-derived heading levels, Obsidian callouts, environment placeholders, Mermaid, tables, and a custom render hook.

CLI

npx --package @tobisk/markdown-manuals markdown-manual build \
  --source docs/help \
  --config docs/help/manual.json \
  --allowed-env-vars LIGHT_MANUAL_VERSION \
  --html-dir .artifacts/manual/html \
  --html-archive .artifacts/manual/manual-html.zip \
  --pdf .artifacts/manual/manual.pdf

The three output flags are optional overrides. Paths in the JSON config are resolved relative to the config file; CLI paths are resolved relative to the current working directory.

Configuration

{
  "title": "ToskLight Operator Manual",
  "subtitle": "Control, Architect, and Pixel",
  "author": "ToskLight contributors",
  "language": "en",
  "version": "${LIGHT_MANUAL_VERSION}",
  "description": "ToskLight operator documentation",
  "brand": { "logo": "../../assets/brand/icon.png" },
  "chapterIndexNames": ["index.md", "index.markdown"],
  "output": {
    "htmlDir": "../../.artifacts/generated/manual/html/tosklight-manual",
    "htmlArchive": "../../.artifacts/generated/manual/html/tosklight-manual-html.zip",
    "pdf": "../../.artifacts/generated/manual/pdf/tosklight-manual.pdf"
  },
  "theme": {
    "accent": "#0f8f82",
    "navigationBackground": "#071621",
    "calloutDanger": "#b42318",
    "callouts": { "warning": "#b7791f", "tip": "#16803c" }
  },
  "pdf": {
    "pageSize": "A4",
    "contentsDepth": 3,
    "header": "{title} - {chapter}",
    "footer": "ToskLight Operator Manual",
    "margins": { "top": 54, "right": 50, "bottom": 54, "left": 50 }
  },
  "mermaid": {
    "theme": "base",
    "backgroundColor": "transparent",
    "width": 1400,
    "height": 900,
    "scale": 2,
    "themeVariables": {
      "primaryColor": "#d7f3ef",
      "primaryBorderColor": "#0f8f82",
      "lineColor": "#425466",
      "edgeLabelBackground": "#ffffff"
    }
  },
  "inlineTokens": [
    { "pattern": "\\[([A-Z0-9.]+)\\]", "kind": "desk-key", "labelGroup": 1 }
  ],
  "hookModules": ["manual-hooks.mjs"],
  "layout": {
    "columns": 1,
    "columnGap": 18,
    "hierarchyHeadings": true,
    "maxHeadingDepth": 6,
    "justifyText": true
  }
}

${NAME} placeholders in both the JSON configuration and Markdown sources are read from the environment only when NAME is listed by --allowed-env-vars. The option accepts a comma-separated allowlist such as --allowed-env-vars APP_VERSION,BUILD_NUMBER. Unlisted placeholders remain literal even when the process has a matching environment variable, preventing Markdown from accidentally exposing CI secrets or other ambient values. An allowed but unset variable fails the build. Write \${NAME} when the literal placeholder syntax should be printed. Inline-token patterns are JavaScript regular-expression sources and must not match an empty string.

layout.columns accepts 1 or 2. In two-column mode, major headings, tables, figures, code, and callouts span both columns. With hierarchyHeadings enabled, a folder index keeps the folder's heading level, an ordinary file is one level deeper, and nested folders deepen the hierarchy even when they have no index. maxHeadingDepth caps the effective level.

Every top-level folder index becomes a dedicated section-divider page in PDF and a full-height divider in HTML. If a standalone image is immediately before or after that index's H1, the image stays with the centered title. With hierarchical headings enabled, effective H2 headings are rendered as labelled chapters, H3 headings as ruled sections, H4 headings in the accent color, and H5 headings as compact subheads.

Render hooks

Hook modules are resolved relative to the configuration file. They export one hook, an array of hooks, or a default export. A hook can replace any normalized Markdown node before both HTML and PDF rendering:

export default {
  name: "control-keys",
  transform(node, context) {
    if (node.type !== "inlineToken" || node.data?.kind !== "desk-key") return;
    return {
      ...node,
      data: {
        ...node.data,
        presentation: {
          component: "key-sequence",
          keys: [{ label: node.value, variant: node.value === "REC" ? "record" : "regular" }],
        },
      },
    };
  },
};

The built-in key-sequence presentation supports regular, record, clear, preload, keyboard, and shift variants. Keys accept the vector-backed shift and backspace icons. Use the command-line presentation on an inlineCode node to render a desk command-line readout. Use control-sequence with ordered { text } and { keys } segments to place padded keys inside one dark inline command strip.

Contents directives

Keep an individual heading out of generated contents by putting <!-- toc: exclude --> directly before it. Put <!-- toc: exclude-headings --> anywhere on a page to retain the page title while excluding every lower-level heading on that page. Both directives are removed from HTML and PDF.

Table directives

Configure a particular table in its Markdown source rather than coupling document layout to a render hook. Put a directive immediately before the table:

<!-- table: columns=12,15,18,55; rows-per-page=17; row-weight=1.5; continue-after-table -->
| Desk key | Button | Computer keyboard | What it does |
| --- | --- | --- | --- |

columns contains positive relative widths and must have exactly one value per table column. rows-per-page and row-weight tune unusually long PDF tables. continue-after-table permits following content to share the same generated PDF page. Unknown options, invalid numbers, column count mismatches, and directives that are not directly followed by a table fail the build.

Image directives

Size a standalone image from its Markdown source with a directive immediately before it:

<!-- image: width=72% -->
![System diagram](system.png)

Widths accept positive percentages up to 100%, CSS pixels such as 320px, or PDF points such as 240pt. Pixel widths are converted to points for PDF output. The directive is removed from both outputs and fails the build unless the next block is a standalone image. PDF pagination keeps every heading with the first meaningful block that follows it, including an image, paragraph, table, list, callout, or code block.

Mermaid diagrams

Use an ordinary fenced Mermaid block:

```mermaid
flowchart LR
  Control[ToskLight Control] -->|Art-Net or sACN| Architect[ToskLight Architect]
  Control -->|DMX and CITP/MSEX| Pixel[ToskLight Pixel]
```

The build uses Mermaid's official renderer once and embeds its scalable SVG in HTML and a high-resolution PNG in the React-PDF document. Invalid diagrams fail with the diagram number and Mermaid's parsing error. The mermaid configuration controls the Mermaid theme, theme variables, background, viewport, and scale. Puppeteer supplies the isolated build-time browser; the generated HTML does not need Mermaid JavaScript or a network connection.

Obsidian syntax

> [!danger] Graphic missing
> Add the missing workflow diagram here.

See [[Patching|the patching chapter]].

Callout types are case-insensitive. [!note]+ and [!note]- fold markers are retained in the shared model; static HTML and PDF intentionally render their content expanded. A wikilink resolves by unique page title, basename, or root-relative Markdown path. ![[image.png|Caption]] embeds a local image.

The renderer supports Obsidian's standard callout types and aliases: note; abstract, summary, and tldr; info; todo; tip, hint, and important; success, check, and done; question, help, and faq; warning, caution, and attention; failure, fail, and missing; danger and error; bug; example; and quote and cite. Each family has its own semantic accent and tinted background in both HTML and PDF. Unknown callout types remain visible and use the note appearance. theme.callouts can override an individual type or family color.

CommonMark line-break rules are preserved across both outputs. A single source newline inside a paragraph becomes a space, two trailing spaces before a newline create a visible hard break, and a blank line starts a new paragraph.

Development

npm install
npm run typecheck
npm test
npm run build
npm run example

The package targets Node.js 22 or newer. It does not fetch remote images, execute raw Markdown HTML, or allow local Markdown assets to escape the supplied source root.

Releases

Forgejo is authoritative for main, release commits, and vX.Y.Z tags. Its configured mirror delivers those commits and tags to GitHub. Forgejo CI runs semantic-release after typecheck, tests, build, and package inspection; it updates package.json, package-lock.json, and CHANGELOG.md, then creates the release commit and tag. GitHub Actions rebuilds a mirrored tag, publishes @tobisk/markdown-manuals through npm trusted publishing, and creates the GitHub Release. No npm token is stored in the repository.

Conventional Commits control releases: fix: and perf: create a patch, feat: creates a minor, and ! or a BREAKING CHANGE: footer creates a major. Documentation, tests, refactors, build, CI, and chores do not release by default. Preview the next release locally with npm run release:dry-run; it requires access to full repository history and the Forgejo remote but does not publish in dry-run mode. The remote is private, so the local Git credentials must be able to read it.

Publishing requires two repository-side settings:

  • Forgejo secret SEMANTIC_RELEASE_TOKEN, scoped to push the release commit and tags.
  • An npm trusted publisher for package @tobisk/markdown-manuals, GitHub repository kellertobias/markdown-docgen, workflow release.yml.