comark-ui 0.0.0 → 0.7.2
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/LICENSE +69 -0
- package/README.md +69 -1
- package/dist/client-Bjd6VcGQ.mjs +241 -0
- package/dist/client.d.mts +22 -0
- package/dist/client.mjs +3 -0
- package/dist/editor-BAdok66p.mjs +835 -0
- package/dist/editor-CNivEuMH.d.mts +131 -0
- package/dist/editor-vue.d.mts +57 -0
- package/dist/editor-vue.mjs +85 -0
- package/dist/editor.d.mts +2 -0
- package/dist/editor.mjs +1 -0
- package/dist/html.d.mts +13 -0
- package/dist/html.mjs +108 -0
- package/dist/index-jkwx6SyG.d.mts +175 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/nuxt-ui.d.mts +17 -0
- package/dist/nuxt-ui.mjs +488 -0
- package/dist/react.d.mts +8 -0
- package/dist/react.mjs +97 -0
- package/dist/skills/comark-ui/SKILL.md +138 -0
- package/dist/skills/comark-ui/agents/openai.yaml +3 -0
- package/dist/skills/comark-ui/references/annotate.md +65 -0
- package/dist/skills/comark-ui/references/badge.md +52 -0
- package/dist/skills/comark-ui/references/cards.md +57 -0
- package/dist/skills/comark-ui/references/carousel.md +31 -0
- package/dist/skills/comark-ui/references/cast.md +48 -0
- package/dist/skills/comark-ui/references/charts.md +80 -0
- package/dist/skills/comark-ui/references/chat.md +25 -0
- package/dist/skills/comark-ui/references/chips.md +33 -0
- package/dist/skills/comark-ui/references/cjk.md +23 -0
- package/dist/skills/comark-ui/references/code-notes.md +34 -0
- package/dist/skills/comark-ui/references/code.md +84 -0
- package/dist/skills/comark-ui/references/colors.md +33 -0
- package/dist/skills/comark-ui/references/critic.md +30 -0
- package/dist/skills/comark-ui/references/deflist.md +38 -0
- package/dist/skills/comark-ui/references/details.md +49 -0
- package/dist/skills/comark-ui/references/diagrams.md +51 -0
- package/dist/skills/comark-ui/references/diff.md +43 -0
- package/dist/skills/comark-ui/references/embed.md +40 -0
- package/dist/skills/comark-ui/references/fields.md +48 -0
- package/dist/skills/comark-ui/references/figure.md +25 -0
- package/dist/skills/comark-ui/references/file-tree.md +42 -0
- package/dist/skills/comark-ui/references/footnotes.md +26 -0
- package/dist/skills/comark-ui/references/github.md +44 -0
- package/dist/skills/comark-ui/references/icon.md +28 -0
- package/dist/skills/comark-ui/references/include.md +44 -0
- package/dist/skills/comark-ui/references/keys.md +28 -0
- package/dist/skills/comark-ui/references/links.md +43 -0
- package/dist/skills/comark-ui/references/lint.md +96 -0
- package/dist/skills/comark-ui/references/magic-move.md +34 -0
- package/dist/skills/comark-ui/references/mark.md +22 -0
- package/dist/skills/comark-ui/references/math.md +25 -0
- package/dist/skills/comark-ui/references/media.md +29 -0
- package/dist/skills/comark-ui/references/metrics.md +34 -0
- package/dist/skills/comark-ui/references/outline.md +44 -0
- package/dist/skills/comark-ui/references/properties.md +32 -0
- package/dist/skills/comark-ui/references/qr.md +20 -0
- package/dist/skills/comark-ui/references/quiz.md +45 -0
- package/dist/skills/comark-ui/references/reading-time.md +32 -0
- package/dist/skills/comark-ui/references/sources.md +24 -0
- package/dist/skills/comark-ui/references/steps.md +34 -0
- package/dist/skills/comark-ui/references/table.md +48 -0
- package/dist/skills/comark-ui/references/tabs.md +46 -0
- package/dist/skills/comark-ui/references/tests.md +59 -0
- package/dist/skills/comark-ui/references/ui.md +54 -0
- package/dist/skills/comark-ui/references/xref.md +48 -0
- package/dist/skills/comark-ui/references/zoom.md +35 -0
- package/dist/src-DROB05U6.mjs +119 -0
- package/dist/svelte/Mermaid.svelte +16 -0
- package/dist/svelte/Typeset.svelte +7 -0
- package/dist/svelte/index.d.ts +9 -0
- package/dist/svelte/index.js +80 -0
- package/dist/typeset-css-5aJ7cSmh.mjs +216 -0
- package/dist/typeset-css-DdOGL7WF.d.mts +10 -0
- package/dist/vue.d.mts +7 -0
- package/dist/vue.mjs +113 -0
- package/package.json +170 -4
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Partials and snippets from files
|
|
2
|
+
|
|
3
|
+
comark-include pastes a Markdown file, or lines of a source file as a code block, into the document before Comark parses it. Use it to keep one copy of a section that several pages share, or to show real source that stays in step with the code.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
:include{src="./parts/install.md"}
|
|
11
|
+
|
|
12
|
+
The bug estimate, straight from the source:
|
|
13
|
+
|
|
14
|
+
:include{src="/src/ducks.ts" region="estimate"}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Prop | What it does |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `src` | Required. A local file. A relative path starts at the document's folder, or at the including partial's; a leading `/` starts at `root`. |
|
|
20
|
+
| `lines` | `"10:24"` keeps lines 10 to 24, 1-based and inclusive; `"10:"` and `":24"` leave one end open. Lines count from the top of the file, frontmatter included. |
|
|
21
|
+
| `region` | Keeps the lines between `#region estimate` and `#endregion estimate` and drops the marker lines. A marker starts its line, after a comment opener such as `//`, `#`, or `<!--`. |
|
|
22
|
+
| `code` | Shows a Markdown file as a code block instead of pasting it. |
|
|
23
|
+
| `lang` | Sets the code block's language in place of the extension, and makes a Markdown file a code block too. |
|
|
24
|
+
|
|
25
|
+
The plugin pastes a `.md`, `.mdc`, or `.markdown` file without its frontmatter, and that file's own includes resolve from its folder. Any other file becomes a fenced code block in its extension's language, with a fence longer than any backtick run inside. `lines` and `region` don't combine, and the plugin dedents the lines either one keeps.
|
|
26
|
+
|
|
27
|
+
## Place it
|
|
28
|
+
|
|
29
|
+
- Write `:include` with one colon, alone on its line. A `::include` takes in the rest of the document. An include that shares its line with text or a list marker, or sits in a blockquote, renders nothing. The plugin reports all three.
|
|
30
|
+
- To put a partial in a list item, indent the include on its own line under the item's text. The pasted lines take its indent.
|
|
31
|
+
- Close every `::` block inside the partial. The plugin closes one left open at the end of its paste, and reports it.
|
|
32
|
+
- An include inside fenced code stays as written, so a page can show the syntax.
|
|
33
|
+
- No Comark renderer runs on GitHub or npm, so a README there shows `:include` as written. In a README, use automd's `file` generator, which takes the same `src`, `lines`, `code`, and `lang` and writes the text into the file.
|
|
34
|
+
|
|
35
|
+
The plugin drops an include that fails and reports why, such as a missing file, a path that leaves `root`, a URL, a range past the end, a region the file doesn't mark, a cycle, or nesting past `depth`.
|
|
36
|
+
|
|
37
|
+
## Lint it
|
|
38
|
+
|
|
39
|
+
comark-linter doesn't load comark-include, so it reports `:include` as an unknown component until a [lint config](lint.md) adds `include({ root })` to its `plugins`. That one `include()` serves every file in the run, so a relative `src` resolves from its `base`. In documents outside `base`, start `src` with `/` so it resolves the same in the app and the linter.
|
|
40
|
+
|
|
41
|
+
## Register
|
|
42
|
+
|
|
43
|
+
- In `comark-ui`: off, since it reads files from disk. `include: { root: "content", base: "guide" }` turns it on; `include: true` reads under the working directory.
|
|
44
|
+
- Alone: `pnpm add comark comark-include`, then `parseMarkdown(markdown, { plugins: [include({ root: "content", base: "guide" })] })` with `include` from `comark-include`, first in `plugins`. It has no components, because the pasted text renders like the rest of the document in any renderer, and it doesn't work with streaming parses. `root` is the folder includes can't leave (default `"."`). `base` is the document's folder under `root` (default `""`); Comark doesn't tell plugins where a document lives, so set it per document. In a browser or an edge runtime, pass `read(path)`, which returns a file's text by its POSIX path under `root`, or `undefined`, and may be async. The default reader refuses symlinks that lead out of `root`, and your own must refuse them too. `depth` caps nesting (default 8). `meta.includes` lists every file a document read or tried to read, so a watcher can rebuild the page when one changes or appears.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Keyboard shortcuts
|
|
2
|
+
|
|
3
|
+
comark-keys renders `++ctrl+shift+k++` as one `<kbd>` per key, joined by `+`, each named the way people read it. Use it wherever a guide or a bug report tells the reader which keys to press.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
Press ++ctrl+shift+k++ to open the pigeon counter, and ++esc++ to close it. On a Mac, it's ++cmd+opt+k++.
|
|
9
|
+
|
|
10
|
+
Hold ++shift++ and press ++"Duck Button"++ to page the rubber duck.
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- Write the keys between `++` and `++`, joined by single `+` signs, with no spaces. Names aren't case sensitive.
|
|
14
|
+
- Letters and digits render uppercase, `f1` to `f24` as F1 to F24, and these names as their usual labels:
|
|
15
|
+
- Modifiers: `ctrl`, `alt`, `shift`, `cmd`, `opt`, `option`, `meta`, `super`, `win`, `fn`, `altgr`.
|
|
16
|
+
- Editing: `enter`, `return`, `tab`, `esc`, `space`, `backspace`, `bksp`, `del`, `ins`, `caps-lock`.
|
|
17
|
+
- Moving: `up`, `down`, `left`, `right`, `home`, `end`, `pg-up`, `pg-dn`, `page-up`, `page-down`.
|
|
18
|
+
- Others: `print-screen`, `prtsc`, `menu`.
|
|
19
|
+
- Punctuation, by name: `plus`, `minus`, `equal`, `comma`, `period`, `slash`, `backslash`, `semicolon`, `colon`, `grave`, `tilde`, `bracket-left`, `bracket-right`, `brace-left`, `brace-right`, `less`, `greater`, `question`, `exclam`, `bar`, `underscore`, `single-quote`, `double-quote`.
|
|
20
|
+
- Quote a key, in single or double quotes, to show any other text, such as a button, a mouse click, or a numpad key: `++ctrl+"Click"++`. A backslash escapes a quote inside.
|
|
21
|
+
- `return` reads Enter. Write `++"Return"++` for a Mac's label.
|
|
22
|
+
|
|
23
|
+
One unknown name, a space, or a bare symbol, as in `++ctrl+/++`, leaves the whole shortcut as text, and nothing reports it. Check each name against the list, or quote the key. `++` right after a letter, a digit, or `+` never opens a shortcut, so `C++` stays text.
|
|
24
|
+
|
|
25
|
+
## Register
|
|
26
|
+
|
|
27
|
+
- In `comark-ui`: on by default. `keys: { separator: "", keys: { hyper: "Hyper" } }` changes what sits between two keys (default `"+"`; `""` puts nothing) and adds keys or renames built-in ones, by lowercase name.
|
|
28
|
+
- Alone: `pnpm add comark comark-keys`, then `plugins: [keys()]` with `import keys from "comark-keys"`, and the same options. It ships no styles, and browsers show `<kbd>` in a monospace font, so draw keycaps with the page's CSS on `.ckb kbd`. Each named key also gets a class with its full name, such as `ckb-control`. A `{.class}` after a shortcut replaces its `ckb` class, so skip it when the page styles `.ckb`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Heading links and new-tab links
|
|
2
|
+
|
|
3
|
+
comark-links has no blocks of its own. `anchors()` puts a `#` link beside each h2, h3, and h4, so readers can share a section. `external()` writes `target` and `rel` on links to other sites, and warns before any link opens a new tab.
|
|
4
|
+
|
|
5
|
+
## What you write
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
## Rollout plan {#rollout}
|
|
9
|
+
|
|
10
|
+
Week one is internal teams only. The [rollback steps](#rollback) apply from day one.
|
|
11
|
+
|
|
12
|
+
## Rollback {#rollback}
|
|
13
|
+
|
|
14
|
+
Turn off the `new-checkout` flag in **Settings**, then redeploy.
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- A heading's `#` link uses its id. Comark makes the id from the heading's text, so write `{#rollout}` after a heading that other pages link to, and the link survives a reworded heading.
|
|
18
|
+
- A link opens a new tab only when the host sets `target` or the link says `{target="_blank"}`. Leave both off, so the reader decides; add one only where leaving loses the reader's work, such as a page with a half-filled form. Any link that opens a new tab, on your site or another, gets `rel="noopener"`, a ↗, and "(opens in a new tab)" for screen readers, so leave that out of the link's text.
|
|
19
|
+
- A link keeps the `target` and `rel` written on it, such as `{rel="me"}` on a profile link, or `{target="_self"}` to keep one link in the tab when the host opens the others in a new one.
|
|
20
|
+
|
|
21
|
+
## Options
|
|
22
|
+
|
|
23
|
+
| `anchors()` | Default | What it does |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `levels` | `[2, 3, 4]` | The heading levels that get a link. |
|
|
26
|
+
| `label` | `Section titled "…"` | A function from the heading's text to what screen readers hear for the link. |
|
|
27
|
+
| `icon` | `"#"` | What the link shows. |
|
|
28
|
+
|
|
29
|
+
Only a heading with an id gets a link, so under Comark's `headingIds: false` only headings with `{#id}` get one.
|
|
30
|
+
|
|
31
|
+
| `external()` | Default | What it does |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `target` | `false` | Where external links open, such as `"_blank"`. |
|
|
34
|
+
| `rel` | `[]` | Words added to external links' `rel`, such as `["nofollow", "ugc"]`. |
|
|
35
|
+
| `hosts` | `[]` | Your own hosts, whose links stay internal. Each entry matches one host exactly, so list every subdomain you link to. |
|
|
36
|
+
| `test` | http, https, and `//` links not on `hosts` | A function of the `href` that says whether a link is external. It replaces the default, so `hosts` stops counting. |
|
|
37
|
+
| `icon` | `true` | Draws ↗ after a link that opens a new tab. |
|
|
38
|
+
| `label` | `"(opens in a new tab)"` | What screen readers hear after that link. `""` leaves it out. |
|
|
39
|
+
|
|
40
|
+
## Register
|
|
41
|
+
|
|
42
|
+
- In `comark-ui`: both on, as `links: { anchors, external }`, and either takes `false` or its options. With no options, `external()` opens nothing in a new tab. comark-chips runs first and turns every web link into a chip, and a chip drops the link's `target` and `rel`, both its own and `external()`'s. Write `{chip=false}` on a web link that needs them, such as `{chip=false rel="me"}`.
|
|
43
|
+
- Alone: `pnpm add comark @comark/html comark-links`, then `createHtmlRenderer({ plugins: [anchors(), external()], components })` with all three from `comark-links/html`, which has no default export. Register either plugin or both, in any order. Either one needs the components, which draw the heading links and style the new-tab warning. `comark-links/vue`, `/react`, and `/svelte` export the same for those renderers.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Checking documents
|
|
2
|
+
|
|
3
|
+
comark-linter reports what Comark forgives, such as a `::` block never closed, a misspelled component, or a link to a missing file. It also reports every problem a plugin puts in `meta.issues`, each under its own rule. Check the result in SKILL.md runs it on one file; this page covers its flags, its config file, the library, ESLint, and Vite Doctor.
|
|
4
|
+
|
|
5
|
+
## Run the command
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx lint-comark "docs/**/*.md" --format json --check-urls
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Pass files or globs. Quote a glob so the shell passes it as written; lint-comark expands it and skips `node_modules` and `dist`.
|
|
12
|
+
- It prints `file:line:column rule message fix` for each problem, or `No problems.` when there are none. It exits with 0 when clean, 1 on any problem, and 2 when a file can't be read, a glob matches nothing, or the config fails to load.
|
|
13
|
+
- `--format json` prints an array of `{ file, rule, line, column, message, fix }` instead.
|
|
14
|
+
- `--check-urls` also requests every external URL, and reports one that answers 404 or 410, has no DNS record, or fails its TLS check. It caches answers for a day in `node_modules/.cache/comark-linter/urls.json`, except a 404, 410, 429, or 5xx, which it requests again on the next run.
|
|
15
|
+
- `--config lint-comark.config.ts` loads a config. The command finds none on its own.
|
|
16
|
+
|
|
17
|
+
Run it from the project root. A relative link or image resolves from the document's folder, and a `/` path from the working directory. Each must point at a file that exists, so save the screenshots before you lint.
|
|
18
|
+
|
|
19
|
+
It lints with every comark-ui package installed, `comark-ui` first; the [comark-ui reference](ui.md) says what it turns on there. Besides each plugin's own rules, named `comark/<package>/<kind>`, it reports unclosed blocks, unknown components, an unclosed `{` or a key set twice, broken links and anchors, frontmatter that isn't valid YAML, `javascript:` and other unsafe URLs, duplicate ids, images without alt text unless `{alt=""}` marks them as decoration, and empty links. It also reports links that stop working for other readers: localhost, private network, and `file:` URLs, tunnels such as ngrok, and Vercel or Netlify deploy previews. In PR evidence, link the production page or the pull request, and write a local address as code, such as `` `localhost:3000` ``, since Comark links a bare URL.
|
|
20
|
+
|
|
21
|
+
## Config file
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// lint-comark.config.ts
|
|
25
|
+
import { defineConfig } from "comark-linter";
|
|
26
|
+
import include from "comark-include";
|
|
27
|
+
|
|
28
|
+
export default defineConfig({
|
|
29
|
+
plugins: [include({ root: "content" })],
|
|
30
|
+
components: ["duck-count"],
|
|
31
|
+
links: { routes: { "/": "content" }, skip: ["staging.example.com"] },
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `plugins` and `components` add to those of the installed packages. `components` takes names, or the components map you render with. Comark keeps the first plugin of each name and drops the rest, so a config can add plugins, such as your own or [include](include.md), and can't change the options of one an installed package registers.
|
|
36
|
+
- It takes every library option below except `file`. Node reads a `.ts` config from 22.18 on; on older versions, write it as `.mjs`.
|
|
37
|
+
|
|
38
|
+
`links` sets how the linter checks links. It applies `skip`, `block`, and `allow` in that order, then checks the file or the URL:
|
|
39
|
+
|
|
40
|
+
| Option | What it does |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `skip` | Links that skip every check, the built-in block list included. |
|
|
43
|
+
| `block` | Patterns added to the built-in list of local, private, tunnel, and preview URLs. `false` turns that list off. |
|
|
44
|
+
| `allow` | When set, the linter blocks a link to any external host it doesn't match. |
|
|
45
|
+
| `routes` | Where a `/` link finds its file. A record maps URL prefixes to folders from `root`, and the longest prefix wins. A function gets the path and returns the file's path from `root`, `true` for a page with no Markdown to check, or `undefined` when nothing serves it. By default, a `/` link resolves from `root`, as on GitHub. |
|
|
46
|
+
| `urls` | `true`, or these options, does what `--check-urls` does: `timeout` (10000 ms), `concurrency` (8, at most 2 to one host), `retries` (1), `cache` (the file above, or `false`), and `maxAge` (one day, in ms). |
|
|
47
|
+
|
|
48
|
+
A pattern is a host (`example.com`), its subdomains (`*.example.com`), a host with a port or a path (`example.com:8080`, `example.com/docs`), a RegExp tested against the link as written, or a function that gets the resolved `URL`.
|
|
49
|
+
|
|
50
|
+
## Library
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { lint, lintFiles, resolveConfig } from "comark-linter";
|
|
54
|
+
import annotate, { components } from "comark-annotate/html";
|
|
55
|
+
|
|
56
|
+
const diagnostics = await lint(markdown, { plugins: [annotate()], components, file: "docs/review.md" });
|
|
57
|
+
const results = await lintFiles(["docs/index.md", "docs/setup.md"], await resolveConfig());
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- `lint(markdown, options)` returns `{ rule, line, column?, message, fix }[]` in source order and never throws. A rule that crashes reports one diagnostic saying so, and the others still run.
|
|
61
|
+
- Pass `plugins` and `components`, the ones you render with; without `components`, it skips the unknown-component check. Pass `file`, the document's path, to check its links to files. `root` defaults to the working directory, and `read`, `exists`, and `list` replace node:fs, by absolute path. Pass `headingIds` if you render with it, so anchors match, and `links` as above.
|
|
62
|
+
- `lintFiles(paths, options)` parses each file once, checks links between them against each other's headings, and requests each URL once. It returns `{ file, text, diagnostics }` for each path, with no `text` when the file can't be read.
|
|
63
|
+
- `resolveConfig(config)` returns the installed packages' plugins and components plus the config's, as the command uses them. `rules` maps every rule to its title.
|
|
64
|
+
|
|
65
|
+
## ESLint
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
// eslint.config.js
|
|
69
|
+
import comark from "comark-linter/eslint";
|
|
70
|
+
|
|
71
|
+
export default [comark.configs.recommended, { settings: { comark: { config: "lint-comark.config.mjs" } } }];
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Use a JavaScript ES module (`export default { ... }`) for the linter config across supported Node versions. A `.ts` config requires Node 22.18+ or `--experimental-strip-types` on Node 22.12–22.17, including in the editor. Restart ESLint after editing the config or its imports; they are cached for the process lifetime.
|
|
75
|
+
|
|
76
|
+
Install `eslint` (9.15 or later) and `@eslint/markdown` (8 or later). The recommended config lints `**/*.md` and `**/*.mdc` with `@eslint/markdown`'s `markdown/commonmark` language and YAML frontmatter, and turns on every rule but `comark/dead-url`, which requests every URL; turn that one on in `rules` to check them. Each rule has the name the command prints and is configured like any ESLint rule, `<!-- eslint-disable-next-line comark/unknown-component -->` included. ESLint prints the fix after the message. `settings.comark.config`, from the working directory, loads the same config file as `--config`. oxlint can't run it: its JS plugins don't lint `.md` files, so run `lint-comark` beside `oxlint`.
|
|
77
|
+
|
|
78
|
+
## Vite Doctor
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
// doctor.config.ts
|
|
82
|
+
import comark from "comark-linter/doctor";
|
|
83
|
+
import { defineDoctorConfig } from "vite-doctor/config";
|
|
84
|
+
|
|
85
|
+
export default defineDoctorConfig({
|
|
86
|
+
extends: ["comark/recommended"],
|
|
87
|
+
extensions: [comark({ include: "content/**/*.md" })],
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Run `vite-doctor . --config doctor.config.ts --max-warnings 0`. The extension takes the config file's options plus `include` (default `**/*.{md,mdc}`) and `exclude` (default `node_modules` and `dist`), and needs vite-doctor 0.1 or later. Each rule reports under its own code, from `COMARK0001` for the core rules and a hundred per package. Unsafe URLs, invalid frontmatter, and paths that leave a plugin's `root` are errors; the rest are warnings, which Vite Doctor passes unless you add `--max-warnings 0`.
|
|
92
|
+
|
|
93
|
+
## Register
|
|
94
|
+
|
|
95
|
+
- In `comark-ui`: not included. Install it beside the preset, which it finds on its own.
|
|
96
|
+
- Alone: `pnpm add -D comark-linter`. The command is `lint-comark`, the library `comark-linter`, the ESLint plugin `comark-linter/eslint`, and the extension `comark-linter/doctor`. npm refused `comark-lint` as too close to `remark-lint`, hence the name.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Magic move
|
|
2
|
+
|
|
3
|
+
comark-magic-move animates code from one step to the next, as Shiki Magic Move does: the words and marks two steps share move to their new places, new ones fade in, and old ones fade out. Use it to tell a change to code one move at a time, such as a refactor in a tutorial.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
````md
|
|
8
|
+
::magic-move{title="Extract the rate"}
|
|
9
|
+
```ts [Start]
|
|
10
|
+
const total = price * 2
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts [Extract the rate]
|
|
14
|
+
const rate = 2
|
|
15
|
+
const total = price * rate
|
|
16
|
+
```
|
|
17
|
+
::
|
|
18
|
+
````
|
|
19
|
+
|
|
20
|
+
- `::magic-move`: every code block inside is one step, in order. Anything else inside is dropped and reported, and a block with fewer than two code blocks is reported too. `title` names its tabs for screen readers.
|
|
21
|
+
- The `[label]` after a fence's language names its step's tab, `Step 2` and so on without one. It isn't a file name there.
|
|
22
|
+
- The steps are tabs over one code block, like a code group. Opening a tab moves the code there, from any step to any other.
|
|
23
|
+
- Without the script, as in a feed reader, an email, or a screenshot of the page, the steps read one after another under their labels.
|
|
24
|
+
|
|
25
|
+
## Choose the steps
|
|
26
|
+
|
|
27
|
+
- Change one thing per step. When most of the code moves at once, the reader can't follow any of it.
|
|
28
|
+
- Keep the code that stays the same identical, spaces aside, so it moves rather than fading out and in.
|
|
29
|
+
- For a change a reviewer approves, write `::diff`: it shows every removed and added line at once, which an animation can't.
|
|
30
|
+
|
|
31
|
+
## Register
|
|
32
|
+
|
|
33
|
+
- In `comark-ui`: on, as `magicMove`, with no options.
|
|
34
|
+
- Alone: `pnpm add comark @comark/html comark-magic-move`, then `createHtmlRenderer({ plugins: [magicMove()], components })` with both from `comark-magic-move/html`. `comark-magic-move/vue`, `/react`, and `/svelte` export the same pair for those renderers.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Highlights and superscripts
|
|
2
|
+
|
|
3
|
+
comark-mark renders `==text==` as a native `<mark>` and `^text^` as `<sup>`. Use a highlight for the few words a reader should see first, such as the number that changed or the condition a fix adds, and a superscript for exponents and ordinals.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
The deploy waits while ==more than 12 pigeons== sit on the roof, about 10^3^ by Friday.
|
|
9
|
+
|
|
10
|
+
==Do not deploy=={.warning} before the 1^st^ count of the day.
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- `==text==` highlights. Markdown works inside, and `{attributes}` work after the closing pair, so `==Do not deploy=={.warning}` renders `<mark class="cmk-mark warning">`. A single `=`, as in `a = b`, stays text, and so does a `==` with nothing to close it.
|
|
14
|
+
- `^text^` is a superscript of plain text with no spaces; escape a space with a backslash, `^spaced\ text^`. `[^1]` never opens one, so footnotes keep working.
|
|
15
|
+
- There is no subscript, since GitHub reads `~text~` as strikethrough. Write `H<sub>2</sub>O`.
|
|
16
|
+
|
|
17
|
+
Highlight a few words per paragraph at most. When you highlight a whole paragraph, nothing in it stands out. For a reviewer's highlight with a comment, use comark-critic's `{==text==}{>>comment<<}` instead.
|
|
18
|
+
|
|
19
|
+
## Register
|
|
20
|
+
|
|
21
|
+
- In `comark-ui`: on by default, with its components. `mark: false` turns it off; it takes no options.
|
|
22
|
+
- Alone: `pnpm add comark comark-mark`, then `plugins: [mark()]` and `components` from `comark-mark/html`, `/vue`, `/react`, or `/svelte`. The components tint a highlight amber, readable on light and dark pages, and keep a superscript from opening a gap in its line. Set `--cmk-mark` to recolor it, on the page or on a class from `{.attributes}`. Without the components, every renderer draws plain `mark` and `sup`, which browsers paint yellow. It works beside any footnotes plugin and in streaming parses.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Math
|
|
2
|
+
|
|
3
|
+
comark-mathml typesets LaTeX formulas to MathML while parsing, so the page loads no math script, fonts, or stylesheet. Use it for a formula the prose would garble, such as a complexity bound, a rate, or how a number in the report was computed. Plain numbers and units stay text.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
The cache holds $n$ entries, and each lookup costs \(O(\log n)\).
|
|
9
|
+
|
|
10
|
+
$$
|
|
11
|
+
\text{hit rate} = \frac{\text{hits}}{\text{hits} + \text{misses}}
|
|
12
|
+
$$
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- `$…$` and `\(…\)` are inline math. The opening `$` can't be followed by a space or a digit, and the closing one can't follow a space or come before a digit, so `$5 and $10` stay text. Escape a dollar before a letter as `\$x`.
|
|
16
|
+
- `$$…$$`, `\[…\]`, and a fence with the language `math` are display math, centered on their own line. `\[1\]`, numbers in brackets, stays a citation.
|
|
17
|
+
- Math is never read inside code spans or code blocks.
|
|
18
|
+
- `\href`, `\url`, and `\includegraphics` don't render, and a macro that calls itself stops after 1,000 expansions.
|
|
19
|
+
|
|
20
|
+
The plugin reports a formula whose TeX doesn't typeset, with the typesetter's reason, and renders its TeX in its place. Fix the TeX, or put it in code when it isn't math.
|
|
21
|
+
|
|
22
|
+
## Register
|
|
23
|
+
|
|
24
|
+
- In `comark-ui`: on by default, in place of Comark's own math plugin. `math: { macros: { "\\R": "\\mathbb{R}" } }` defines commands every formula can use. `math: { output: "htmlAndMathml" }` uses KaTeX's HTML instead, which needs `katex` installed and `katex/dist/katex.css` on the page.
|
|
25
|
+
- Alone: `pnpm add comark @comark/html comark-mathml`, then `plugins: [math()]` and `components` from `comark-mathml/html` (or `/vue`, `/react`, `/svelte`). It is named `math`, and Comark keeps the first plugin of a name, so remove `comark/plugins/math` from the list instead of adding this one after it.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Videos and audio
|
|
2
|
+
|
|
3
|
+
comark-media plays an image that points at a video or a sound file, and a GitHub attachment link alone on its line, with the alt text as the player's label. Use it for a screen recording in PR evidence: a transition, a loading state, or a sound, which a screenshot can't show.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
The spinner now stops once the build finishes:
|
|
9
|
+
|
|
10
|
+
{loop muted}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- A `src` that ends in `.mp4`, `.webm`, `.mov`, or `.m4v`, before any `?` or `#`, plays as a video, and one in `.mp3`, `.wav`, `.ogg`, or `.m4a` as audio. Every other image stays an image.
|
|
14
|
+
- The alt text is required. It names the player for screen readers, and it's the text of a link to the file where the browser can't play it.
|
|
15
|
+
- `autoplay`, `loop`, and `muted` are off by default. Browsers start a file by itself only when it's muted, so `autoplay` needs `muted`. `poster="frame.png"` shows an image before a video plays; audio takes none. `{loop=false}` turns a prop off. Other props, such as `width` or a class, are dropped and reported.
|
|
16
|
+
- A GitHub attachment link, `https://github.com/user-attachments/assets/<id>`, plays as a video when the bare URL sits alone in its paragraph, as on GitHub. With text of its own, or inside a sentence, it stays a link.
|
|
17
|
+
|
|
18
|
+
## Record the evidence
|
|
19
|
+
|
|
20
|
+
- Record only the part of the screen that changes, for a few seconds.
|
|
21
|
+
- Encode it as H.264 `.mp4` for every browser, or `.webm` for a smaller file. A `.mov` from macOS doesn't always play in Firefox.
|
|
22
|
+
- Say what happens in the alt text, so a reader who can't play the file still learns what it shows.
|
|
23
|
+
- Add `loop muted` to a clip of one motion, so it reads like an animated screenshot once played.
|
|
24
|
+
- A GitHub attachment from a private repository plays only for readers signed in with access to it. For a page that others read, download the file and serve it with the page.
|
|
25
|
+
|
|
26
|
+
## Register
|
|
27
|
+
|
|
28
|
+
- In `comark-ui`: on. `media: { controls: "native" }` keeps the browser's own controls and leaves the script out.
|
|
29
|
+
- Alone: `pnpm add comark @comark/html comark-media`, then `createHtmlRenderer({ plugins: [media()], components })` with both from `comark-media/html`. `comark-media/vue`, `/react`, and `/svelte` render it too. Its place in `plugins` doesn't matter. It reads the tokens before any plugin sees the tree, so comark-zoom never zooms a video and comark-chips never turns an attachment link into a chip. The HTML renderer inlines the players' script once per document, and the browser's own controls show until it runs and whenever a file can't play.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Numbers before and after
|
|
2
|
+
|
|
3
|
+
comark-metrics draws `:delta` as a number before and after with its arrow, percent, and verdict, `::stats` as a table of befores and afters with a Change column, and `:progress` as a progress bar. Use them where a pull request claims that a size or a speed changed, next to the claim. The plugin works out every change while parsing, so write the two numbers and leave the percent to it.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
Moving the map to a worker cut the main bundle :delta{from=412 to=388 unit="kB" better="lower"} on every page.
|
|
9
|
+
|
|
10
|
+
::stats{better="lower"}
|
|
11
|
+
| Metric | Before | After |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| JavaScript | 412 kB | 388 kB |
|
|
14
|
+
| Largest paint | 2.4 s | 1.9 s |
|
|
15
|
+
| Requests | 38 | 41 |
|
|
16
|
+
::
|
|
17
|
+
|
|
18
|
+
Tests moved to Vitest: :progress{value=17 max=20 label="Migrated"}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- `:delta` reads `412 kB → 388 kB ↓ 5.8%`. `from` and `to` are required numbers, and `unit`, such as `kB`, `s`, or `%`, goes after both. `better` is `lower` for sizes and durations or `higher` for scores. It colors the change green when it's better and red when it's worse; without it the change is gray. The percent is of `from`, to one decimal. From 0 there is no percent, so the change is the difference, such as `↑ 1.5 s`.
|
|
22
|
+
- `::stats` holds one Markdown table of three columns: a name, the value before, and the value after, under any headers. A cell is a number, with commas if you like, and an optional unit after it, the same in both cells of a row. `better` applies to every row, and `unit` names a difference from 0 for cells written without one. The plugin drops anything else inside the block and reports it.
|
|
23
|
+
- `:progress` is a native `<progress>` with its percent after it. `value` is required: a fraction from 0 to 1, or a count up to `max`, which is 1 by default. `label` shows before the bar and names it for screen readers. The plugin clamps a `value` outside 0 to `max` and reports it.
|
|
24
|
+
|
|
25
|
+
## Write the numbers
|
|
26
|
+
|
|
27
|
+
- Copy the numbers from the tool's output as it prints them: `1,204` and `2.4 s` both read.
|
|
28
|
+
- In `from`, `to`, and `value`, write the number alone and put the unit in `unit`. Write a percent of progress as `value=0.85`, or as `value=85 max=100`.
|
|
29
|
+
- `better` covers the whole table. When some rows are better lower, such as sizes, and others better higher, such as scores, write two tables.
|
|
30
|
+
|
|
31
|
+
## Register
|
|
32
|
+
|
|
33
|
+
- In `comark-ui`: on. It takes no options.
|
|
34
|
+
- Alone: `pnpm add comark @comark/html comark-metrics`, then `createHtmlRenderer({ plugins: [metrics()], components })` with both from `comark-metrics/html`. `comark-metrics/vue`, `/react`, and `/svelte` render it too. It has no options and no script.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Table of contents
|
|
2
|
+
|
|
3
|
+
comark-outline turns `::toc` into a nested list of links to the document's headings. Put one in a review or a docs page that runs past a screen or two, so the reader sees what it covers and jumps to the part they own.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
The redesign moves the filters into one row and adds an empty state.
|
|
9
|
+
|
|
10
|
+
::toc{title="On this page"}
|
|
11
|
+
::
|
|
12
|
+
|
|
13
|
+
## Filters
|
|
14
|
+
|
|
15
|
+
### Toolbar
|
|
16
|
+
|
|
17
|
+
## Empty state
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
| Prop | What it does |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `depth` | The deepest heading it lists, a whole number from 2 to 6. Default 3, so h2 and h3. |
|
|
23
|
+
| `title` | A small title above the list. It also names the `nav` for screen readers; without it, the name is "Contents". |
|
|
24
|
+
|
|
25
|
+
- Close the block with `::` on the next line, with nothing between. The plugin drops and reports anything inside.
|
|
26
|
+
- Put it after the summary paragraph, and keep headings short, since they become the links.
|
|
27
|
+
- The list starts at h2, since the h1 is the page's title. Each heading nests under the closest heading above it of a higher level, so a skipped level still nests.
|
|
28
|
+
- It lists headings inside other blocks too, such as each `###` step of a `::steps`. Set `depth=2` to keep them out.
|
|
29
|
+
- Each link goes to the heading's id, its own `{#id}` when it has one, and reads as the heading's plain text.
|
|
30
|
+
- Every `::toc` lists the whole document, wherever it sits. With no heading from h2 to `depth`, it renders nothing, and the plugin reports it.
|
|
31
|
+
- Write `::toc` where other tools use `[[toc]]`, which Comark reads as a span.
|
|
32
|
+
|
|
33
|
+
## Side outline
|
|
34
|
+
|
|
35
|
+
The `rail` option adds an "On this page" outline beside the text of every document with two headings or more to list, and marks the section being read. It isn't Markdown: a document can't turn it on, and `::toc` still works beside it. Write short h2 and h3 headings, since they become its links.
|
|
36
|
+
|
|
37
|
+
- Options: `rail: true`, or `{ title, depth }` (default "On this page" and 3).
|
|
38
|
+
- The page sets `--comark-inset-end` on `:root` for a panel that covers its right edge. While the outline keeps a column beside wide blocks, it sets `data-comark-rail` and `--comark-rail-width` on `<html>`, and the page shrinks `--comark-wide-end` by it, so wide blocks move toward the room before the text.
|
|
39
|
+
- It hides where it doesn't fit, and rather than narrow a diagram.
|
|
40
|
+
|
|
41
|
+
## Register
|
|
42
|
+
|
|
43
|
+
- In `comark-ui`: on in every entry; `outline: { rail: true }` adds the side outline.
|
|
44
|
+
- Alone: `pnpm add comark @comark/html comark-outline`, then `createHtmlRenderer({ plugins: [outline()], components })` with both from `comark-outline/html`. `comark-outline/vue`, `/react`, and `/svelte` render it too. It reads the headings itself, so it works in any plugin order and without Comark's toc plugin, which only fills `meta.toc` for a sidebar. The npm name is `comark-outline` because npm refused `comark-toc`.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# A properties box from the frontmatter
|
|
2
|
+
|
|
3
|
+
comark-properties shows chosen frontmatter keys, such as a review's status, pull request, author, date, and tags, as a box of properties on the page. Use it when a reviewer should scan those facts first, and a script should read them from the frontmatter too.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
---
|
|
9
|
+
status: Ready for review
|
|
10
|
+
pr: https://github.com/onmax/comark-ui/pull/111
|
|
11
|
+
author: onmax
|
|
12
|
+
date: 2026-10-23
|
|
13
|
+
tags: [colors, docs]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Palette page
|
|
17
|
+
|
|
18
|
+
::properties
|
|
19
|
+
::
|
|
20
|
+
|
|
21
|
+
The palette page now shows every token of the theme.
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- The box shows the host's `keys`, by default `status`, `pr`, `author`, `date`, and `tags`, in that order. A key that's missing or empty is skipped, and the rest of the frontmatter, such as a layout or a slug, stays off the page.
|
|
25
|
+
- The box goes first in the document. To put it somewhere else, such as under the title, write `::properties` there and close it with `::` on the next line. It takes no props and no content; choosing keys is the host's job.
|
|
26
|
+
- Text, a number, `true`, and `false` show as written, and so does a date such as `2026-10-23`. A GitHub or GitLab pull request, merge request, or issue link shows as `owner/repo#123`, any other http(s) link as its host and path, and a list as one badge per item. An object or a list of lists doesn't show, and is reported.
|
|
27
|
+
- Each label is its key capitalized, with `pr` as PR, unless the host names it.
|
|
28
|
+
|
|
29
|
+
## Register
|
|
30
|
+
|
|
31
|
+
- In `comark-ui`: on, with the default keys, so a document whose frontmatter has any of them gets the box at the top. `properties: { keys: ["status", "owner", "due"], labels: { due: "Due date" } }` picks the keys and their order and names their labels. `top: false` shows the box only where `::properties` sits.
|
|
32
|
+
- Alone: `pnpm add comark @comark/html comark-properties`, then `createHtmlRenderer({ plugins: [properties()], components })` with both from `comark-properties/html`. It takes the same options. `comark-properties/vue`, `/react`, and `/svelte` render it too.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# QR codes
|
|
2
|
+
|
|
3
|
+
comark-qr draws `:qr` as a QR code, an inline SVG encoded while parsing, with no script and no image to fetch. Put one beside a preview link, so a reviewer at a desk clicks and one with a phone scans.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
| Preview | Phone |
|
|
9
|
+
| --- | --- |
|
|
10
|
+
| [pr-123.preview.example.com](https://pr-123.preview.example.com) | :qr{value="https://pr-123.preview.example.com"} |
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`value` is the one prop, and it's required: the text to encode, usually a link, up to about 2,300 characters. `:value="frontmatter.preview"` takes it from the frontmatter. `:qr` is inline, so it sits in a sentence, a table cell, or a list item. Write a caption outside it, since the plugin drops text in its brackets. A `:qr` with no `value`, or one too long to encode, renders nothing and is reported.
|
|
14
|
+
|
|
15
|
+
The code is black on white in both color schemes and 8rem wide, with error correction level M and a two-module quiet zone. Screen readers announce it as "QR code:" followed by its value. Encode the same link the text beside it shows, so both open one page.
|
|
16
|
+
|
|
17
|
+
## Register
|
|
18
|
+
|
|
19
|
+
- In `comark-ui`: on. It takes no options.
|
|
20
|
+
- Alone: `pnpm add comark @comark/html comark-qr`, then `createHtmlRenderer({ plugins: [qr()], components })` with both from `comark-qr/html`. `comark-qr/vue`, `/react`, and `/svelte` render it too. It has no options and no script; `--cqr-size` on `.cqr` sets the width.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Lessons and quizzes
|
|
2
|
+
|
|
3
|
+
comark-quiz adds `::question`, `::poll`, and `::recall` blocks for interactive lessons. Questions check one or more answers and explain each option, polls collect votes, and recall cards reveal an answer for self-testing. Without its script, the prompt, options, and answer disclosure remain readable HTML.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
::question{#event-loop-order}
|
|
9
|
+
Which callback runs first once the current script ends?
|
|
10
|
+
|
|
11
|
+
- [x] A resolved promise's `then` callback
|
|
12
|
+
> Promise callbacks are microtasks.
|
|
13
|
+
- [ ] A `setTimeout(fn, 0)` callback
|
|
14
|
+
> A timer queues a task.
|
|
15
|
+
|
|
16
|
+
#hint
|
|
17
|
+
Two queues are involved.
|
|
18
|
+
|
|
19
|
+
#explain
|
|
20
|
+
After every task, the event loop empties the microtask queue.
|
|
21
|
+
::
|
|
22
|
+
|
|
23
|
+
::poll{#next-lesson}
|
|
24
|
+
What should the next lesson cover?
|
|
25
|
+
|
|
26
|
+
- [ ] Web Workers
|
|
27
|
+
- [ ] Streams
|
|
28
|
+
::
|
|
29
|
+
|
|
30
|
+
::recall{#why-microtasks}
|
|
31
|
+
Why does a promise callback run before a zero-delay timer?
|
|
32
|
+
|
|
33
|
+
#answer
|
|
34
|
+
Promise callbacks are microtasks, and a timer's callback is a task.
|
|
35
|
+
::
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
A question uses radios when one option is marked `[x]`; `multiple` makes it a checkbox question. Options are shuffled deterministically unless `shuffle="false"`. Questions and recall cards share numbering, while polls are not numbered. A missing id is derived from the block content. Use `#hint` and `#explain` on questions, and `#answer` on recall cards. Polls accept `multiple` and a date in `closes`.
|
|
39
|
+
|
|
40
|
+
The HTML renderer inlines the quiz script and styles. Hosts can listen for `comark-quiz:answer` and `comark-quiz:ready`, and restore answers or results with `comark-quiz:state`; see the package [getting started guide](../../packages/quiz/docs/getting-started.md) for the event shapes and storage behavior.
|
|
41
|
+
|
|
42
|
+
## Register
|
|
43
|
+
|
|
44
|
+
- In `comark-ui`: on, as `quiz`, with optional `{ labels }` for translated UI text.
|
|
45
|
+
- Alone: `pnpm add comark @comark/html comark-quiz`, then `createHtmlRenderer({ plugins: [quiz()], components })` with `quiz, { components }` from `comark-quiz/html`. The HTML renderer is required because the blocks need its inlined script; there are no Vue, React, or Svelte entries.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Reading time
|
|
2
|
+
|
|
3
|
+
comark-reading-time counts a document's words while parsing and writes them to `meta.readingTime` as `{ words, minutes }`. It has no syntax and draws nothing. The host's layout shows the numbers, such as "7 min read" under the title.
|
|
4
|
+
|
|
5
|
+
## What it counts
|
|
6
|
+
|
|
7
|
+
- The text a reader reads. It leaves out code blocks, frontmatter, and comments, and counts inline code.
|
|
8
|
+
- Each Chinese, Japanese, or Korean character counts as a word, since those languages put no spaces between words.
|
|
9
|
+
- `minutes` is `words` over `wordsPerMinute`, rounded up, so any text takes at least a minute. It is 0 only for a document with no words.
|
|
10
|
+
- `countWords(text)`, also exported, counts any string the same way, such as an excerpt.
|
|
11
|
+
|
|
12
|
+
## Show it
|
|
13
|
+
|
|
14
|
+
With `@comark/html`, parse once, read the meta, and render that document:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { renderHtmlFromDocument } from "@comark/html";
|
|
18
|
+
import { parseMarkdown } from "comark";
|
|
19
|
+
import comarkUi from "comark-ui/html";
|
|
20
|
+
|
|
21
|
+
const { plugins, components } = comarkUi();
|
|
22
|
+
const document = await parseMarkdown(markdown, { plugins });
|
|
23
|
+
const body = await renderHtmlFromDocument(document, { components });
|
|
24
|
+
const html = `<p class="byline">${document.meta.readingTime.minutes} min read</p>${body}`;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
With `@comark/vue` or `@comark/react`, read `meta.readingTime` from the parsed document you pass to `MarkdownDocument`.
|
|
28
|
+
|
|
29
|
+
## Register
|
|
30
|
+
|
|
31
|
+
- In `comark-ui`: on in every entry. `readingTime: { wordsPerMinute: 230 }` sets the speed. The default, 200, is a cautious pace for technical prose; 230 to 250 suits light reading, and a site mostly in CJK may want more.
|
|
32
|
+
- Alone: `pnpm add comark comark-reading-time`, then `parseMarkdown(markdown, { plugins: [readingTime()] })` with `readingTime` from `comark-reading-time`. It has no components, so it works with any renderer.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Citations and sources
|
|
2
|
+
|
|
3
|
+
comark-sources turns `:cite[n]` into a numbered link to the nth source in the document's `::sources` list, and gives each source a link back to every citation of it. Use it when the claims in a document rest on links, such as the research behind a change.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
Pigeons find home by the sun and by the earth's magnetic field :cite[1], and some by smell too :cite[2,3].
|
|
9
|
+
|
|
10
|
+
::sources
|
|
11
|
+
1. [Magnetoreception in homing pigeons](https://en.wikipedia.org/wiki/Magnetoreception)
|
|
12
|
+
2. [Olfactory navigation](https://en.wikipedia.org/wiki/Pigeon_intelligence), on smell
|
|
13
|
+
3. [How do homing pigeons navigate?](https://www.sciencefocus.com/nature/how-do-homing-pigeons-navigate)
|
|
14
|
+
::
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- `:cite[2]` cites the second source, and `:cite[1,3]` several. Each becomes a superscript `[1]` that links to its source. Hovering or focusing it shows a card with the source's title, its site, and the icon the list shows for it, a globe when there's none. Put a space before `:cite`, which the plugin removes; `word:cite[1]` stays text, and nothing reports it.
|
|
18
|
+
- `::sources` holds one numbered list with one source per item, each with its link. Sources count by their place in the list. Text after the link, such as `, on smell`, stays in the item. `title` names the list, "Sources" by default. The plugin drops anything beside the list and reports it.
|
|
19
|
+
- A document has one `::sources`, anywhere in it, and citations before and after it both count. A citation of a number the list doesn't have, a source with no link, and a second list are reported.
|
|
20
|
+
|
|
21
|
+
## Register
|
|
22
|
+
|
|
23
|
+
- In `comark-ui`: on. It takes no options.
|
|
24
|
+
- Alone: `pnpm add comark @comark/html comark-sources`, then `createHtmlRenderer({ plugins: [sources()], components })` with both from `comark-sources/html`. `comark-sources/vue`, `/react`, and `/svelte` render it too. Citations are plain `sup` links that work in any renderer, and the `sources` component draws the list and carries the styles for both. It has no options. The HTML renderer inlines the card's script once per document, and the components start it when they mount.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Numbered steps
|
|
2
|
+
|
|
3
|
+
comark-steps turns the headings inside `::steps`, or one numbered list, into numbered steps joined by a line. Use it for instructions the reader follows in order, such as how to reproduce a bug or set up a project.
|
|
4
|
+
|
|
5
|
+
## Syntax
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
::steps
|
|
9
|
+
### Open the dashboard
|
|
10
|
+
Go to **Correlations** and pick the Berlin office.
|
|
11
|
+
|
|
12
|
+
### Add three filters
|
|
13
|
+
Add **Moon phase**, **Vibes**, and **Rubber ducks**.
|
|
14
|
+
|
|
15
|
+
### Narrow the window
|
|
16
|
+
At 1024 px wide, **Apply** drops under the filter chips.
|
|
17
|
+
::
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- Each `###` heading starts a step that holds everything up to the next one, code blocks and images included. The heading keeps its id, so a link to `#add-three-filters` and a table of contents still find it.
|
|
21
|
+
- `level` is the level of the headings that start each step, from 2 to 6, default 3. Write `::steps{level="2"}` for `##` headings. Deeper headings stay inside their step.
|
|
22
|
+
- A block whose only content is one numbered list makes a step of each item, and a list that starts at 3 numbers its steps from 3. Use the list when every step fits on one line.
|
|
23
|
+
- A `:::steps{level="4"}` inside a step keeps its own headings and numbers.
|
|
24
|
+
|
|
25
|
+
The syntax is Nuxt UI's `::steps`, so the same Markdown renders under Nuxt UI.
|
|
26
|
+
|
|
27
|
+
## Keep only steps inside
|
|
28
|
+
|
|
29
|
+
Put any introduction above `::steps`, since the block holds only steps. A paragraph before the first heading renders above the steps, and a paragraph beside a numbered list turns the whole block into plain content. Both are reported. The block drops its own `#id` and `.class` without a report, so link to a step through its heading's id.
|
|
30
|
+
|
|
31
|
+
## Register
|
|
32
|
+
|
|
33
|
+
- In `comark-ui`: on, as `steps`, with no options.
|
|
34
|
+
- Alone: `pnpm add comark @comark/html comark-steps`, then `createHtmlRenderer({ plugins: [steps()], components })` with both from `comark-steps/html`. `comark-steps/vue`, `/react`, and `/svelte` export the same pair for those renderers. It takes no options and goes anywhere in `plugins`.
|