Overview

The Scimax VS Code extension provides an org-publishing system for converting multiple org-mode documents into a cohesive HTML website. This is ideal for creating documentation sites, blogs, or project pages hosted on GitHub Pages.

The publishing system follows a wizard-first approach - most users can get started with a few clicks, while power users can customize everything through a JSON configuration file.

Quick Start

Step 1: Initialize a Publishing Project

Run the command Scimax: Initialize Publishing Project (scimax.publish.init) from the Command Palette to start the 5-step wizard:

  1. Project name - A short identifier (e.g., "my-website")

  2. Source directory - Where your org files are located (default: ./org)

  3. Output directory - Where HTML files will be published (default: ./docs for GitHub Pages)

  4. Publishing target - GitHub Pages (recommended) or Local/Custom

  5. Sitemap generation - Whether to auto-generate an index page

Step 2: Add Content

Create or edit `.org' files in your source directory. Each file becomes an HTML page.

Example ./org/about.org:

#+TITLE: About This Project
#+AUTHOR: Your Name
#+DATE: 2026-01-16

* Introduction

Welcome to my project documentation!

* Features

- Feature one
- Feature two

Step 3: Publish

Run Scimax: Publish Project (scimax.publish.project) to generate HTML files, or run scimax publish from a terminal (see CLI: Publish).

Step 4: Deploy to GitHub Pages

  1. Commit the ./docs folder to your repository

  2. Go to your repository's Settings → Pages

  3. Set Source to "Deploy from a branch"

  4. Select main branch and /docs folder

  5. Your site will be live at https:username.github.iorepo-name

Commands

These commands have no default keybindings; run them from the Command Palette. The same publishing is available from the command line with scimax publish (see CLI: Publish).

CommandKeybindingDescription
scimax.publish.init-Initialize publishing project
scimax.publish.project-Publish selected project
scimax.publish.file-Publish current file only
scimax.publish.all-Publish all configured projects
scimax.publish.preview-Preview published site in browser
scimax.publish.openConfig-Open .org-publish.json config

Configuration File

The publishing system supports two configuration formats:

  1. _config.yml - YAML format (Jupyter Book compatible, takes precedence)

  2. `.org-publish.json' - JSON format

The wizard creates a `.org-publish.json' file by default. You can also create or edit either file manually.

✅ YAML Configuration (_config.yml)

For Jupyter Book compatibility, create a _config.yml in your project root:

# _config.yml - Jupyter Book compatible
title: "My Documentation"

publish:
  base_directory: "./docs"
  publishing_directory: "./public"
  recursive: true
  auto_sitemap: true
  sitemap_filename: "index.org"

html:
  use_default_theme: true
  toc_depth: 3
  css_files:
    - "./css/custom.css"

github_pages: true

A _config.yml defines a single project. Its name is the title lowercased, with spaces replaced by hyphens (title: "My Documentation" gives the project my-documentation; with no title it is default). Pass that name to scimax publish <project>.

Other keys read from _config.yml:

KeyDefaultDescription
publish.base_directory./Source directory
publish.publishing_directory./_build/htmlOutput directory
publish.recursivetrueProcess subdirectories
publish.exclude-Glob pattern of files to skip (e.g. "drafts/*")
publish.auto_sitemaptrueGenerate a sitemap page
publish.sitemap_filenamesitemap.orgSitemap file name
source_directory-Top-level alias for publish.base_directory
publish_directory-Top-level alias for publish.publishing_directory
html.use_default_themetrueInclude the built-in CSS
html.toc_depth-Table of contents depth
html.css_files-List of CSS files to include
html.js_files-List of JavaScript files to include
github_pages-Write .nojekyll (and CNAME) for GitHub Pages
custom_domain-Custom domain written to CNAME
theme-Site theme; see Book Theme

publish.* keys take precedence over the top-level aliases.

JSON Configuration (`.org-publish.json')

{
  "projects": {
    "website": {
      "baseDirectory": "./org",
      "publishingDirectory": "./docs",
      "recursive": true,
      "autoSitemap": true
    }
  },
  "githubPages": true
}

Full Configuration Reference

{
  "projects": {
    "my-website": {
      "baseDirectory": "./org",
      "baseExtension": "org",
      "publishingDirectory": "./docs",
      "recursive": true,

      "exclude": "drafts/*",
      "include": ["extra/special.org"],

      "publishingFunction": "org-html-publish-to-html",

      "autoSitemap": true,
      "sitemapFilename": "index.org",
      "sitemapTitle": "Home",
      "sitemapStyle": "list",
      "sitemapSortFiles": "alphabetically",
      "sitemapSortFolders": "first",

      "htmlPreamble": "./templates/nav.html",
      "htmlPostamble": "./templates/footer.html",
      "htmlHead": "<meta name=\"keywords\" content=\"org-mode, documentation\">",
      "htmlHeadExtra": "<link rel=\"icon\" href=\"/favicon.ico\">",
      "cssFiles": ["./css/style.css"],
      "jsFiles": ["./js/custom.js"],
      "useDefaultTheme": true,

      "withAuthor": true,
      "withCreator": true,
      "withToc": true,
      "sectionNumbers": false,
      "defaultTitle": "Untitled"
    }
  },
  "githubPages": true,
  "customDomain": "docs.example.com",
  "defaultProject": "my-website"
}

Configuration Options

File Selection Options

OptionTypeDefaultDescription
baseDirectorystring-Source directory containing org files
baseExtensionstring"org"File extension to match (without dot)
publishingDirectorystring-Output directory for HTML files
recursivebooleantrueProcess subdirectories
excludestring-Glob pattern to exclude files (e.g., "drafts/*")
includestring[]=[]Extra files to include

Publishing Function Options

OptionTypeDefaultDescription
publishingFunctionstring"org-html-publish-to-html"How to process files

Available functions:

  • `org-html-publish-to-html' - Convert org files to HTML

  • `md-html-publish-to-html' - Convert Markdown files to HTML

  • `ipynb-html-publish-to-html' - Convert Jupyter notebooks to HTML

  • `auto' - Auto-detect by file extension (default behavior)

  • `copy' - Copy files as-is (for static assets)

Supported File Types

The publishing system supports multiple file types:

ExtensionDescriptionConversion
.orgOrg-mode documentsFull org parsing with code blocks
.mdMarkdown filesBasic Markdown to HTML conversion
.ipynbJupyter notebooksNotebook cells to HTML with syntax

When using `auto' or `org-html-publish-to-html', files are processed based on their extension.

Sitemap Options

OptionTypeDefaultDescription
autoSitemapbooleantrueGenerate sitemap automatically
sitemapFilenamestring"sitemap.org"Sitemap file name
sitemapTitlestring"Site Map"Title for sitemap page
sitemapStyle"list" / "tree""list"Sitemap display format
sitemapSortFilesstring"alphabetically"Sort order for files
sitemapSortFoldersstring"first"Where to place folders in listing

Sort options for sitemapSortFiles:

  • `alphabetically' - Sort A-Z by title

  • chronologically - Oldest first (by #+DATE)

  • `anti-chronologically' - Newest first (for blogs)

HTML Template Options

OptionTypeDescription
htmlPreamblestringFile path or HTML string for header/navigation
htmlPostamblestringFile path or HTML string for footer
htmlHeadstringExtra content for <head> section
htmlHeadExtrastringAdditional head content (appended)
cssFilesstring[]CSS files to include
jsFilesstring[]JavaScript files to include
useDefaultThemebooleanInclude built-in CSS theme (default: true)

Export Options

OptionTypeDefaultDescription
withAuthorbooleantrueInclude author in output
withCreatorbooleantrueInclude generator info
withTocboolean / numbertrueTable of contents (or depth)
sectionNumbersbooleanfalseNumber sections
defaultTitlestring-Default title if not specified

Global Options

OptionTypeDescription
githubPagesbooleanEnable GitHub Pages features
customDomainstringCustom domain (creates CNAME file)
defaultProjectstringProject to publish when none specified

Multi-Project Configuration

You can define multiple projects for different parts of your site:

{
  "projects": {
    "pages": {
      "baseDirectory": "./org",
      "publishingDirectory": "./docs",
      "recursive": true,
      "autoSitemap": true
    },
    "static": {
      "baseDirectory": "./assets",
      "publishingDirectory": "./docs/assets",
      "baseExtension": "css|js|png|jpg|svg",
      "publishingFunction": "copy"
    },
    "full-site": {
      "components": ["pages", "static"]
    }
  }
}

Component Projects

A component project groups multiple projects together:

{
  "full-site": {
    "components": ["pages", "static", "images"]
  }
}

When you publish "full-site", all component projects are published in order.

✅ Explicit Table of Contents (_toc.yml)

By default, the publishing system discovers files automatically and generates an alphabetical sitemap. For curated documentation or books where order matters, you can create an explicit table of contents using a _toc.yml file.

Overview

When a _toc.yml file exists in your source directory, the publishing system:

  1. Publishes files in the order specified (not alphabetically)

  2. Generates prev/next navigation links on each page

  3. Creates an index page reflecting the TOC structure

  4. Only includes files listed in the TOC (plus unlisted if configured)

Basic _toc.yml Format

Create a _toc.yml file in your source directory (e.g., ./docs/_toc.yml):

# Simple flat structure
format: scimax
root: index

chapters:
  - file: getting-started
  - file: installation
  - file: configuration
  - file: advanced-usage

With Sections (Nested Chapters)

format: scimax
root: index

chapters:
  - file: getting-started
    sections:
      - file: getting-started/installation
      - file: getting-started/first-steps
  - file: user-guide
    sections:
      - file: user-guide/basics
      - file: user-guide/advanced
  - file: reference

With Parts (Book Format)

For larger documentation with multiple parts/volumes:

format: jb-book
root: intro

parts:
  - caption: Getting Started
    chapters:
      - file: installation
      - file: quickstart
      - file: tutorials

  - caption: User Guide
    chapters:
      - file: basics
        sections:
          - file: basics/syntax
          - file: basics/formatting
      - file: advanced

  - caption: Reference
    chapters:
      - file: api-reference
      - file: changelog

TOC Options

FieldDescription
format"scimax", "jb-book", or "jb-article"
rootLanding page file (without extension)
chaptersList of chapter entries
partsList of parts (with caption and chapters)

Entry Options

FieldDescription
fileFile path without extension (relative to baseDir)
titleOverride display title (optional)
sectionsNested child entries (optional)
urlExternal URL link (optional)

Custom Titles

You can override the title from the file's #+TITLE:

chapters:
  - file: api
    title: API Reference (Complete)
  - file: changelog
    title: Version History

Combining with Configuration

Your .org-publish.json works the same way - the _toc.yml just controls ordering:

{
  "projects": {
    "docs": {
      "baseDirectory": "./docs",
      "publishingDirectory": "./public",
      "autoSitemap": true,
      "sitemapFilename": "index.org",
      "sitemapTitle": "Documentation"
    }
  }
}

With ./docs/_toc.yml:

format: scimax
root: intro
chapters:
  - file: getting-started
  - file: user-guide
  - file: reference

File Extension Resolution

The TOC references files without extensions. The system automatically finds files with these extensions (in order):

  1. `.org'

  2. `.md'

  3. `.ipynb'

So file: getting-started will match getting-started.org, getting-started.md, or getting-started.ipynb.

Templates

HTML Preamble (Navigation)

Create a file like ./templates/nav.html:

<nav class="site-nav">
    <a href="index.html">Home</a>
    <a href="about.html">About</a>
    <a href="sitemap.html">Contents</a>
</nav>

Reference it in your config:

{
  "htmlPreamble": "./templates/nav.html"
}

Custom CSS

Create ./css/style.css and reference it:

{
  "cssFiles": ["./css/style.css"]
}

✅ Book Theme

The book theme publishes a Jupyter Book style site: a header bar, a left sidebar with the site navigation, a right sidebar with the page's headings, prev/next links, client-side search, and a light/dark toggle. Turn it on in _config.yml with a theme section that names it:

theme:
  name: "book"
  layout:
    show_left_sidebar: true
    show_right_sidebar: true
    toc_depth: 3
  header:
    logo: "_static/logo.png"
    title: "My Documentation"
    navbar_links:
      - text: "GitHub"
        url: "https://github.com/user/repo"
  footer:
    copyright: "2026 Your Name"
    links:
      - text: "Contact"
        url: "mailto:you@example.com"
  appearance:
    primary_color: "#0d6efd"
    enable_dark_mode: true
    default_mode: "auto"
  search:
    enabled: true
  custom_css: "_static/custom.css"

theme: book on one line is the same as a theme section with only name: book.

Requirements

Theme publishing requires a _toc.yml in the source directory (see Explicit Table of Contents). Without it, publishing fails with Theme-based publishing requires a _toc.yml file.

A _static/ folder in the source directory is copied to the output, so put the logo and custom CSS there.

Theme Options

OptionDefaultDescription
namedefaultbook for this theme; default for plain pages
layout.show_left_sidebartrueShow the site navigation sidebar
layout.show_right_sidebartrueShow the page headings sidebar (if the page has any)
layout.toc_depth3Deepest heading level in the right sidebar
header.logo-Logo image path, relative to the site root
header.title"Documentation"Site title in the header
header.navbar_links-List of text / url links in the header
footer.copyrightcurrent yearCopyright text
footer.links-List of text / url links in the footer
appearance.primary_color"#0d6efd"Colour of links, buttons, and the chat button
appearance.enable_dark_modetrueShow the light/dark toggle
appearance.default_mode"auto"light, dark, or auto (follow the system)
search.enabledtrueBuild the search index and show the search box
custom_css-Extra stylesheet path, relative to the site root
chat.*off"Ask the docs" chat; see Docs Chat

The defaults apply to every key you leave out, including keys missing from a section you do set: a header section with only a logo keeps the default title.

Docs Chat (WebLLM)

The book theme can add an "Ask the docs" chat to a published site. It finds the sections of the site that best match a question and lists them with the matching text. On request, a language model running in the reader's browser (WebLLM, on WebGPU) writes an answer from those sections. Nothing the reader types leaves their computer, and the site needs no server: it is still plain files, so it works on GitHub Pages.

✅ Turning It On

The chat is off unless theme.chat.enabled is true in _config.yml:

theme:
  name: "book"
  chat:
    enabled: true

When it is on, publish:

  • adds an "Ask" button in the bottom right corner of every page, in the site's primary_color (an icon-only circle on narrow screens)

  • writes _static/chat-index.json, the text of every page split at its headings

  • copies book-chat.js, book-chat-worker.js and book-chat.css to _static/

✅ How Questions Are Answered

  1. The question is matched against the sections with BM25 ranking (headings count more than body text, pairs of adjacent words count as phrases, and a dotted name such as scimax.export.pdf.profile matches whole or in parts).

  2. The best 30 are reranked by a small cross-encoder model (ms-marco-MiniLM-L-6-v2, about 23 MB, run in the browser with transformers.js) that reads the question and each section together. Its order is blended with the BM25 order, so neither one alone decides. If the reranker cannot load, the BM25 order is used.

  3. The top sections, at most two from one page, are listed right away as links, each with the part of its text that matches the question highlighted.

  4. A "Write an answer from these sections" button gives the numbered sections to the model, which answers from them and cites them as [1], [2]. Citations become links to the sections.

A short follow-up ("and for HTML?") is searched together with the question before it, and the last two questions and answers are given to the model.

The ranking model loads by itself the first time a reader asks. The first time a reader asks for a written answer, the chat asks before downloading the language model. The browser keeps both for later visits. In a browser without WebGPU, the chat lists the matching sections without the answer button.

✅ Choosing a Model

Readers pick the model from the menu at the top of the chat panel. The smallest is the default; larger ones answer better but take longer to download and need more GPU memory. The reader's choice is remembered in the browser.

ModelDownload
SmolLM2-360M-Instruct380 MB
Llama-3.2-1B-Instruct880 MB
Qwen2.5-1.5B-Instruct1.6 GB
Llama-3.2-3B-Instruct2.3 GB

The small models can be wrong, which is why the chat always shows the sections it used.

✅ Chat Options

KeyDefaultDescription
enabledfalseAdd the chat and write the section index
modelSmolLM2-360M-Instruct-q4f16_1-MLCModel selected until the reader picks another
modelsthe four models aboveWebLLM prebuilt model IDs offered in the menu
reranktrueRerank the search results with the cross-encoder; false uses BM25 only
top_k5Sections given to the model with each question
max_context_chars6000Most characters of documentation per question
system_promptbuilt inReplaces the instructions given to the model; {site} is the header title
titleAsk the docsTitle of the chat panel

The model IDs are those of WebLLM 0.2.85, which the chat loads from jsDelivr.

theme:
  chat:
    enabled: true
    model: "Llama-3.2-1B-Instruct-q4f16_1-MLC"
    models:
      - "Llama-3.2-1B-Instruct-q4f16_1-MLC"
      - "Qwen2.5-3B-Instruct-q4f16_1-MLC"
    top_k: 4

GitHub Pages Integration

When githubPages is enabled:

  1. A `.nojekyll' file is created in the output directory (disables Jekyll processing)

  2. If `customDomain' is set, a `CNAME' file is created

  3. The default output directory is ./docs (GitHub Pages convention)

  4. The default sitemap becomes `index.org' → `index.html'

Custom Domain Setup

To use a custom domain:

{
  "githubPages": true,
  "customDomain": "docs.example.com"
}

Then configure your DNS to point to GitHub Pages.

Workflow Examples

Documentation Site

{
  "projects": {
    "docs": {
      "baseDirectory": "./docs-src",
      "publishingDirectory": "./docs",
      "autoSitemap": true,
      "sitemapFilename": "index.org",
      "sitemapTitle": "Documentation",
      "withToc": 2
    }
  },
  "githubPages": true
}

Blog

{
  "projects": {
    "blog": {
      "baseDirectory": "./posts",
      "publishingDirectory": "./docs",
      "autoSitemap": true,
      "sitemapFilename": "index.org",
      "sitemapTitle": "Blog",
      "sitemapSortFiles": "anti-chronologically",
      "htmlPreamble": "./templates/blog-header.html"
    }
  },
  "githubPages": true
}

Project with Static Assets

{
  "projects": {
    "pages": {
      "baseDirectory": "./org",
      "publishingDirectory": "./docs",
      "autoSitemap": true
    },
    "images": {
      "baseDirectory": "./images",
      "publishingDirectory": "./docs/images",
      "baseExtension": "png|jpg|gif|svg",
      "publishingFunction": "copy"
    },
    "css": {
      "baseDirectory": "./css",
      "publishingDirectory": "./docs/css",
      "baseExtension": "css",
      "publishingFunction": "copy"
    },
    "site": {
      "components": ["pages", "images", "css"]
    }
  },
  "githubPages": true,
  "defaultProject": "site"
}

Troubleshooting

"No publishing configuration found"

Run Scimax: Initialize Publishing Project (scimax.publish.init), or scimax publish --init from a terminal, to create a .org-publish.json file.

"Base directory does not exist"

Create the source directory specified in `baseDirectory', or update the config to point to an existing directory.

GitHub Pages not updating

  1. Ensure the ./docs folder is committed

  2. Check that GitHub Pages is configured to serve from /docs

  3. Wait a few minutes for GitHub to rebuild the site