@entropicwarrior/sdoc 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ Markdown has no formal structure — section boundaries are ambiguous, extractio
15
15
 
16
16
  ## What's in the Box
17
17
 
18
- **The format** — a formal specification (`lexica/specification.sdoc`) with EBNF grammar, plus a comprehensive authoring guide written as an AI agent skill document. Drop `lexica/sdoc-authoring.sdoc` into any AI agent's context and it can read and write SDOC immediately.
18
+ **The format** — a formal specification (`lexica/specification.sdoc`) with EBNF grammar, plus a comprehensive authoring guide written as an AI agent skill document. Drop `docs/reference/sdoc-authoring.sdoc` into any AI agent's context and it can read and write SDOC immediately.
19
19
 
20
20
  **A zero-dependency JavaScript parser** — `src/sdoc.js` parses SDOC into a format-neutral AST. No runtime dependencies, works anywhere Node runs. Parsing and rendering are cleanly separated — build your own renderers on top.
21
21
 
@@ -44,7 +44,7 @@ Open any `.sdoc` file and click the preview icon in the editor title bar, or run
44
44
  node tools/build-slides.js deck.sdoc -o slides.html
45
45
  ```
46
46
 
47
- Each top-level scope becomes a slide. Set `type: slides` in `@meta`. See `lexica/slide-authoring.sdoc` for the full authoring guide.
47
+ Each top-level scope becomes a slide. Set `type: slides` in `@meta`. See `docs/reference/slide-authoring.sdoc` for the full authoring guide.
48
48
 
49
49
  ### Export to PDF or HTML
50
50
 
@@ -71,11 +71,10 @@ Key resources for agents:
71
71
 
72
72
  | Resource | What it gives you |
73
73
  |---|---|
74
- | [`lexica/sdoc-authoring.sdoc`](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/lexica/sdoc-authoring.sdoc) | Skill document — drop into context to read/write SDOC immediately |
74
+ | [`docs/reference/sdoc-authoring.sdoc`](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/docs/reference/sdoc-authoring.sdoc) | Skill document — drop into context to read/write SDOC immediately |
75
75
  | [`lexica/specification.sdoc`](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/lexica/specification.sdoc) | Formal spec with EBNF grammar |
76
- | [`SDOC_GUIDE.md`](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/SDOC_GUIDE.md) | Quick reference in Markdown |
77
76
 
78
- All files in `lexica/` are SDOC-format knowledge documents designed for progressive disclosure — read the `@about` scope first (~50 tokens), then scan headings, then load only the section you need.
77
+ All `.sdoc` files are designed for progressive disclosure — read the `@about` scope first (~50 tokens), then scan headings, then load only the section you need.
79
78
 
80
79
  ## Format at a Glance
81
80
 
@@ -175,9 +174,10 @@ Per-folder `sdoc.config.json` or per-file `@meta` scope for custom CSS, headers,
175
174
  ## Learning the Format
176
175
 
177
176
  - `docs/guide/intro.sdoc` — what SDOC is and why
177
+ - `docs/guide/why-sdoc.sdoc` — the case for SDOC over Markdown
178
178
  - `docs/tutorials/first-steps.sdoc` — write your first document
179
+ - `docs/reference/sdoc-authoring.sdoc` — authoring guide with quick reference and common mistakes
179
180
  - `docs/reference/syntax.sdoc` — full syntax reference
180
- - `SDOC_GUIDE.md` — quick reference in Markdown (also used by AI tools)
181
181
 
182
182
  ## Contributing
183
183
 
@@ -0,0 +1,112 @@
1
+ # Introduction to SDOC
2
+ {
3
+ @meta {
4
+ sdoc-version: 0.1
5
+ }
6
+
7
+ # What is SDOC?
8
+ {
9
+ SDOC is a plain text format for writing structured documentation. It uses explicit scoping to define document structure, with optional braces for unambiguous nesting.
10
+
11
+ The simplest SDOC document is just a heading followed by content:
12
+
13
+ ```
14
+ # My Document
15
+
16
+ This is a paragraph.
17
+ ```
18
+
19
+ For richer structure, use braces to scope content explicitly:
20
+
21
+ ```
22
+ # My Section
23
+ {
24
+ Content goes here.
25
+ }
26
+ ```
27
+
28
+ Scopes nest naturally:
29
+
30
+ ```
31
+ # Outer
32
+ {
33
+ # Inner
34
+ {
35
+ Nested content.
36
+ }
37
+ }
38
+ ```
39
+
40
+ Or use braceless scopes for simpler documents:
41
+
42
+ ```
43
+ # Outer
44
+ {
45
+ # Section A
46
+ Content for section A.
47
+
48
+ # Section B
49
+ Content for section B.
50
+ }
51
+ ```
52
+
53
+ Heading depth is determined by nesting, not by the number of `#` characters. A single `#` is all you need.
54
+ }
55
+
56
+ # Why SDOC?
57
+ {
58
+ # For Humans
59
+ {
60
+ SDOC reads like plain text. The braces are there, but they fade into the background the same way indentation does. You get:
61
+
62
+ {[.]
63
+ - **Unlimited nesting** with no ambiguity. Scope boundaries are explicit, not guessed from heading levels or indentation. A ten-level-deep legal document parses the same way as a three-section readme.
64
+ - **Familiar inline formatting.** Bold, italic, code, links, and lists work the way you expect from Markdown. There is almost nothing new to learn.
65
+ - **No rendering required.** The raw file is the document. You can read and edit it in any text editor, on any device, with no tooling at all.
66
+ - **Stable cross-references.** Tag any scope with `@id` and reference it with `@id` in text. Rename the heading, the reference still works.
67
+ }
68
+ }
69
+
70
+ # For AI Agents and Tools
71
+ {
72
+ This is where SDOC's design pays off most. Every property of the format was chosen to make programmatic consumption reliable and token-efficient.
73
+
74
+ **Deterministic parsing.** Two parsers, same input, same tree. Always. Markdown cannot guarantee this because section boundaries are inferred from heading levels, and different parsers make different choices about where a section ends. In SDOC, a scope starts with `{` and ends with the matching `}`. There is no ambiguity to resolve. This is also a security property: when every parser produces the same tree, there is no interpretation gap that could be exploited to inject or hide content in automated processing pipelines.
75
+
76
+ **Exact section extraction.** An agent that needs "the error handling section" calls `get_section("error-handling")` and gets exactly that scope back, with its children, every time. In Markdown, extracting a section means guessing: does it end at the next heading of equal depth? Lesser depth? What if heading levels are inconsistent? SDOC makes this a solved problem.
77
+
78
+ **Token-efficient navigation.** Agents do not need to load entire files. With progressive disclosure, the cost of a targeted lookup is roughly:
79
+
80
+ {[.]
81
+ - **Discovery:** list all files with summaries \~200 tokens for 50 files
82
+ - **Orientation:** list section headings of one file \~50-100 tokens
83
+ - **Detail:** load one section \~200-1000 tokens
84
+ }
85
+
86
+ Total: roughly 750 tokens for a precise answer. Loading the whole file in Markdown would cost 5,000+ tokens and pollute the context window with irrelevant material.
87
+
88
+ **AI agents can write it reliably.** Brace matching is unambiguous to produce. A template that says "fill in the content between the braces" is straightforward. In Markdown, agents must track heading levels carefully, and a single `##` where `###` was needed silently restructures the entire document.
89
+
90
+ **Structured metadata in the same format.** The `@meta` scope uses the same SDOC syntax as everything else. No YAML frontmatter with different escaping rules parsed by different tools. Metadata is just another scope.
91
+ }
92
+ }
93
+
94
+ # Core Concepts
95
+ {
96
+ # Scopes
97
+ {
98
+ A scope is the fundamental building block. It has a heading line starting with `#`, an optional `@id` tag, and a body block in braces:
99
+
100
+ ```
101
+ # Section Title @section-id
102
+ {
103
+ Body text here.
104
+ }
105
+ ```
106
+
107
+ Scopes nest to any depth. Inline formatting (`*bold*`, `` `code` ``, `[links](url)`, `@references`, `$math$`) works the same way as Markdown inside any scope.
108
+
109
+ See `reference/syntax.sdoc` for the full list of features, or follow `tutorials/first-steps.sdoc` to try it hands-on.
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,177 @@
1
+ # Notion Sync
2
+ {
3
+ @meta {
4
+ sdoc-version: 0.1
5
+ }
6
+
7
+ SDOC files can be synced to Notion as native blocks -- headings, paragraphs, lists, tables, code, and images all render as real Notion content. The repo is the source of truth; Notion is a read-only mirror that stays up to date automatically via CI.
8
+
9
+ # How It Works
10
+ {
11
+ {[#]
12
+ - An SDOC file declares its Notion page ID in its `@meta` scope
13
+ - The sync tool parses the file and converts the AST to Notion API block objects
14
+ - It clears the target page and replaces the content with the rendered blocks
15
+ - A callout banner is added at the top: "Auto-synced from path/file.sdoc — do not edit in Notion"
16
+ }
17
+
18
+ Top-level SDOC scopes render as **toggle headings** in Notion, matching the collapsible behaviour of the HTML preview. Deeper scopes render as flat headings due to Notion API nesting limits.
19
+ }
20
+
21
+ # Setup
22
+ {
23
+ # 1. Create a Notion Integration
24
+ {
25
+ {[#]
26
+ - Go to [https://www.notion.so/profile/integrations](https://www.notion.so/profile/integrations)
27
+ - Click **New integration**
28
+ - Give it a name (e.g. "SDOC Sync") and select your workspace
29
+ - Copy the **Internal Integration Secret** (starts with `secret_`)
30
+ }
31
+ }
32
+
33
+ # 2. Share Pages with the Integration
34
+ {
35
+ For each Notion page you want to sync to:
36
+
37
+ {[#]
38
+ - Open the page in Notion
39
+ - Click the **...** menu (top right) and select **Connect to**
40
+ - Find and select your integration
41
+ }
42
+
43
+ Copy the page ID from the URL. It is the 32-character hex string at the end:
44
+
45
+ ```
46
+ https://www.notion.so/My-Page-3135217a0d6380acbcecfed64316ec91
47
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
48
+ this is the page ID
49
+ ```
50
+ }
51
+
52
+ # 3. Add the Page ID to Your SDOC File
53
+ {
54
+ Add a `notion-page` property to the file's `@meta` scope:
55
+
56
+ ```sdoc
57
+ # My Document @meta
58
+ {
59
+ type: doc
60
+ notion-page: 3135217a0d6380acbcecfed64316ec91
61
+ }
62
+ ```
63
+
64
+ Only files with `notion-page` in their meta will be synced. Files without it are ignored.
65
+ }
66
+
67
+ # 4. Set the Notion Token
68
+ {
69
+ The token is **never committed to the repo**. Set it as an environment variable:
70
+
71
+ ```bash
72
+ export NOTION_TOKEN=secret_abc123...
73
+ ```
74
+
75
+ For CI, add it as a repository secret (see @ci-setup below).
76
+ }
77
+ }
78
+
79
+ # Running Manually
80
+ {
81
+ Sync a single file:
82
+
83
+ ```bash
84
+ node tools/sync-notion.js path/to/file.sdoc --verbose
85
+ ```
86
+
87
+ Scan a directory and sync all files that have `notion-page` in their meta:
88
+
89
+ ```bash
90
+ node tools/sync-notion.js docs/ --verbose
91
+ ```
92
+
93
+ Dry run (renders blocks to stdout without calling the Notion API):
94
+
95
+ ```bash
96
+ node tools/sync-notion.js docs/ --dry-run
97
+ ```
98
+
99
+ Override the page ID from the command line (useful for testing):
100
+
101
+ ```bash
102
+ node tools/sync-notion.js file.sdoc --page-id abc123def456 --verbose
103
+ ```
104
+ }
105
+
106
+ # CI Setup @ci-setup
107
+ {
108
+ This section describes a workflow to add to **your own repository** that uses SDOC (the sdoc repo itself does not include this workflow). A GitHub Actions workflow can sync automatically on every push. Add the Notion token as a repository secret:
109
+
110
+ {[#]
111
+ - Go to your repo's **Settings > Secrets and variables > Actions**
112
+ - Click **New repository secret**
113
+ - Name: `NOTION_TOKEN`, Value: your integration secret
114
+ }
115
+
116
+ Then add a workflow file at `.github/workflows/sync-notion.yml`:
117
+
118
+ ```yaml
119
+ name: Sync SDOC to Notion
120
+
121
+ on:
122
+ push:
123
+ branches: [main, develop]
124
+ paths:
125
+ - '**.sdoc'
126
+
127
+ jobs:
128
+ sync:
129
+ runs-on: ubuntu-latest
130
+ steps:
131
+ - uses: actions/checkout@v4
132
+
133
+ - uses: actions/setup-node@v4
134
+ with:
135
+ node-version: '20'
136
+
137
+ - name: Sync to Notion
138
+ env:
139
+ NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
140
+ run: node tools/sync-notion.js . --verbose
141
+ ```
142
+
143
+ This triggers only when `.sdoc` files change on push to `main` or `develop`.
144
+ }
145
+
146
+ # What Gets Rendered
147
+ {
148
+ {[table]
149
+ SDOC Element | Notion Block
150
+ Scopes (depth 1) | Toggle heading (collapsible)
151
+ Scopes (depth 2+) | Flat headings with content as siblings
152
+ Paragraphs | Paragraph blocks
153
+ Bullet lists | Bulleted list items
154
+ Numbered lists | Numbered list items
155
+ Task lists | To-do items (checked/unchecked)
156
+ Tables | Table blocks with rows
157
+ Code blocks | Code blocks (with language highlighting)
158
+ Blockquotes | Quote blocks
159
+ Horizontal rules | Divider blocks
160
+ Images (absolute URLs) | Image blocks
161
+ Images (relative paths) | Skipped (not supported by Notion)
162
+ Links | Rich text with link
163
+ Bold, italic, strikethrough, code | Rich text annotations
164
+ Refs (`@id`) | Literal text
165
+ }
166
+ }
167
+
168
+ # Limitations
169
+ {
170
+ {[.]
171
+ - **One-way sync only.** The repo is the source of truth. Edits made in Notion are overwritten on the next sync.
172
+ - **Images require absolute URLs.** Notion cannot fetch images from relative file paths. Use full `https://` URLs for images that should appear in Notion.
173
+ - **Nesting depth.** Notion's API limits block nesting to 2 levels. Only the top-level scope renders as a toggle heading; deeper scopes render as flat headings.
174
+ - **Refs are literal.** SDOC `@references` render as plain text in Notion since there is no equivalent for internal document cross-references.
175
+ }
176
+ }
177
+ }
@@ -0,0 +1,112 @@
1
+ # Setup
2
+ {
3
+ @meta {
4
+ sdoc-version: 0.1
5
+ }
6
+
7
+ # Requirements
8
+ {
9
+ {[.]
10
+ - [Visual Studio Code](https://code.visualstudio.com/) (v1.95 or later)
11
+ - The SDOC extension (`.vsix` file)
12
+ }
13
+ }
14
+
15
+ # Installing the Extension
16
+ {
17
+ Build the extension from source:
18
+
19
+ ```bash
20
+ npm install
21
+ npm run package
22
+ ```
23
+
24
+ This produces a `.vsix` file in the `dist/` folder. Install it:
25
+
26
+ ```bash
27
+ code --install-extension dist/sdoc-*.vsix
28
+ ```
29
+
30
+ Or install from within VS Code: open the Command Palette, run "Extensions: Install from VSIX...", and select the `.vsix` file.
31
+ }
32
+
33
+ # Previewing Documents
34
+ {
35
+ Once installed, open any `.sdoc` file and use one of:
36
+
37
+ {[.]
38
+ - **SDOC: Open Preview** to preview in the current pane
39
+ - **SDOC: Open Preview to the Side** for a side-by-side view
40
+ }
41
+
42
+ The preview updates live as you edit.
43
+ }
44
+
45
+ # Interactive Preview
46
+ {
47
+ The preview panel supports interactive features:
48
+
49
+ {[.]
50
+ - **Click-to-navigate**: click any element in the preview to jump to its source line in the editor
51
+ - **Collapsible scopes**: hover over a heading with children to reveal a toggle triangle. Click it to collapse or expand the section. Collapse state persists across refreshes.
52
+ - **Links**: clicking a hyperlink in the preview opens it in your default browser
53
+ }
54
+ }
55
+
56
+ # Formatting Documents
57
+ {
58
+ Use **Format Document** (Shift+Option+F on macOS, Shift+Alt+F on Windows/Linux) to auto-indent the current `.sdoc` file based on brace depth. The formatter respects your VS Code tab size and spaces/tabs preference.
59
+
60
+ Formatting is purely cosmetic -- it adjusts indentation without changing document structure. Code blocks are left untouched.
61
+ }
62
+
63
+ # Export and Print
64
+ {
65
+ Three export commands are available from the Command Palette or the editor title bar when an `.sdoc` file is open:
66
+
67
+ {[.]
68
+ - **SDOC: Export HTML** -- saves a standalone `.html` file with all styles embedded
69
+ - **SDOC: Export PDF** -- exports the document as an A4 PDF via headless Chrome
70
+ - **SDOC: Open in Browser** -- opens the document in the system browser
71
+ }
72
+
73
+ HTML and browser outputs support collapsible scope toggles. PDF export requires Chrome or Chromium installed on the system. For command-line export, see `tools/build-doc.js` in the [CLI reference](../reference/cli.sdoc).
74
+ }
75
+
76
+ # Configuration
77
+ {
78
+ Place a `sdoc.config.json` in your project root to set defaults for all documents:
79
+
80
+ ```json
81
+ {
82
+ "style": "path/to/custom.css",
83
+ "header": "My Project",
84
+ "footer": "Footer text"
85
+ }
86
+ ```
87
+
88
+ {[.]
89
+ - `style` replaces the default stylesheet (path relative to the config file)
90
+ - `styleAppend` adds CSS after the base stylesheet
91
+ - `header` and `footer` set page-level text with inline formatting
92
+ }
93
+
94
+ Configs are hierarchical: place `sdoc.config.json` in subfolders to override parent settings for documents in that folder.
95
+
96
+ See `examples/sdoc.config.json` for a sample config and `examples/sdoc.template.css` for the default theme.
97
+ }
98
+
99
+ # Document Server
100
+ {
101
+ Browse all `.sdoc` files in the project with a local HTTP server:
102
+
103
+ ```bash
104
+ python3 tools/serve_docs.py # serve current dir on :4070
105
+ python3 tools/serve_docs.py docs/ -p 8080 # serve docs/ on :8080
106
+ ```
107
+
108
+ Or use the **SDOC: Browse Documents** command from the VSCode Command Palette. It prompts you to serve from the entire workspace or choose a specific subfolder.
109
+
110
+ The viewer opens in your browser with a sidebar, search, and split-pane comparison. Edits to `.sdoc` files are reflected on browser refresh.
111
+ }
112
+ }
@@ -0,0 +1,39 @@
1
+ # SDOC Documentation
2
+ {
3
+ @meta {
4
+ sdoc-version: 0.1
5
+ }
6
+
7
+ # Overview
8
+ {
9
+ SDOC ("Simple/Smart Documentation") is a plain text documentation format with explicit scoping. Unlike Markdown, structure is never inferred — braces make scope boundaries explicit, which means tools and AI agents can extract exactly the section they need, every time, with no heuristics. Inline formatting is borrowed from Markdown, so there is almost nothing new to learn.
10
+
11
+ Use the sidebar to navigate. Shift+Click two docs to compare them side by side.
12
+ }
13
+
14
+ # Quick Links
15
+ {
16
+ {[.]
17
+ - Guide: Introduction — `guide/intro.sdoc`
18
+ - Guide: Why SDOC — `guide/why-sdoc.sdoc`
19
+ - Guide: Setup — `guide/setup.sdoc`
20
+ - Guide: Notion Sync — `guide/notion-sync.sdoc`
21
+ - Tutorial: First Steps — `tutorials/first-steps.sdoc`
22
+ - Reference: SDOC Authoring — `reference/sdoc-authoring.sdoc`
23
+ - Reference: Slide Authoring — `reference/slide-authoring.sdoc`
24
+ - Reference: Syntax — `reference/syntax.sdoc`
25
+ - Reference: API — `reference/api.sdoc`
26
+ - Reference: CLI — `reference/cli.sdoc`
27
+ }
28
+ }
29
+
30
+ # Sections
31
+ {[.]
32
+ - Guide
33
+ { Getting started with SDOC: what it is, why it exists, and how to set it up. }
34
+ - Reference
35
+ { Authoring guides, syntax details, the JavaScript API, and CLI tools. }
36
+ - Tutorials
37
+ { Step-by-step walkthroughs for common tasks. }
38
+ }
39
+ }
@@ -0,0 +1,208 @@
1
+ # JavaScript API Reference
2
+ {
3
+ @meta {
4
+ sdoc-version: 0.1
5
+ }
6
+
7
+ The SDOC parser and renderer are in `src/sdoc.js`. All functions are exported via CommonJS. The package is available on npm as `@entropicwarrior/sdoc`.
8
+
9
+ # Installation
10
+ {
11
+ ```bash
12
+ npm install @entropicwarrior/sdoc
13
+ ```
14
+
15
+ Subpath exports are available for individual renderers:
16
+
17
+ ```javascript
18
+ const { parseSdoc, extractMeta } = require("@entropicwarrior/sdoc");
19
+ const { renderNotionBlocks } = require("@entropicwarrior/sdoc/notion");
20
+ const { renderSlides } = require("@entropicwarrior/sdoc/slides");
21
+ const { exportSlidePdf, exportDocPdf } = require("@entropicwarrior/sdoc/slide-pdf");
22
+ const { knowledgeDir } = require("@entropicwarrior/sdoc/knowledge");
23
+ ```
24
+ }
25
+
26
+ # parseSdoc(text)
27
+ {
28
+ Parses an SDOC string into an AST.
29
+
30
+ ```javascript
31
+ const { parseSdoc } = require("@entropicwarrior/sdoc");
32
+ const { nodes, errors } = parseSdoc(sdocText);
33
+ ```
34
+
35
+ {[.]
36
+ - `text` -- the SDOC source string
37
+ - Returns `{ nodes, errors }` where `nodes` is an array of AST nodes and `errors` is an array of `{ message, line }` objects
38
+ }
39
+ }
40
+
41
+ # extractMeta(nodes)
42
+ {
43
+ Extracts the `@meta` scope from parsed nodes and returns the remaining body nodes.
44
+
45
+ ```javascript
46
+ const { parseSdoc, extractMeta } = require("@entropicwarrior/sdoc");
47
+ const parsed = parseSdoc(text);
48
+ const { nodes, meta } = extractMeta(parsed.nodes);
49
+ ```
50
+
51
+ {[.]
52
+ - `nodes` -- the AST node array from `parseSdoc`
53
+ - Returns `{ nodes, meta }` where `meta` contains `stylePath`, `styleAppendPath`, `headerNodes`, `footerNodes`, `headerText`, `footerText`, and `properties`
54
+ - `headerText` and `footerText` are set by key:value meta syntax (e.g., `header: My Header`)
55
+ - `properties` is an object of arbitrary key:value pairs from the meta scope (e.g., `author`, `date`, `version`)
56
+ }
57
+ }
58
+
59
+ # renderHtmlDocument(text, title, options)
60
+ {
61
+ Parses and renders a complete HTML page from SDOC source.
62
+
63
+ ```javascript
64
+ const { renderHtmlDocument } = require("@entropicwarrior/sdoc");
65
+ const html = renderHtmlDocument(sdocText, "Page Title", {
66
+ config: { header: "My Header", footer: "My Footer" },
67
+ cssOverride: customCssString,
68
+ cssAppend: additionalCssString
69
+ });
70
+ ```
71
+
72
+ {[.]
73
+ - `text` -- SDOC source string
74
+ - `title` -- HTML page title
75
+ - `options.config` -- object with `header` and `footer` strings
76
+ - `options.cssOverride` -- replaces the default stylesheet
77
+ - `options.cssAppend` -- appended after the base stylesheet
78
+ - Automatically extracts and applies `@meta` scope settings
79
+ }
80
+ }
81
+
82
+ # renderHtmlDocumentFromParsed(parsed, title, options)
83
+ {
84
+ Renders a complete HTML page from pre-parsed data. Use this when you need to parse and extract meta separately.
85
+
86
+ ```javascript
87
+ const { parseSdoc, extractMeta, renderHtmlDocumentFromParsed } = require("@entropicwarrior/sdoc");
88
+ const parsed = parseSdoc(text);
89
+ const metaResult = extractMeta(parsed.nodes);
90
+ const html = renderHtmlDocumentFromParsed(
91
+ { nodes: metaResult.nodes, errors: parsed.errors },
92
+ "Page Title",
93
+ { meta: metaResult.meta }
94
+ );
95
+ ```
96
+ }
97
+
98
+ # renderFragment(nodes, depth)
99
+ {
100
+ Renders an array of AST nodes to an HTML fragment (no `<html>` wrapper).
101
+
102
+ ```javascript
103
+ const { parseSdoc, renderFragment } = require("@entropicwarrior/sdoc");
104
+ const { nodes } = parseSdoc(text);
105
+ const html = renderFragment(nodes, 2);
106
+ ```
107
+
108
+ {[.]
109
+ - `nodes` -- array of AST nodes
110
+ - `depth` -- starting heading depth (default: 2)
111
+ }
112
+ }
113
+
114
+ # renderTextParagraphs(text)
115
+ {
116
+ Renders a plain text string as HTML paragraphs with inline formatting. Used internally for header/footer rendering from config values.
117
+ }
118
+
119
+ # formatSdoc(text, indentStr)
120
+ {
121
+ Reformats an SDOC string by adjusting indentation based on brace depth. Structure is not changed -- only whitespace is modified.
122
+
123
+ ```javascript
124
+ const { formatSdoc } = require("@entropicwarrior/sdoc");
125
+ const formatted = formatSdoc(sdocText, " ");
126
+ ```
127
+
128
+ {[.]
129
+ - `text` -- the SDOC source string
130
+ - `indentStr` -- the string to use for each indentation level (default: four spaces)
131
+ - Returns the reformatted string
132
+ - Code block content is passed through raw
133
+ - Inline blocks (`{ content }`) are kept on one line
134
+ - K&R style lines (heading ending with opener) are handled correctly
135
+ }
136
+ }
137
+
138
+ # resolveIncludes(nodes, resolverFn)
139
+ {
140
+ Walks the AST and resolves code blocks with `src` metadata by loading the referenced file contents. This is an async function.
141
+
142
+ ```javascript
143
+ const { parseSdoc, resolveIncludes } = require("@entropicwarrior/sdoc");
144
+ const parsed = parseSdoc(text);
145
+ await resolveIncludes(parsed.nodes, async (src) => {
146
+ return fs.readFileSync(path.resolve(docDir, src), "utf8");
147
+ });
148
+ ```
149
+
150
+ {[.]
151
+ - `nodes` -- the AST node array to walk (modified in place)
152
+ - `resolverFn` -- async function `(src) => string` that resolves a `src:` path to file contents
153
+ - Applies `lines` range filtering if present on the code node
154
+ - If the resolver throws, the code block text is set to an error message
155
+ }
156
+ }
157
+
158
+ # AST Node Types
159
+ {
160
+ The parser produces these node types:
161
+
162
+ {[.]
163
+ - `scope` -- a section with `title`, `id`, `children`, `hasHeading`, and optional `task`
164
+ - `paragraph` -- a text block with `text`
165
+ - `list` -- a list with `listType` (`"bullet"` or `"number"`) and `items`
166
+ - `blockquote` -- a quote with `paragraphs` (array of strings)
167
+ - `table` -- a table with `headers` (array of strings), `rows` (array of string arrays), and optional `options` (`{ borderless, headerless }`)
168
+ - `code` -- a code block with `lang`, `text`, and optional `src` (file path/URL) and `lines` (`{ start, end }`)
169
+ - `hr` -- a horizontal rule
170
+ }
171
+
172
+ Inline image nodes (`type: "image"`) within paragraphs include `src`, `alt`, and optional `width` (e.g. `"50%"`) and `align` (`"center"`, `"left"`, or `"right"`).
173
+ }
174
+
175
+ # exportSlidePdf(htmlPath, pdfPath)
176
+ {
177
+ Exports an HTML file to PDF using headless Chrome with 16:9 landscape dimensions (slide format).
178
+
179
+ ```javascript
180
+ const { exportSlidePdf } = require("@entropicwarrior/sdoc/slide-pdf");
181
+ await exportSlidePdf("/tmp/slides.html", "/tmp/slides.pdf");
182
+ ```
183
+
184
+ {[.]
185
+ - `htmlPath` -- path to the HTML file to convert
186
+ - `pdfPath` -- path for the output PDF
187
+ - Returns a Promise that resolves with the output path
188
+ - Requires Chrome or Chromium (auto-detected, or set `CHROME_PATH`)
189
+ }
190
+ }
191
+
192
+ # exportDocPdf(htmlPath, pdfPath)
193
+ {
194
+ Exports an HTML file to PDF using headless Chrome with A4 portrait dimensions (document format).
195
+
196
+ ```javascript
197
+ const { exportDocPdf } = require("@entropicwarrior/sdoc/slide-pdf");
198
+ await exportDocPdf("/tmp/document.html", "/tmp/document.pdf");
199
+ ```
200
+
201
+ {[.]
202
+ - `htmlPath` -- path to the HTML file to convert
203
+ - `pdfPath` -- path for the output PDF
204
+ - Returns a Promise that resolves with the output path
205
+ - Uses A4 paper (8.27 x 11.69 inches) with print-optimized styles
206
+ }
207
+ }
208
+ }