markupress 0.1.0 → 0.2.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
@@ -26,18 +26,21 @@ The example serves at `/manual/`. Open a version-specific link directly to verif
26
26
  pnpm add markupress @nextwebwg/html-next @threadlabs/looma
27
27
  ```
28
28
 
29
- Create `docs/01.index.md` and optionally `markupress.config.json`:
29
+ 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
30
 
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.
31
+ | File | Is |
32
+ | --- | --- |
33
+ | `docs/index.md` | The version home, which every version needs. A number prefix such as `01.index.md` is allowed. |
34
+ | `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. |
35
+ | `docs/404.md` | The not-found page. |
36
+ | `docs/_footer.md` | The footer: a heading per column, a list of links under each, and a copyright paragraph. |
37
+ | `public/logo.svg`, `public/logo-dark.svg` | The logo before the title, and its dark-theme version. |
38
+ | `public/favicon.svg` | The site icon. |
39
+ | `app/layouts/default.html` | A layout that replaces the Markupress shell (see [Layouts](#layouts)). |
40
+ | `htmlkit.config.ts` | HTMLKit options and plugins. |
41
+ | `versions/<id>/` | A recorded version, written by `markupress snapshot` (see [Versions](#versions)). |
42
+
43
+ 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
44
 
42
45
  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
46
 
@@ -64,7 +67,6 @@ export default defineConfig({ plugins: [markdown()] });
64
67
  ---
65
68
  title: Installation
66
69
  description: Install the project and run your first example.
67
- sidebarLabel: Install
68
70
  id: install
69
71
  aliases: [/getting-started/]
70
72
  ---
@@ -87,22 +89,21 @@ Front matter is a YAML mapping. Each field becomes HTMLKit page metadata or page
87
89
 
88
90
  | Field | Type | Effect |
89
91
  | --- | --- | --- |
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. |
92
+ | `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. |
93
+ | `description` | string | `<meta name="description">`, and the page's line in its folder's generated index. |
94
+ | `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. |
95
+ | `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
96
  | `id` | string | The document identity across versions, independent of the file name and route. |
96
97
  | `aliases` | list of route paths | Further URLs serving the page, relative to its version (HTMLKit `hk:alias`). |
97
98
  | `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. |
99
+ | `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
100
  | `head` | mapping | `meta` (name to content), `og` (Open Graph property without `og:`, to content) and `canonical` (a URL), rendered into the document head. |
100
101
  | `editLink` | URL or `false` | The page's "Edit this page" URL, as `page.editLink` for the layout. `false` asks the layout to show none. |
101
102
 
102
103
  ```yaml
103
104
  ---
104
105
  title: Installation
105
- toc: [2, 4]
106
+ outline: [2, 4]
106
107
  editLink: https://github.com/org/repo/edit/main/docs/01.guide/02.install.md
107
108
  head:
108
109
  meta: { keywords: "install, setup" }
@@ -111,11 +112,11 @@ head:
111
112
  ---
112
113
  ```
113
114
 
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.
115
+ 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
116
 
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.
117
+ 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; 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. `navigation: hidden` hides a document from navigation while keeping its route available. Aliases do not add duplicate sidebar entries.
117
118
 
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:
119
+ 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
120
 
120
121
  ````markdown
121
122
  ::: warning Before you upgrade
@@ -146,6 +147,17 @@ Usage examples.
146
147
  == API
147
148
  The reference.
148
149
  :::
150
+
151
+ ::toc{depth=2}
152
+
153
+ Status :chip[Ready]{tone=green}, owned by :mention[Ada Lovelace]{id=u1}.
154
+
155
+ - [x] Read the changelog
156
+ - [ ] Run the migration
157
+
158
+ ::: figure The build, from *source* to release
159
+ ![The build pipeline](./pipeline.svg){width=640 placement=center}
160
+ :::
149
161
  ````
150
162
 
151
163
  | Markdown | Renders |
@@ -154,9 +166,15 @@ The reference.
154
166
  | `> [!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
167
  | `::: details Title`, with `{open}` to start expanded | Native `<details><summary>Title</summary>`, which opens without JavaScript. The title defaults to "Details". |
156
168
  | `::: 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. |
169
+ | `::: 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. |
170
+ | `::: figure Caption` | `<figure>` with the caption, inline Markdown, as its `<figcaption>`. |
171
+ | `::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`. |
172
+ | `:chip[Label]{tone=green}` | An inline chip. Its tone is a hue: `neutral` (the default), `blue`, `green`, `yellow`, `red` or `purple`. |
173
+ | `:mention[Name]{id=u1}` | A mention of the entity `u1`, shown as `@Name`. |
174
+ | `- [x] Done` | A task list, when every item of the list starts with `[ ]` or `[x]`. |
175
+ | `![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
176
 
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.
177
+ 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
178
 
161
179
  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
180
 
@@ -181,10 +199,10 @@ const d = 4;
181
199
  ```
182
200
  ````
183
201
 
184
- - `{1,3-4}` highlights lines 1, 3 and 4. VitePress's spelling, with no space (```` ```ts{1} ````), also works.
202
+ - `{1,3-4}` highlights lines 1, 3 and 4, with or without a space after the language (```` ```ts{1} ````).
185
203
  - 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
204
  - `: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.
205
+ - 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
206
 
189
207
  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
208
 
@@ -226,17 +244,17 @@ markupress snapshot v1
226
244
  markupress build
227
245
  ```
228
246
 
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.
247
+ 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
248
 
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.
249
+ 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
250
 
233
- For explicit versions, configure their order and labels:
251
+ The `versions` option sets the versions, their order and their labels in place of the folder list:
234
252
 
235
253
  ```json
236
254
  {
237
255
  "versions": [
238
256
  { "id": "current", "label": "Next", "directory": "docs" },
239
- { "id": "v1", "label": "Version 1", "directory": "versioned_docs/v1/_source/docs", "publicDir": "versioned_docs/v1/_source/public" }
257
+ { "id": "v1", "label": "Version 1", "directory": "versions/v1/docs", "publicDir": "versions/v1/public" }
240
258
  ],
241
259
  "defaultVersion": "v1"
242
260
  }
@@ -252,56 +270,43 @@ Styles follow the component boundaries. Looma's tokens, its light and dark theme
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
 
@@ -309,7 +314,7 @@ collapsed: true # starts closed, except on the pages inside it
309
314
 
310
315
  ### Layouts
311
316
 
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:
317
+ `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
318
 
314
319
  ```html
315
320
  <template component="site-layout"><defs>
@@ -329,16 +334,18 @@ collapsed: true # starts closed, except on the pages inside it
329
334
  | `title` | The site title (`title` option, default "Documentation"). |
330
335
  | `home` | The site's base URL. |
331
336
  | `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. |
337
+ | `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">`. |
338
+ | `nav` | The sections, then the `nav` option's links: each has `label`, `href`, `current` and `icon` (a URL, or `""`). |
339
+ | `footer` | `_footer.md`'s `columns`, each a `title` and `items` of links with `label`, `href` and `current`, and its `copyright`. |
340
+ | `logo`, `favicon`, `badge` | The logo's `light` and `dark` URLs, the icon URL (both `null` when absent), and the badge text. |
335
341
  | `crumbs` | Navigation items from the version home down to this page. |
336
342
  | `previous`, `next` | The neighbouring navigation items, absent at either end. |
337
343
  | `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`. |
344
+ | `outline` | The page's headings in its `outline` range: `id`, `href` (`#id`), `label` and `depth`. |
345
+ | `contents` | A generated index's entries, `label`, `href` and `description`, and an empty list on every other page. |
346
+ | `page` | The page's front matter, plus its resolved `title` and document `id`. |
340
347
 
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`:
348
+ 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
349
 
343
350
  ```ts
344
351
  export function load({ parent }) { return { props: { section: parent.markupress.page.title } }; }
@@ -350,6 +357,35 @@ HTMLKit plugins compose with Markupress through the site's `htmlkit.config.ts`,
350
357
 
351
358
  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
359
 
360
+ ## Configuration
361
+
362
+ `markupress.config.json` is plain JSON data, read by the `markupress` command. Every key is optional:
363
+
364
+ ```json
365
+ {
366
+ "title": "Looma",
367
+ "base": "/looma/",
368
+ "nav": [
369
+ { "label": "HTML Next", "href": "https://nextwebwg.org/declarative-components/" },
370
+ { "label": "GitHub", "href": "https://github.com/threadlabs-studio/looma", "icon": "/github.svg" }
371
+ ]
372
+ }
373
+ ```
374
+
375
+ | Option | Default | Does |
376
+ | --- | --- | --- |
377
+ | `title` | `"Documentation"` | The site title in the top bar. |
378
+ | `base` | `/` | The URL path the site is served under. It starts and ends with `/`. |
379
+ | `contentDir` | `docs` | The current version's content directory. |
380
+ | `outDir` | `site` | Where `build` writes the static site. |
381
+ | `versions`, `defaultVersion` | the folders in `versions/`, `current` | The versions and their order and labels, and the version the site root serves (see [Versions](#versions)). |
382
+ | `lineNumbers` | `false` | Numbers the lines of every code block. |
383
+ | `nav` | none | Links after the sections in the top navigation: `{ "label", "href" }`, with an optional `icon` image shown in place of the label. |
384
+ | `logo` | `public/logo.svg` and `public/logo-dark.svg` | One image path, or `{ "light", "dark" }`, in place of the logo files. |
385
+ | `favicon` | `public/favicon.svg` | The site icon's path, in place of the icon file. |
386
+
387
+ Image paths starting with `/` are files in `public/`. An unknown key fails with the list of options. 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.
388
+
353
389
  ## Verification and contribution
354
390
 
355
391
  ```sh
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":"AAUA,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,gFAAgF;IAChF,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;CACpC;AACD,sGAAsG;AACtG,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,wFAAwF;IACxF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,yIAAyI;IACzI,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC7C,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,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrD,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,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,6FAA6F;IAC7F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,yGAAyG;AACzG,eAAO,MAAM,OAAO,SAAU,MAAM,YAA+B,CAAC;AACpE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAA8D;AAwI5G,oHAAoH;AACpH,MAAM,WAAW,gBAAgB;IAC/B,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyBtI;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuDzG"}