markupress 0.1.0 → 0.4.0

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
@@ -2,21 +2,18 @@
2
2
 
3
3
  Build a versioned Markdown documentation site with native HTML Next components, HTMLKit routing and a Looma theme. Markupress provides `build`, `dev`, `preview` and `snapshot` commands. Production output is a portable static directory; controllers enhance the rendered HTML in the browser.
4
4
 
5
- The npm package `markupress@0.0.1` only reserves the name. The implementation below is being prepared for the first product release.
5
+ ## Get started
6
6
 
7
- ## Try the source build
8
-
9
- Use Node 22.22.2 or later in Node 22, or Node 24.15 or later in Node 24, and pnpm through Corepack:
7
+ Markupress is published on npm as `markupress`. Use Node 22.22.2 or later in Node 22, or Node 24.15 or later in Node 24:
10
8
 
11
9
  ```sh
12
- corepack pnpm install --frozen-lockfile
13
- corepack pnpm build
14
- node dist/cli.js dev --root examples/site
15
- node dist/cli.js build --root examples/site
16
- node dist/cli.js preview --root examples/site
10
+ npm install markupress
11
+ npx markupress dev # serve docs/ with live reload
12
+ npx markupress build # write the static site to site/
13
+ npx markupress preview # serve the built site
17
14
  ```
18
15
 
19
- The example serves at `/manual/`. Open a version-specific link directly to verify that a reload preserves the selected version. Markupress is a thin layer over HTMLKit: it translates Markdown and manages versions, and HTMLKit routes, orders, renders, watches and builds the pages. The dev server reloads open pages after any edit, addition or deletion. `build` writes a static site, which is the only production output for now.
16
+ `docs/index.md` is a complete site. Markupress is a thin layer over HTMLKit: it translates Markdown and manages versions, and HTMLKit routes, orders, renders, watches and builds the pages. The dev server reloads open pages after any edit, addition or deletion. `build` writes a static site, which is the production output.
20
17
 
21
18
  ## Author a site
22
19
 
@@ -26,27 +23,31 @@ The example serves at `/manual/`. Open a version-specific link directly to verif
26
23
  pnpm add markupress @nextwebwg/html-next @threadlabs/looma
27
24
  ```
28
25
 
29
- Create `docs/01.index.md` and optionally `markupress.config.json`:
26
+ A site needs no configuration. `docs/index.md` is a complete site, and `markupress dev` serves it. Files in known places shape the rest:
30
27
 
31
- ```json
32
- {
33
- "title": "Project documentation",
34
- "contentDir": "docs",
35
- "base": "/manual/",
36
- "outDir": "site"
37
- }
38
- ```
39
-
40
- Every version needs an `index.md` at its root; a numeric prefix such as `01.index.md` is allowed. `base` starts and ends with `/`. `layout` names a layout component that replaces the Markupress shell (see [Layouts](#layouts)). Configuration is JSON. The programmatic `buildSite`, `devSite` and `previewSite` APIs accept the same options plus `root`, `host` and `port`; `siteOptions` returns the HTMLKit options they use.
28
+ | File | Is |
29
+ | --- | --- |
30
+ | `docs/index.md` | The version home, which every version needs. A number prefix such as `01.index.md` is allowed. |
31
+ | `docs/<folder>/index.md` | The folder's page and its metadata (see [Folders](#navigation-logo-and-footer)). Each top-level folder with an index page is a section: a top navigation link with its own sidebar. |
32
+ | `docs/404.md` | The not-found page. |
33
+ | `docs/_footer.md` | The footer: a heading per column, a list of links under each, and a copyright paragraph. |
34
+ | `public/logo.svg`, `public/logo-dark.svg` | The logo before the title, and its dark-theme version. |
35
+ | `public/favicon.svg` | The site icon. |
36
+ | `app/layouts/default.html` | A layout that replaces the Markupress shell (see [Layouts](#layouts)). |
37
+ | `app/layouts/home.html` | A layout that replaces the home layout (see [Home page](#home-page)). |
38
+ | `htmlkit.config.ts` | HTMLKit options and plugins. |
39
+ | `versions/<id>/` | A recorded version, written by `markupress snapshot` (see [Versions](#versions)). |
40
+
41
+ Pages and their front matter come next, and `markupress.config.json` comes last: plain JSON data for the title, the base path and a few more settings (see [Configuration](#configuration)).
41
42
 
42
43
  The `markupress` command and these APIs also load the site's `htmlkit.config.ts` (or `.js`) with HTMLKit's `loadConfig`, and add `markupress()` after its plugins, so HTMLKit plugins and options compose with Markupress:
43
44
 
44
45
  ```ts
45
46
  // htmlkit.config.ts
46
47
  import { defineConfig } from '@nextwebwg/htmlkit';
47
- import { sitemap } from './plugins/sitemap.js';
48
+ import { feed } from './plugins/feed.js';
48
49
 
49
- export default defineConfig({ plugins: [sitemap()], redirects: { '/start/': '/guide/install/' } });
50
+ export default defineConfig({ plugins: [feed()], redirects: { '/start/': '/guide/install/' } });
50
51
  ```
51
52
 
52
53
  `base` and `outDir` from `markupress.config.json` or the command line win over the HTMLKit config's; without either, they are `/` and `site`. `markupress()` includes the `markdown()` plugin, so the HTMLKit config lists neither.
@@ -64,7 +65,6 @@ export default defineConfig({ plugins: [markdown()] });
64
65
  ---
65
66
  title: Installation
66
67
  description: Install the project and run your first example.
67
- sidebarLabel: Install
68
68
  id: install
69
69
  aliases: [/getting-started/]
70
70
  ---
@@ -77,9 +77,9 @@ aliases: [/getting-started/]
77
77
  <demo-counter></demo-counter>
78
78
  ```
79
79
 
80
- `01.guide/02.install.md` becomes `/v/current/guide/install/`. Numeric prefixes order HTMLKit navigation and disappear from URLs. The unversioned `/guide/install/` alias selects the configured default version. Changing `02.` to `10.` changes ordering without changing the path. Duplicate paths or document IDs fail with both source filenames.
80
+ `01.guide/02.install.md` becomes `/v/current/guide/install/`. Numeric prefixes order HTMLKit navigation and disappear from URLs. In the default version it is also `/guide/install/`, the page's canonical copy (see [Versions](#versions)). Changing `02.` to `10.` changes ordering without changing the path. Duplicate paths or document IDs fail with both source filenames.
81
81
 
82
- Markdown text and fenced examples are literal, including braces and component syntax. Raw HTML outside fences can declare or use native components. Resource links remain relative to the original Markdown file. A component declared in Markdown is hoisted alongside the generated page carrier; its expressions retain HTML Next semantics. Explicit `<title>`, `<meta>` and non-component `<link>` elements also become page metadata.
82
+ Markdown text and fenced examples are literal, including braces and component syntax. Raw HTML outside fences can declare or use native components. Resource links remain relative to the original Markdown file. A component declared in Markdown is hoisted alongside the generated page carrier; its expressions retain HTML Next semantics. Explicit `<title>`, `<meta>` and non-component `<link>` elements become head declarations in the page component's `<defs>`; an explicit `<title>` takes the place of the generated one. A `<link rel="stylesheet">` becomes an `@import` in the page component's `<style>`, so it styles that page as the page's own CSS. Any other `<link>` that is not a head declaration, such as `rel="pingback"`, fails with HTML Next's `HC025`.
83
83
 
84
84
  ### Front matter
85
85
 
@@ -87,22 +87,21 @@ Front matter is a YAML mapping. Each field becomes HTMLKit page metadata or page
87
87
 
88
88
  | Field | Type | Effect |
89
89
  | --- | --- | --- |
90
- | `title` | string | The document `<title>`, the default navigation label, and `page.title`. Defaults to the first heading, then the file name. |
91
- | `description` | string | `<meta name="description">`. |
92
- | `sidebarLabel` | string | The navigation, breadcrumb and pager label (HTMLKit `hk:label`). |
93
- | `sidebarHidden` | boolean | `true` leaves the page out of navigation and keeps its route (HTMLKit `hk:navigation`). |
94
- | `sidebarPosition` | whole number | The page's place among its folder's pages, in place of its file's number prefix (HTMLKit `hk:order`). On a folder's index page it places the whole folder, and on the version home it is an error. |
90
+ | `title` | string | The document `<title>`, the navigation, breadcrumb and pager label (HTMLKit `hk:label`), and `page.title`. Defaults to the first heading, then the file name. On a folder's index page it labels the folder. |
91
+ | `description` | string | `<meta name="description">`, and the page's line in its folder's generated index. |
92
+ | `order` | whole number | The page's place among its folder's pages, in place of its file's number prefix (HTMLKit `hk:order`). On a folder's index page it places the whole folder, and on the version home it is an error. |
93
+ | `navigation` | `hidden` or `collapsed` | `hidden` leaves the page out of navigation and keeps its route (HTMLKit `hk:navigation`). On a folder's index page, `collapsed` starts the folder's sidebar group closed. |
95
94
  | `id` | string | The document identity across versions, independent of the file name and route. |
96
95
  | `aliases` | list of route paths | Further URLs serving the page, relative to its version (HTMLKit `hk:alias`). |
97
- | `layout` | string or `false` | An HTMLKit layout, `app/layouts/<name>.html`, in place of the site's layout for this page (HTMLKit `hk:layout`). `false` renders the page with no layout. |
98
- | `toc` | `false` or `[min, max]` | The heading levels in the page's outline, `[2, 3]` by default. `false` gives an empty outline. |
96
+ | `layout` | string or `false` | An HTMLKit layout, `app/layouts/<name>.html`, in place of the site's layout for this page (HTMLKit `hk:layout`). `home` is Markupress's [home page](#home-page). `false` renders the page with no layout. |
97
+ | `outline` | `false` or `[min, max]` | The heading levels in the page's "On this page" outline, `[2, 3]` by default. `false` gives an empty outline. |
99
98
  | `head` | mapping | `meta` (name to content), `og` (Open Graph property without `og:`, to content) and `canonical` (a URL), rendered into the document head. |
100
- | `editLink` | URL or `false` | The page's "Edit this page" URL, as `page.editLink` for the layout. `false` asks the layout to show none. |
99
+ | `editLink` | URL or `false` | The page's "Edit this page" URL, in place of the one the [`editLink` option](#edit-link-and-last-updated) gives. `false` shows none. |
101
100
 
102
101
  ```yaml
103
102
  ---
104
103
  title: Installation
105
- toc: [2, 4]
104
+ outline: [2, 4]
106
105
  editLink: https://github.com/org/repo/edit/main/docs/01.guide/02.install.md
107
106
  head:
108
107
  meta: { keywords: "install, setup" }
@@ -111,11 +110,11 @@ head:
111
110
  ---
112
111
  ```
113
112
 
114
- Number prefixes order pages, and `sidebarPosition` takes the place of a file's prefix: `sidebarPosition: 2` on `install.md` orders it as `02.install.md` would. Pages with the same number sort by name.
113
+ Number prefixes order pages, and `order` takes the place of a file's prefix: `order: 2` on `install.md` orders it as `02.install.md` would. Pages with the same number sort by name.
115
114
 
116
- Heading IDs are deterministic (`installation`, then `installation-1` for a duplicate). `## Setup {#install}` sets the ID to `install` and removes the marker from the heading; a duplicate still gets a suffix. A custom ID wins: a generated ID skips every custom ID and every ID already used, taking the next free suffix instead. Relative `.md` links resolve to the corresponding version-specific document, retaining fragments and queries; missing documents fail at build time. Relative assets are copied, and image paths starting with `/` resolve from that edition's public directory under the deployment base. Remote links remain unchanged. `sidebarHidden: true` hides a document from navigation while keeping its route available. Aliases do not add duplicate sidebar entries.
115
+ Every heading has an anchor, derived by the Looma Markdown rule: the text is normalized to NFC and lowercased, letters, numbers and combining marks in any script stay, and every other run of characters becomes one hyphen. "Über uns" is `über-uns`, "設定 と 使い方" is `設定-と-使い方`, and a duplicate takes the first free suffix (`installation-1`). `## Setup {#install}` sets the ID to `install` and removes the marker from the heading. A custom ID is reserved for its heading, so a generated ID skips it. Relative `.md` links resolve to the corresponding version-specific document, retaining fragments and queries; a link to a missing `.md` document fails when its page compiles, in `dev` as in `build`, naming the Markdown file. A link starting with `/` is a page path in the page's version, as in the footer and the `nav` option, so `[Tokens](/tokens/)` opens `/v/<id>/tokens/` under the deployment base; with `markdown()` alone it is a path under the base. A root-relative link naming a file, such as `/report.pdf`, is a file in the public directory. Relative assets are copied, and image paths starting with `/` resolve from that edition's public directory under the deployment base. Remote links remain unchanged. `navigation: hidden` hides a document from navigation while keeping its route available. Aliases do not add duplicate sidebar entries.
117
116
 
118
- Containers and GitHub alerts render [Looma's shared blocks](https://github.com/threadlabs-studio/looma/blob/main/apps/docs/docs/shared-blocks.md), so a block looks the same in Markupress, Looma's components and LoomaEditor. The container name is matched without regard to case, and the rest of the opening line is its title:
117
+ Markupress renders [Looma Markdown](https://github.com/threadlabs-studio/looma/blob/main/apps/docs/docs/looma-markdown.md): CommonMark with GitHub Flavored Markdown (tables, task lists, strikethrough and autolinks), plus Looma's blocks and inline elements. It parses with Looma's markdown-it plugin and is tested against Looma Markdown's conformance fixtures, so a document reads the same in Markupress, Looma's components and LoomaEditor. A container's name is matched without regard to case, and the rest of the opening line is its title:
119
118
 
120
119
  ````markdown
121
120
  ::: warning Before you upgrade
@@ -146,6 +145,17 @@ Usage examples.
146
145
  == API
147
146
  The reference.
148
147
  :::
148
+
149
+ ::toc{depth=2}
150
+
151
+ Status :chip[Ready]{tone=green}, owned by :mention[Ada Lovelace]{id=u1}.
152
+
153
+ - [x] Read the changelog
154
+ - [ ] Run the migration
155
+
156
+ ::: figure The build, from *source* to release
157
+ ![The build pipeline](./pipeline.svg){width=640 placement=center}
158
+ :::
149
159
  ````
150
160
 
151
161
  | Markdown | Renders |
@@ -154,9 +164,15 @@ The reference.
154
164
  | `> [!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]` or `[!CAUTION]` alone on a quote's first line | An untitled `<ui-callout>`: NOTE is `note` and CAUTION is `danger`. |
155
165
  | `::: details Title`, with `{open}` to start expanded | Native `<details><summary>Title</summary>`, which opens without JavaScript. The title defaults to "Details". |
156
166
  | `::: tabs` with `== Label` panels | `<ui-tabs group="tabs">`, one `<section aria-label="Label">` per panel. |
157
- | `::: code-group` with a fence per panel | `<ui-tabs group="code-group">`, each panel labelled by its fence's `title`. `== Label` panels also work. |
167
+ | `::: code-group` with a fence per panel | `<ui-tabs group="code-group">`, each panel labelled by its fence's `title`, or its language. `== Label` panels also work. |
168
+ | `::: figure Caption` | `<figure>` with the caption, inline Markdown, as its `<figcaption>`. |
169
+ | `::toc{depth=2 format=numbered}` | A table of contents linking the page's headings by their anchors. `depth` is 1 to 3 (default 3), and `format` is `plain`, `bulleted` or `numbered`. |
170
+ | `:chip[Label]{tone=green}` | An inline chip. Its tone is a hue: `neutral` (the default), `blue`, `green`, `yellow`, `red` or `purple`. |
171
+ | `:mention[Name]{id=u1}` | A mention of the entity `u1`, shown as `@Name`. |
172
+ | `- [x] Done` | A task list, when every item of the list starts with `[ ]` or `[x]`. |
173
+ | `![Alt](./image.svg){width=640 height=360 placement=wrap-left}` | An image with its size in CSS pixels and a placement: `center`, `wrap-left` or `wrap-right`. |
158
174
 
159
- Tabs in one group keep one selection across the page, matched by label, and remember it for the next visit: choosing "npm" in one code group switches every code group. `::: tabs name` or `::: code-group name` sets the group. Without JavaScript every panel shows. Any other container name stays text. To nest containers, give the outer one more colons (`::::` around `:::`). Titles and labels are literal text.
175
+ Tabs in one group keep one selection across the page, matched by label, and remember it for the next visit: choosing "npm" in one code group switches every code group. `::: tabs name` or `::: code-group name` sets the group. Without JavaScript every panel shows. Any other container name stays text. A closing `:::` closes the innermost open container, and a fence's content is code, so a `:::` line inside a fence stays code. Give an outer container more colons (`::::` around `:::`) so the nesting shows in the source. Titles and labels are literal text.
160
176
 
161
177
  A page links the Looma components its blocks use by package name, as the theme links `ui-button`, so `@threadlabs/looma` resolves from the site. The Markupress theme spaces the blocks and styles the Markdown inside them.
162
178
 
@@ -181,10 +197,10 @@ const d = 4;
181
197
  ```
182
198
  ````
183
199
 
184
- - `{1,3-4}` highlights lines 1, 3 and 4. VitePress's spelling, with no space (```` ```ts{1} ````), also works.
200
+ - `{1,3-4}` highlights lines 1, 3 and 4, with or without a space after the language (```` ```ts{1} ````).
185
201
  - A `// [!code highlight]` comment at the end of a line highlights that line, and the comment is removed. `#`, `--`, `/* */` and `<!-- -->` comments work too. `[!code highlight:3]` covers three lines, and a marker alone on its line applies to the lines after it.
186
202
  - `:line-numbers` numbers the lines. The `lineNumbers: true` option, in `markupress.config.json`, `markupress()` or `markdown()`, numbers every block, and `:no-line-numbers` opts a block out.
187
- - These features apply to languages Shiki highlights and to plain text (`text`, `txt`, `plain` or `plaintext`). A fence with an unknown language or no language stays escaped text.
203
+ - Line highlighting and numbers apply to languages Shiki highlights and to plain text (`text`, `txt`, `plain` or `plaintext`). A fence with an unknown language or no language stays escaped text, without its `[!code highlight]` comments.
188
204
 
189
205
  Every code block has Copy and Wrap buttons. Copy puts the code on the clipboard, without line numbers or highlight comments. Wrap toggles line wrapping. They are native buttons, reached with Tab, and a page with code loads a small controller (`markupress/controllers/code.js`) that handles them through one delegated listener. The buttons stay hidden until that controller runs, so without JavaScript a code block is only its code. The theme shows them while the block is hovered or holds focus, and always on touch screens.
190
206
 
@@ -226,90 +242,129 @@ markupress snapshot v1
226
242
  markupress build
227
243
  ```
228
244
 
229
- The command validates a build and writes `versioned_docs/v1/`, then atomically appends its descriptor to `versioned_docs/versions.json`. It mirrors Markdown, public assets and project-local component, controller, stylesheet and asset dependencies, preserving relative imports. Existing snapshots are never replaced. Keep those files in version control. Package imports continue to use the project's installed dependencies and lockfile; snapshots freeze authored sources rather than vendoring npm packages.
245
+ The command validates a build and writes `versions/v1/`, a copy of the project's layout: `versions/v1/docs/` and `versions/v1/public/`. It stages the copy in a `.`-prefixed folder and renames it into place, so a version appears whole. The folders in `versions/` are the version list: the current version first, then each snapshot, highest number first (`v10`, `v2`, `v1`), labelled by its folder name. It mirrors Markdown, public assets and project-local component, controller, stylesheet and asset dependencies, preserving relative imports. Existing snapshots are never replaced. Keep those files in version control. Package imports continue to use the project's installed dependencies and lockfile; snapshots freeze authored sources rather than vendoring npm packages.
230
246
 
231
- Use relative project-local imports for snapshot content. Absolute filesystem imports and remote resources retain their external ownership. If a snapshot process crashes, check that it has stopped before removing its `versioned_docs/.snapshot-lock` directory and recovering any incomplete destination.
247
+ Use relative project-local imports for snapshot content. Absolute filesystem imports and remote resources retain their external ownership. If a snapshot process crashes, check that it has stopped before removing its `versions/.snapshot-lock` directory; the next snapshot replaces any `.<id>-pending` folder it left.
232
248
 
233
- For explicit versions, configure their order and labels:
249
+ The `versions` option sets the versions, their order and their labels in place of the folder list:
234
250
 
235
251
  ```json
236
252
  {
237
253
  "versions": [
238
254
  { "id": "current", "label": "Next", "directory": "docs" },
239
- { "id": "v1", "label": "Version 1", "directory": "versioned_docs/v1/_source/docs", "publicDir": "versioned_docs/v1/_source/public" }
255
+ { "id": "v1", "label": "Version 1", "directory": "versions/v1/docs", "publicDir": "versions/v1/public" }
240
256
  ],
241
257
  "defaultVersion": "v1"
242
258
  }
243
259
  ```
244
260
 
245
- Version IDs are URL slugs; they need not follow SemVer. A document's `id` identifies it across versions independently of its filename, route and generated component name. The version selector links to that identity in the other version, even if its route changed. If it is absent, the selector explains the fallback and links to that version's home. Selection lives in the URL and survives reloads.
261
+ A site with several versions shows their links in the sidebar and the selected version's label beside the title. Version IDs are URL slugs; they need not follow SemVer. A document's `id` identifies it across versions independently of its filename, route and generated component name. The version selector links to that identity in the other version, even if its route changed. If it is absent, the selector explains the fallback and links to that version's home. Selection lives in the URL and survives reloads.
262
+
263
+ The site root serves the default version, and its pages are the canonical copies. The same pages under `/v/<default>/` are HTMLKit aliases: each has a `<link rel="canonical">` to its root copy, unless its front matter sets `head.canonical`, and `sitemap.xml` lists only the root copies. A version whose directory an earlier version already serves is an alias of it too.
246
264
 
247
265
  ## Theme and migration inputs
248
266
 
249
- The default theme imports Looma's button and badge resources and CSS exports selectively. Its shell has a skip link, a top navigation bar, a grouped sidebar per section, native version links, an "on this page" outline, a footer and a saved light/dark preference, which an inline head script applies before first paint. It uses the platform's existing runtime.
267
+ The default theme imports Looma's button and badge resources and CSS exports selectively. Its shell has a skip link, a top navigation bar, a grouped sidebar per section, native version links, an "on this page" outline, an edit link and last-updated date, a footer and a saved light/dark preference, which an inline head script applies before first paint. It uses the platform's existing runtime.
250
268
 
251
- Styles follow the component boundaries. Looma's tokens, its light and dark themes and the document defaults (`styles/base.css`) are page-wide, delivered through HTMLKit's `css` option. Each Markdown page component imports the documentation typography (`styles/prose.css`), so it styles that page and stops at any component the page embeds. The shell's own `<style>` styles the header, sidebar, columns and footer. `theme/theme.css` is page-wide too, because HTMLKit's built-in breadcrumbs and pager have no styles of their own and no component's scoped style reaches them; it also picks the logo for the theme in use. With `markdown()` alone, `markdown({ styles: [file] })` makes every Markdown page component import the given stylesheet files.
269
+ Styles follow the component boundaries. Looma's tokens, its light and dark themes and the document defaults (`styles/base.css`) are page-wide, delivered through HTMLKit's `css` option. Each Markdown page component imports the documentation typography (`styles/prose.css`), so it styles that page and stops at any component the page embeds. The shell's own `<style>` styles the header, sidebar, columns and footer. `theme/theme.css` is page-wide too, because HTMLKit's built-in breadcrumbs and pager have no styles of their own and no component's scoped style reaches them; it also picks the logo for the theme in use, and draws an accent bar along the top of the window while HTMLKit renders a page in place, when `<html>` has `data-hk-navigating`. With `markdown()` alone, `markdown({ styles: [file] })` makes every Markdown page component import the given stylesheet files.
252
270
 
253
271
  ### Navigation, logo and footer
254
272
 
255
- `markupress.config.json` (or `markupress()`) configures the shell around the pages:
273
+ The shell around the pages comes from the content. **Top navigation.** Each top-level folder with an index page is a section, linked from the top navigation by its index page's `title`, in navigation order. The `nav` option adds links after the sections. A link to the page being read, or to a section holding it, is marked current. **Logo and icon.** `public/logo.svg` is the logo, `public/logo-dark.svg` replaces it in the dark theme, and `public/favicon.svg` is the site icon. **Badge.** A site with several versions shows the selected version's label beside the title. **Footer.** `_footer.md` in a version's content directory is that version's footer. HTMLKit routes no file whose name starts with `_`, so the footer has no URL of its own:
256
274
 
257
- ```json
258
- {
259
- "title": "Looma",
260
- "logo": { "light": "/img/mark.svg", "dark": "/img/mark-dark.svg" },
261
- "favicon": "/favicon.svg",
262
- "versionBadge": "v0.28",
263
- "nav": [
264
- { "label": "Get started", "href": "/" },
265
- { "label": "Components", "href": "/components/" },
266
- { "label": "Resources", "items": [{ "label": "HTML Next", "href": "https://nextwebwg.org/declarative-components/" }] }
267
- ],
268
- "social": [{ "label": "GitHub", "href": "https://github.com/threadlabs-studio/looma", "icon": "/img/github.svg" }],
269
- "sidebar": {
270
- "/": ["/", { "label": "Foundations", "items": ["/architecture/", "/tokens/"] }],
271
- "/editor/": ["/editor/", { "label": "Tables", "href": "/editor/#tables" }]
272
- },
273
- "footer": {
274
- "columns": [{ "title": "Docs", "items": [{ "label": "Tokens", "href": "/tokens/" }] }],
275
- "copyright": "Copyright 2026 Threadlabs Studio contributors."
276
- }
277
- }
275
+ ```markdown
276
+ ## Docs
277
+
278
+ - [Tokens](/tokens/)
279
+ - [Proposal](https://nextwebwg.org/declarative-components/)
280
+
281
+ ## Project
282
+
283
+ - [GitHub](https://github.com/threadlabs-studio/looma)
284
+
285
+ Copyright 2026 Threadlabs Studio contributors.
278
286
  ```
279
287
 
280
- | Option | Does |
281
- | --- | --- |
282
- | `logo` | An image before the title: one path, or `{ "light", "dark" }`, one per theme. Paths starting with `/` are files in `public/`. |
283
- | `favicon` | The site icon, a `<link rel="icon">` on every page. |
284
- | `versionBadge` | The badge text beside the title. Without it, a site with several versions shows the selected version's label. |
285
- | `nav` | Top navigation links, and dropdowns (`{ "label", "items" }`), which are native `<details>`. A link to the page being read, or to a section holding it, is marked current. |
286
- | `social` | Links at the end of the top bar, each shown as its `icon` image, labelled by `label`, or as its label. |
287
- | `sidebar` | Explicit sidebars by section path. |
288
- | `footer` | Link columns (`{ "title", "items" }`) and a `copyright` line. |
288
+ Each heading starts a column, and each item in the list below it is a link. A paragraph after the lists is the copyright line. Anything else in the file fails the build with the file's path.
289
289
 
290
- An `href` starting with `/` is a page path in the version being read, so `/components/` opens that version's components. Other links stay as written.
290
+ In the footer and the `nav` option, an `href` starting with `/` is a page path in the version being read, so `/components/` opens that version's components. Other links stay as written.
291
291
 
292
- **Sidebars.** Each top-level folder of a version is a section with its own sidebar, generated from the folder's pages; a page at the version's root shows every page. A `sidebar` entry replaces the generated sidebar of the section at its path. Its entries are page paths, which take the page's label, links (`{ "label", "href" }`), and groups (`{ "label", "items", "collapsed" }`). A path such as `/editor/` that names a page also makes that page a section. A page path that names no page fails the build.
292
+ **Sidebars.** Each top-level folder of a version is a section with its own sidebar, generated from the folder's pages; a page at the version's root shows every page. Folders, `order` and `navigation` front matter shape every sidebar.
293
293
 
294
- **Groups.** A generated sidebar is HTMLKit's navigation tree, rendered by `<hk-nav>`: each folder below the section is a labelled group, a native `<details>` that collapses without JavaScript, nested as deep as the folders go. A folder with an index page is that page's entry, and its summary links to the page. A group starts open, unless its folder's metadata collapses it, and a group holding the current page always starts open. A `_category.yml` in the folder sets the group's metadata; Docusaurus's `_category_.json` and `_category_.yml` work too:
294
+ **Folders.** A folder is a page: its index page (`index.md`, or `01.index.md` with a number prefix) is the folder's entry, at the folder's URL, and its front matter is the folder's metadata. A generated sidebar is HTMLKit's navigation tree, rendered by `<hk-nav>`: each folder below the section is a labelled group, a native `<details>` that collapses without JavaScript, nested as deep as the folders go, and its summary links to the index page.
295
295
 
296
- ```yaml
297
- label: Forms # default: the index page's sidebarLabel, then its title, then the folder name
298
- position: 3 # orders the folder among its siblings like a 03. folder prefix
299
- collapsed: true # starts closed, except on the pages inside it
296
+ ```markdown
297
+ ---
298
+ title: Forms # labels the folder in the sidebar, breadcrumbs and pager
299
+ description: Form controls.
300
+ order: 3 # places the folder among its siblings like a 03. folder prefix
301
+ navigation: collapsed # starts the group closed, except on the pages inside it
302
+ ---
300
303
  ```
301
304
 
302
- `label` and `position` become the folder's index page's `sidebarLabel` and `sidebarPosition` (HTMLKit `hk:label` and `hk:order`), which its own front matter overrides, so the label also names the index page in breadcrumbs and the pager. A folder with a `_category` file needs an index page, and the build fails without one. `position` is a whole number. Navigation, breadcrumbs and the previous/next links follow the same order.
305
+ A group starts open unless its index page says `navigation: collapsed`, and a group holding the current page always starts open. A folder without an index page is labelled by its name. To group pages under a label with no folder URL of its own, use an HTMLKit route group: a folder named in parentheses, such as `03.(Getting started)/`, labels and orders its pages and adds no URL segment. Navigation, breadcrumbs and the previous/next links follow the same order.
306
+
307
+ **Generated index.** An index page with front matter and an empty body lists its folder: below its `title` as the heading, each page and folder directly inside it links to that page with its `description`, in navigation order. A route group's pages appear in its place, and hidden pages stay out. The list comes from HTMLKit's navigation tree, so it follows every page added, renamed or reordered.
303
308
 
304
- **Outline.** A page with headings in its `toc` range (h2 and h3 by default) shows them in an "On this page" outline beside the content on wide screens. The links are plain fragment links. A small controller (`markupress/controllers/outline.js`, also exported) marks the heading being read with `aria-current="location"`, using an IntersectionObserver; without JavaScript only that marking is missing.
309
+ **Outline.** A page with headings in its `outline` range (h2 and h3 by default) shows them in an "On this page" outline beside the content on wide screens. The links are plain fragment links. A small controller (`markupress/controllers/outline.js`, also exported) marks the heading being read with `aria-current="location"`, using an IntersectionObserver; without JavaScript only that marking is missing.
305
310
 
306
311
  **Narrow screens.** Below 769px the sidebar is a drawer: a native popover, closed until the Navigation button opens it, and it opens without JavaScript. Escape or a tap outside closes it, and following a link in it closes it.
307
312
 
308
313
  **Not found.** A `404.md` in the default version's content directory is the site's not-found page (HTMLKit's not-found page, R7). It renders in the shell for every unmatched path, including those under `/v/<id>/`, with status 404 in `dev` and `preview`, and `build` writes it to `404.html`. It is no page of its version, so it has no URL of its own and no sidebar entry.
309
314
 
315
+ ### Home page
316
+
317
+ `layout: home` makes a page a home page: a hero and feature cards, full width between the shell's top navigation and footer. The page's Markdown is the data, so it reads the same anywhere Markdown renders:
318
+
319
+ ```markdown
320
+ ---
321
+ layout: home
322
+ ---
323
+ # Markupress
324
+
325
+ Versioned documentation from Markdown, with native HTML Next components.
326
+
327
+ ![](/hero.svg)
328
+
329
+ [Get started](01.guide/01.install.md) [GitHub](https://github.com/threadlabs-studio/markupress)
330
+
331
+ ## Components in Markdown
332
+
333
+ Declare an HTML Next component in a page and use it on the next line.
334
+
335
+ ## Versions
336
+
337
+ `markupress snapshot v1` records the docs as they stand.
338
+ ```
339
+
340
+ | Markdown | Is |
341
+ | --- | --- |
342
+ | Everything before the first `##` heading | The hero. |
343
+ | The `#` heading | The hero's title. Without one, the page's `title` is. |
344
+ | A paragraph of only links | The actions: a `ui-button` link each, the first solid and the rest outlined. |
345
+ | An image alone in its paragraph | The hero image, beside the text on wide screens. |
346
+ | Any other paragraph | The tagline. |
347
+ | Each `##` heading and everything up to the next | A feature card, a Looma `ui-card` in a `ui-grid`. A card holds any Markdown: lists, code, links, images. |
348
+
349
+ The hero and cards use Looma's components and `--ui-*` tokens, in both themes. `app/layouts/home.html` replaces the home layout as an ordinary HTMLKit layout, with its own `home.server.ts` loader when it needs data. The page keeps its hero and card structure, so the site's layout supplies only the frame and styles.
350
+
351
+ ### Edit link and last updated
352
+
353
+ Each page in the shell ends with an "Edit this page" link and the date its file last changed, above the previous/next links.
354
+
355
+ **Edit link.** The `editLink` option is a URL ending in `/`, and each page's link is that URL followed by the page file's path in the project. For a site at `examples/site/` in a GitHub repository:
356
+
357
+ ```json
358
+ { "editLink": "https://github.com/threadlabs-studio/markupress/edit/main/examples/site/" }
359
+ ```
360
+
361
+ `docs/01.guide/01.authoring.md` links to `…/edit/main/examples/site/docs/01.guide/01.authoring.md`, and a snapshot's page to its file in `versions/<id>/`. A page's own `editLink` front matter takes its place, and `editLink: false` shows none.
362
+
363
+ **Last updated.** A page's date is the author date of the last commit that touched its file, as `YYYY-MM-DD` in a `<time>` element. It needs no configuration: one `git log` call reads the dates of every page when a build or development server starts. Outside a git checkout, or for a file with no commit yet, the page shows no date. A shallow clone dates every file by its oldest fetched commit, so a CI build that shows dates fetches the full history (`fetch-depth: 0` in `actions/checkout`).
364
+
310
365
  ### Layouts
311
366
 
312
- `"layout": "theme/layout.html"` in `markupress.config.json`, or `markupress({ layout })`, replaces the Markupress shell with a site's own HTML Next layout for every documentation page. The layout renders `<slot name="page">` where the page goes, and receives Markupress's loader data as props. It receives each prop it declares with `<prop>`, so it declares only the data it uses:
367
+ `app/layouts/default.html` replaces the Markupress shell with the site's own HTML Next layout for every documentation page, as it is the default layout of the site's other HTMLKit pages. The layout renders `<slot name="page">` where the page goes, and receives Markupress's loader data as props. It receives each prop it declares with `<prop>`, so it declares only the data it uses:
313
368
 
314
369
  ```html
315
370
  <template component="site-layout"><defs>
@@ -329,16 +384,20 @@ collapsed: true # starts closed, except on the pages inside it
329
384
  | `title` | The site title (`title` option, default "Documentation"). |
330
385
  | `home` | The site's base URL. |
331
386
  | `navigation` | HTMLKit navigation items for the selected version: `href`, `label`, `current` (`"page"` or `"false"`), `depth` and `pageName`. |
332
- | `sidebar` | The page's section sidebar, in `<hk-nav>`'s tree shape: entries with `label`, `current`, `open`, `depth` and `children`, and an `href` on a page or link. A folder with an index page has both `href` and `children`; a configured group has no `href`. Pass it to `<hk-nav from:items="$sidebar">`. |
333
- | `nav`, `social`, `footer` | The resolved `nav`, `social` and `footer` options: links have `label`, `href` and `current`, a dropdown has `items`, and a social link has its `icon` URL. |
334
- | `logo`, `favicon`, `badge` | The logo's `light` and `dark` URLs, the icon URL (both `null` when unset), and the badge text. |
387
+ | `sidebar` | The page's section sidebar, in `<hk-nav>`'s tree shape: entries with `label`, `current`, `open`, `depth` and `children`, and an `href` on a page. A folder with an index page has both `href` and `children`. Pass it to `<hk-nav from:items="$sidebar">`. |
388
+ | `nav` | The sections, then the `nav` option's links: each has `label`, `href`, `current` and `icon` (a URL, or `""`). |
389
+ | `footer` | `_footer.md`'s `columns`, each a `title` and `items` of links with `label`, `href` and `current`, and its `copyright`. |
390
+ | `logo`, `favicon`, `badge` | The logo's `light` and `dark` URLs, the icon URL (both `null` when absent), and the badge text. |
335
391
  | `crumbs` | Navigation items from the version home down to this page. |
336
392
  | `previous`, `next` | The neighbouring navigation items, absent at either end. |
337
393
  | `versions` | Version links: `href`, `label`, `current` and `note`, which explains a fallback to that version's home. |
338
- | `outline` | The page's headings in its `toc` range: `id`, `href` (`#id`), `label` and `depth`. |
339
- | `page` | The page's front matter, plus its resolved `title`, `label` and document `id`. |
394
+ | `outline` | The page's headings in its `outline` range: `id`, `href` (`#id`), `label` and `depth`. |
395
+ | `contents` | A generated index's entries, `label`, `href` and `description`, and an empty list on every other page. |
396
+ | `page` | The page's front matter, plus its resolved `title` and document `id`, its `editLink` (absent when it has none), and `lastUpdated`, the `YYYY-MM-DD` date its file last changed in git (absent outside git). |
397
+
398
+ A layout declares its head content in its `<defs>`, as HTMLKit pages and layouts do: `<title>`, `<meta>` and `<link>`, bound as `<title>{$title}</title>`, `from:content` and `from:href`. A page's title, meta names and canonical link win over its layout's. A `<title>`, `<meta>` or `<link>` outside `<defs>` fails the build. The Markupress shell and home layout declare `<title>{$title}</title>` and `<link rel="icon" from:href="$favicon">`, which a null favicon leaves out, so a replacement layout declares the icon it wants.
340
399
 
341
- A folder's `_layout.html` inside a version's content wraps that folder's pages inside the site layout, as in any HTMLKit site. Its `_layout.server.ts` loader reads all of the data above from `parent.markupress`:
400
+ Markupress gives the layout its loader data, so the layout has no `default.server.ts` of its own. A folder's `_layout.html` inside a version's content wraps that folder's pages inside the site layout, as in any HTMLKit site. Its `_layout.server.ts` loader reads all of the data above from `parent.markupress`:
342
401
 
343
402
  ```ts
344
403
  export function load({ parent }) { return { props: { section: parent.markupress.page.title } }; }
@@ -350,8 +409,79 @@ HTMLKit plugins compose with Markupress through the site's `htmlkit.config.ts`,
350
409
 
351
410
  Existing Markdown, HTML/CSS/Less/JavaScript/TypeScript examples, local assets, heading anchors and route aliases are migration inputs. The installed consumer fixture checks those under a nested base. A live Jess/Less documentation migration and visual/content parity comparison remain separate work.
352
411
 
412
+ ## Configuration
413
+
414
+ `markupress.config.json` is plain JSON data, read by the `markupress` command. Every key is optional:
415
+
416
+ ```json
417
+ {
418
+ "title": "Looma",
419
+ "base": "/looma/",
420
+ "nav": [
421
+ { "label": "HTML Next", "href": "https://nextwebwg.org/declarative-components/" },
422
+ { "label": "GitHub", "href": "https://github.com/threadlabs-studio/looma", "icon": "/github.svg" }
423
+ ]
424
+ }
425
+ ```
426
+
427
+ | Option | Default | Does |
428
+ | --- | --- | --- |
429
+ | `title` | `"Documentation"` | The site title in the top bar. |
430
+ | `base` | `/` | The URL path the site is served under. It starts and ends with `/`. |
431
+ | `origin` | none | The site's origin, such as `"https://example.com"`. With it, `build` writes `sitemap.xml` at the base, listing every page. |
432
+ | `robots` | none | `build` writes `robots.txt`: `true` allows every crawler, and a string is the file's text. With `origin` set, the file ends with a `Sitemap` line. |
433
+ | `contentDir` | `docs` | The current version's content directory. |
434
+ | `outDir` | `site` | Where `build` writes the static site. |
435
+ | `versions`, `defaultVersion` | the folders in `versions/`, `current` | The versions and their order and labels, and the version the site root serves (see [Versions](#versions)). |
436
+ | `lineNumbers` | `false` | Numbers the lines of every code block. |
437
+ | `nav` | none | Links after the sections in the top navigation: `{ "label", "href" }`, with an optional `icon` image shown in place of the label. |
438
+ | `logo` | `public/logo.svg` and `public/logo-dark.svg` | One image path, or `{ "light", "dark" }`, in place of the logo files. |
439
+ | `favicon` | `public/favicon.svg` | The site icon's path, in place of the icon file. |
440
+ | `editLink` | none | A URL ending in `/`, which each page's file path follows to make its "Edit this page" link (see [Edit link and last updated](#edit-link-and-last-updated)). |
441
+
442
+ Image paths starting with `/` are files in `public/`. `base`, `origin`, `robots` and `outDir` set the HTMLKit options of the same names, in place of the HTMLKit config's. An unknown key fails with the list of options.
443
+
444
+ `build` checks every link in the pages it writes. A link within the site to a page or file missing from the output, or to a `#fragment` naming no heading or element in its page, fails the build with each page and link. A `nav` or footer link starting with `/` is checked in every version, so the page it names must be in each one. A site whose links are not ready yet builds with HTMLKit's `linkCheck` option set to `"warn"` in its `htmlkit.config.ts`, which reports the same links as a warning:
445
+
446
+ ```ts
447
+ // htmlkit.config.ts
448
+ import { defineConfig } from '@nextwebwg/htmlkit';
449
+
450
+ export default defineConfig({ linkCheck: 'warn' });
451
+ ``` The programmatic `buildSite`, `devSite` and `previewSite` APIs, and the `markupress()` plugin, take the same options plus `root`, `host` and `port`; `siteOptions` returns the HTMLKit options they use.
452
+
453
+ ### Content Security Policy
454
+
455
+ HTMLKit gives every page a Content Security Policy it generates from what the site declares: `script-src` with `'self'` for HTMLKit's modules and the controllers, the origin of each declared script, and a hash of each inline head script, plus `object-src 'none'` and `base-uri 'none'`. Built pages carry it as a `<meta http-equiv="Content-Security-Policy">`, and `dev` and `preview` also send it as a header. The Markupress theme's script, which applies the saved theme before first paint, is a head script, so its hash is in the policy. The shell, code and outline controllers are modules from the site, so they run under it unchanged.
456
+
457
+ A script the site adds from another origin, such as analytics, needs that origin. Load it from `app/head.js`, which HTMLKit inlines and hashes too, and add its origin with HTMLKit's `csp` option in `htmlkit.config.ts`. A directive such as `connect-src`, which the generated policy leaves open, needs no entry:
458
+
459
+ ```js
460
+ // app/head.js
461
+ document.head.append(Object.assign(document.createElement('script'), { src: 'https://analytics.example/script.js', defer: true }));
462
+ ```
463
+
464
+ ```ts
465
+ // htmlkit.config.ts
466
+ import { defineConfig } from '@nextwebwg/htmlkit';
467
+
468
+ export default defineConfig({ csp: { 'script-src': ['https://analytics.example'] } });
469
+ ```
470
+
471
+ The added sources join the generated ones. `reportOnly: true` in the `csp` option sends `Content-Security-Policy-Report-Only` instead, which only a header can, so built pages then carry no policy; `csp: false` turns it off. `dev` reports each violation in the terminal and the browser console.
472
+
353
473
  ## Verification and contribution
354
474
 
475
+ Build and try the source with pnpm through Corepack. The example site serves at `/manual/`; open a version-specific link directly to see a reload keep the selected version:
476
+
477
+ ```sh
478
+ corepack pnpm install --frozen-lockfile
479
+ corepack pnpm build
480
+ node dist/cli.js dev --root examples/site
481
+ node dist/cli.js build --root examples/site
482
+ node dist/cli.js preview --root examples/site
483
+ ```
484
+
355
485
  ```sh
356
486
  corepack pnpm verify:inner
357
487
  corepack pnpm verify:pr
@@ -359,7 +489,7 @@ corepack pnpm exec playwright install chromium
359
489
  MARKUPRESS_BROWSER_TEST=1 corepack pnpm exec vitest run tests/package.test.ts --maxWorkers=1
360
490
  ```
361
491
 
362
- The package contract packs and installs the actual package, runs its CLI, checks frozen imports and checks controller types. The browser proof exercises production and development controllers, keyboard version selection, reloads, mobile navigation and an axe accessibility audit. CI runs that browser proof separately.
492
+ The package contract packs and installs the actual package, runs its CLI, checks frozen imports and checks controller types. The browser proof exercises production and development controllers under HTMLKit's generated Content Security Policy, failing on any violation, keyboard version selection, reloads, mobile navigation, the home page and an axe accessibility audit. CI runs that browser proof separately.
363
493
 
364
494
  Foundation pins TypeScript, Vitest, Oxlint and pnpm. [AGENTS.md](AGENTS.md) is the contributor contract; [threadlabs.config.json](threadlabs.config.json) records ownership. The [implementation plan](docs/plans/2026-10-04-1830-feat-markupress-platform-plan.md) defines product boundaries. Publishing uses the protected GitHub release workflow and npm environment.
365
495
 
package/dist/content.d.ts CHANGED
@@ -7,23 +7,27 @@ export interface MarkdownOptions {
7
7
  readonly styles?: readonly string[];
8
8
  /** Number the lines of highlighted code blocks unless a fence says `:no-line-numbers`. */
9
9
  readonly lineNumbers?: boolean;
10
- /** Front matter the page's own fields override, such as its folder's `_category` label and position on an index page. */
10
+ /** Front matter the page's own fields override, such as a site-wide default. */
11
11
  readonly frontmatter?: FrontMatter;
12
12
  }
13
13
  /** A page's validated front matter. Unknown or mistyped fields fail with the Markdown file's name. */
14
14
  export interface FrontMatter {
15
15
  readonly title?: string;
16
+ /** `<meta name="description">`, and the page's line in its folder's generated index. */
16
17
  readonly description?: string;
17
- readonly sidebarLabel?: string;
18
- readonly sidebarHidden?: boolean;
19
18
  /** The page's place among its folder's pages, as a number prefix gives it; on an index page, the folder's place (HTMLKit `hk:order`). */
20
- readonly sidebarPosition?: number;
19
+ readonly order?: number;
20
+ /**
21
+ * `hidden` leaves the page out of navigation and keeps its route (HTMLKit `hk:navigation`). On a folder's index
22
+ * page, `collapsed` starts the folder's sidebar group closed.
23
+ */
24
+ readonly navigation?: 'hidden' | 'collapsed';
21
25
  readonly id?: string;
22
26
  readonly aliases?: readonly string[];
23
27
  /** An HTMLKit layout name (app/layouts/<name>.html) in place of the site's layout, or false for none. */
24
28
  readonly layout?: string | false;
25
29
  /** The heading levels the page's outline lists, such as [2, 3], or false for no outline. */
26
- readonly toc?: false | readonly [number, number];
30
+ readonly outline?: false | readonly [number, number];
27
31
  readonly head?: {
28
32
  /** `<meta name content>` by name. */
29
33
  readonly meta?: Readonly<Record<string, string | number>>;
@@ -45,15 +49,36 @@ export interface OutlineItem {
45
49
  export interface CompiledMarkdown {
46
50
  readonly resource: string;
47
51
  readonly title: string;
48
- readonly label: string;
49
52
  readonly id?: string;
50
53
  readonly aliases: readonly string[];
51
- readonly sidebarHidden: boolean;
52
54
  readonly frontmatter: FrontMatter;
53
- /** The headings in the page's `toc` range, [2, 3] by default, in document order. */
55
+ /** The headings in the page's `outline` range, [2, 3] by default, in document order. */
54
56
  readonly outline: readonly OutlineItem[];
57
+ /** An index page with an empty body: its layout lists the folder's pages below its title. */
58
+ readonly generated: boolean;
55
59
  }
60
+ /** Whether a Markdown file is its folder's index page, which HTMLKit reads as the folder's own entry. */
61
+ export declare const isIndex: (file: string) => boolean;
56
62
  export declare function escapeHTML(value: string): string;
57
63
  export declare function literal(value: string): string;
64
+ /** A Markdown document rendered by Looma Markdown and Markupress's presentation, before Markupress's page layer. */
65
+ export interface RenderedMarkdown {
66
+ /** The document's HTML, as HTML Next template content: Markdown text escapes its braces. */
67
+ readonly html: string;
68
+ /** The front matter mapping as parsed, before any host schema reads it. */
69
+ readonly frontmatter: Readonly<Record<string, unknown>>;
70
+ /** The document after its front matter. */
71
+ readonly body: string;
72
+ /** Every heading, in document order. */
73
+ readonly headings: readonly OutlineItem[];
74
+ readonly hasCode: boolean;
75
+ /** The Looma components the document's blocks render. */
76
+ readonly components: ReadonlySet<string>;
77
+ }
78
+ /**
79
+ * The Looma Markdown layer: splits the front matter, parses with Looma's plugin and renders with Markupress's
80
+ * presentation. Front matter fields are left to the host; compileMarkdown validates Markupress's.
81
+ */
82
+ export declare function renderMarkdown(source: string, options: Pick<MarkdownOptions, 'file' | 'lineNumbers'>): Promise<RenderedMarkdown>;
58
83
  export declare function compileMarkdown(source: string, options: MarkdownOptions): Promise<CompiledMarkdown>;
59
84
  //# sourceMappingURL=content.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,KAAK,MAAM,CAAC;IACxE,gGAAgG;IAChG,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,0FAA0F;IAC1F,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,yHAAyH;IACzH,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;CACpC;AACD,sGAAsG;AACtG,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,yIAAyI;IACzI,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,yGAAyG;IACzG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACjC,4FAA4F;IAC5F,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,CAAC,EAAE;QACd,qCAAqC;QACrC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;QAC1D,6FAA6F;QAC7F,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;QACxD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC;AACD,0CAA0C;AAC1C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AACD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AACD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAA8D;AA0G5G,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsNzG"}
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"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"}