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.
Files changed (78) hide show
  1. package/LICENSE +69 -0
  2. package/README.md +69 -1
  3. package/dist/client-Bjd6VcGQ.mjs +241 -0
  4. package/dist/client.d.mts +22 -0
  5. package/dist/client.mjs +3 -0
  6. package/dist/editor-BAdok66p.mjs +835 -0
  7. package/dist/editor-CNivEuMH.d.mts +131 -0
  8. package/dist/editor-vue.d.mts +57 -0
  9. package/dist/editor-vue.mjs +85 -0
  10. package/dist/editor.d.mts +2 -0
  11. package/dist/editor.mjs +1 -0
  12. package/dist/html.d.mts +13 -0
  13. package/dist/html.mjs +108 -0
  14. package/dist/index-jkwx6SyG.d.mts +175 -0
  15. package/dist/index.d.mts +3 -0
  16. package/dist/index.mjs +3 -0
  17. package/dist/nuxt-ui.d.mts +17 -0
  18. package/dist/nuxt-ui.mjs +488 -0
  19. package/dist/react.d.mts +8 -0
  20. package/dist/react.mjs +97 -0
  21. package/dist/skills/comark-ui/SKILL.md +138 -0
  22. package/dist/skills/comark-ui/agents/openai.yaml +3 -0
  23. package/dist/skills/comark-ui/references/annotate.md +65 -0
  24. package/dist/skills/comark-ui/references/badge.md +52 -0
  25. package/dist/skills/comark-ui/references/cards.md +57 -0
  26. package/dist/skills/comark-ui/references/carousel.md +31 -0
  27. package/dist/skills/comark-ui/references/cast.md +48 -0
  28. package/dist/skills/comark-ui/references/charts.md +80 -0
  29. package/dist/skills/comark-ui/references/chat.md +25 -0
  30. package/dist/skills/comark-ui/references/chips.md +33 -0
  31. package/dist/skills/comark-ui/references/cjk.md +23 -0
  32. package/dist/skills/comark-ui/references/code-notes.md +34 -0
  33. package/dist/skills/comark-ui/references/code.md +84 -0
  34. package/dist/skills/comark-ui/references/colors.md +33 -0
  35. package/dist/skills/comark-ui/references/critic.md +30 -0
  36. package/dist/skills/comark-ui/references/deflist.md +38 -0
  37. package/dist/skills/comark-ui/references/details.md +49 -0
  38. package/dist/skills/comark-ui/references/diagrams.md +51 -0
  39. package/dist/skills/comark-ui/references/diff.md +43 -0
  40. package/dist/skills/comark-ui/references/embed.md +40 -0
  41. package/dist/skills/comark-ui/references/fields.md +48 -0
  42. package/dist/skills/comark-ui/references/figure.md +25 -0
  43. package/dist/skills/comark-ui/references/file-tree.md +42 -0
  44. package/dist/skills/comark-ui/references/footnotes.md +26 -0
  45. package/dist/skills/comark-ui/references/github.md +44 -0
  46. package/dist/skills/comark-ui/references/icon.md +28 -0
  47. package/dist/skills/comark-ui/references/include.md +44 -0
  48. package/dist/skills/comark-ui/references/keys.md +28 -0
  49. package/dist/skills/comark-ui/references/links.md +43 -0
  50. package/dist/skills/comark-ui/references/lint.md +96 -0
  51. package/dist/skills/comark-ui/references/magic-move.md +34 -0
  52. package/dist/skills/comark-ui/references/mark.md +22 -0
  53. package/dist/skills/comark-ui/references/math.md +25 -0
  54. package/dist/skills/comark-ui/references/media.md +29 -0
  55. package/dist/skills/comark-ui/references/metrics.md +34 -0
  56. package/dist/skills/comark-ui/references/outline.md +44 -0
  57. package/dist/skills/comark-ui/references/properties.md +32 -0
  58. package/dist/skills/comark-ui/references/qr.md +20 -0
  59. package/dist/skills/comark-ui/references/quiz.md +45 -0
  60. package/dist/skills/comark-ui/references/reading-time.md +32 -0
  61. package/dist/skills/comark-ui/references/sources.md +24 -0
  62. package/dist/skills/comark-ui/references/steps.md +34 -0
  63. package/dist/skills/comark-ui/references/table.md +48 -0
  64. package/dist/skills/comark-ui/references/tabs.md +46 -0
  65. package/dist/skills/comark-ui/references/tests.md +59 -0
  66. package/dist/skills/comark-ui/references/ui.md +54 -0
  67. package/dist/skills/comark-ui/references/xref.md +48 -0
  68. package/dist/skills/comark-ui/references/zoom.md +35 -0
  69. package/dist/src-DROB05U6.mjs +119 -0
  70. package/dist/svelte/Mermaid.svelte +16 -0
  71. package/dist/svelte/Typeset.svelte +7 -0
  72. package/dist/svelte/index.d.ts +9 -0
  73. package/dist/svelte/index.js +80 -0
  74. package/dist/typeset-css-5aJ7cSmh.mjs +216 -0
  75. package/dist/typeset-css-DdOGL7WF.d.mts +10 -0
  76. package/dist/vue.d.mts +7 -0
  77. package/dist/vue.mjs +113 -0
  78. package/package.json +170 -4
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: comark-ui
3
+ description: Writes PR evidence, explanations of how something works, and docs pages in Markdown with comark-ui. Use when a document needs an annotated screenshot, a before/after, a chart, a diagram, a code change, or another comark block, or when setting up comark-ui or a comark-* package.
4
+ ---
5
+
6
+ # comark-ui
7
+
8
+ You write Markdown; the comark plugins render it as static HTML, from marks on screenshots and charts drawn from tables to code changes, diagrams, and steps. The reader sees only what you measured and wrote down, so measure every mark from the page, take every number from the data, and check the render before you hand it over.
9
+
10
+ ## Install
11
+
12
+ Prefer `comark-ui`, every plugin in one install, registered in the order they need:
13
+
14
+ ```sh
15
+ pnpm add comark @comark/html comark-ui
16
+ ```
17
+
18
+ ```ts
19
+ import { createHtmlRenderer } from "@comark/html";
20
+ import comarkUi from "comark-ui/html";
21
+
22
+ const render = createHtmlRenderer(comarkUi());
23
+ ```
24
+
25
+ Every plugin is on except `include`. The [comark-ui reference](references/ui.md) covers its options, the plugins that read files or use the network, and its Vue, React, and Svelte entries.
26
+
27
+ When a project wants only some plugins, install those packages and register each plugin with its components:
28
+
29
+ ```sh
30
+ pnpm add comark @comark/html comark-annotate
31
+ ```
32
+
33
+ ```ts
34
+ import { createHtmlRenderer } from "@comark/html";
35
+ import annotate, { components } from "comark-annotate/html";
36
+
37
+ const render = createHtmlRenderer({ plugins: [annotate()], components });
38
+ ```
39
+
40
+ Each reference ends with a Register section that says how its package registers alone and which options it needs.
41
+
42
+ Either way, the HTML renderer inlines the styles once per document, and the Vue, React, and Svelte components add them on mount. On a server-rendered page, import each package's `style.css`, such as `comark-cards/style.css`, so the page doesn't shift. The `--comark-*` tokens in https://comark-ui.onmax.me/guide/theming/ theme every plugin at once. In a Nuxt UI app the plugins follow its `--ui-*` variables with no setup; for a page rendered outside the app, such as review evidence, put the CSS from `nuxtUiTheme({ colors: appConfig.ui?.colors, css: mainCss })` in `comark-ui/nuxt-ui` in a `<style>`, so the page matches the app.
43
+
44
+ Problems land in `meta.issues` after `parseMarkdown`, one sentence each, and a host can refuse a document while any remain. For untrusted input, add Comark's `security` plugin with `blockedTags` such as `script`, `style`, and `iframe`; it cleans the Markdown inside blocks too.
45
+
46
+ ## Pick the blocks
47
+
48
+ Read the reference for each block you use before you write it. Mix the blocks freely with ordinary Markdown, such as headings, tables, and lists.
49
+
50
+ ### Review blocks
51
+
52
+ | To show | Write | Reference |
53
+ | --- | --- | --- |
54
+ | One state of a screen, with marks, numbered notes, or arrows | `::screenshot` | [annotate](references/annotate.md) |
55
+ | A change on screen, before and after | `::compare` | [annotate](references/annotate.md) |
56
+ | A command's output, such as a test run or a build, as colored text | `::cast` | [cast](references/cast.md) |
57
+ | Numbers, such as sizes, timings, before/after metrics, a trend, or a timeline | `::chart` | [charts](references/charts.md) |
58
+ | A flow, a sequence, an architecture, or a schema as a diagram | a `mermaid`, `d2`, `dot`, `plantuml`, `dbml`, `goat`, or `svgbob` fence | [diagrams](references/diagrams.md) |
59
+ | Links and `@mentions` with the site's favicon or the person's avatar | links, `@user`, `:chip` | [chips](references/chips.md) |
60
+ | Colors, such as design tokens before and after, or a palette | a color in inline code, `::palette` | [colors](references/colors.md) |
61
+ | A suggested change to one file, before and after | `::diff` | [diff](references/diff.md) |
62
+ | A video, a design file, or a live reproduction from its link | `::embed` | [embed](references/embed.md) |
63
+ | A screen recording or a sound that a screenshot can't show | `![alt](clip.mp4)` | [media](references/media.md) |
64
+ | A number before and after, a table of befores and afters, or progress | `:delta`, `::stats`, `:progress` | [metrics](references/metrics.md) |
65
+ | Data from a CSV, TSV, or JSON fence or file, as a table that copies and sorts | `::table`, a `csv` fence | [table](references/table.md) |
66
+ | A test run's passed, failed, and skipped counts and its failures, from a JUnit report | `::tests` | [tests](references/tests.md) |
67
+ | A note, tip, or warning box, or long output folded away, as on GitHub | `> [!NOTE]`, `<details>` | [github](references/github.md) |
68
+ | The review's status, pull request, author, date, and tags in a box | frontmatter, `::properties` | [properties](references/properties.md) |
69
+ | Claims backed by links, with numbered citations | `:cite`, `::sources` | [sources](references/sources.md) |
70
+ | An icon in a sentence or a table cell, such as a logo or a status | `:icon` | [icon](references/icon.md) |
71
+ | A preview link a reviewer can scan with a phone | `:qr` | [qr](references/qr.md) |
72
+ | Why a line of code is there, as numbered notes under it | `// <1>` and a numbered list | [code-notes](references/code-notes.md) |
73
+
74
+ ### Page behavior
75
+
76
+ | To show | Write | Reference |
77
+ | --- | --- | --- |
78
+ | A stable link to a section, or a link that opens a new tab | `{#id}`, `{target="_blank"}` | [links](references/links.md) |
79
+ | A table, code block, or diagram too small to read in place | `::zoom` | [zoom](references/zoom.md) |
80
+ | A sequence to page through in one place, such as the screens of a flow | `::carousel` | [carousel](references/carousel.md) |
81
+ | Versions of one thing where each reader needs only one, such as an OS or a package manager | `::tabs`, `::code-group`, `pm` | [tabs](references/tabs.md) |
82
+ | Code with lines marked added, removed, or highlighted, or a pasted `git diff` | `ins={4}` after a fence's language, `diff lang="ts"` | [code](references/code.md) |
83
+
84
+ ### Docs blocks
85
+
86
+ | To show | Write | Reference |
87
+ | --- | --- | --- |
88
+ | Content a reader may skip, such as a long log, behind a summary | `::details`, `::accordion`, `::collapsible` | [details](references/details.md) |
89
+ | Folders and files, with what a change added, removed, or changed | `::file-tree` | [file-tree](references/file-tree.md) |
90
+ | A component's props or a function's options, with types and defaults | `::field` | [fields](references/fields.md) |
91
+ | A version, status, or deprecation tag in a heading or a sentence | `:badge` | [badge](references/badge.md) |
92
+ | Links to the next pages as cards, or blocks side by side | `::card-group`, `::columns` | [cards](references/cards.md) |
93
+ | Instructions the reader follows in order, such as repro or setup steps | `::steps` | [steps](references/steps.md) |
94
+ | Code that changes over several steps, such as a refactor told one move at a time | `::magic-move` | [magic-move](references/magic-move.md) |
95
+ | An interactive lesson question, poll, or recall card | `::question`, `::poll`, `::recall` | [quiz](references/quiz.md) |
96
+
97
+ ### Markdown syntax
98
+
99
+ | To show | Write | Reference |
100
+ | --- | --- | --- |
101
+ | Bold or italics in Chinese, Japanese, or Korean text | `**…**`, `*…*` | [cjk](references/cjk.md) |
102
+ | An image with a caption under it | `![alt](src "Caption")` | [figure](references/figure.md) |
103
+ | A highlighted phrase or a superscript | `==text==`, `^2^` | [mark](references/mark.md) |
104
+ | A suggested edit or a comment inside the text | `{++ ++}`, `{-- --}`, `{~~ ~> ~~}`, `{== ==}`, `{>> <<}` | [critic](references/critic.md) |
105
+ | Terms and their definitions, such as a glossary | `Term`, then `: definition` | [deflist](references/deflist.md) |
106
+ | A numbered note at the end of the page, such as a source or a caveat | `[^label]`, `^[note]` | [footnotes](references/footnotes.md) |
107
+ | A formula, such as a complexity bound or how a number was computed | `$…$`, `$$…$$` | [math](references/math.md) |
108
+ | A keyboard shortcut | `++ctrl+k++` | [keys](references/keys.md) |
109
+
110
+ ### Authoring tools
111
+
112
+ | To show | Write | Reference |
113
+ | --- | --- | --- |
114
+ | A shared section, or real source lines, pasted from a file | `:include{src}` | [include](references/include.md) |
115
+ | A table of contents of the page's headings | `::toc` | [outline](references/outline.md) |
116
+ | Figures and tables numbered and referred to by number | `{#fig-name}`, `:ref{#fig-name}` | [xref](references/xref.md) |
117
+ | Word count and reading time, for the page's layout | nothing; it writes `meta.readingTime` | [reading-time](references/reading-time.md) |
118
+
119
+ ### Chat
120
+
121
+ | To show | Use | Reference |
122
+ | --- | --- | --- |
123
+ | An AI SDK chat message, with its reasoning, tool calls, sources, and files | `MessageParts` from `comark-chat` | [chat](references/chat.md) |
124
+
125
+ ## Marks
126
+
127
+ Screenshots and charts draw the same marks. Each `::annotation` is one mark and its note, written in Markdown.
128
+
129
+ - `mark` is `box` (default), `circle`, `underline`, `highlight`, `bracket`, `cross`, `check`, or `none`, which draws nothing, so its note needs a `side` and the arrow alone points.
130
+ - `color` is `violet` (default) to point something out, `red` for a problem, `amber` for a warning, `green` for a fix, `blue` for a second voice, `gray` for neutral, or a 3- or 6-digit hex color.
131
+ - `side` is `top`, `bottom`, `left`, or `right`, and puts the note in that margin with an arrow to its mark. Notes without one are numbered in a list under the figure.
132
+
133
+ Mark only what your text talks about. Each note says what changed or what is wrong in the words the screen or the table shows, so the reader can check it against the figure; a value the figure doesn't show stays out of the note.
134
+
135
+ ## Check the result
136
+
137
+ 1. Lint the file: `npx lint-comark report.md`, in a project with `comark-linter` and the plugins installed, where `comark-ui` counts as every plugin. It prints one line per problem with its line, its rule, and how to fix it, and exits with 1 while any remain. Fix every one, and run it again until it exits with 0. The [lint reference](references/lint.md) covers its flags, its config file, the library, ESLint, and Vite Doctor.
138
+ 2. Look at the render, made as the block's reference says. Open it in a browser and take a screenshot: each mark sits on its target, each number matches its note, and each chart says what your text says. If you can't verify something, say so when you hand the document over instead of guessing.
@@ -0,0 +1,3 @@
1
+ interface:
2
+ display_name: "comark-ui"
3
+ short_description: "Write review documents and docs with every comark plugin"
@@ -0,0 +1,65 @@
1
+ # Screenshots and compares
2
+
3
+ comark-annotate draws `::screenshot` and `::compare`. Their marks take the values under Marks in SKILL.md.
4
+
5
+ ## Choose the layout
6
+
7
+ Pick what explains the change best:
8
+
9
+ - One `::screenshot` with marks, when a single state shows the point.
10
+ - A `::compare` of two screenshots, when the change is the point. Capture both at the same viewport, zoom, scroll, and page state, and set the same `viewport` on both. Each image gets under half the page width, where the text of a full-page capture is too small to read, so clip both captures to the same region around the change, such as Playwright's `screenshot({ clip })`.
11
+ - `::compare{mode="slider"}` instead, when both screenshots show the same spot and the change is small, such as a number or a label at the same place. The reader drags a divider across one full-width image instead of reading two half-width ones. Side notes become numbered notes there, so skip `side`.
12
+ - A side note for the two or three points that matter most. With `mark="none"` and `to` on the exact spot, the arrow alone points and nothing covers the screen, which reads cleanest. Other notes stay numbered in the list under the image. In a side-by-side compare only `top` and `bottom` keep their arrows; left and right notes join the list.
13
+
14
+ For a review the reader should follow without a key, write it the way a designer marks up a board: the default ink with no `color`, every note a side note in the margin nearest its point, set off with `at` so its arrow sweeps in, and a `bracket` where a note covers a run of rows. Write the notes of a compare as a pair that argue the change: "The type gets the headline…" on the before, "The message becomes the starting point." on the after.
15
+
16
+ Mark the change at its own size: the bar, field, or label that changed. What stayed the same goes unmarked, or in one sentence above the screenshots. Keep it to about six marks per screenshot, fewer in a compare.
17
+
18
+ ## Syntax
19
+
20
+ ```md
21
+ ::compare
22
+ :::screenshot{src="before.png" alt="Filter toolbar wrapping onto a second row" viewport="1440x900" href="https://pr12.preview.example.com/correlations"}
23
+ ::::annotation{x=3.6 y=20.6 w=92.9 h=31.4 color="red"}
24
+ Moon phase and vibes don't fit, so the toolbar wraps and pushes the table down.
25
+ ::::
26
+ :::
27
+
28
+ :::screenshot{src="after.png" alt="Filter toolbar on one row" viewport="1440x900"}
29
+ ::::annotation{x=87.6 y=24.2 w=7.4 h=9.9 mark="check" color="green"}
30
+ **Apply** lines up with the chips.
31
+ ::::
32
+ :::
33
+ ::
34
+ ```
35
+
36
+ - `::compare`: `title` is optional and names the comparison for screen readers. `mode` is `columns` (default, side by side) or `slider`. `ink` colors every annotation in both screenshots that doesn't set its own `color`. Its two screenshots are titled Before and After unless they set their own.
37
+ - `::screenshot`: `src` and `alt` are required; `title` (the heading above the image), `href` (the captured page, linked by its host), `viewport` (like `1440x900`), and `ink` are optional. Other content becomes the caption.
38
+ - `::annotation`: `x y w h` are required percentages of the image, numbers from 0 to 100 without `%`. `mark` is `box` (default), `circle`, `underline`, `line` (straight through the middle, along the longer side), `highlight`, `bracket`, `cross`, `check`, or `none`, which needs a `side`. `color` is `violet` (default) to point something out, `red` for a problem, `amber` for a warning, `green` for a fix, `blue` for a second voice, `gray` for neutral, or a 3- or 6-digit hex color. `side` is `top`, `bottom`, `left`, or `right`; keep top or bottom notes at least a quarter of the image apart and left or right notes an eighth apart.
39
+ - `at` moves a side note along its margin, as a percentage of the image (across for top and bottom, down for left and right). A note set off from its point reads away from it, so put it on the side with room: a top note at `at=40` pointing left of 40 reads to the right. `to="x,y"` is the point the arrow lands on, measured like the box; without it the arrow meets the middle of the mark's facing edge. Both need a `side`.
40
+ - Annotations must be direct children of a screenshot.
41
+
42
+ ## Measure every mark
43
+
44
+ Take each mark's coordinates from the page, in the browser you captured. Measure the element and divide by the viewport:
45
+
46
+ ```js
47
+ const r = document.querySelector("SELECTOR").getBoundingClientRect();
48
+ [(r.x / innerWidth) * 100, (r.y / innerHeight) * 100, (r.width / innerWidth) * 100, (r.height / innerHeight) * 100].map((v) => +v.toFixed(1));
49
+ ```
50
+
51
+ For a full-page screenshot, add `scrollX`/`scrollY` and divide by `document.documentElement.scrollWidth` and `scrollHeight` instead.
52
+ For a clipped capture, `getBoundingClientRect()` and Playwright's main-frame `clip` both use viewport-relative coordinates, so the formula takes no scroll offsets. Make sure the clip fully contains every marked element. Subtract the clip's `x` and `y` and divide by its `width` and `height`; if an element crosses the clip edge, intersect its measured rectangle with the clip before normalizing so every value stays between 0 and 100. If the clip is in document coordinates, convert it to viewport coordinates before subtracting.
53
+
54
+ ## Preview
55
+
56
+ ```sh
57
+ npx comark-annotate render report.md -o report.html
58
+ ```
59
+
60
+ `render` writes a standalone page of the screenshots and compares, and relative `src` paths resolve next to it. Without comark-linter, `npx comark-annotate check report.md` lists these blocks' problems on its own. It names each one like `screenshot "after.png", annotation 2`, counting annotations within their screenshot, and exits with 1 while any remain.
61
+
62
+ ## Register
63
+
64
+ - In `comark-ui`: on, in `comark-ui/html` only.
65
+ - Alone: `pnpm add comark @comark/html comark-annotate`, then `createHtmlRenderer({ plugins: [annotate()], components })` with both from `comark-annotate/html`. It renders only to HTML and takes no options. Each document inlines the styles once, with style attributes for positions, so a strict Content Security Policy needs `style-src 'unsafe-inline'`.
@@ -0,0 +1,52 @@
1
+ # Badges
2
+
3
+ comark-badge draws a small tinted label for a version, a status, or a deprecation. Use it to tag an API as deprecated or new, or a heading with the release that added it.
4
+
5
+ ## Syntax
6
+
7
+ ```md
8
+ ## useAsyncData :badge[deprecated]{color="warning"}
9
+
10
+ Use `useFetch` instead. :badge[v4]{color="info"} :badge[stable]{color="success"}
11
+
12
+ ::badge
13
+ **v4.0.0**
14
+ ::
15
+ ```
16
+
17
+ Write a badge inline as `:badge[text]`, or as Nuxt UI's block `::badge` with its text inside. The plugin unwraps the block's paragraph, so it renders as one badge and keeps its inline Markdown.
18
+
19
+ For a prose badge, `color` is `primary` (default), `secondary`, `success`, `info`, `warning`, `error`, or `neutral`. The plugin shows any other color as primary and reports it, and it reports an empty badge. Nuxt UI's own prose badge ignores `color` and is always primary.
20
+
21
+ ## Write badges
22
+
23
+ - Keep a badge to a word or two, such as a version or a status, and pick the color by meaning: `warning` for deprecated, `success` for new or stable, `error` for removed, and `info` for a version.
24
+ - A badge in a heading joins the heading's id, so `## useFetch :badge[deprecated]` links as `#usefetch-deprecated`. Link to that id, or put the badge in the text under the heading to keep `#usefetch`.
25
+ - The plugin drops `{#id}` and `{.class}` on a badge without a report, so set its look with `color` or the theme.
26
+
27
+ ## Image and live badges
28
+
29
+ Use image mode for README-style label/value badges. Static images embed their SVG. Live sources fetch npm or GitHub data while parsing and embed it too, as of the build; with `download: false`, or when the data is unavailable, they load a hosted image.
30
+
31
+ ```md
32
+ :badge[Comark UI]{label="built with" icon="package" color="#15803d"}
33
+ :badge{source="npm-version" package="@scope/name"}
34
+ :badge{source="npm-downloads" package="comark-ui" period="month"}
35
+ :badge{source="github-actions" repository="onmax/comark-ui" workflow="ci.yml" branch="main"}
36
+ ```
37
+
38
+ - `source`: `static`, `npm-version`, `npm-downloads`, or `github-actions`. A static image needs `value` or bracket text. npm needs `package`; GitHub needs a public `repository` and a YAML `workflow` filename. Set `branch` to select a branch; otherwise GitHub returns the latest run across branches.
39
+ - `label` and `value` hold at most 80 characters. Use bound JSON string attributes when text contains quotes or braces; the builder produces these for you.
40
+ - `icon`: `none`, `package`, `npm`, `npmx`, `nuxt`, `github`, `comark`, `shadcn`, or `check`. npm sources pick npm, npmx, or Nuxt to match the preset; GitHub Actions uses GitHub. Explicit icons override these defaults.
41
+ - `preset`: `default`, `npmx`, `nuxt`, `flat`, or `for-the-badge`. npmx uses monospace text and a colored slash. Nuxt uses a navy label and green value block, with status colors for CI. Flat draws a compact 20px badge; for-the-badge draws a 28px uppercase badge. Both use npm as the default logo for npm sources. All presets except default ignore `style` and `labelColor`; `theme` still applies, though Nuxt, flat, and for-the-badge keep their palettes and only add an outline in dark mode.
42
+ - `style`: `shadcn` by default, `subtle`, or `solid`; `theme`: `auto` by default, following the page's light or dark theme (on GitHub too), or `light` or `dark`. `color` and `labelColor` accept semantic names or six-digit hex colors. In the default preset, shadcn draws an outline with neutral text; `labelColor` applies to subtle and solid backgrounds. CI chooses its own status color. Images embed colors and do not inherit the document's CSS.
43
+ - `period`: `day`, `week`, or `month` for downloads. Hosted values cache for five minutes for versions, one hour for downloads, and one minute for CI. Failures display `unavailable` for 30 seconds. Downloaded SVGs are snapshots.
44
+
45
+ The builder at `/badges/` lives in the docs layout, shares its configuration and export format in the query string, and exports Comark, Markdown, an image URL, and SVG. The image endpoint is `/badge.svg`, with the same props as query parameters. Preserve the existing prose syntax for inline tags that should follow the page's theme.
46
+
47
+ ## Register
48
+
49
+ - In `comark-ui`: on, under the key `badge`, with optional `{ endpoint, download }` for live images.
50
+ - Alone: `pnpm add comark @comark/html comark-badge`, then `createHtmlRenderer({ plugins: [badge()], components })` with both from `comark-badge/html`. It accepts `{ endpoint }`, an HTTPS URL without credentials, and `{ download: false }` to skip fetching while parsing, and needs no script. Colors read Nuxt UI's `--ui-*` variables, and primary follows shadcn/ui's `--primary`, else the text color. `comark-badge/vue`, `/react`, and `/svelte` render it too.
51
+
52
+ Host images with `createBadgeHandler()` from `comark-badge/server`. Generate static SVG with `readBadge()` and `badgeSvg()` from `comark-badge/svg`. The server entry fetches only public APIs and uses no token.
@@ -0,0 +1,57 @@
1
+ # Cards and columns
2
+
3
+ comark-cards draws cards, link cards, and card groups, and lays blocks out in columns, in Nuxt UI's syntax. Use link cards to send readers to the next pages, and columns to set two short texts side by side, such as a before and an after.
4
+
5
+ ## Syntax
6
+
7
+ ```md
8
+ ::card-group
9
+ :::card{title="Renderers" icon="lucide:rocket" to="/guide/renderers/"}
10
+ Render with HTML, Vue, React, or Svelte.
11
+ :::
12
+
13
+ :::card{title="Source on GitHub" icon="lucide:github" to="https://github.com/onmax/comark-ui" color="neutral"}
14
+ Read the code, or open an issue.
15
+ :::
16
+ ::
17
+ ```
18
+
19
+ | Prop | What it does |
20
+ | --- | --- |
21
+ | `title` | The card's title. A `#title` slot wins over it and can hold Markdown; then write the body after `#default`. |
22
+ | `description` | The text shown when the card has no content of its own. |
23
+ | `icon` | An icon above the title, in the card's color, named as in [icon](icon.md), such as `lucide:rocket`. The plugin reports one it can't draw. |
24
+ | `to` | Makes it a link card. One link covers the card and takes the title as its name, and links in the card's text still open on their own. A web address opens in a new tab with an arrow in the corner. |
25
+ | `target` | Where the link opens, such as `_self` to keep a web address in the tab. |
26
+ | `color` | Tints the icon and a hovered link card's border: `primary` (default), `secondary`, `success`, `info`, `warning`, `error`, or `neutral`. |
27
+
28
+ `::card-group` sets its cards in two columns, or one when the group is narrower than 36rem, so a group in a sidebar stacks too. A card works on its own as well.
29
+
30
+ ```md
31
+ ::columns{cols="2"}
32
+ :::column
33
+ **Before**
34
+
35
+ One long page of options.
36
+ :::
37
+
38
+ :::column
39
+ **After**
40
+
41
+ Each option under its own heading.
42
+ :::
43
+ ::
44
+ ```
45
+
46
+ Each block directly inside `::columns` is one cell, and `:::column` groups several blocks into one cell. `cols` is 1 to 6, default 2. Every row stacks into one column when the block is narrower than 36rem.
47
+
48
+ ## Write link cards
49
+
50
+ - Give every link card a `title`, since it names the link for screen readers. Without one, they hear "Card link".
51
+ - Link with `to`. A card with `href` still links, but Nuxt UI ignores `href`, so the plugin reports it. A `javascript:`, `vbscript:`, or `data:` link doesn't link, and the plugin reports it too.
52
+ - For screenshots side by side, use `::compare` from [annotate](annotate.md) instead of columns.
53
+
54
+ ## Register
55
+
56
+ - In `comark-ui`: on, under the key `cards`, which takes only `true` or `false`. Its icons come from the preset's shared resolver, so `icon: { collections }` applies to them.
57
+ - Alone: `pnpm add comark @comark/html comark-cards`, then `createHtmlRenderer({ plugins: [cards()], components })` with both from `comark-cards/html`. By default icons come from the Iconify API while parsing. To share one resolver with the page's other plugins, add `comark-icon`, make one with `createIcons()`, and pass it as `cards({ iconResolver })`. The output needs no script. `comark-cards/vue`, `/react`, and `/svelte` render it too.
@@ -0,0 +1,31 @@
1
+ # Carousels
2
+
3
+ comark-carousel turns the blocks inside `::carousel` into slides that scroll and snap, with arrows, a counter, arrow keys, and zoom. Use it for a sequence the reader pages through in one place, such as the screens of a flow or one page at several widths.
4
+
5
+ ## Syntax
6
+
7
+ ```md
8
+ ::carousel{title="Checkout at three widths"}
9
+ ![Checkout at 1440 px, with the summary beside the form](checkout-1440.png)
10
+
11
+ ![Checkout at 768 px, with the summary under the form](checkout-768.png)
12
+
13
+ ![Checkout at 375 px, with the summary folded into a bar](checkout-375.png)
14
+ ::
15
+ ```
16
+
17
+ - `::carousel`: every block inside is one slide, unless it holds `:::slide` blocks. `title` names the carousel for screen readers, default `Carousel`, and heads the zoomed view of a slide that has no title or image of its own. `columns` is `1` (default), `2`, or `3`, the slides a wide page shows at once; a narrow page shows one. `zoom=false` turns zoom off.
18
+ - `:::slide`: one slide that holds several blocks. Its `title` heads the slide's zoomed view and doesn't show on the page. Once a carousel has a `:::slide`, the blocks outside every `:::slide` are dropped and reported.
19
+ - A click zooms a slide, with the others in a strip below it. An image slide takes the image's alt text as its heading there.
20
+ - Props can be bound, such as `:columns="2"`. A bound `zoom` or `title` reads only the document and its frontmatter, since each slide takes them while parsing.
21
+
22
+ ## Choose the slides
23
+
24
+ - A paragraph after an image becomes a slide of its own. Wrap an image and its text in a `:::slide` to keep them together, and write a heading inside it when the page should show the slide's name.
25
+ - Each `:::screenshot` from comark-annotate is one slide with its marks and notes, and its `title` names the step. Use that for a walkthrough of annotated screens.
26
+ - For one view before and after a change, write annotate's `::compare` instead. It is a two-column carousel already, titles the pair Before and After, and takes marks.
27
+
28
+ ## Register
29
+
30
+ - In `comark-ui`: on, as `carousel`, with no options.
31
+ - Alone: `pnpm add comark @comark/html comark-carousel`, then `createHtmlRenderer({ plugins: [carousel()], components })` with both from `comark-carousel/html`. `comark-carousel/vue`, `/react`, and `/svelte` export the same pair for those renderers. comark-zoom installs with it, so slides zoom without `zoom()`. A page rendered on a server with Vue, React, or Svelte needs zoom's styles too: add `comark-zoom` and import `comark-zoom/style.css` beside `comark-carousel/style.css`.
@@ -0,0 +1,48 @@
1
+ # Terminal output
2
+
3
+ comark-cast replays an asciinema recording while parsing and shows what the terminal printed as colored text, with no player and no script. Use it to show a command's output in a review, such as a test run or a build, as text the reader can search and copy, colors included.
4
+
5
+ ## Syntax
6
+
7
+ ```md
8
+ The flaky homing test passes now:
9
+
10
+ ::cast{src="evidence/test.cast" title="pnpm test"}
11
+ ::
12
+ ```
13
+
14
+ | Prop | What it does |
15
+ | --- | --- |
16
+ | `src` | The cast file, a local path under the host's `root`. It reads no URLs. |
17
+ | `mode` | `screen` (default) shows the terminal's last screen, so a progress bar redrawn in place shows its last state and a cleared screen is gone. `log` shows every line printed, from the first. |
18
+ | `title` | The caption above the terminal, such as the command. |
19
+
20
+ To carry the output without a file, paste the recorded cast into a code block inside the block in place of `src`. Set one or the other; with both, it shows the file and reports the pair.
21
+
22
+ ````md
23
+ ::cast{title="pnpm build" mode="log"}
24
+ ```json
25
+ {"version": 2, "width": 52, "height": 3}
26
+ [0.3, "o", "$ pnpm build\r\n"]
27
+ [0.9, "o", "\u001b[32m✓ built in 412ms\u001b[39m\r\n"]
28
+ ```
29
+ ::
30
+ ````
31
+
32
+ A cast is in asciinema's v2 or v3 format. Its first line is a JSON header whose `width` and `height` (`term.cols` and `term.rows` in v3) size the terminal, 80 by 24 by default. Each line after it is one event: output (`"o"`) prints, a resize (`"r"`) resizes, and input, markers, and the exit code draw nothing. A full-screen app such as `vim` or `less` leaves nothing behind once it quits, as in a real terminal.
33
+
34
+ ## Record the output
35
+
36
+ ```sh
37
+ asciinema rec --cols 80 --rows 12 --command "pnpm test" evidence/test.cast
38
+ ```
39
+
40
+ - Keep `--cols` at 80 or fewer, since wider lines scroll sideways.
41
+ - Keep `--rows` small for `screen`, so the last screen holds only the end of the run.
42
+ - Use `mode="log"` when the part that matters scrolled off the screen, such as the first error of a long build.
43
+ - Run the linter from the folder the `src` paths start from, since it reads them from the folder it runs in.
44
+
45
+ ## Register
46
+
47
+ - In `comark-ui`: on, but it reads no cast files until you pass `cast: { root: "docs" }`, or `{ read }` to read them yourself. Inline casts render either way.
48
+ - Alone: `pnpm add comark @comark/html comark-cast`, then `createHtmlRenderer({ plugins: [cast({ root: "docs" })], components })` with both from `comark-cast/html`. `root` (default `"."`, the working directory) is the folder `src` paths start from, and it refuses a path or symlink that leads out of it. `read(path)` returns a file's text or `undefined`; pass it in a browser or an edge runtime. `comark-cast/vue`, `/react`, and `/svelte` render it too.
@@ -0,0 +1,80 @@
1
+ # Charts
2
+
3
+ comark-charts draws the Markdown table inside `::chart` as an SVG chart and keeps the table under it. People see the chart, and anyone reading the Markdown or the page, an agent included, reads the numbers from the table. So the table is the source of truth: write the real numbers, and name every column so its header labels the chart.
4
+
5
+ On the page, a Copy button over the chart's corner copies it as a PNG, and its menu copies the data as a table for a spreadsheet or as Markdown. A chart with marks copies its data, since the PNG would leave the marks out.
6
+
7
+ ## Write the table
8
+
9
+ ```md
10
+ ::chart{type="line" title="Coffee cups and commits, per month (r = 0.99)"}
11
+ | month | coffee cups | commits |
12
+ | --- | --: | --: |
13
+ | 2026-04 | 820 | 610 |
14
+ | 2026-05 | 890 | 640 |
15
+ ::
16
+ ```
17
+
18
+ - The first column is x: one row per bar, point, or step. Every other column is a series of numbers, named by its header.
19
+ - Cells hold plain numbers, like `1200` or `1,200`. Put the unit in the header or the title, like `size (kB)` or `Build time (ms)`. A cell that isn't a number is left out and reported.
20
+ - Dates on x read as time when they look like `2026`, `2026-04`, `2026-04-15`, or an ISO date and time.
21
+ - Inside the block, the table comes first, then any marks or Vega-Lite spec. Write explanations above or below the block.
22
+
23
+ Props, all optional: `type` (below, default `bar` whatever the table holds, so write `type="line"` for a trend), `x` (the x column, default the first), `y` (the columns to draw, comma-separated, default all the others), `title` (the caption and the chart's name for screen readers), `labels` (prints the numbers on bars, waterfall steps, dumbbell rows, sankey nodes, and gantt bars, as in `::chart{type="stacked" labels}`; without it the axis and the data table carry them), and `horizontal` (turns `bar` and `stacked` bars sideways, for long names such as file paths; past 40 bars they turn on their own). Give every chart a `title` that says what it measures and in what unit. Two or more series always get a legend over the plot.
24
+
25
+ ## Choose the type
26
+
27
+ | Data | `type` | Table |
28
+ | --- | --- | --- |
29
+ | One number per name, such as size per package | `bar` (default) | names, then one column; add `horizontal` for long names |
30
+ | Several numbers per name, compared side by side | `bar` | names, then one column per series |
31
+ | Parts that add up to a total per name | `stacked` | names, then one column per part |
32
+ | A trend over time | `line` | dates, then one column per series |
33
+ | Totals over time that add up | `area` | dates, then one column per part |
34
+ | One measure against another | `scatter` | numbers, then one column per series |
35
+ | Before and after for several metrics | `dumbbell` | metric names, then a `before` and an `after` column |
36
+ | How changes add up to a total | `waterfall` | the start total, each change (negative for a drop), then the end total, which must be the start plus every change, in one column |
37
+ | Shares of one whole, five or fewer | `pie` | names, then one column of non-negative numbers |
38
+ | Steps or tasks over time, such as a build trace, a deploy, or a plan | `gantt` | the task, then a start and an end column, as numbers or dates; a column of names, such as a run or a phase, colors the bars |
39
+ | Where an amount goes, such as a budget, a cashflow, or a bundle's bytes | `sankey` | where each flow comes from, where it goes, and how much; a flow can't loop back, and a node in the middle can't send out more than it takes in |
40
+
41
+ When none of these fit, such as a box plot or a heatmap, put a Vega-Lite spec after the table in a fenced `vega-lite` block. The table is its data, so leave `data` out of the spec and name the table's headers as fields; marks don't apply.
42
+
43
+ In a `gantt`, each row is one bar in table order, and a row that ends where it starts is a milestone, drawn as a diamond. Write dates as `2026-04-15` or `2026-04-15T09:30`; a time without a zone is UTC. To compare runs, add a column naming the run and repeat the steps in each.
44
+
45
+ For a before and after of one or two metrics, grouped `bar` reads best; past three, use `dumbbell`. Keep the rows in the order the reader should read them: charts draw them in table order.
46
+
47
+ ## Mark what matters
48
+
49
+ Point at the value the text talks about with an `::annotation` inside the chart, after the table. `at` is the row's x value copied from the table (on a `sankey`, the node's name), and `series` picks one column in that row, or on a `gantt`, the group of the bar when a task repeats. `mark`, `color`, and `side` are under Marks in SKILL.md.
50
+
51
+ ```md
52
+ ::chart{type="line" title="Coffee cups and commits, per month"}
53
+ | month | coffee cups | commits |
54
+ | --- | --: | --: |
55
+ | 2026-06 | 940 | 660 |
56
+ | 2026-07 | 1,010 | 720 |
57
+ :::annotation{at="2026-07" series="commits" mark="circle" color="green"}
58
+ The new espresso machine arrives, and commits pass 700.
59
+ :::
60
+ ::
61
+ ```
62
+
63
+ For a target, budget, or limit, draw a line across the plot: `mark="line"` with `value`, a number on the value axis, and no `at`. The axis grows to reach it.
64
+
65
+ ```md
66
+ :::annotation{mark="line" value="200" color="red" side="right"}
67
+ The 200 ms budget.
68
+ :::
69
+ ```
70
+
71
+ `mark`, `color`, and `side` work as on screenshots: red for a problem, green for a fix, and `side="top"` with `mark="none"` for the one point that matters most. The renderer finds the bar, point, or slice and draws the mark around it, so there is nothing to measure. Keep it to two or three marks a chart.
72
+
73
+ ## Preview
74
+
75
+ The linter names each chart by its position, such as `` `::chart` 2 ``. No command renders a chart on its own. When you can render the document, with the app that publishes it or the renderer under Install in SKILL.md, open it and confirm the chart says what your text says: the right type, every series named, and the numbers in the expected order.
76
+
77
+ ## Register
78
+
79
+ - In `comark-ui`: on. Marks on charts show only through `comark-ui/html`.
80
+ - Alone: `pnpm add comark @comark/html comark-charts`, then `createHtmlRenderer({ plugins: [charts()], components })` with both from `comark-charts/html`. Flint and Vega draw each chart while parsing, so the browser runs no chart code. `comark-charts/vue`, `/react`, and `/svelte` place the same SVG without its marks. `charts({ draw: false })` only checks the charts, which suits a lint step.
@@ -0,0 +1,25 @@
1
+ # comark-chat
2
+
3
+ Renders AI SDK `UIMessage` parts in Vue, React, or Svelte: text as streaming Comark Markdown, images and files, and each run of reasoning, tool calls, and sources as one trace that folds open to the thought or the call's input, output, and error. It renders only. The app sends, stores, and lays out messages, for example with `useChat()`.
4
+
5
+ ## Render a message
6
+
7
+ ```vue
8
+ <script setup lang="ts">
9
+ import { MessageParts } from "comark-chat/vue";
10
+ </script>
11
+
12
+ <template>
13
+ <MessageParts :parts="message.parts" :streaming="isLast && status === 'streaming'" />
14
+ </template>
15
+ ```
16
+
17
+ - `streaming` marks the message that is still arriving: the last thought reads "Thinking" and text shows a caret.
18
+ - `plugins`, `components`, and `options` go to every Markdown part, so `comarkUi()`'s plugins let the model write the same blocks as documents.
19
+ - Vue slots, React's `render` functions, and Svelte snippets replace a kind of part: `text`, `reasoning`, `tool`, `source`, `file`, `step`, and `fallback` for other parts, such as `data-*` parts, which render nothing without it. Vue and React also take `part`, which replaces any part. Each gets what `read()` made of the part, with `index` and `part`. `markdown` gets `{ value, streaming }` and replaces the Markdown of text and reasoning, for an app with its own Markdown component.
20
+ - `read(parts, streaming)` from `comark-chat` is what every renderer uses. It never throws; tool values become JSON text, and only http, https, named data, and blob image URLs become links or images.
21
+
22
+ ## Register
23
+
24
+ - In `comark-ui`: not included. `comark-ui` renders documents; install `comark-chat` next to it and pass `comarkUi()`'s `plugins` and `components` to `MessageParts`.
25
+ - Alone: `pnpm add comark-chat @comark/vue` (or `@comark/react`, `@comark/svelte`), then import `MessageParts` from `comark-chat/vue`, `/react`, or `/svelte`. The components add their styles on mount; a server-rendered page also imports `comark-chat/style.css`.
@@ -0,0 +1,33 @@
1
+ # Links and mentions as chips
2
+
3
+ comark-chips turns every web link and every `@user` into a chip with the site's favicon or the account's avatar. It downloads each image while parsing and inlines it, so readers' browsers ask no other site. Write links and mentions as usual; the props below change one chip.
4
+
5
+ ## Syntax
6
+
7
+ ```md
8
+ Moves the build from [webpack](https://webpack.js.org) to [Vite](https://vite.dev), reviewed by @posva. Ask [Anthony](https://antfu.me){avatar="@antfu"} about the plugin, or read the [migration guide](https://vite.dev/guide/migration){chip=false}. The docs render with :chip[Comark]{href="https://comark.dev"}.
9
+ ```
10
+
11
+ - Every `http` or `https` link becomes a chip with the site's favicon, and a bare URL loses its scheme. Relative links, anchors, `mailto:` links, a link around an image such as a badge, and a link in raw HTML stay plain.
12
+ - `@user` becomes a chip that links to `https://github.com/user`, with the GitHub avatar. It must stand alone: `me@example.com`, an npm scope such as `@comark/html`, and anything in a link or in code stay text.
13
+ - A link into an account shows the account's avatar, and link text written as `@handle` loses its `@`. That covers `github.com/user` and `x.com/user` (or `twitter.com/user`) with any page under them, `bsky.app/profile/handle`, `youtube.com/@handle`, and `gitlab.com/user`.
14
+ - `:chip[Text]{href="https://…"}` writes a chip for text that isn't a link. `href` is required and must be a web address.
15
+
16
+ | Prop | On | What it does |
17
+ | --- | --- | --- |
18
+ | `chip=false` | a link | Keeps a plain link. |
19
+ | `avatar="@user"` | a link or `:chip` | Shows that GitHub account's avatar. |
20
+ | `avatar="provider:name"` | a link or `:chip` | Shows the account's avatar on any network unavatar.io knows, such as `bluesky:sindresorhus.com`. |
21
+ | `avatar="https://…"` | a link or `:chip` | Shows this image, round like an avatar. |
22
+ | `title` | a link or `:chip` | Sets the tooltip. |
23
+
24
+ ## Mentions
25
+
26
+ Copy each `@user` from the person's GitHub profile, since a mention always means a GitHub login. The linter downloads nothing, so it can't tell whether a login exists; a build that downloads reports one with no avatar. For someone on another network, link their profile, or set `avatar="provider:name"`.
27
+
28
+ ## Register
29
+
30
+ - In `comark-ui`: on, with the options below under `chips`. Pass `chips: { download: false }` when parsing in a browser, which may not download another site's images.
31
+ - Alone: `pnpm add comark @comark/html comark-chips`, then `createHtmlRenderer({ plugins: [chips()], components })` with both from `comark-chips/html`. `comark-chips/vue`, `/react`, and `/svelte` render it too.
32
+
33
+ `download` (default `true`) inlines each image; `false` leaves its URL for the reader's browser. `favicon(host)` and `avatar(login)` return an image URL in place of Google's favicon service and the GitHub avatar. `cache` is the folder that keeps images for a week, `node_modules/.cache/comark-chips` by default, and `false` keeps them in memory. Avatars from X, Bluesky, YouTube, GitLab, and `provider:name` come through unavatar.io, which allows 25 downloads a day per IP address. In CI, set `cache` to a folder you commit, such as `.comark/chips`, so a build downloads only new images.
@@ -0,0 +1,23 @@
1
+ # Emphasis in Chinese, Japanese, and Korean
2
+
3
+ comark-cjk lets `**`, `*`, and `~~` open and close next to Chinese, Japanese, and Korean punctuation. These languages put no space after `。`, `」`, or `.`, so CommonMark leaves the asterisks on the page when a `**` sits between that punctuation and the next word. A document in one of these languages needs it.
4
+
5
+ ## What changes
6
+
7
+ ```md
8
+ **この文は太字。**次の文は普通です。
9
+
10
+ 会議は*「金曜日」*に移りました。
11
+
12
+ **이 문장은 굵게.**다음 문장은 아닙니다.
13
+ ```
14
+
15
+ The syntax stays CommonMark's. Only the rule for where a delimiter opens and closes changes, and only next to a Chinese, Japanese, or Korean character. Other text parses as before, so `**foo.**bar` still shows its asterisks.
16
+
17
+ - Write `**` and `*`. `_` and `__` still don't open or close inside a word.
18
+ - GitHub follows CommonMark here. For text that has to read the same on GitHub, close the bold before the punctuation: `**太字**。`.
19
+
20
+ ## Register
21
+
22
+ - In `comark-ui`: on by default. `cjk: false` turns it off; it takes no options.
23
+ - Alone: `pnpm add comark comark-cjk`, then `plugins: [cjk()]` with `import cjk from "comark-cjk"`. It has no components and no styles, so it works in any renderer and in streaming parses.
@@ -0,0 +1,34 @@
1
+ # Notes on lines of code
2
+
3
+ comark-code-notes turns a numbered comment at the end of a line, like `// <1>`, into a marker that links to its note in the numbered list right after the fence, and back. Use it when a reviewer would ask why a line is there.
4
+
5
+ ## Syntax
6
+
7
+ ````md
8
+ ```ts [src/pigeons.ts]
9
+ export async function countPigeons(plaza: string) {
10
+ const res = await fetch(`/api/plazas/${plaza}/pigeons`) // <1>
11
+ if (!res.ok) return 0 // <2>
12
+ return (await res.json()).length
13
+ }
14
+ ```
15
+
16
+ 1. One request per plaza, so the map loads the counts in parallel.
17
+ 2. A plaza the census hasn't reached yet counts as empty, so the page still loads.
18
+ ````
19
+
20
+ - End a line with a comment that holds its note's number in angle brackets. The comment goes and a marker takes its place. A comment with words keeps them: `// retry once <2>` shows `// retry once` and marker 2.
21
+ - A line can carry several markers, like `// <1> <2>`, and two lines can share a number.
22
+ - Write the comment the language accepts, so the code stays valid: `// <1>`, `# <1>`, `-- <1>`, `; <1>`, `% <1>`, `/* <1> */`, or `<!-- <1> -->`. Any of them works in any fence.
23
+ - Put the numbered list right after the fence, with nothing but a blank line between them. Item 1 is note 1, and a list that starts at `3.` numbers its notes from 3. Each note starts with a marker that links back to its line.
24
+ - A fence with no numbered list right after it keeps its markers as written, and is reported. A ` ```md ` fence never changes, so show the syntax in one.
25
+ - `meta.issues` also reports a marker with no note, a note with no marker, and Shiki running after the plugin.
26
+
27
+ ## Write the notes
28
+
29
+ Mark the lines a reviewer would ask about: the request that runs in parallel, the case that returns early, the number from a benchmark. Keep each note to a sentence or two, and put a longer explanation in the text around the block. The plugin pairs markers and notes by number alone, so read each note against its line. CSS draws the marker numbers, so copied code leaves them out. On GitHub, the comments stay in the code and the notes read as a list under it.
30
+
31
+ ## Register
32
+
33
+ - In `comark-ui`: on as `codeNotes`, the last plugin. The preset leaves Shiki out; add it right before comark-code, as the [code reference](code.md) shows, which keeps it ahead of code notes.
34
+ - Alone: `pnpm add comark @comark/html comark-code-notes`, then `createHtmlRenderer({ plugins: [shiki(), codeNotes()], components })` with `shiki` from `@comark/html/plugins/shiki`, and `codeNotes` and `components` from `comark-code-notes/html`. It goes after `shiki`; in the other order the code isn't highlighted and `meta.issues` says so. Without Shiki it draws the lines itself. It takes no options, and `comark-code-notes/vue`, `/react`, and `/svelte` render it too.