comark-ui 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/client.d.mts +2 -2
- package/dist/client.mjs +2 -2
- package/dist/editor-vue.mjs +1 -4
- package/dist/html.d.mts +2 -2
- package/dist/html.mjs +3 -3
- package/dist/{index-DVGSDzBS.d.mts → index-Cq3zytGw.d.mts} +70 -6
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/nodes-CwDBFXnO.mjs +5 -0
- package/dist/react.d.mts +2 -2
- package/dist/react.mjs +3 -3
- package/dist/skills/comark-ui/SKILL.md +1 -1
- package/dist/skills/comark-ui/references/code.md +2 -2
- package/dist/skills/comark-ui/references/markdown-it-to-comark.md +28 -0
- package/dist/skills/comark-ui/references/marked-to-comark.md +28 -0
- package/dist/skills/comark-ui/references/migrate-to-comark.md +33 -0
- package/dist/skills/comark-ui/references/quiz.md +1 -1
- package/dist/skills/comark-ui/references/remark-to-comark.md +9 -3
- package/dist/skills/comark-ui/references/ui.md +2 -1
- package/dist/skills.mjs +18 -6
- package/dist/src-MvpWiv9v.mjs +293 -0
- package/dist/svelte/index.d.ts +1 -1
- package/dist/svelte/index.js +1 -1
- package/dist/{typeset-css-blSNpFNK.mjs → typeset-css-BOtdXbMg.mjs} +24 -7
- package/dist/{typeset-css-DdOGL7WF.d.mts → typeset-css-DtOLAa1Z.d.mts} +7 -1
- package/dist/typeset.css +1 -0
- package/dist/vue.d.mts +2 -2
- package/dist/vue.mjs +7 -7
- package/package.json +50 -47
- package/dist/src-PkmYCSNu.mjs +0 -120
package/dist/skills.mjs
CHANGED
|
@@ -42,7 +42,7 @@ const skills = [
|
|
|
42
42
|
"files": [
|
|
43
43
|
{
|
|
44
44
|
"path": "SKILL.md",
|
|
45
|
-
"text": "---\nname: comark-ui\ndescription: 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.\n---\n\n# comark-ui\n\nYou write Markdown, and the comark plugins render it as static HTML. 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.\n\n## Install\n\nInstall `comark-ui` by default. It holds every plugin and registers them in the order they need:\n\n```sh\npnpm add comark @comark/html comark-ui\n```\n\nRun the comark CLIs with the [global Vite+ CLI](https://viteplus.dev/guide/global-cli): `vp dlx` for a package the project doesn't install, `vp exec` for one it does. Without Vite+, `pnpm dlx` and `pnpm exec` take the same arguments.\n\n```ts\nimport { createHtmlRenderer } from \"@comark/html\";\nimport comarkUi from \"comark-ui/html\";\n\nconst render = createHtmlRenderer(comarkUi());\n```\n\nEvery 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.\n\nTo use only some plugins, install those packages and register each plugin with its components:\n\n```sh\npnpm add comark @comark/html comark-annotate\n```\n\n```ts\nimport { createHtmlRenderer } from \"@comark/html\";\nimport annotate, { components } from \"comark-annotate/html\";\n\nconst render = createHtmlRenderer({ plugins: [annotate()], components });\n```\n\nEach reference ends with a Register section that says how its package registers alone and which options it needs.\n\nEither 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.\n\nThe `--comark-*` tokens theme every plugin at once; https://comark-ui.onmax.me/guide/theming/ lists them. 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 })` (from `comark-ui/nuxt-ui`) in a `<style>`, so the page matches the app.\n\nA project that renders Markdown with remark moves to Comark with `remark-to-comark`; the [remark-to-comark reference](references/remark-to-comark.md) walks through it.\n\nProblems 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.\n\n## Shape the document\n\nThis skill teaches the blocks. Four sibling skills teach a whole document, what goes in it and in what order: `before-after-evidence`, `review-document`, `visual-explanation`, and `teach-lesson`. When the user asks for one of those and it isn't installed, suggest `vp dlx skills add onmax/comark-ui` and picking it from the list.\n\n## Pick the blocks\n\nRead the reference for each block you use before you write it. Mix the blocks freely with ordinary Markdown, such as headings, tables, and lists.\n\n### Review blocks\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| One state of a screen, with marks, numbered notes, or arrows | `::screenshot` | [annotate](references/annotate.md) |\n| A change on screen, before and after | `::compare` | [annotate](references/annotate.md) |\n| A command's output, such as a test run or a build, as colored text | `::cast` | [cast](references/cast.md) |\n| Numbers, such as sizes, timings, before/after metrics, a trend, or a timeline | `::chart` | [charts](references/charts.md) |\n| 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) |\n| Links and `@mentions` with the site's favicon or the person's avatar | links, `@user`, `:chip` | [chips](references/chips.md) |\n| Colors, such as design tokens before and after, or a palette | a color in inline code, `::palette` | [colors](references/colors.md) |\n| A suggested change to one file, before and after | `::diff` | [diff](references/diff.md) |\n| Suggested edits and comment threads on prose, such as a copy review, each note in a card in the margin | `:suggestion`, `:comment` | [comments](references/comments.md) |\n| A video, a design file, or a live reproduction from its link | `::embed` | [embed](references/embed.md) |\n| A screen recording or a sound that a screenshot can't show | `` | [media](references/media.md) |\n| A number before and after, a table of befores and afters, or progress | `:delta`, `::stats`, `:progress` | [metrics](references/metrics.md) |\n| 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) |\n| A test run's passed, failed, and skipped counts and its failures, from a JUnit report | `::tests` | [tests](references/tests.md) |\n| A note, tip, or warning box, or long output folded away, as on GitHub | `> [!NOTE]`, `<details>` | [github](references/github.md) |\n| The review's status, pull request, author, date, and tags in a box | frontmatter, `::properties` | [properties](references/properties.md) |\n| Claims backed by links, with numbered citations | `:cite`, `::sources` | [sources](references/sources.md) |\n| An icon in a sentence or a table cell, such as a logo or a status | `:icon` | [icon](references/icon.md) |\n| A preview link a reviewer can scan with a phone | `:qr` | [qr](references/qr.md) |\n| Why a line of code is there, as numbered notes under it | `// <1>` and a numbered list | [code-notes](references/code-notes.md) |\n\n### Page behavior\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| A stable link to a section, or a link that opens a new tab | `{#id}`, `{target=\"_blank\"}` | [links](references/links.md) |\n| A table, code block, or diagram too small to read in place | `::zoom` | [zoom](references/zoom.md) |\n| A sequence to page through in one place, such as the screens of a flow | `::carousel` | [carousel](references/carousel.md) |\n| 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) |\n| 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) |\n\n### Docs blocks\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| Content a reader may skip, such as a long log, behind a summary | `::details`, `::accordion`, `::collapsible` | [details](references/details.md) |\n| Folders and files, with what a change added, removed, or changed | `::file-tree` | [file-tree](references/file-tree.md) |\n| A component's props or a function's options, with types and defaults | `::field` | [fields](references/fields.md) |\n| A version, status, or deprecation tag in a heading or a sentence | `:badge` | [badge](references/badge.md) |\n| Links to the next pages as cards, or blocks side by side | `::card-group`, `::columns` | [cards](references/cards.md) |\n| Instructions the reader follows in order, such as repro or setup steps | `::steps` | [steps](references/steps.md) |\n| Code that changes over several steps, such as a refactor told one move at a time | `::magic-move` | [magic-move](references/magic-move.md) |\n| An interactive lesson question, poll, or recall card | `::question`, `::poll`, `::recall` | [quiz](references/quiz.md) |\n\n### Markdown syntax\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| Bold or italics in Chinese, Japanese, or Korean text | `**…**`, `*…*` | [cjk](references/cjk.md) |\n| An image with a caption under it | `` | [figure](references/figure.md) |\n| A highlighted phrase or a superscript | `==text==`, `^2^` | [mark](references/mark.md) |\n| A suggested edit or a comment inside the text | `{++ ++}`, `{-- --}`, `{~~ ~> ~~}`, `{== ==}`, `{>> <<}` | [critic](references/critic.md) |\n| Terms and their definitions, such as a glossary | `Term`, then `: definition` | [deflist](references/deflist.md) |\n| A numbered note at the end of the page, such as a source or a caveat | `[^label]`, `^[note]` | [footnotes](references/footnotes.md) |\n| A formula, such as a complexity bound or how a number was computed | `$…$`, `$$…$$` | [math](references/math.md) |\n| A keyboard shortcut | `++ctrl+k++` | [keys](references/keys.md) |\n\n### Authoring tools\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| A shared section, or real source lines, pasted from a file | `:include{src}` | [include](references/include.md) |\n| A table of contents of the page's headings | `::toc` | [outline](references/outline.md) |\n| Figures and tables numbered and referred to by number | `{#fig-name}`, `:ref{#fig-name}` | [xref](references/xref.md) |\n| Word count and reading time, for the page's layout | nothing; it writes `meta.readingTime` | [reading-time](references/reading-time.md) |\n\n### Chat\n\n| To show | Use | Reference |\n| --- | --- | --- |\n| An AI SDK chat message, with its reasoning, tool calls, sources, and files | `MessageParts` from `comark-chat` | [chat](references/chat.md) |\n\n## Marks\n\nScreenshots and charts draw the same marks. Each `::annotation` is one mark and its note, written in Markdown.\n\n- `mark` is `box` (default), `circle`, `underline`, `line`, `highlight`, `bracket`, `cross`, `check`, or `none`, which draws nothing, so its note needs a `side` and the arrow alone points.\n- `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.\n- `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.\n\nMark only what your text talks about. Write each note in the words the screen or table shows, saying what changed or what is wrong, so the reader can check it against the figure. Leave out a value the figure doesn't show.\n\n## Check the result\n\n1. Lint the file with `vp exec comark-lint report.md`, in a project with `comark-linter` and the plugins installed (`comark-ui` counts as every plugin). It prints one line per problem with its line, its rule, and the fix, and exits with 1 while any remain. Fix every problem and run it again until it exits with 0. The [lint reference](references/lint.md) covers the flags, the config file, the library, ESLint, and Vite Doctor.\n2. Render the document as the block's reference says, open it in a browser, and take a screenshot. Each mark must sit on its target, each number must match its note, and each chart must say what your text says. Name anything you couldn't verify when you hand the document over.\n"
|
|
45
|
+
"text": "---\nname: comark-ui\ndescription: 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.\n---\n\n# comark-ui\n\nYou write Markdown, and the comark plugins render it as static HTML. 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.\n\n## Install\n\nInstall `comark-ui` by default. It holds every plugin and registers them in the order they need:\n\n```sh\npnpm add comark @comark/html comark-ui\n```\n\nRun the comark CLIs with the [global Vite+ CLI](https://viteplus.dev/guide/global-cli): `vp dlx` for a package the project doesn't install, `vp exec` for one it does. Without Vite+, `pnpm dlx` and `pnpm exec` take the same arguments.\n\n```ts\nimport { createHtmlRenderer } from \"@comark/html\";\nimport comarkUi from \"comark-ui/html\";\n\nconst render = createHtmlRenderer(comarkUi());\n```\n\nEvery 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.\n\nTo use only some plugins, install those packages and register each plugin with its components:\n\n```sh\npnpm add comark @comark/html comark-annotate\n```\n\n```ts\nimport { createHtmlRenderer } from \"@comark/html\";\nimport annotate, { components } from \"comark-annotate/html\";\n\nconst render = createHtmlRenderer({ plugins: [annotate()], components });\n```\n\nEach reference ends with a Register section that says how its package registers alone and which options it needs.\n\nEither 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.\n\nThe `--comark-*` tokens theme every plugin at once; https://comark-ui.onmax.me/guide/theming/ lists them. 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 })` (from `comark-ui/nuxt-ui`) in a `<style>`, so the page matches the app.\n\nTo move a project to Comark, start with `migrate-to-comark`, which finds its Markdown libraries and runs the migrator for each; see the [migrate-to-comark reference](references/migrate-to-comark.md). A project that renders Markdown with remark moves to Comark with `remark-to-comark`; the [remark-to-comark reference](references/remark-to-comark.md) walks through it. One that renders with markdown-it moves with `markdown-it-to-comark`; see the [markdown-it-to-comark reference](references/markdown-it-to-comark.md). A project on marked moves with `marked-to-comark`; see the [marked-to-comark reference](references/marked-to-comark.md).\n\nProblems 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.\n\n## Shape the document\n\nThis skill teaches the blocks. Four sibling skills teach a whole document, what goes in it and in what order: `before-after-evidence`, `review-document`, `visual-explanation`, and `teach-lesson`. When the user asks for one of those and it isn't installed, suggest `vp dlx skills add onmax/comark-ui` and picking it from the list.\n\n## Pick the blocks\n\nRead the reference for each block you use before you write it. Mix the blocks freely with ordinary Markdown, such as headings, tables, and lists.\n\n### Review blocks\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| One state of a screen, with marks, numbered notes, or arrows | `::screenshot` | [annotate](references/annotate.md) |\n| A change on screen, before and after | `::compare` | [annotate](references/annotate.md) |\n| A command's output, such as a test run or a build, as colored text | `::cast` | [cast](references/cast.md) |\n| Numbers, such as sizes, timings, before/after metrics, a trend, or a timeline | `::chart` | [charts](references/charts.md) |\n| 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) |\n| Links and `@mentions` with the site's favicon or the person's avatar | links, `@user`, `:chip` | [chips](references/chips.md) |\n| Colors, such as design tokens before and after, or a palette | a color in inline code, `::palette` | [colors](references/colors.md) |\n| A suggested change to one file, before and after | `::diff` | [diff](references/diff.md) |\n| Suggested edits and comment threads on prose, such as a copy review, each note in a card in the margin | `:suggestion`, `:comment` | [comments](references/comments.md) |\n| A video, a design file, or a live reproduction from its link | `::embed` | [embed](references/embed.md) |\n| A screen recording or a sound that a screenshot can't show | `` | [media](references/media.md) |\n| A number before and after, a table of befores and afters, or progress | `:delta`, `::stats`, `:progress` | [metrics](references/metrics.md) |\n| 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) |\n| A test run's passed, failed, and skipped counts and its failures, from a JUnit report | `::tests` | [tests](references/tests.md) |\n| A note, tip, or warning box, or long output folded away, as on GitHub | `> [!NOTE]`, `<details>` | [github](references/github.md) |\n| The review's status, pull request, author, date, and tags in a box | frontmatter, `::properties` | [properties](references/properties.md) |\n| Claims backed by links, with numbered citations | `:cite`, `::sources` | [sources](references/sources.md) |\n| An icon in a sentence or a table cell, such as a logo or a status | `:icon` | [icon](references/icon.md) |\n| A preview link a reviewer can scan with a phone | `:qr` | [qr](references/qr.md) |\n| Why a line of code is there, as numbered notes under it | `// <1>` and a numbered list | [code-notes](references/code-notes.md) |\n\n### Page behavior\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| A stable link to a section, or a link that opens a new tab | `{#id}`, `{target=\"_blank\"}` | [links](references/links.md) |\n| A table, code block, or diagram too small to read in place | `::zoom` | [zoom](references/zoom.md) |\n| A sequence to page through in one place, such as the screens of a flow | `::carousel` | [carousel](references/carousel.md) |\n| 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) |\n| 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) |\n\n### Docs blocks\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| Content a reader may skip, such as a long log, behind a summary | `::details`, `::accordion`, `::collapsible` | [details](references/details.md) |\n| Folders and files, with what a change added, removed, or changed | `::file-tree` | [file-tree](references/file-tree.md) |\n| A component's props or a function's options, with types and defaults | `::field` | [fields](references/fields.md) |\n| A version, status, or deprecation tag in a heading or a sentence | `:badge` | [badge](references/badge.md) |\n| Links to the next pages as cards, or blocks side by side | `::card-group`, `::columns` | [cards](references/cards.md) |\n| Instructions the reader follows in order, such as repro or setup steps | `::steps` | [steps](references/steps.md) |\n| Code that changes over several steps, such as a refactor told one move at a time | `::magic-move` | [magic-move](references/magic-move.md) |\n| An interactive lesson question, poll, or recall card | `::question`, `::poll`, `::recall` | [quiz](references/quiz.md) |\n\n### Markdown syntax\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| Bold or italics in Chinese, Japanese, or Korean text | `**…**`, `*…*` | [cjk](references/cjk.md) |\n| An image with a caption under it | `` | [figure](references/figure.md) |\n| A highlighted phrase or a superscript | `==text==`, `^2^` | [mark](references/mark.md) |\n| A suggested edit or a comment inside the text | `{++ ++}`, `{-- --}`, `{~~ ~> ~~}`, `{== ==}`, `{>> <<}` | [critic](references/critic.md) |\n| Terms and their definitions, such as a glossary | `Term`, then `: definition` | [deflist](references/deflist.md) |\n| A numbered note at the end of the page, such as a source or a caveat | `[^label]`, `^[note]` | [footnotes](references/footnotes.md) |\n| A formula, such as a complexity bound or how a number was computed | `$…$`, `$$…$$` | [math](references/math.md) |\n| A keyboard shortcut | `++ctrl+k++` | [keys](references/keys.md) |\n\n### Authoring tools\n\n| To show | Write | Reference |\n| --- | --- | --- |\n| A shared section, or real source lines, pasted from a file | `:include{src}` | [include](references/include.md) |\n| A table of contents of the page's headings | `::toc` | [outline](references/outline.md) |\n| Figures and tables numbered and referred to by number | `{#fig-name}`, `:ref{#fig-name}` | [xref](references/xref.md) |\n| Word count and reading time, for the page's layout | nothing; it writes `meta.readingTime` | [reading-time](references/reading-time.md) |\n\n### Chat\n\n| To show | Use | Reference |\n| --- | --- | --- |\n| An AI SDK chat message, with its reasoning, tool calls, sources, and files | `MessageParts` from `comark-chat` | [chat](references/chat.md) |\n\n## Marks\n\nScreenshots and charts draw the same marks. Each `::annotation` is one mark and its note, written in Markdown.\n\n- `mark` is `box` (default), `circle`, `underline`, `line`, `highlight`, `bracket`, `cross`, `check`, or `none`, which draws nothing, so its note needs a `side` and the arrow alone points.\n- `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.\n- `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.\n\nMark only what your text talks about. Write each note in the words the screen or table shows, saying what changed or what is wrong, so the reader can check it against the figure. Leave out a value the figure doesn't show.\n\n## Check the result\n\n1. Lint the file with `vp exec comark-lint report.md`, in a project with `comark-linter` and the plugins installed (`comark-ui` counts as every plugin). It prints one line per problem with its line, its rule, and the fix, and exits with 1 while any remain. Fix every problem and run it again until it exits with 0. The [lint reference](references/lint.md) covers the flags, the config file, the library, ESLint, and Vite Doctor.\n2. Render the document as the block's reference says, open it in a browser, and take a screenshot. Each mark must sit on its target, each number must match its note, and each chart must say what your text says. Name anything you couldn't verify when you hand the document over.\n"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
"path": "agents/openai.yaml",
|
|
@@ -90,7 +90,7 @@ const skills = [
|
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
"path": "references/code.md",
|
|
93
|
-
"text": "# Code blocks\n\ncomark-code puts Copy and Wrap on every code block. It also reads options after a fence's language, written the way Expressive Code writes them. They mark lines as added, deleted, or worth a look, fold lines away, number them, and frame shell sessions as a terminal. Use the markers to point at the lines your text talks about, such as the fix in a pull request.\n\n## Syntax\n\n````md\n```ts title=\"src/zoom.ts\" del={3} ins={4} mark={1}\nfunction close() {\n dialog.close();\n history.back();\n if (opened) history.back();\n}\n```\n````\n\n| Option | What it does |\n| --- | --- |\n| `ins={4}` | Marks lines as added, in green with a `+`. |\n| `del={3}` | Marks lines as deleted, in red with a `−`. Copy leaves them out, so the block copies as the code after the change. |\n| `mark={1}` | Highlights lines to look at, like Comark's own `{1}`. |\n| `collapse={2-7}` | Folds lines behind a row that counts them. A click opens them. |\n| `showLineNumbers` | Numbers the lines. |\n| `startLineNumber=120` | Numbers the lines from 120, and turns numbers on. |\n| `title=\"src/zoom.ts\"` | Titles the block, like Comark's `[src/zoom.ts]`. A block without one shows its language. |\n| `frame=\"terminal\"` | Draws a terminal
|
|
93
|
+
"text": "# Code blocks\n\ncomark-code puts Copy and Wrap on every code block. It also reads options after a fence's language, written the way Expressive Code writes them. They mark lines as added, deleted, or worth a look, fold lines away, number them, and frame shell sessions as a terminal. Use the markers to point at the lines your text talks about, such as the fix in a pull request.\n\n## Syntax\n\n````md\n```ts title=\"src/zoom.ts\" del={3} ins={4} mark={1}\nfunction close() {\n dialog.close();\n history.back();\n if (opened) history.back();\n}\n```\n````\n\n| Option | What it does |\n| --- | --- |\n| `ins={4}` | Marks lines as added, in green with a `+`. |\n| `del={3}` | Marks lines as deleted, in red with a `−`. Copy leaves them out, so the block copies as the code after the change. |\n| `mark={1}` | Highlights lines to look at, like Comark's own `{1}`. |\n| `collapse={2-7}` | Folds lines behind a row that counts them. A click opens them. |\n| `showLineNumbers` | Numbers the lines. |\n| `startLineNumber=120` | Numbers the lines from 120, and turns numbers on. |\n| `title=\"src/zoom.ts\"` | Titles the block, like Comark's `[src/zoom.ts]`. A block without one shows its language. |\n| `frame=\"terminal\"` | Draws a terminal, with a line under its header. `frame=\"code\"` or `frame=\"none\"` draws a plain block. |\n| `wrap` | Starts the block with Wrap on. |\n\nA range is one line, `{7}`, a run, `{3-4}`, or a list of both, `{1, 3-4}`, always in braces. Ranges count the block's own lines from 1, folded lines included, whatever `startLineNumber` says: `startLineNumber=120 ins={3}` marks the line numbered 122.\n\n`sh`, `bash`, `zsh`, `shell`, `shellscript`, `shellsession`, `console`, and `ansi` blocks draw as terminals unless their title is a file name, such as `install.sh`. A terminal's header shows its title but not its language. In a code group the tab names the block, so a terminal there draws no line under its header.\n\nWhen the host runs Shiki's notation transformers, comments such as `// [!code ++]`, `// [!code --]`, `// [!code highlight]`, `// [!code focus]`, `// [!code error]`, and `// [!code warning]` mark lines too, and comark-code styles them.\n\n## Show a change\n\n- Mark a change of a few lines with `ins` and `del` on the code you write out.\n- Paste `git diff` output into a `diff` fence with `lang`, as below. The code highlights as that language, each leading `+` or `-` becomes `ins` or `del`, and the `@@` hunk headers and the file headers above them go away. Without `lang`, the fence stays a plain diff.\n- For a suggested change, the way a pull request review shows one, write comark-diff's `::diff` with the code before and after.\n- `mark` the lines your text names, and `collapse` the imports and setup the reader can skip.\n\n````md\n```diff lang=\"ts\"\n@@ -12,4 +12,4 @@ export function close() {\n function close() {\n dialog.close();\n- history.back();\n+ if (opened) history.back();\n }\n```\n````\n\nA range past the block's last line is reported. One that lands on the wrong line inside the block isn't, so count the lines before you mark them.\n\n## Add buttons\n\n`code({ actions })` adds the host's own buttons before Wrap and Copy, on the blocks in each action's `languages`, or on every block when it lists none. A button shows a `label`, an `icon`, or both. The icon is an Iconify name, such as `lucide:play`, or an image URL, and an icon-only button needs a `title` for its tooltip and screen readers.\n\n```ts\ncode({ actions: [{ id: \"run\", label: \"Run\", icon: \"lucide:play\", languages: [\"sh\", \"bash\"] }] });\n```\n\nThe page decides what a click does. `onCodeAction()` from `comark-code/client` calls your function with the block's code and language, and returns a function that stops listening:\n\n```ts\nimport { onCodeAction } from \"comark-code/client\";\n\nconst stop = onCodeAction(\"run\", ({ code, language }) => terminal.run(code));\n```\n\nA click sends a bubbling `comark-code-action` event with `{ action, code, language }` as its `detail`, so one listener on the page can handle every action. Icons resolve while parsing, through the `iconResolver` option, a `createIcons()` from comark-icon, or from the Iconify API without one. An icon that can't be drawn is reported, and its button shows its title instead.\n\n## Register\n\n- In `comark-ui`: on, as `code`. Pass `code: { actions }` for buttons of your own, and the preset's `icon` options resolve their icons. The preset leaves Shiki out. To add it, put it right before comark-code, which keeps it after comark-tabs:\n\n ```ts\n import shiki from \"comark/plugins/shiki\";\n\n const { plugins, components } = comarkUi();\n plugins.splice(plugins.findIndex((plugin) => plugin.name === \"comark-code\"), 0, shiki());\n ```\n\n- Alone: `pnpm add comark @comark/html comark-code`, then `createHtmlRenderer({ plugins: [code()], components })` with both from `comark-code/html`. `comark-code/vue`, `/react`, and `/svelte` (Svelte 5.20 or later) export the same pair for those renderers. With Shiki, list `shiki()` before `code()`. Shiki redraws every block, so comark-code listed first loses its markers, numbers, and folds, and reports it. Without Shiki, comark-code splits the lines itself.\n"
|
|
94
94
|
},
|
|
95
95
|
{
|
|
96
96
|
"path": "references/colors.md",
|
|
@@ -172,6 +172,14 @@ const skills = [
|
|
|
172
172
|
"path": "references/mark.md",
|
|
173
173
|
"text": "# Highlights and superscripts\n\ncomark-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.\n\n## Syntax\n\n```md\nThe deploy waits while ==more than 12 pigeons== sit on the roof, about 10^3^ by Friday.\n\n==Do not deploy=={.warning} before the 1^st^ count of the day.\n```\n\n- `==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.\n- `^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.\n- There is no subscript, since GitHub reads `~text~` as strikethrough. Write `H<sub>2</sub>O`.\n\nHighlight 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.\n\n## Register\n\n- In `comark-ui`: on by default, with its components. `mark: false` turns it off; it takes no options.\n- 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.\n"
|
|
174
174
|
},
|
|
175
|
+
{
|
|
176
|
+
"path": "references/markdown-it-to-comark.md",
|
|
177
|
+
"text": "# Moving a project from markdown-it\n\nmarkdown-it-to-comark moves a project that renders Markdown with markdown-it to Comark. It reports what it finds as diagnostics, `MIC0101` to `MIC0303`, each with a fix and a link to https://comark-ui.onmax.me/markdown-it-to-comark/diagnostics/.\n\n## Run it\n\n```sh\nvp exec markdown-it-to-comark check . --json\nvp exec markdown-it-to-comark content \"**/*.md\" --write\nvp exec markdown-it-to-comark rewrite . --write\nvp exec markdown-it-to-comark check . --json\n```\n\n- `check` lists the markdown-it instances and framework hooks it finds, what each option and plugin becomes, the instances `rewrite` can replace and why the others stay, and the Markdown files `content` would change.\n- `rewrite` replaces `const md = new MarkdownIt(options).use(plugin)` with `const md = comarkOptions({ ...options, plugins: [markdownItPlugin(plugin)] })` and each `md.render(src)` with `(await renderHtml(src, md))`. It only does so when every `render` call is in an async function, since Comark renders asynchronously. Install `comark`, `@comark/html`, and `markdown-it-to-comark`.\n- `content` escapes `{`, `[`, and `:name` that Comark would read as attributes, spans, or components, and keeps an edit only if markdown-it renders the file byte for byte as before. It reads with the project's preset, options, and plugins, so `::: tip` containers and `[^1]` footnotes stay as written.\n- Run `check` again at the end. What's left is work for you: framework setups (MIC0101), plugins and hand-made changes nobody compared (MIC0105), setups it couldn't rewrite (MIC0201), and blocks left as written (MIC0303).\n\n## What it leaves to you\n\n- A framework's instance, such as VitePress' `markdown.config(md)` or Eleventy's `setLibrary(\"md\", …)`, stays as it is: the framework renders. Render with `renderHtml(markdown, comarkOptions())` where the framework lets you.\n- A markdown-it plugin keeps its output on Comark through `markdownItPlugin(plugin, ...options)` from `markdown-it-to-comark/plugins`, renderer rules included. A comark-ui package that does the same job is reported with it (MIC0102); its markup differs.\n- A renderer rule set by hand (`md.renderer.rules.fence = …`) moves into a plugin function registered with `markdownItPlugin()`.\n\n## Register\n\n- In `comark-ui`: not included. Install it beside the preset for the migration.\n- Alone: `pnpm add -D markdown-it-to-comark`. It installs the command `markdown-it-to-comark`, the library, the Comark setup `markdown-it-to-comark/setup` and the bridge `markdown-it-to-comark/plugins` (add the package to your runtime dependencies when rewritten code imports them), and the Vite Doctor extension `markdown-it-to-comark/doctor`.\n"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"path": "references/marked-to-comark.md",
|
|
181
|
+
"text": "# Moving a project from marked\n\nmarked-to-comark moves a project that renders Markdown with marked to Comark. It reports what it finds as diagnostics, `MKC0101` to `MKC0304`, each with a fix and a link to https://comark-ui.onmax.me/marked-to-comark/diagnostics/.\n\n## Run it\n\n```sh\nvp exec marked-to-comark check . --json\nvp exec marked-to-comark content \"posts/**/*.md\" --write\nvp exec marked-to-comark rewrite . --write\nvp exec marked-to-comark check . --json\n```\n\n- `check` lists the marked setups it finds (the shared `marked`, `new Marked()` instances) and what each option and extension becomes, the calls `rewrite` can replace and why the others stay, and the Markdown files `content` would change. `--json` prints each finding with its code, file, line, fix, and docs link.\n- `rewrite` replaces `marked.parse(markdown)` inside an async function with `await renderHtml(markdown, comarkOptions({ … }))`, importing `comarkOptions` from `marked-to-comark/setup`. Install what the new code imports: `comark`, `@comark/html`, and `marked-to-comark`.\n- `content` escapes `{`, `[`, and `:name` that Comark would read as attributes, spans, or components, so marked still renders every file byte for byte as before. It checks each edit with the project's own marked. Run it before `rewrite`, which removes the marked setup it reads options from.\n- Run `check` again at the end. What's left is work for you: custom renderers and extensions (MKC0105), options with no counterpart (MKC0106), calls it couldn't rewrite (MKC0201), and blocks left as written (MKC0303).\n\n## What it leaves to you\n\n- A call in a function that isn't async stays: `renderHtml` returns a promise. Make the function async, update its callers, and run `rewrite` again.\n- A custom `renderer` method becomes a component for the element it draws (`components: { a }` in @comark/html, or your framework's renderer); a tokenizer or extension becomes a Comark plugin.\n- Strings marked renders at runtime, such as chat replies, need no content changes. For a reply that streams in, render with `{ ...comarkOptions(), autoClose: true }` so unfinished Markdown looks finished at every step.\n\n## Register\n\n- In `comark-ui`: not included. Install it beside the preset for the migration.\n- Alone: `pnpm add -D marked-to-comark`. It installs the command `marked-to-comark`, the library `marked-to-comark`, the Comark setup `marked-to-comark/setup` (add it to your runtime dependencies when rewritten code imports it), and the Vite Doctor extension `marked-to-comark/doctor`.\n"
|
|
182
|
+
},
|
|
175
183
|
{
|
|
176
184
|
"path": "references/math.md",
|
|
177
185
|
"text": "# Math\n\ncomark-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.\n\n## Syntax\n\n```md\nThe cache holds $n$ entries, and each lookup costs \\(O(\\log n)\\).\n\n$$\n\\text{hit rate} = \\frac{\\text{hits}}{\\text{hits} + \\text{misses}}\n$$\n```\n\n- `$…$` 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`.\n- `$$…$$`, `\\[…\\]`, and a fence with the language `math` are display math, centered on their own line. `\\[1\\]`, numbers in brackets, stays a citation.\n- Math is never read inside code spans or code blocks.\n- `\\href`, `\\url`, and `\\includegraphics` don't render, and a macro that calls itself stops after 1,000 expansions.\n\nThe 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.\n\n## Register\n\n- 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.\n- 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.\n"
|
|
@@ -184,6 +192,10 @@ const skills = [
|
|
|
184
192
|
"path": "references/metrics.md",
|
|
185
193
|
"text": "# Numbers before and after\n\ncomark-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.\n\n## Syntax\n\n```md\nMoving the map to a worker cut the main bundle :delta{from=412 to=388 unit=\"kB\" better=\"lower\"} on every page.\n\n::stats{better=\"lower\"}\n| Metric | Before | After |\n| --- | --- | --- |\n| JavaScript | 412 kB | 388 kB |\n| Largest paint | 2.4 s | 1.9 s |\n| Requests | 38 | 41 |\n::\n\nTests moved to Vitest: :progress{value=17 max=20 label=\"Migrated\"}\n```\n\n- `: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`.\n- `::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.\n- Add `tiles` to `::stats` to show each row as a tile, with the name above the after value and the change below it. The table can then have two columns, a name and a value; a value with no before shows without a change. `better` and `unit` work as for the table.\n- `: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.\n\n## Write the numbers\n\n- Copy the numbers from the tool's output as it prints them: `1,204` and `2.4 s` both read.\n- 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`.\n- `better` covers the whole table. When some rows are better lower, such as sizes, and others better higher, such as scores, write two tables.\n\n## Register\n\n- In `comark-ui`: on. It takes no options.\n- 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.\n"
|
|
186
194
|
},
|
|
195
|
+
{
|
|
196
|
+
"path": "references/migrate-to-comark.md",
|
|
197
|
+
"text": "# Moving a project to Comark\n\nmigrate-to-comark finds the Markdown libraries a project uses and moves it with each library's migrator: remark-to-comark, markdown-it-to-comark, and marked-to-comark. Start here when you don't know which library the project uses, or it uses more than one.\n\n## Run it\n\n```sh\nnpx migrate-to-comark --json # what it found and would change; writes nothing\nnpx migrate-to-comark --yes # write every change\nnpx migrate-to-comark --yes --only marked # one library at a time\n```\n\n- It reads every package.json for remark, markdown-it, and marked, and the frameworks built on them: Astro, Docusaurus, and react-markdown for remark; VitePress, VuePress, and Eleventy for markdown-it; svelte-markdown and ngx-markdown for marked.\n- It writes only what each migrator's `rewrite --write` and `content --write` would. A file two migrators want goes to the library its nearest package.json declares. When that doesn't settle it, `conflicts` in the JSON names the file and both migrators, and the file stays as it is.\n- Code that renders Markdown switches last. While a Markdown file its migrator handles needs a hand fix or is in `conflicts`, `held` in the JSON names the code file and the Markdown files it waits on. Fix those, then run it again.\n- In an agent's shell it can't ask, so without `--yes` it reports and writes nothing.\n\n## What it leaves to you\n\nEverything each migrator reports. Run each one's check at the end, and follow its reference for the fixes:\n\n```sh\nnpx remark-to-comark check . --json\nnpx markdown-it-to-comark check . --json\nnpx marked-to-comark check . --json\n```\n\nSee the [remark-to-comark](remark-to-comark.md), [markdown-it-to-comark](markdown-it-to-comark.md), and [marked-to-comark](marked-to-comark.md) references.\n\n## Register\n\n- In `comark-ui`: not included. Run it with `npx`, or install it beside the preset for the migration.\n- Alone: `pnpm add -D migrate-to-comark`. It installs the command `migrate-to-comark`, the library `migrate-to-comark` (`detect`, `plan`, `apply`), and the three migrators.\n"
|
|
198
|
+
},
|
|
187
199
|
{
|
|
188
200
|
"path": "references/outline.md",
|
|
189
201
|
"text": "# Table of contents\n\ncomark-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.\n\n## Syntax\n\n```md\nThe redesign moves the filters into one row and adds an empty state.\n\n::toc{title=\"On this page\"}\n::\n\n## Filters\n\n### Toolbar\n\n## Empty state\n```\n\n| Prop | What it does |\n| --- | --- |\n| `depth` | The deepest heading it lists, a whole number from 2 to 6. Default 3, so h2 and h3. |\n| `title` | A small title above the list. It also names the `nav` for screen readers; without it, the name is \"Contents\". |\n\n- Close the block with `::` on the next line, with nothing between. The plugin drops and reports anything inside.\n- Put it after the summary paragraph, and keep headings short, since they become the links.\n- 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.\n- It lists headings inside other blocks too, such as each `###` step of a `::steps`. Set `depth=2` to keep them out.\n- Each link goes to the heading's id, its own `{#id}` when it has one, and reads as the heading's plain text.\n- Every `::toc` lists the whole document, wherever it sits. With no heading from h2 to `depth`, it renders nothing, and the plugin reports it.\n- Write `::toc` where other tools use `[[toc]]`, which Comark reads as a span.\n\n## Side outline\n\nThe `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.\n\n- Options: `rail: true`, or `{ title, depth }` (default \"On this page\" and 3).\n- 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.\n- It hides where it doesn't fit, and hides rather than narrow a wide diagram.\n\n## Register\n\n- In `comark-ui`: on in every entry; `outline: { rail: true }` adds the side outline.\n- 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.\n"
|
|
@@ -198,7 +210,7 @@ const skills = [
|
|
|
198
210
|
},
|
|
199
211
|
{
|
|
200
212
|
"path": "references/quiz.md",
|
|
201
|
-
"text": "# Lessons and quizzes\n\ncomark-quiz adds `::question`, `::poll`, and `::recall` blocks for interactive lessons. A question checks one or more answers and explains each option, a poll collects votes, and a recall card hides an answer until the reader asks for it. Without the script, the prompt, options, and answer disclosure stay readable HTML.\n\n## Syntax\n\n```md\n::question{#event-loop-order}\nWhich callback runs first once the current script ends?\n\n- [x] A resolved promise's `then` callback\n > Promise callbacks are microtasks.\n- [ ] A `setTimeout(fn, 0)` callback\n > A timer queues a task.\n\n#hint\nTwo queues are involved.\n\n#explain\nAfter every task, the event loop empties the microtask queue.\n::\n\n::poll{#next-lesson}\nWhat should the next lesson cover?\n\n- [ ] Web Workers\n- [ ] Streams\n::\n\n::recall{#why-microtasks}\nWhy does a promise callback run before a zero-delay timer?\n\n#answer\nPromise callbacks are microtasks, and a timer's callback is a task.\n::\n```\n\n| Block | Props and slots |\n| --- | --- |\n| `::question` | `#id`, `multiple`, `shuffle=\"false\"`, `number=\"false\"`, slots `#hint` and `#explain`. |\n| `::poll` | `#id`, `multiple`, `closes` (an ISO date; after it the poll shows as closed). Options are never shuffled. |\n| `::recall` | `#id`, `number=\"false\"`, and the required slot `#answer`. |\n\n- Mark a right option `[x]` and a wrong one `[ ]`. One right option makes radios and several make checkboxes; `multiple` forces checkboxes even with one. Mark no option in a poll.\n- Put a `>` line under an option to say why it is right or wrong. Give every wrong option a reason, since that is the lesson for a reader who picked it.\n- Leave a blank line before `#hint` and `#explain`. Without it, the text above swallows the slot, and the plugin reports it.\n- Give every block an `#id`. Hosts save answers by id. Without one the id derives from the prompt and options, changes when you reword them, and the plugin reports it. Keep the id when you reword the prompt.\n- Questions and recall cards share one count, so a lesson numbers them 1, 2, 3 down the page. Polls are not numbered.\n- Options are shuffled the same way for every reader, so do not write options that point at others by place, such as \"All of the above\" or \"Both A and B\". Keep the options about as long as each other, since a right option much longer than the rest is reported.\n- Keep a block to six options or fewer. Mark at least one right option in a question, or ask it as a poll. Put no block inside another block's prompt, option, or slot.\n\nThe 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
|
|
213
|
+
"text": "# Lessons and quizzes\n\ncomark-quiz adds `::question`, `::poll`, and `::recall` blocks for interactive lessons. A question checks one or more answers and explains each option, a poll collects votes, and a recall card hides an answer until the reader asks for it. Without the script, the prompt, options, and answer disclosure stay readable HTML.\n\n## Syntax\n\n```md\n::question{#event-loop-order}\nWhich callback runs first once the current script ends?\n\n- [x] A resolved promise's `then` callback\n > Promise callbacks are microtasks.\n- [ ] A `setTimeout(fn, 0)` callback\n > A timer queues a task.\n\n#hint\nTwo queues are involved.\n\n#explain\nAfter every task, the event loop empties the microtask queue.\n::\n\n::poll{#next-lesson}\nWhat should the next lesson cover?\n\n- [ ] Web Workers\n- [ ] Streams\n::\n\n::recall{#why-microtasks}\nWhy does a promise callback run before a zero-delay timer?\n\n#answer\nPromise callbacks are microtasks, and a timer's callback is a task.\n::\n```\n\n| Block | Props and slots |\n| --- | --- |\n| `::question` | `#id`, `multiple`, `shuffle=\"false\"`, `number=\"false\"`, slots `#hint` and `#explain`. |\n| `::poll` | `#id`, `multiple`, `closes` (an ISO date; after it the poll shows as closed). Options are never shuffled. |\n| `::recall` | `#id`, `number=\"false\"`, and the required slot `#answer`. |\n\n- Mark a right option `[x]` and a wrong one `[ ]`. One right option makes radios and several make checkboxes; `multiple` forces checkboxes even with one. Mark no option in a poll.\n- Put a `>` line under an option to say why it is right or wrong. Give every wrong option a reason, since that is the lesson for a reader who picked it.\n- Leave a blank line before `#hint` and `#explain`. Without it, the text above swallows the slot, and the plugin reports it.\n- Give every block an `#id`. Hosts save answers by id. Without one the id derives from the prompt and options, changes when you reword them, and the plugin reports it. Keep the id when you reword the prompt.\n- Questions and recall cards share one count, so a lesson numbers them 1, 2, 3 down the page. Polls are not numbered.\n- Options are shuffled the same way for every reader, so do not write options that point at others by place, such as \"All of the above\" or \"Both A and B\". Keep the options about as long as each other, since a right option much longer than the rest is reported.\n- Keep a block to six options or fewer. Mark at least one right option in a question, or ask it as a poll. Put no block inside another block's prompt, option, or slot.\n\nThe 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` (or `window.__comarkQuizState` set before the script runs). In an iframe, `data-quiz-relay` on the lesson's `<html>` relays all three over postMessage to the parent; see [Saving answers](https://comark-ui.onmax.me/quiz/#saving-answers) on its page for the event shapes and storage behavior.\n\n## Register\n\n- In `comark-ui`: on, as `quiz`, with optional `{ labels }` for translated UI text.\n- Alone: `pnpm add comark @comark/html comark-quiz`, then `createHtmlRenderer({ plugins: [quiz()], components })` with `quiz, { components }` from `comark-quiz/html`. Use the HTML renderer, since the blocks need its inlined script; there are no Vue, React, or Svelte entries.\n"
|
|
202
214
|
},
|
|
203
215
|
{
|
|
204
216
|
"path": "references/reading-time.md",
|
|
@@ -206,7 +218,7 @@ const skills = [
|
|
|
206
218
|
},
|
|
207
219
|
{
|
|
208
220
|
"path": "references/remark-to-comark.md",
|
|
209
|
-
"text": "# Moving a project from remark\n\nremark-to-comark moves a project that renders Markdown with remark, rehype, unified, or react-markdown to Comark. It reports what it finds as diagnostics, `RTC0101` to `RTC0303`, each with a fix and a link to https://comark-ui.onmax.me/remark-to-comark/diagnostics/.\n\n## Run it\n\n```sh\nvp exec remark-to-comark check . --json\nvp exec remark-to-comark content \"**/*.{md,mdx}\" --write\nvp exec remark-to-comark rewrite
|
|
221
|
+
"text": "# Moving a project from remark\n\nremark-to-comark moves a project that renders Markdown with remark, rehype, unified, or react-markdown to Comark. It reports what it finds as diagnostics, `RTC0101` to `RTC0303`, each with a fix and a link to https://comark-ui.onmax.me/remark-to-comark/diagnostics/.\n\n## Run it\n\n```sh\nvp exec remark-to-comark migrate\n```\n\n`migrate` runs `content --write`, then `rewrite --write`, then `check`, on a clean git tree (`--force` skips that check). To review each step first, run them yourself:\n\n```sh\nvp exec remark-to-comark check --json\nvp exec remark-to-comark content \"**/*.{md,mdx}\" --write\nvp exec remark-to-comark rewrite --write\nvp exec remark-to-comark check --json\n```\n\n- `check` lists the remark setups it finds and what each plugin becomes, the unified chains `rewrite` can replace and why the others stay, the Markdown files `content` would change, and the MDX it leaves alone. `--json` prints each finding with its code, file, line, fix, and docs link.\n- `rewrite` replaces a chain like `unified().use(remarkParse).use(remarkRehype).use(rehypeStringify).process(markdown)` with `renderHtml(markdown, comarkOptions({ … }))`, importing `comarkOptions` from `remark-to-comark/setup`. Install what the new code imports: `comark`, `@comark/html`, and `remark-to-comark`.\n- `content` escapes `{`, `[`, and `:name` that Comark would read as attributes, spans, or components, so remark still renders every file byte for byte as before. It parses with the project's syntax plugins, so `$x_{i}$` stays math. Run it before `rewrite`, which removes the remark code it reads them from.\n- Run `check` again at the end. What's left is work for you: framework configs (RTC0101), the project's own plugins (RTC0105), chains it couldn't rewrite (RTC0201), MDX (RTC0301), and blocks left as written (RTC0303).\n\n## What it leaves to you\n\n- A framework config, such as Astro's `markdown.remarkPlugins` or Docusaurus' `remarkPlugins`, stays as it is: the framework calls remark. Render with `renderHtml(markdown, comarkOptions())` where the framework lets you, such as a content loader or a route.\n- react-markdown becomes `<Markdown value={markdown} options={…} plugins={[…]} />` from `@comark/react`, with the plugins the findings name.\n- A plugin a comark-ui package replaces is reported with that package (RTC0102); its markup differs from the remark plugin's, so check pages that style it.\n- MDX stays MDX. Convert a page by hand only when its JSX just passes props: rename it to `.md`, run `content` on the prose, then write the components as Comark blocks. `content` escapes Comark syntax, so don't run it after you add them.\n\n## Register\n\n- In `comark-ui`: not included. Install it beside the preset for the migration.\n- Alone: `pnpm add -D remark-to-comark`. It installs the command `remark-to-comark`, the library `remark-to-comark`, the Comark setup `remark-to-comark/setup` (add it to your runtime dependencies when rewritten code imports it), the ports `remark-to-comark/plugins`, and the Vite Doctor extension `remark-to-comark/doctor`.\n"
|
|
210
222
|
},
|
|
211
223
|
{
|
|
212
224
|
"path": "references/sources.md",
|
|
@@ -230,7 +242,7 @@ const skills = [
|
|
|
230
242
|
},
|
|
231
243
|
{
|
|
232
244
|
"path": "references/ui.md",
|
|
233
|
-
"text": "# Every plugin in one install\n\ncomark-ui registers every comark-ui package for one renderer, in the order they need, with one icon resolver shared by `:icon`, cards, details, file trees, tabs, and code actions. SKILL.md's Install section shows the basic use. This page covers the options, what the preset turns on, the other renderers, and how the linter uses it.\n\n## Options\n\n`comarkUi(options)` returns `{ plugins, components }`. Each key is a package's name without `comark-`, in camelCase, such as `fileTree`, `codeNotes`, or `readingTime`. The exceptions are `math` for comark-mathml, `outline` for comark-outline, and `mermaid` and `typeset`, which are not packages. `false` turns a plugin off along with its components, and an object turns it on with those options. `true` turns on `include` and leaves the others as they are:\n\n```ts\ncomarkUi({\n zoom: false,\n code: { actions: [{ id: \"run\", label: \"Run\" }] },\n github: { callouts: false, references: { repo: \"owner/name\" } },\n table: { root: \"data\" },\n include: { root: \"content\", base: \"guide\" },\n});\n```\n\n`github` takes its three plugins by name, `callouts`, `references`, and `html`, and `links` takes `external` and `anchors`. Each package's reference lists its own options. The order stays fixed whatever you pass: `include` runs first, so the others read what it pastes, and `code` and `codeNotes` run late, after every plugin that writes code blocks.\n\n## What it turns on\n\n- Every plugin except `include`, which reads files from disk. `include: { root, base }` turns it on, or `{ read }` in a browser or an edge runtime. `include: true` reads under the working directory.\n- `cast`, `table`, and `tests` get a reader that finds no file. Inline casts, CSV fences, and pasted JUnit reports render, and a `src` file doesn't load until you pass `{ root }` or `{ read }`. Any other options object replaces that reader too, so the plugin reads under the working directory.\n- Three plugins use the network while parsing by default: `chips` downloads favicons and avatars, `icon` fetches icon sets from the Iconify API, and `diagrams` sends each diagram's source to `https://kroki.io`. In a browser, pass `chips: { download: false }`, because a browser may not download another site's images. To parse offline, also pass `icon: { fetch: false }` with your icon sets in `collections`, and `diagrams: { draw: false }`. To keep diagram sources private, pass your own Kroki `server`.\n- Media sizing reads only data URIs by default. `zoom: { size: true }` and `media: { size: true }` also fetch remote files while parsing, and `annotate: { size: true }` does so at HTML render time. Enable these only for trusted documents, because the requests follow redirects and can reach private networks. `{ size: false }` turns measurement off for that plugin; authored dimensions and screenshot viewports still work.\n- `icon`'s options apply to every icon the preset draws, in cards, details, file trees, tabs, and code actions too.\n- `mermaid` draws `mermaid` fences with comark-pele, on the server and offline. In the HTML entry the diagram zooms with comark-zoom unless `zoom` is `false`. `mermaid: false` turns it off.\n- comark-critic has no component to add its styles, so put `criticCss`, which every entry exports, in the page.\n- The preset styles the text around the blocks, scoped to the element the document renders into, on the same `--comark-*` tokens. Pass `typeset: false` when the site already styles Markdown, such as with Tailwind Typography's `prose`.\n\n## Renderers\n\n| Renderer | Entry |\n| --- | --- |\n| `@comark/html` | `comark-ui/html` |\n| `@comark/vue` | `comark-ui/vue` |\n| `@comark/react` | `comark-ui/react` |\n| `@comark/svelte` | `comark-ui/svelte` |\n\nThe Vue, React, and Svelte entries leave out annotate, comments, diff, and quiz, which render only to HTML. Screenshots, compares, diffs, suggestions and comments, and the marks on charts need `comark-ui/html`.\n\nTo use another renderer, install it in place of `@comark/html`, such as `pnpm add comark @comark/vue comark-ui`, and pass it `plugins` and `components` as you would one package's. The preset needs Vue 3.5, React 18, or Svelte 5.20 or later. Every entry exports the `PresetOptions` type, and `comark-ui` itself exports `preset(renderer, options)`, which builds the same pair from renderer entries you pass.\n\n## In the linter\n\ncomark-linter imports `comark-ui/html` when the project has it, and lints with all its plugins and components, ahead of any comark package installed on its own, so none runs twice. It calls `comarkUi()` with options of its own, so the app's options don't reach it:\n\n- It draws no chart or diagram, and downloads no chip image.\n- `::cast`, `::table`, and `::tests` read their `src` files from the working directory.\n- `include` stays off until a lint config adds it, as [include](include.md) says.\n\n## Register\n\n- In `comark-ui`: this is the preset itself, registered as Install in SKILL.md shows.\n- Alone: each plugin is its own package, registered as its reference says. The preset ships in lockstep with them, so `comark-ui@X` holds every plugin at version `X`.\n"
|
|
245
|
+
"text": "# Every plugin in one install\n\ncomark-ui registers every comark-ui package for one renderer, in the order they need, with one icon resolver shared by `:icon`, cards, details, file trees, tabs, and code actions. SKILL.md's Install section shows the basic use. This page covers the options, what the preset turns on, the other renderers, and how the linter uses it.\n\n## Options\n\n`comarkUi(options)` returns `{ plugins, components }`. Each key is a package's name without `comark-`, in camelCase, such as `fileTree`, `codeNotes`, or `readingTime`. The exceptions are `math` for comark-mathml, `outline` for comark-outline, and `mermaid`, `shiki`, and `typeset`, which are not packages. `false` turns a plugin off along with its components, and an object turns it on with those options. `true` turns on `include` and leaves the others as they are:\n\n```ts\ncomarkUi({\n zoom: false,\n code: { actions: [{ id: \"run\", label: \"Run\" }] },\n github: { callouts: false, references: { repo: \"owner/name\" } },\n table: { root: \"data\" },\n include: { root: \"content\", base: \"guide\" },\n});\n```\n\n`github` takes its three plugins by name, `callouts`, `references`, and `html`, and `links` takes `external` and `anchors`. Each package's reference lists its own options. The order stays fixed whatever you pass: `include` runs first, so the others read what it pastes, and `code` and `codeNotes` run late, after every plugin that writes code blocks.\n\n## What it turns on\n\n- Every plugin except `include`, which reads files from disk. `include: { root, base }` turns it on, or `{ read }` in a browser or an edge runtime. `include: true` reads under the working directory.\n- `cast`, `table`, and `tests` get a reader that finds no file. Inline casts, CSV fences, and pasted JUnit reports render, and a `src` file doesn't load until you pass `{ root }` or `{ read }`. Any other options object replaces that reader too, so the plugin reads under the working directory.\n- Three plugins use the network while parsing by default: `chips` downloads favicons and avatars, `icon` fetches icon sets from the Iconify API, and `diagrams` sends each diagram's source to `https://kroki.io`. In a browser, pass `chips: { download: false }`, because a browser may not download another site's images. To parse offline, also pass `icon: { fetch: false }` with your icon sets in `collections`, and `diagrams: { draw: false }`. To keep diagram sources private, pass your own Kroki `server`.\n- Media sizing reads only data URIs by default. `zoom: { size: true }` and `media: { size: true }` also fetch remote files while parsing, and `annotate: { size: true }` does so at HTML render time. Enable these only for trusted documents, because the requests follow redirects and can reach private networks. `{ size: false }` turns measurement off for that plugin; authored dimensions and screenshot viewports still work.\n- `icon`'s options apply to every icon the preset draws, in cards, details, file trees, tabs, and code actions too.\n- `mermaid` draws `mermaid` fences with comark-pele, on the server and offline. In the HTML entry the diagram zooms with comark-zoom unless `zoom` is `false`. `mermaid: false` turns it off.\n- `shiki` highlights fenced code with your own Shiki, before comark-code: `{ highlighter: (language) => shiki | undefined, themes: { light, dark } }`. It is off until you pass it, so the preset bundles no grammars. `maxBlocks` and `maxSource` cap how many blocks, and how many characters of code, a document highlights; the blocks past them show plain, each with an issue.\n- comark-critic has no component to add its styles, so put `criticCss`, which every entry exports, in the page.\n- The preset styles the text around the blocks, scoped to the element the document renders into, on the same `--comark-*` tokens. Pass `typeset: false` when the site already styles Markdown, such as with Tailwind Typography's `prose`.\n\n## Renderers\n\n| Renderer | Entry |\n| --- | --- |\n| `@comark/html` | `comark-ui/html` |\n| `@comark/vue` | `comark-ui/vue` |\n| `@comark/react` | `comark-ui/react` |\n| `@comark/svelte` | `comark-ui/svelte` |\n\nThe Vue, React, and Svelte entries leave out annotate, comments, diff, and quiz, which render only to HTML. Screenshots, compares, diffs, suggestions and comments, and the marks on charts need `comark-ui/html`.\n\nTo use another renderer, install it in place of `@comark/html`, such as `pnpm add comark @comark/vue comark-ui`, and pass it `plugins` and `components` as you would one package's. The preset needs Vue 3.5, React 18, or Svelte 5.20 or later. Every entry exports the `PresetOptions` type, and `comark-ui` itself exports `preset(renderer, options)`, which builds the same pair from renderer entries you pass.\n\n## In the linter\n\ncomark-linter imports `comark-ui/html` when the project has it, and lints with all its plugins and components, ahead of any comark package installed on its own, so none runs twice. It calls `comarkUi()` with options of its own, so the app's options don't reach it:\n\n- It draws no chart or diagram, and downloads no chip image.\n- `::cast`, `::table`, and `::tests` read their `src` files from the working directory.\n- `include` stays off until a lint config adds it, as [include](include.md) says.\n\n## Register\n\n- In `comark-ui`: this is the preset itself, registered as Install in SKILL.md shows.\n- Alone: each plugin is its own package, registered as its reference says. The preset ships in lockstep with them, so `comark-ui@X` holds every plugin at version `X`.\n"
|
|
234
246
|
},
|
|
235
247
|
{
|
|
236
248
|
"path": "references/xref.md",
|
|
@@ -325,6 +337,6 @@ const skills = [
|
|
|
325
337
|
}
|
|
326
338
|
];
|
|
327
339
|
/** The comark-ui version the skills come from, for a host's cache key or a digest. */
|
|
328
|
-
const version = "0.
|
|
340
|
+
const version = "0.9.0";
|
|
329
341
|
//#endregion
|
|
330
342
|
export { skills, version };
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { n as isElement, t as childrenOf } from "./nodes-CwDBFXnO.mjs";
|
|
2
|
+
import cjk from "comark-cjk";
|
|
3
|
+
import critic, { css as criticCss } from "comark-critic";
|
|
4
|
+
import deflist from "comark-deflist";
|
|
5
|
+
import figure from "comark-figure";
|
|
6
|
+
import footnotes from "comark-footnotes";
|
|
7
|
+
import { HTML_TAGS } from "comark-github";
|
|
8
|
+
import { createIcons } from "comark-icon";
|
|
9
|
+
import include from "comark-include";
|
|
10
|
+
import keys from "comark-keys";
|
|
11
|
+
import readingTime from "comark-reading-time";
|
|
12
|
+
import xref from "comark-xref";
|
|
13
|
+
import { defineComarkPlugin } from "comark";
|
|
14
|
+
import { css as commentsCss } from "comark-comments";
|
|
15
|
+
//#region src/mermaid-limits.ts
|
|
16
|
+
function diagramsOf(nodes, found = []) {
|
|
17
|
+
for (const node of nodes) {
|
|
18
|
+
if (!isElement(node)) continue;
|
|
19
|
+
if (node[0] === "pele") found.push(node);
|
|
20
|
+
else diagramsOf(childrenOf(node), found);
|
|
21
|
+
}
|
|
22
|
+
return found;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Turns the diagrams past a document's limits back into code blocks, before comark-code reads them, so a host that
|
|
26
|
+
* renders other people's documents bounds the time Pele spends drawing. Each one it turns back is a `meta.issues` entry.
|
|
27
|
+
*/
|
|
28
|
+
var mermaid_limits_default = defineComarkPlugin(({ maxDiagrams = Infinity, maxSource = Infinity } = {}) => ({
|
|
29
|
+
name: "comark-ui-mermaid-limits",
|
|
30
|
+
post({ tree }) {
|
|
31
|
+
const issues = [];
|
|
32
|
+
let drawn = 0;
|
|
33
|
+
let source = 0;
|
|
34
|
+
for (const [n, node] of diagramsOf(tree.nodes).entries()) {
|
|
35
|
+
const content = node[1].content;
|
|
36
|
+
const why = drawn >= maxDiagrams ? ["comark/mermaid/too-many-diagrams", `a document draws at most ${maxDiagrams} Mermaid diagrams. Move it to another document`] : source + content.length > maxSource ? ["comark/mermaid/source-too-long", `a document draws at most ${maxSource} characters of Mermaid. Shorten it or move it to another document`] : void 0;
|
|
37
|
+
if (!why) {
|
|
38
|
+
drawn++;
|
|
39
|
+
source += content.length;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const language = node[1].language ?? "mermaid";
|
|
43
|
+
node.splice(0, node.length, "pre", { language }, [
|
|
44
|
+
"code",
|
|
45
|
+
{ class: `language-${language}` },
|
|
46
|
+
content.replace(/\n$/, "")
|
|
47
|
+
]);
|
|
48
|
+
issues.push([why[0], `Mermaid diagram ${n + 1} shows its source as code, because ${why[1]}.`]);
|
|
49
|
+
}
|
|
50
|
+
if (!issues.length) return;
|
|
51
|
+
tree.meta.issues = [...tree.meta.issues ?? [], ...issues.map(([, message]) => message)];
|
|
52
|
+
tree.meta.issueRules = {
|
|
53
|
+
...tree.meta.issueRules,
|
|
54
|
+
...Object.fromEntries(issues.map(([rule, message]) => [message, rule]))
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}));
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/shiki.ts
|
|
60
|
+
function blocksOf(nodes, found = []) {
|
|
61
|
+
for (const node of nodes) {
|
|
62
|
+
if (!isElement(node)) continue;
|
|
63
|
+
if (node[0] === "pre" && node[1].$?.html !== 1) found.push(node);
|
|
64
|
+
else blocksOf(childrenOf(node), found);
|
|
65
|
+
}
|
|
66
|
+
return found;
|
|
67
|
+
}
|
|
68
|
+
const spanOf = (token) => {
|
|
69
|
+
const style = Object.entries(token.htmlStyle ?? (token.color ? { color: token.color } : {})).map(([name, value]) => `${name}:${value}`).join(";");
|
|
70
|
+
return style ? [
|
|
71
|
+
"span",
|
|
72
|
+
{ style },
|
|
73
|
+
token.content
|
|
74
|
+
] : token.content;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Highlights fenced code with the host's own Shiki, before comark-code reads the lines, within a document's limits, so
|
|
78
|
+
* a host that renders other people's documents bounds the time Shiki spends. Each block past them is a `meta.issues`
|
|
79
|
+
* entry. A grammar that fails leaves its block plain.
|
|
80
|
+
*/
|
|
81
|
+
var shiki_default = defineComarkPlugin((options) => ({
|
|
82
|
+
name: "comark-ui-shiki",
|
|
83
|
+
async post({ tree }) {
|
|
84
|
+
const { highlighter, themes, maxBlocks = Infinity, maxSource = Infinity } = options;
|
|
85
|
+
const issues = [];
|
|
86
|
+
let drawn = 0;
|
|
87
|
+
let source = 0;
|
|
88
|
+
for (const [n, pre] of blocksOf(tree.nodes).entries()) {
|
|
89
|
+
const code = childrenOf(pre)[0];
|
|
90
|
+
const language = pre[1].language;
|
|
91
|
+
if (!language || !isElement(code) || code[0] !== "code") continue;
|
|
92
|
+
const [text, ...rest] = childrenOf(code);
|
|
93
|
+
if (text === void 0 || Array.isArray(text) || rest.length) continue;
|
|
94
|
+
const why = drawn >= maxBlocks ? ["comark/shiki/too-many-blocks", `a document highlights at most ${maxBlocks} code blocks. Move it to another document`] : source >= maxSource || source + text.length > maxSource ? ["comark/shiki/source-too-long", `a document highlights at most ${maxSource} characters of code. Shorten it or move it to another document`] : void 0;
|
|
95
|
+
if (why) {
|
|
96
|
+
issues.push([why[0], `Code block ${n + 1} shows without colors, because ${why[1]}.`]);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
let shiki;
|
|
100
|
+
try {
|
|
101
|
+
shiki = await highlighter(language);
|
|
102
|
+
} catch {}
|
|
103
|
+
if (!shiki) continue;
|
|
104
|
+
let tokens;
|
|
105
|
+
try {
|
|
106
|
+
tokens = shiki.codeToTokens(text, {
|
|
107
|
+
lang: language,
|
|
108
|
+
themes
|
|
109
|
+
}).tokens;
|
|
110
|
+
} catch {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
drawn++;
|
|
114
|
+
source += text.length;
|
|
115
|
+
const lines = tokens.flatMap((line, index) => [...index ? ["\n"] : [], [
|
|
116
|
+
"span",
|
|
117
|
+
{ class: "line" },
|
|
118
|
+
...line.map(spanOf)
|
|
119
|
+
]]);
|
|
120
|
+
code.length = 2;
|
|
121
|
+
for (const line of lines) code.push(line);
|
|
122
|
+
pre[1].class = ["shiki", pre[1].class].filter(Boolean).join(" ");
|
|
123
|
+
}
|
|
124
|
+
if (!issues.length) return;
|
|
125
|
+
tree.meta.issues = [...tree.meta.issues ?? [], ...issues.map(([, message]) => message)];
|
|
126
|
+
tree.meta.issueRules = {
|
|
127
|
+
...tree.meta.issueRules,
|
|
128
|
+
...Object.fromEntries(issues.map(([rule, message]) => [message, rule]))
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}));
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/typeset.ts
|
|
134
|
+
/** The node that adds the styles. The plugin adds one at the end of every document. */
|
|
135
|
+
const TYPESET_TAG = "typeset-styles";
|
|
136
|
+
/** Adds the node that writes the typeset styles at the end of the document. The preset turns it on by default. */
|
|
137
|
+
const typeset = defineComarkPlugin(() => ({
|
|
138
|
+
name: "comark-ui-typeset",
|
|
139
|
+
post(state) {
|
|
140
|
+
state.tree.nodes.push([TYPESET_TAG, {}]);
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/index.ts
|
|
145
|
+
const words = (text) => text.split(" ");
|
|
146
|
+
const MARKDOWN_TAGS = words("a blockquote br code del em h1 h2 h3 h4 h5 h6 hr img input li ol p pre span strong table tbody td th thead tr ul");
|
|
147
|
+
const TAGS = [
|
|
148
|
+
["carousel", words("div")],
|
|
149
|
+
["codeNotes", words("div span")],
|
|
150
|
+
["critic", words("del ins mark")],
|
|
151
|
+
["deflist", words("dd dl dt")],
|
|
152
|
+
["figure", words("figcaption figure")],
|
|
153
|
+
["footnotes", words("section sup")],
|
|
154
|
+
["keys", words("kbd")],
|
|
155
|
+
["mark", words("mark sup")],
|
|
156
|
+
["metrics", words("dd div dt")],
|
|
157
|
+
["outline", words("a div li ul")],
|
|
158
|
+
["properties", words("dd div dt time")],
|
|
159
|
+
["quiz", words("div quiz-after quiz-answer quiz-explain quiz-hint quiz-label quiz-option quiz-options quiz-prompt quiz-rationale")],
|
|
160
|
+
["sources", words("ol sup")],
|
|
161
|
+
["table", words("button div span")],
|
|
162
|
+
["tabs", words("div tabs-item")],
|
|
163
|
+
["tests", words("details div summary")],
|
|
164
|
+
["xref", words("caption figcaption figure")]
|
|
165
|
+
];
|
|
166
|
+
const BLOCKS = [
|
|
167
|
+
["details", words("accordion-item collapsible details")],
|
|
168
|
+
["metrics", words("progress stats")],
|
|
169
|
+
["table", words("table")],
|
|
170
|
+
["tabs", words("code-group tabs-item")],
|
|
171
|
+
["xref", words("ref")]
|
|
172
|
+
];
|
|
173
|
+
const on = (value, fallback = true) => value === void 0 ? fallback : value !== false;
|
|
174
|
+
const optionsOf = (value) => value === true || value === false ? void 0 : value;
|
|
175
|
+
const noFiles = { read: () => void 0 };
|
|
176
|
+
/** Builds a renderer's preset. Use the one from `comark-ui/html`, `/vue`, `/react`, or `/svelte`. */
|
|
177
|
+
function preset(renderer, options = {}) {
|
|
178
|
+
const plugins = [];
|
|
179
|
+
const components = {};
|
|
180
|
+
const add = (part, value, settings = optionsOf(value)) => {
|
|
181
|
+
if (!part || !on(value)) return;
|
|
182
|
+
plugins.push(part.default(settings));
|
|
183
|
+
Object.assign(components, part.components);
|
|
184
|
+
};
|
|
185
|
+
const parse = (plugin, value) => {
|
|
186
|
+
if (on(value)) plugins.push(plugin(optionsOf(value)));
|
|
187
|
+
};
|
|
188
|
+
const icons = createIcons(optionsOf(options.icon));
|
|
189
|
+
if (on(options.include, false)) plugins.push(include(optionsOf(options.include)));
|
|
190
|
+
add(renderer.annotate, options.annotate);
|
|
191
|
+
add(renderer.carousel, options.carousel);
|
|
192
|
+
add(renderer.cast, options.cast, optionsOf(options.cast) ?? noFiles);
|
|
193
|
+
parse(cjk, options.cjk);
|
|
194
|
+
parse(deflist, options.deflist);
|
|
195
|
+
parse(figure, options.figure);
|
|
196
|
+
parse(critic, options.critic);
|
|
197
|
+
add(renderer.charts, options.charts);
|
|
198
|
+
add(renderer.chips, options.chips);
|
|
199
|
+
add(renderer.colors, options.colors);
|
|
200
|
+
add(renderer.diagrams, options.diagrams);
|
|
201
|
+
if (on(options.mermaid)) {
|
|
202
|
+
const { maxDiagrams, maxSource, ...pele } = optionsOf(options.mermaid) ?? {};
|
|
203
|
+
add(renderer.mermaid, true, pele);
|
|
204
|
+
if (maxDiagrams !== void 0 || maxSource !== void 0) plugins.push(mermaid_limits_default({
|
|
205
|
+
maxDiagrams,
|
|
206
|
+
maxSource
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
add(renderer.diff, options.diff);
|
|
210
|
+
add(renderer.embed, options.embed);
|
|
211
|
+
parse(footnotes, options.footnotes);
|
|
212
|
+
if (on(options.github)) {
|
|
213
|
+
const github = optionsOf(options.github) ?? {};
|
|
214
|
+
if (on(github.callouts)) {
|
|
215
|
+
plugins.push(renderer.github.callouts(optionsOf(github.callouts)));
|
|
216
|
+
Object.assign(components, renderer.github.components);
|
|
217
|
+
}
|
|
218
|
+
if (on(github.references)) plugins.push(renderer.github.references(optionsOf(github.references)));
|
|
219
|
+
if (on(github.html)) plugins.push(renderer.github.html());
|
|
220
|
+
}
|
|
221
|
+
add(renderer.icon, options.icon, icons);
|
|
222
|
+
add(renderer.mark, options.mark);
|
|
223
|
+
add(renderer.math, options.math);
|
|
224
|
+
add(renderer.details, options.details, {
|
|
225
|
+
iconResolver: icons,
|
|
226
|
+
...optionsOf(options.details)
|
|
227
|
+
});
|
|
228
|
+
if (on(options.links)) {
|
|
229
|
+
const links = optionsOf(options.links) ?? {};
|
|
230
|
+
if (on(links.external)) plugins.push(renderer.links.external(optionsOf(links.external)));
|
|
231
|
+
if (on(links.anchors)) plugins.push(renderer.links.anchors(optionsOf(links.anchors)));
|
|
232
|
+
if (on(links.external) || on(links.anchors)) Object.assign(components, renderer.links.components);
|
|
233
|
+
}
|
|
234
|
+
add(renderer.fileTree, options.fileTree, {
|
|
235
|
+
iconResolver: icons,
|
|
236
|
+
...optionsOf(options.fileTree)
|
|
237
|
+
});
|
|
238
|
+
add(renderer.fields, options.fields);
|
|
239
|
+
add(renderer.properties, options.properties);
|
|
240
|
+
parse(keys, options.keys);
|
|
241
|
+
add(renderer.badge, options.badge);
|
|
242
|
+
add(renderer.media, options.media);
|
|
243
|
+
add(renderer.metrics, options.metrics);
|
|
244
|
+
add(renderer.table, options.table, optionsOf(options.table) ?? noFiles);
|
|
245
|
+
add(renderer.cards, options.cards, { iconResolver: icons });
|
|
246
|
+
add(renderer.qr, options.qr);
|
|
247
|
+
add(renderer.sources, options.sources);
|
|
248
|
+
add(renderer.steps, options.steps);
|
|
249
|
+
add(renderer.quiz, options.quiz);
|
|
250
|
+
parse(readingTime, options.readingTime);
|
|
251
|
+
add(renderer.tabs, options.tabs, {
|
|
252
|
+
iconResolver: icons,
|
|
253
|
+
...optionsOf(options.tabs)
|
|
254
|
+
});
|
|
255
|
+
add(renderer.magicMove, options.magicMove);
|
|
256
|
+
add(renderer.outline, options.outline);
|
|
257
|
+
parse(xref, options.xref);
|
|
258
|
+
add(renderer.tests, options.tests, optionsOf(options.tests) ?? noFiles);
|
|
259
|
+
add(renderer.zoom, options.zoom);
|
|
260
|
+
if (options.shiki) plugins.push(shiki_default(options.shiki));
|
|
261
|
+
add(renderer.code, options.code, {
|
|
262
|
+
iconResolver: icons,
|
|
263
|
+
...optionsOf(options.code)
|
|
264
|
+
});
|
|
265
|
+
add(renderer.codeNotes, options.codeNotes);
|
|
266
|
+
add(renderer.comments, options.comments);
|
|
267
|
+
if (on(options.typeset)) {
|
|
268
|
+
plugins.push(typeset());
|
|
269
|
+
components[TYPESET_TAG] = renderer.typeset;
|
|
270
|
+
}
|
|
271
|
+
const { annotate, comments, diff, quiz } = renderer;
|
|
272
|
+
const absent = Object.entries({
|
|
273
|
+
annotate,
|
|
274
|
+
comments,
|
|
275
|
+
diff,
|
|
276
|
+
quiz
|
|
277
|
+
}).flatMap(([key, part]) => part ? [] : [key]);
|
|
278
|
+
const enabled = (key) => on(options[key], key !== "include") && !absent.includes(key);
|
|
279
|
+
const tags = /* @__PURE__ */ new Set([
|
|
280
|
+
...MARKDOWN_TAGS,
|
|
281
|
+
...on(options.github) && on(optionsOf(options.github)?.html) ? HTML_TAGS : [],
|
|
282
|
+
...TAGS.flatMap(([key, list]) => enabled(key) ? list : [])
|
|
283
|
+
]);
|
|
284
|
+
const blocks = BLOCKS.flatMap(([key, list]) => enabled(key) ? list : []);
|
|
285
|
+
return {
|
|
286
|
+
plugins,
|
|
287
|
+
components,
|
|
288
|
+
tags: [...tags],
|
|
289
|
+
blocks
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
export { typeset as a, TYPESET_TAG as i, criticCss as n, preset as r, commentsCss as t };
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Component } from "svelte";
|
|
2
2
|
import type { Preset, PresetOptions } from "comark-ui";
|
|
3
3
|
|
|
4
|
-
export { criticCss } from "comark-ui";
|
|
4
|
+
export { commentsCss, criticCss } from "comark-ui";
|
|
5
5
|
export type { PresetOptions } from "comark-ui";
|
|
6
6
|
|
|
7
7
|
/** Every plugin with components for `@comark/svelte`, which leaves out annotate, comments, diff, and quiz. */
|
package/dist/svelte/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import * as zoom from "comark-zoom/svelte";
|
|
|
35
35
|
import Mermaid from "./Mermaid.svelte";
|
|
36
36
|
import typeset from "./Typeset.svelte";
|
|
37
37
|
|
|
38
|
-
export { criticCss } from "comark-ui";
|
|
38
|
+
export { commentsCss, criticCss } from "comark-ui";
|
|
39
39
|
|
|
40
40
|
const mermaid = { default: pele, components: { pele: Mermaid } };
|
|
41
41
|
|
|
@@ -137,7 +137,8 @@ const rules = `
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
&:where(img, video) { border-radius: var(--_ty-radius); max-width: 100%; height: auto; margin-block: var(--_ty-flow) 0; }
|
|
140
|
-
|
|
140
|
+
/* An image in a line of text sits on the text; a block's top margin would push it below. */
|
|
141
|
+
&:where(:is(p, li, td, th, a) img) { margin-block-start: 0; }
|
|
141
142
|
&:where(figure) { margin-block: var(--_ty-flow) 0; margin-inline: 0; }
|
|
142
143
|
&:where(figcaption, caption) {
|
|
143
144
|
color: var(--_ty-muted); font-size: 0.875em; text-align: center; margin-block-start: calc(0.75em / 0.875);
|
|
@@ -175,18 +176,28 @@ const rules = `
|
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
/* Code highlighted with a light and a dark theme follows the page into dark mode, as the tokens do: a .dark class
|
|
178
|
-
above it, or the system's dark mode on a page with no .light class.
|
|
179
|
-
.shiki:where(.dark *) span
|
|
179
|
+
above it, or the system's dark mode on a page with no .light class. A span switches what it has a dark value for. */
|
|
180
|
+
.shiki:where(.dark *) span {
|
|
181
|
+
&[style*="--shiki-dark:"] { color: var(--shiki-dark) !important; }
|
|
182
|
+
&[style*="--shiki-dark-font-style:"] { font-style: var(--shiki-dark-font-style) !important; }
|
|
183
|
+
&[style*="--shiki-dark-font-weight:"] { font-weight: var(--shiki-dark-font-weight) !important; }
|
|
184
|
+
}
|
|
180
185
|
@media (prefers-color-scheme: dark) {
|
|
181
|
-
.shiki:not(:where(.light *)) span
|
|
186
|
+
.shiki:not(:where(.light *)) span {
|
|
187
|
+
&[style*="--shiki-dark:"] { color: var(--shiki-dark) !important; }
|
|
188
|
+
&[style*="--shiki-dark-font-style:"] { font-style: var(--shiki-dark-font-style) !important; }
|
|
189
|
+
&[style*="--shiki-dark-font-weight:"] { font-weight: var(--shiki-dark-font-weight) !important; }
|
|
190
|
+
}
|
|
182
191
|
}
|
|
183
192
|
|
|
184
193
|
/* A block marked data-wide, such as a before/after comparison or a diagram wider than the text, grows past the column
|
|
185
194
|
into the room the page gives it on each side, --comark-wide-start and --comark-wide-end, up to its own width
|
|
186
195
|
(--comark-wide-width) and --comark-wide-max. It stays centered on the column while both sides have room, and moves
|
|
187
196
|
toward the side that has more when one doesn't. Without room set, it keeps to the column. Only the document's own
|
|
188
|
-
blocks grow, not one inside a list or tabs.
|
|
189
|
-
|
|
197
|
+
blocks grow, not one inside a list or tabs. A heading right above a comparison grows with it, so it starts where
|
|
198
|
+
Before does and reads as its title. comark-links may wrap the heading, and the comparison's styles may sit between them. */
|
|
199
|
+
:scope > [data-wide],
|
|
200
|
+
:scope > :is(h2, h3, h4, .cl-heading:has(> :is(h2, h3, h4))):has(+ .ca-compare, + style + .ca-compare) {
|
|
190
201
|
/* Room a page works out from the window can come out negative on a phone, where the column fills it: none, then. */
|
|
191
202
|
--_ty-start: max(0px, var(--comark-wide-start, 0px));
|
|
192
203
|
--_ty-end: max(0px, var(--comark-wide-end, 0px));
|
|
@@ -212,5 +223,11 @@ const rules = `
|
|
|
212
223
|
* the page wins over both.
|
|
213
224
|
*/
|
|
214
225
|
const typesetCss = `@layer comark{@layer typeset{@scope{${theme(":scope", "primary")}${rules}}}}`.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/ ?([{};,>(]) ?/g, "$1").replace(/: /g, ":").trim();
|
|
226
|
+
/**
|
|
227
|
+
* The same styles for a page's own stylesheet, scoped to every element that matches `selector`, for Comark content
|
|
228
|
+
* that isn't a rendered document, such as a rich-text editor. `comark-ui/typeset.css` is this with `.comark-typeset`.
|
|
229
|
+
* The rules are the same string as `typesetCss`, so the two can't drift.
|
|
230
|
+
*/
|
|
231
|
+
const typesetStylesheet = (selector = ".comark-typeset") => typesetCss.replace("@scope{", `@scope (${selector}){`);
|
|
215
232
|
//#endregion
|
|
216
|
-
export { typesetCss as t };
|
|
233
|
+
export { typesetStylesheet as n, typesetCss as t };
|
|
@@ -6,5 +6,11 @@
|
|
|
6
6
|
* the page wins over both.
|
|
7
7
|
*/
|
|
8
8
|
declare const typesetCss: string;
|
|
9
|
+
/**
|
|
10
|
+
* The same styles for a page's own stylesheet, scoped to every element that matches `selector`, for Comark content
|
|
11
|
+
* that isn't a rendered document, such as a rich-text editor. `comark-ui/typeset.css` is this with `.comark-typeset`.
|
|
12
|
+
* The rules are the same string as `typesetCss`, so the two can't drift.
|
|
13
|
+
*/
|
|
14
|
+
declare const typesetStylesheet: (selector?: string) => string;
|
|
9
15
|
//#endregion
|
|
10
|
-
export { typesetCss as t };
|
|
16
|
+
export { typesetStylesheet as n, typesetCss as t };
|