comark-ui 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,7 +29,9 @@ comarkUi({
29
29
  });
30
30
  ```
31
31
 
32
- `include` is off because it reads files from disk; turn it on to paste files into a document. `cast`, `table`, and `tests` are on but read no files until you pass `{ root }` or `{ read }`. When you parse in a browser, pass `chips: { download: false }`, since a browser may not download another site's images. comark-critic has no component for its styles, so put the exported `criticCss` in the page. The text around the blocks (headings, paragraphs, lists, tables, code, and quotes) is styled too, from shadcn/typeset on the same tokens and scoped to the document; pass `typeset: false` when your site already styles Markdown. See [Typeset](https://comark-ui.onmax.me/guide/typeset/).
32
+ `include` is off because it reads files from disk; turn it on to paste files into a document. `cast`, `table`, and `tests` are on but read no files until you pass `{ root }` or `{ read }`. When you parse in a browser, pass `chips: { download: false }`, since a browser may not download another site's images. comark-critic has no component for its styles, so put the exported `criticCss` in the page. The text around the blocks (headings, paragraphs, lists, tables, code, and quotes) is styled too, from shadcn/typeset on the same tokens and scoped to the document; pass `typeset: false` when your site already styles Markdown. For content outside a document, such as an editor, import `comark-ui/typeset.css` and add the `comark-typeset` class. See [Typeset](https://comark-ui.onmax.me/guide/typeset/).
33
+
34
+ It also returns `tags` and `blocks`. `tags` is every element a parsed document can hold besides the components; with the components' names, it's the allowlist for Comark's `security` plugin. `blocks` are the blocks the plugins replace while parsing, such as `::code-group`, which a linter needs beside the components' names.
33
35
 
34
36
  ## Renderers
35
37
 
@@ -54,7 +56,7 @@ Vue, React, and Svelte leave out annotate and diff, which render only to HTML.
54
56
  | `fromMarkdown`, `toMarkdown` | Convert between Markdown and the editor's JSON with Comark's own parser and writer. Front matter stays beside the document. |
55
57
  | `checkMarkdown(markdown, { plugins })` | Whether the editor can save the document without changing what readers see, judged with the plugins the page renders with. A document it can't keep opens read-only. |
56
58
  | `Upload` | Uploads pasted and dropped files. Each shows a placeholder with its progress until `upload(file, progress)` returns its URL. |
57
- | `createEditorCodec({ plugins })` | `{ extensions, fromMarkdown, toMarkdown, verify }` for a collaborative room, such as ViteHub Realtime's `editor` option. |
59
+ | `createEditorCodec({ plugins })` | `{ extensions, split, join, fromMarkdown, toMarkdown, verify }` for a collaborative room, such as ViteHub Realtime's `editor` option. A room keeps the body as the editor's document and the frontmatter as its fields, so a text edit never overwrites the metadata. |
58
60
 
59
61
  `comark-ui/editor/vue` binds the editor to a Markdown string with `useComarkEditor(markdown)` or `<ComarkEditor v-model="markdown" />`. Run `pnpm playground` in this package to try it.
60
62
 
@@ -178,8 +178,6 @@ function addStyles(name, css) {
178
178
  style.textContent = css;
179
179
  document.head.append(style);
180
180
  }
181
- //#endregion
182
- //#region src/client.ts
183
181
  /**
184
182
  * comark-pele's diagrams on the shared `--comark-*` tokens, on a card like comark-diagrams'. Pele paints with its
185
183
  * `--pele-*` variables, so setting them on the wrapper themes every diagram, light and dark, without drawing it
@@ -187,8 +185,17 @@ function addStyles(name, css) {
187
185
  */
188
186
  const mermaidCss = `@layer comark{${[
189
187
  theme(".cmm,.pele-diagram"),
190
- theme(":root", "status"),
191
- `:where(:root){--pele-bg:var(--_comark-background);--pele-fg:var(--_comark-foreground);--pele-muted:var(--_comark-muted);--pele-line:var(--_comark-muted);--pele-surface:var(--_comark-accent);--pele-surface-alt:color-mix(in srgb,var(--_comark-accent),var(--_comark-foreground) 6%);--pele-border:var(--_comark-border);--pele-accent:var(--ui-primary,var(--primary,var(--_comark-foreground)));--pele-critical:var(--_comark-danger);--pele-error:var(--_comark-danger);--pele-font:var(--_comark-font);--pele-radius:calc(var(--_comark-radius) - 4px);--pele-series-1:var(--comark-chart-1,var(--ui-chart-1,var(--chart-1,#3b82f6)));--pele-series-2:var(--comark-chart-2,var(--ui-chart-2,var(--chart-2,#f97316)));--pele-series-3:var(--comark-chart-3,var(--ui-chart-3,var(--chart-3,#10b981)));--pele-series-4:var(--comark-chart-4,var(--ui-chart-4,var(--chart-4,#a855f7)));--pele-series-5:var(--comark-chart-5,var(--ui-chart-5,var(--chart-5,#f43f5e)));--pele-series-6:var(--comark-chart-6,var(--ui-chart-6,var(--chart-6,#eab308)));--pele-series-7:var(--comark-chart-7,var(--ui-chart-7,var(--chart-7,#06b6d4)));--pele-series-8:var(--comark-chart-8,var(--ui-chart-8,var(--chart-8,#ec4899)))}`,
188
+ theme(":root", "status", "primary"),
189
+ `:where(:root){--pele-bg:var(--_comark-background);--pele-fg:var(--_comark-foreground);--pele-muted:var(--_comark-muted);--pele-line:var(--_comark-muted);--pele-surface:var(--_comark-accent);--pele-surface-alt:color-mix(in srgb,var(--_comark-accent),var(--_comark-foreground) 6%);--pele-border:var(--_comark-border);--pele-accent:var(--_comark-primary);--pele-critical:var(--_comark-danger);--pele-error:var(--_comark-danger);--pele-font:var(--_comark-font);--pele-radius:calc(var(--_comark-radius) - 4px);${[
190
+ "#3b82f6",
191
+ "#f97316",
192
+ "#10b981",
193
+ "#a855f7",
194
+ "#f43f5e",
195
+ "#eab308",
196
+ "#06b6d4",
197
+ "#ec4899"
198
+ ].map((hex, index) => `--pele-series-${index + 1}:var(--comark-chart-${index + 1},var(--ui-chart-${index + 1},var(--chart-${index + 1},${hex})))`).join(";")}}`,
192
199
  ".pele-diagram{box-sizing:border-box;margin-block:1.5rem;padding:1rem;overflow-x:auto;border:1px solid var(--_comark-border);border-radius:var(--_comark-radius);background:var(--_comark-background);color:var(--_comark-foreground);text-align:center}",
193
200
  ".pele-diagram>svg{min-width:min(28rem,var(--cmm-svg,28rem))}",
194
201
  ".pele-source{margin:0;color:var(--_comark-muted);text-align:left}",
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as typesetCss } from "./typeset-css-DdOGL7WF.mjs";
1
+ import { n as typesetStylesheet, t as typesetCss } from "./typeset-css-DtOLAa1Z.mjs";
2
2
  //#region src/client.d.ts
3
3
  /**
4
4
  * comark-pele's diagrams on the shared `--comark-*` tokens, on a card like comark-diagrams'. Pele paints with its
@@ -19,4 +19,4 @@ export declare function mermaidBar(): string;
19
19
  */
20
20
  export declare function startMermaid(): () => void;
21
21
  //#endregion
22
- export { typesetCss };
22
+ export { typesetCss, typesetStylesheet };
package/dist/client.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { t as typesetCss } from "./typeset-css-5aJ7cSmh.mjs";
2
- import { i as startMermaid, n as mermaidBar, r as mermaidCss, t as addMermaidStyles } from "./client-BhzMlpun.mjs";
3
- export { addMermaidStyles, mermaidBar, mermaidCss, startMermaid, typesetCss };
1
+ import { n as typesetStylesheet, t as typesetCss } from "./typeset-css-BYvWbqWz.mjs";
2
+ import { i as startMermaid, n as mermaidBar, r as mermaidCss, t as addMermaidStyles } from "./client-kUEiRbtp.mjs";
3
+ export { addMermaidStyles, mermaidBar, mermaidCss, startMermaid, typesetCss, typesetStylesheet };
@@ -9,7 +9,8 @@ import Image from "@tiptap/extension-image";
9
9
  import { TaskItem, TaskList } from "@tiptap/extension-list";
10
10
  import { TableKit } from "@tiptap/extension-table";
11
11
  import StarterKit from "@tiptap/starter-kit";
12
- import { renderMarkdown } from "comark/render";
12
+ import { parseFrontmatter } from "comark/parse";
13
+ import { renderFrontmatter, renderMarkdown } from "comark/render";
13
14
  import { indent, textContent } from "comark/utils";
14
15
  import { Plugin, PluginKey } from "@tiptap/pm/state";
15
16
  import { Decoration, DecorationSet } from "@tiptap/pm/view";
@@ -1054,16 +1055,90 @@ async function fromMarkdown(markdown) {
1054
1055
  async function toMarkdown(doc, options = {}) {
1055
1056
  const markdown = await renderMarkdown({
1056
1057
  nodes: writeBlocks(doc.content),
1057
- frontmatter: options.frontmatter ?? {},
1058
+ frontmatter: {},
1058
1059
  meta: {}
1059
1060
  }, writeOptions);
1060
- return markdown.trim() ? `${markdown.trimEnd()}\n` : "";
1061
+ return withFrontmatter(markdown.trim() ? `${markdown.trimEnd()}\n` : "", options.frontmatter);
1062
+ }
1063
+ /** Finds the frontmatter block where Comark does, and says why a save wouldn't write it back. */
1064
+ function readFrontmatter(markdown) {
1065
+ if (!markdown.startsWith("---") || !markdown.includes("\n---")) return { kind: "none" };
1066
+ let read;
1067
+ try {
1068
+ read = parseFrontmatter(markdown);
1069
+ } catch (error) {
1070
+ return {
1071
+ kind: "lost",
1072
+ problem: `The frontmatter isn't valid YAML (${String(error instanceof Error && "reason" in error ? error.reason : error)}); fix it to edit the document.`,
1073
+ body: markdown.slice(markdown.indexOf("\n---") + 4).replace(/^(?:\r?\n)+/, "")
1074
+ };
1075
+ }
1076
+ const { data, frontmatterText: yaml } = read;
1077
+ const value = data;
1078
+ if (!(value instanceof Object) || Array.isArray(value)) return {
1079
+ kind: "lost",
1080
+ problem: `The frontmatter is a YAML ${Array.isArray(value) ? "list" : "scalar"}, which a save would rewrite; turn it into \`key: value\` lines to edit the document.`
1081
+ };
1082
+ const comments = hasComment(yaml);
1083
+ if (!Object.keys(data).length) return {
1084
+ kind: "lost",
1085
+ problem: comments ? "The frontmatter holds only YAML comments, which a save would drop; delete them or move them into the text to edit the document." : "The frontmatter block is empty, so a save wouldn't write it back; delete its `---` lines or add a `key: value` line to edit the document."
1086
+ };
1087
+ if (comments) return {
1088
+ kind: "lost",
1089
+ problem: "The frontmatter has YAML comments, which a save would drop; delete them or move them into the text to edit the document."
1090
+ };
1091
+ if (hasProtoKey(data)) return {
1092
+ kind: "lost",
1093
+ problem: "The frontmatter has a `__proto__` key, which the room's fields would drop on save; rename it to edit the document."
1094
+ };
1095
+ return {
1096
+ kind: "kept",
1097
+ body: read.content.replace(/^(?:\r?\n)+/, ""),
1098
+ fields: data
1099
+ };
1100
+ }
1101
+ function hasProtoKey(value, seen = /* @__PURE__ */ new Set()) {
1102
+ if (!(value instanceof Object) || seen.has(value)) return false;
1103
+ seen.add(value);
1104
+ return Object.hasOwn(value, "__proto__") || Object.values(value).some((child) => hasProtoKey(child, seen));
1105
+ }
1106
+ /**
1107
+ * Whether the YAML has a comment. A `#` after a space may sit inside a quoted value, so each candidate counts only when
1108
+ * cutting the line there leaves the same data.
1109
+ */
1110
+ function hasComment(yaml) {
1111
+ const lines = yaml.split("\n");
1112
+ const data = (text) => {
1113
+ try {
1114
+ return { value: parseFrontmatter(`---\n${text}\n---\n`).data };
1115
+ } catch {
1116
+ return;
1117
+ }
1118
+ };
1119
+ const original = data(yaml);
1120
+ return lines.some((line, index) => [...line.matchAll(/(?:^|\s)#/g)].some((match) => {
1121
+ const cut = [...lines];
1122
+ cut[index] = line.slice(0, match.index);
1123
+ const read = data(cut.join("\n"));
1124
+ return read !== void 0 && equal(read.value, original?.value);
1125
+ }));
1126
+ }
1127
+ /** Writes the frontmatter above the body. Comark's own writer leaves out the `---` lines when the body is empty. */
1128
+ function withFrontmatter(body, frontmatter = {}) {
1129
+ if (!Object.keys(frontmatter).length) return body;
1130
+ return `---\n${renderFrontmatter(frontmatter, "")}\n---\n${body ? `\n${body}` : ""}`;
1061
1131
  }
1062
1132
  /**
1063
1133
  * Whether the editor can open this Markdown and save it without changing what readers see. Pass the parse plugins the
1064
1134
  * page renders with, so syntax only they read, such as footnote definitions, counts too.
1065
1135
  */
1066
1136
  async function checkMarkdown(markdown, options = {}) {
1137
+ const frontmatter = readFrontmatter(markdown);
1138
+ if (frontmatter.kind === "lost") return {
1139
+ editable: false,
1140
+ reason: frontmatter.problem
1141
+ };
1067
1142
  const first = await fromMarkdown(markdown);
1068
1143
  if (first.lossy) return { editable: false };
1069
1144
  const written = await toMarkdown(first.doc, first);
@@ -1272,10 +1347,28 @@ function createEditorCodec(options = {}) {
1272
1347
  });
1273
1348
  return {
1274
1349
  extensions: () => [kit],
1275
- fromMarkdown: async (markdown) => (await fromMarkdown(markdown)).doc,
1350
+ async fromMarkdown(markdown) {
1351
+ const frontmatter = readFrontmatter(markdown);
1352
+ return (await fromMarkdown(frontmatter.kind === "lost" ? frontmatter.body ?? markdown : markdown)).doc;
1353
+ },
1276
1354
  toMarkdown: (doc) => toMarkdown(doc),
1355
+ split(markdown) {
1356
+ const frontmatter = readFrontmatter(markdown);
1357
+ return frontmatter.kind === "kept" ? {
1358
+ body: frontmatter.body,
1359
+ fields: frontmatter.fields
1360
+ } : {
1361
+ body: markdown,
1362
+ fields: {}
1363
+ };
1364
+ },
1365
+ join: ({ body, fields }) => withFrontmatter(body, fields),
1277
1366
  async verify(markdown) {
1278
- if (Object.keys((await fromMarkdown(markdown)).frontmatter).length) return { writable: false };
1367
+ const frontmatter = readFrontmatter(markdown);
1368
+ if (frontmatter.kind !== "none") return {
1369
+ writable: false,
1370
+ issues: [{ message: frontmatter.kind === "lost" ? frontmatter.problem : "The frontmatter is in the room's body, where a save would drop it; open the room with the codec's `split()` so it stays in the room's fields." }]
1371
+ };
1279
1372
  const check = await checkMarkdown(markdown, { plugins: options.plugins });
1280
1373
  return check.editable ? {
1281
1374
  writable: true,
@@ -1,4 +1,4 @@
1
- import { x as ComarkKitOptions } from "./editor-KD97dL_1.mjs";
1
+ import { S as ComarkKitOptions } from "./editor-yoVL7Ajq.mjs";
2
2
  import { ComarkPlugin } from "comark";
3
3
  import { AnyExtension } from "@tiptap/core";
4
4
  import { Editor } from "@tiptap/vue-3";
@@ -1,12 +1,9 @@
1
- import { c as fromMarkdown, l as toMarkdown, m as ComarkKit, s as checkMarkdown } from "./editor-ASZBrUYa.mjs";
1
+ import { n as isElement, t as childrenOf } from "./nodes-CwDBFXnO.mjs";
2
+ import { c as fromMarkdown, l as toMarkdown, m as ComarkKit, s as checkMarkdown } from "./editor-9IcfELjG.mjs";
2
3
  import { Editor, EditorContent } from "@tiptap/vue-3";
3
4
  import { rowsOf } from "comark-properties";
4
5
  import { Properties } from "comark-properties/vue";
5
6
  import { defineComponent, h, onScopeDispose, ref, shallowRef, watch } from "vue";
6
- //#region ../../shared/nodes.ts
7
- const isElement = (node) => Array.isArray(node) && node[0] !== null;
8
- const childrenOf = ([, , ...rest]) => rest;
9
- //#endregion
10
7
  //#region src/editor-vue.ts
11
8
  /**
12
9
  * A Tiptap editor bound to a Markdown string: an edit writes the Markdown back, and a change from outside reloads the
@@ -50,7 +50,7 @@ declare const ComarkKit: Extension<ComarkKitOptions, any>;
50
50
  interface EditorDocument {
51
51
  /** The document as Tiptap JSON, for `setContent` or a collaborative room. */
52
52
  doc: JSONContent;
53
- /** The front matter, which stays outside the editor: a collaborative room keeps only the body. */
53
+ /** The front matter, which stays outside the editor. A collaborative room keeps it beside the body, in its fields. */
54
54
  frontmatter: Record<string, unknown>;
55
55
  /** The Markdown holds something the editor can't keep, so writing it back would change more than the edit. */
56
56
  lossy: boolean;
@@ -61,11 +61,17 @@ declare function fromMarkdown(markdown: string): Promise<EditorDocument>;
61
61
  declare function toMarkdown(doc: JSONContent, options?: {
62
62
  frontmatter?: Record<string, unknown>;
63
63
  }): Promise<string>;
64
+ /** A Markdown file as a collaborative room holds it: the body the editor shows, and the frontmatter beside it. */
65
+ interface SplitMarkdown {
66
+ body: string;
67
+ fields: Record<string, unknown>;
68
+ }
64
69
  type MarkdownCheck = {
65
70
  editable: true;
66
71
  /** The Markdown as the editor writes it, which the first save produces. */ markdown: string;
67
72
  } | {
68
73
  editable: false;
74
+ /** Why, when it's the frontmatter the editor can't keep. */ reason?: string;
69
75
  };
70
76
  /**
71
77
  * Whether the editor can open this Markdown and save it without changing what readers see. Pass the parse plugins the
@@ -128,15 +134,33 @@ declare const Upload: Extension<UploadOptions, any>;
128
134
  * A schema and Markdown codec that a server and its browsers share, as a collaborative room needs: the server converts
129
135
  * the stored Markdown with it, and every editor that joins builds the same schema from `extensions()`. ViteHub
130
136
  * Realtime's `editor` option takes it as it is.
137
+ *
138
+ * A room holds the body as the editor's document and the frontmatter as its fields, a map of one value per key, so an
139
+ * edit to the text never writes over the metadata. `split()` and `join()` convert between a stored file and the two.
131
140
  */
132
141
  interface EditorCodec {
133
142
  extensions(): AnyExtension[];
143
+ /** Reads a room's body, without frontmatter. */
134
144
  fromMarkdown(markdown: string): Promise<JSONContent>;
145
+ /** Writes a room's body, without frontmatter. */
135
146
  toMarkdown(doc: JSONContent): Promise<string>;
136
- /** Whether the room may save this document. `canonical` is what the first save writes for it unchanged. */
147
+ /**
148
+ * Splits a stored file into the room's body and fields. Frontmatter a save couldn't write back stays in the body,
149
+ * where `verify()` says why the room can't save it.
150
+ */
151
+ split(markdown: string): SplitMarkdown;
152
+ /** Joins a room's body and fields into the file to store. */
153
+ join(document: SplitMarkdown): string;
154
+ /**
155
+ * Whether the room may save this body. `canonical` is what the first save writes for it unchanged, and `issues` says
156
+ * why a body that still holds frontmatter can't be saved. The fields need no check: every JSON value survives YAML.
157
+ */
137
158
  verify(markdown: string): Promise<{
138
159
  writable: boolean;
139
160
  canonical?: string;
161
+ issues?: {
162
+ message: string;
163
+ }[];
140
164
  }>;
141
165
  }
142
166
  interface EditorCodecOptions {
@@ -146,4 +170,4 @@ interface EditorCodecOptions {
146
170
  }
147
171
  declare function createEditorCodec(options?: EditorCodecOptions): EditorCodec;
148
172
  //#endregion
149
- export { FootnoteDefinition as A, CriticComment as C, DefinitionDescription as D, CriticInsertion as E, InlineFootnote as M, DefinitionList as O, ComarkSlot as S, CriticHighlight as T, ComarkComment as _, Upload as a, ComarkKit as b, startUploads as c, EditorDocument as d, MarkdownCheck as f, ComarkBlock as g, toMarkdown as h, PendingUpload as i, FootnoteReference as j, DefinitionTerm as k, uploadKey as l, fromMarkdown as m, EditorCodecOptions as n, UploadOptions as o, checkMarkdown as p, createEditorCodec as r, findUpload as s, EditorCodec as t, uploadPlugin as u, ComarkImage as v, CriticDeletion as w, ComarkKitOptions as x, ComarkInline as y };
173
+ export { DefinitionTerm as A, ComarkSlot as C, CriticInsertion as D, CriticHighlight as E, FootnoteReference as M, InlineFootnote as N, DefinitionDescription as O, ComarkKitOptions as S, CriticDeletion as T, ComarkBlock as _, Upload as a, ComarkInline as b, startUploads as c, EditorDocument as d, MarkdownCheck as f, toMarkdown as g, fromMarkdown as h, PendingUpload as i, FootnoteDefinition as j, DefinitionList as k, uploadKey as l, checkMarkdown as m, EditorCodecOptions as n, UploadOptions as o, SplitMarkdown as p, createEditorCodec as r, findUpload as s, EditorCodec as t, uploadPlugin as u, ComarkComment as v, CriticComment as w, ComarkKit as x, ComarkImage as y };
package/dist/editor.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as FootnoteDefinition, C as CriticComment, D as DefinitionDescription, E as CriticInsertion, M as InlineFootnote, O as DefinitionList, S as ComarkSlot, T as CriticHighlight, _ as ComarkComment, a as Upload, b as ComarkKit, c as startUploads, d as EditorDocument, f as MarkdownCheck, g as ComarkBlock, h as toMarkdown, i as PendingUpload, j as FootnoteReference, k as DefinitionTerm, l as uploadKey, m as fromMarkdown, n as EditorCodecOptions, o as UploadOptions, p as checkMarkdown, r as createEditorCodec, s as findUpload, t as EditorCodec, u as uploadPlugin, v as ComarkImage, w as CriticDeletion, x as ComarkKitOptions, y as ComarkInline } from "./editor-KD97dL_1.mjs";
2
- export { ComarkBlock, ComarkComment, ComarkImage, ComarkInline, ComarkKit, type ComarkKitOptions, ComarkSlot, CriticComment, CriticDeletion, CriticHighlight, CriticInsertion, DefinitionDescription, DefinitionList, DefinitionTerm, type EditorCodec, type EditorCodecOptions, type EditorDocument, FootnoteDefinition, FootnoteReference, InlineFootnote, type MarkdownCheck, type PendingUpload, Upload, type UploadOptions, checkMarkdown, createEditorCodec, findUpload, fromMarkdown, startUploads, toMarkdown, uploadKey, uploadPlugin };
1
+ import { A as DefinitionTerm, C as ComarkSlot, D as CriticInsertion, E as CriticHighlight, M as FootnoteReference, N as InlineFootnote, O as DefinitionDescription, S as ComarkKitOptions, T as CriticDeletion, _ as ComarkBlock, a as Upload, b as ComarkInline, c as startUploads, d as EditorDocument, f as MarkdownCheck, g as toMarkdown, h as fromMarkdown, i as PendingUpload, j as FootnoteDefinition, k as DefinitionList, l as uploadKey, m as checkMarkdown, n as EditorCodecOptions, o as UploadOptions, p as SplitMarkdown, r as createEditorCodec, s as findUpload, t as EditorCodec, u as uploadPlugin, v as ComarkComment, w as CriticComment, x as ComarkKit, y as ComarkImage } from "./editor-yoVL7Ajq.mjs";
2
+ export { ComarkBlock, ComarkComment, ComarkImage, ComarkInline, ComarkKit, type ComarkKitOptions, ComarkSlot, CriticComment, CriticDeletion, CriticHighlight, CriticInsertion, DefinitionDescription, DefinitionList, DefinitionTerm, type EditorCodec, type EditorCodecOptions, type EditorDocument, FootnoteDefinition, FootnoteReference, InlineFootnote, type MarkdownCheck, type PendingUpload, type SplitMarkdown, Upload, type UploadOptions, checkMarkdown, createEditorCodec, findUpload, fromMarkdown, startUploads, toMarkdown, uploadKey, uploadPlugin };
package/dist/editor.mjs CHANGED
@@ -1 +1 @@
1
- export { u as ComarkBlock, d as ComarkComment, f as ComarkImage, p as ComarkInline, m as ComarkKit, h as ComarkSlot, g as CriticComment, _ as CriticDeletion, v as CriticHighlight, y as CriticInsertion, b as DefinitionDescription, x as DefinitionList, S as DefinitionTerm, C as FootnoteDefinition, w as FootnoteReference, T as InlineFootnote, n as Upload, s as checkMarkdown, t as createEditorCodec, r as findUpload, c as fromMarkdown, i as startUploads, l as toMarkdown, a as uploadKey, o as uploadPlugin } from "./editor-ASZBrUYa.mjs";
1
+ export { u as ComarkBlock, d as ComarkComment, f as ComarkImage, p as ComarkInline, m as ComarkKit, h as ComarkSlot, g as CriticComment, _ as CriticDeletion, v as CriticHighlight, y as CriticInsertion, b as DefinitionDescription, x as DefinitionList, S as DefinitionTerm, C as FootnoteDefinition, w as FootnoteReference, T as InlineFootnote, n as Upload, s as checkMarkdown, t as createEditorCodec, r as findUpload, c as fromMarkdown, i as startUploads, l as toMarkdown, a as uploadKey, o as uploadPlugin } from "./editor-9IcfELjG.mjs";
package/dist/html.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as criticCss, n as Preset, r as PresetOptions } from "./index-DVGSDzBS.mjs";
1
+ import { a as commentsCss, n as Preset, o as criticCss, r as PresetOptions } from "./index-BTyvRIPX.mjs";
2
2
  import { NodeHandler } from "comark";
3
3
  //#region src/html.d.ts
4
4
  /**
@@ -10,4 +10,4 @@ export declare const mermaidScript: string;
10
10
  /** Every plugin for `@comark/html`, with the `components` to render them. */
11
11
  declare const _default: (options?: PresetOptions) => Preset<NodeHandler>;
12
12
  //#endregion
13
- export { type PresetOptions, criticCss, _default as default };
13
+ export { type PresetOptions, commentsCss, criticCss, _default as default };
package/dist/html.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { t as typesetCss } from "./typeset-css-5aJ7cSmh.mjs";
2
- import { n as preset, t as criticCss } from "./src-PkmYCSNu.mjs";
3
- import { a as start, n as mermaidBar, r as mermaidCss } from "./client-BhzMlpun.mjs";
1
+ import { n as criticCss, r as preset, t as commentsCss } from "./src-Baygj6ZQ.mjs";
2
+ import { t as typesetCss } from "./typeset-css-BYvWbqWz.mjs";
3
+ import { a as start, n as mermaidBar, r as mermaidCss } from "./client-kUEiRbtp.mjs";
4
4
  import { t as rawText } from "./text-D8j9clg3.mjs";
5
5
  import { escapeHtml } from "comark/utils";
6
6
  import * as annotate from "comark-annotate/html";
@@ -111,4 +111,4 @@ var html_default = (options) => preset({
111
111
  zoom
112
112
  }, options);
113
113
  //#endregion
114
- export { criticCss, html_default as default, mermaidScript };
114
+ export { commentsCss, criticCss, html_default as default, mermaidScript };
@@ -1,12 +1,14 @@
1
1
  import { CriticOptions, css as criticCss } from "comark-critic";
2
2
  import { FigureOptions } from "comark-figure";
3
3
  import { FootnotesOptions } from "comark-footnotes";
4
+ import { CalloutsOptions, ReferencesOptions } from "comark-github";
4
5
  import { IconOptions, Icons } from "comark-icon";
5
6
  import { IncludeOptions } from "comark-include";
6
7
  import { KeysOptions } from "comark-keys";
7
8
  import { ReadingTimeOptions } from "comark-reading-time";
8
9
  import { XrefOptions } from "comark-xref";
9
10
  import { ComarkPlugin } from "comark";
11
+ import { CommentsOptions, css as commentsCss } from "comark-comments";
10
12
  import { PropertiesOptions } from "comark-properties";
11
13
  import { BadgeOptions } from "comark-badge";
12
14
  import { AnnotateOptions } from "comark-annotate";
@@ -14,11 +16,9 @@ import { CastOptions } from "comark-cast";
14
16
  import { ChartsOptions } from "comark-charts";
15
17
  import { ChipsOptions } from "comark-chips";
16
18
  import { CodeOptions } from "comark-code";
17
- import { CommentsOptions } from "comark-comments";
18
19
  import { DetailsOptions } from "comark-details";
19
20
  import { DiagramsOptions } from "comark-diagrams";
20
21
  import { FileTreeOptions } from "comark-file-tree";
21
- import { CalloutsOptions, ReferencesOptions } from "comark-github";
22
22
  import { AnchorsOptions, ExternalOptions } from "comark-links";
23
23
  import { MathOptions } from "comark-mathml";
24
24
  import { MediaOptions } from "comark-media";
@@ -29,6 +29,14 @@ import { TableOptions } from "comark-table";
29
29
  import { TabsOptions } from "comark-tabs";
30
30
  import { TestsOptions } from "comark-tests";
31
31
  import { ZoomOptions } from "comark-zoom";
32
+ //#region src/mermaid-limits.d.ts
33
+ type MermaidOptions = PeleConfig & {
34
+ /** How many diagrams a document draws. The ones after show their source as a code block. */
35
+ maxDiagrams?: number;
36
+ /** How many characters of Mermaid source a document draws, over all its diagrams. A diagram past it shows its source. */
37
+ maxSource?: number;
38
+ };
39
+ //#endregion
32
40
  //#region src/typeset.d.ts
33
41
  /** The node that adds the styles. The plugin adds one at the end of every document. */
34
42
  declare const TYPESET_TAG = "typeset-styles";
@@ -63,8 +71,11 @@ type PresetOptions = {
63
71
  comments?: CommentsOptions | boolean;
64
72
  /** Sends each diagram's source to `https://kroki.io` unless you pass your own `server`, or `{ draw: false }`. */
65
73
  diagrams?: DiagramsOptions | boolean;
66
- /** Draws `mermaid` fences with comark-pele, on the server and offline. */
67
- mermaid?: PeleConfig | boolean;
74
+ /**
75
+ * Draws `mermaid` fences with comark-pele, on the server and offline. A host that renders documents it didn't write
76
+ * can pass `{ maxDiagrams, maxSource }`: the diagrams past either limit show their source as code, each with an issue.
77
+ */
78
+ mermaid?: MermaidOptions | boolean;
68
79
  /** HTML only: Vue, React, and Svelte have no diff components. */
69
80
  diff?: boolean;
70
81
  embed?: boolean;
@@ -176,8 +187,19 @@ type Renderer<C> = {
176
187
  type Preset<C> = {
177
188
  plugins: AnyPlugin[];
178
189
  components: Record<string, C>;
190
+ /**
191
+ * Every tag but `components`' that a document parsed with `plugins` can hold: Markdown's own, the raw HTML that
192
+ * GitHub's `html` plugin keeps, and the plain elements the plugins draw with. With `components`' names, it's the
193
+ * allowlist a host that sanitizes the tree needs. With GitHub's `html` off, raw HTML is left to the host.
194
+ */
195
+ tags: string[];
196
+ /**
197
+ * The blocks a document may write that the plugins turn into other nodes while parsing, such as `::code-group`. A
198
+ * linter that reports unknown components needs them beside `components`' names.
199
+ */
200
+ blocks: string[];
179
201
  };
180
202
  /** Builds a renderer's preset. Use the one from `comark-ui/html`, `/vue`, `/react`, or `/svelte`. */
181
203
  declare function preset<C>(renderer: Renderer<C>, options?: PresetOptions): Preset<C>;
182
204
  //#endregion
183
- export { criticCss as a, typeset as c, Renderer as i, Preset as n, preset as o, PresetOptions as r, TYPESET_TAG as s, AnyPlugin as t };
205
+ export { commentsCss as a, TYPESET_TAG as c, Renderer as i, typeset as l, Preset as n, criticCss as o, PresetOptions as r, preset as s, AnyPlugin as t, MermaidOptions as u };
package/dist/index.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { t as typesetCss } from "./typeset-css-DdOGL7WF.mjs";
2
- import { a as criticCss, c as typeset, i as Renderer, n as Preset, o as preset, r as PresetOptions, s as TYPESET_TAG, t as AnyPlugin } from "./index-DVGSDzBS.mjs";
3
- export { AnyPlugin, Preset, PresetOptions, Renderer, TYPESET_TAG, criticCss, preset, typeset, typesetCss };
1
+ import { n as typesetStylesheet, t as typesetCss } from "./typeset-css-DtOLAa1Z.mjs";
2
+ import { a as commentsCss, c as TYPESET_TAG, i as Renderer, l as typeset, n as Preset, o as criticCss, r as PresetOptions, s as preset, t as AnyPlugin, u as MermaidOptions } from "./index-BTyvRIPX.mjs";
3
+ export { AnyPlugin, type MermaidOptions, Preset, PresetOptions, Renderer, TYPESET_TAG, commentsCss, criticCss, preset, typeset, typesetCss, typesetStylesheet };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { t as typesetCss } from "./typeset-css-5aJ7cSmh.mjs";
2
- import { i as typeset, n as preset, r as TYPESET_TAG, t as criticCss } from "./src-PkmYCSNu.mjs";
3
- export { TYPESET_TAG, criticCss, preset, typeset, typesetCss };
1
+ import { a as typeset, i as TYPESET_TAG, n as criticCss, r as preset, t as commentsCss } from "./src-Baygj6ZQ.mjs";
2
+ import { n as typesetStylesheet, t as typesetCss } from "./typeset-css-BYvWbqWz.mjs";
3
+ export { TYPESET_TAG, commentsCss, criticCss, preset, typeset, typesetCss, typesetStylesheet };
@@ -0,0 +1,5 @@
1
+ //#region ../../shared/nodes.ts
2
+ const isElement = (node) => Array.isArray(node) && node[0] !== null;
3
+ const childrenOf = ([, , ...rest]) => rest;
4
+ //#endregion
5
+ export { isElement as n, childrenOf as t };
package/dist/react.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- import { a as criticCss, n as Preset, r as PresetOptions } from "./index-DVGSDzBS.mjs";
1
+ import { a as commentsCss, n as Preset, o as criticCss, r as PresetOptions } from "./index-BTyvRIPX.mjs";
2
2
  import { ComponentType } from "react";
3
3
  //#region src/react.d.ts
4
4
  type AnyComponent = ComponentType<any>;
5
5
  /** Every plugin with components for `@comark/react`, which leaves out annotate, comments, diff, and quiz. */
6
6
  declare const _default: (options?: PresetOptions) => Preset<AnyComponent>;
7
7
  //#endregion
8
- export { type PresetOptions, criticCss, _default as default };
8
+ export { type PresetOptions, commentsCss, criticCss, _default as default };
package/dist/react.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { t as typesetCss } from "./typeset-css-5aJ7cSmh.mjs";
2
- import { n as preset, t as criticCss } from "./src-PkmYCSNu.mjs";
3
- import { i as startMermaid, n as mermaidBar, t as addMermaidStyles } from "./client-BhzMlpun.mjs";
1
+ import { n as criticCss, r as preset, t as commentsCss } from "./src-Baygj6ZQ.mjs";
2
+ import { t as typesetCss } from "./typeset-css-BYvWbqWz.mjs";
3
+ import { i as startMermaid, n as mermaidBar, t as addMermaidStyles } from "./client-kUEiRbtp.mjs";
4
4
  import { createElement, useEffect } from "react";
5
5
  import * as badge from "comark-badge/react";
6
6
  import * as cards from "comark-cards/react";
@@ -94,4 +94,4 @@ var react_default = (options) => preset({
94
94
  zoom
95
95
  }, options);
96
96
  //#endregion
97
- export { criticCss, react_default as default };
97
+ export { commentsCss, criticCss, react_default as default };
@@ -45,6 +45,8 @@ Either way, the HTML renderer inlines the styles once per document, and the Vue,
45
45
 
46
46
  The `--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.
47
47
 
48
+ 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.
49
+
48
50
  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.
49
51
 
50
52
  ## Shape the document
@@ -7,12 +7,12 @@ comark-media plays an image that points at a video or a sound file, and a GitHub
7
7
  ```md
8
8
  The spinner now stops once the build finishes:
9
9
 
10
- ![The build spinner stops and the Deploy button turns green](./recordings/build.mp4){loop muted}
10
+ ![The build spinner stops and the Deploy button turns green](./recordings/build.mp4){loop muted width=1280 height=720}
11
11
  ```
12
12
 
13
13
  - A `src` that ends in `.mp4`, `.webm`, `.mov`, or `.m4v`, before any `?` or `#`, plays as a video, and one in `.mp3`, `.wav`, `.ogg`, or `.m4a` as audio. Every other image stays an image.
14
14
  - The alt text is required. It names the player for screen readers, and it's the text of a link to the file where the browser can't play it.
15
- - `autoplay`, `loop`, and `muted` are off by default. Browsers start a file by itself only when it's muted, so `autoplay` needs `muted`. `poster="frame.png"` shows an image before a video plays; audio takes none. `{loop=false}` turns a prop off. A video's `width` and `height` must be paired whole pixel dimensions, like `{width=1280 height=720}`, to reserve its room before loading. Audio takes neither. Other props, such as a class, are dropped and reported.
15
+ - `autoplay`, `loop`, and `muted` are off by default. Browsers start a file by itself only when it's muted, so `autoplay` needs `muted`. `poster="frame.png"` shows an image before a video plays; audio takes none. `{loop=false}` turns a prop off. Give every video its `width` and `height`, paired whole pixel dimensions like `{width=1280 height=720}`, so the page keeps its room before the file loads; read them with `ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 clip.mp4`. Audio takes neither. Other props, such as a class, are dropped and reported.
16
16
  - A GitHub attachment link, `https://github.com/user-attachments/assets/<id>`, plays as a video when the bare URL sits alone in its paragraph, as on GitHub. With text of its own, or inside a sentence, it stays a link.
17
17
 
18
18
  ## Record the evidence
@@ -0,0 +1,29 @@
1
+ # Moving a project from remark
2
+
3
+ remark-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/.
4
+
5
+ ## Run it
6
+
7
+ ```sh
8
+ vp exec remark-to-comark check . --json
9
+ vp exec remark-to-comark content "**/*.{md,mdx}" --write
10
+ vp exec remark-to-comark rewrite . --write
11
+ vp exec remark-to-comark check . --json
12
+ ```
13
+
14
+ - `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.
15
+ - `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`.
16
+ - `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.
17
+ - 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).
18
+
19
+ ## What it leaves to you
20
+
21
+ - 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.
22
+ - react-markdown becomes `<Markdown value={markdown} options={…} plugins={[…]} />` from `@comark/react`, with the plugins the findings name.
23
+ - 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.
24
+ - 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.
25
+
26
+ ## Register
27
+
28
+ - In `comark-ui`: not included. Install it beside the preset for the migration.
29
+ - 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`.
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\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 | `![alt](clip.mp4)` | [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 | `![alt](src \"Caption\")` | [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\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 | `![alt](clip.mp4)` | [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 | `![alt](src \"Caption\")` | [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",
@@ -178,7 +178,7 @@ const skills = [
178
178
  },
179
179
  {
180
180
  "path": "references/media.md",
181
- "text": "# Videos and audio\n\ncomark-media plays an image that points at a video or a sound file, and a GitHub attachment link alone on its line, with the alt text as the player's label. Use it for a screen recording in PR evidence: a transition, a loading state, or a sound, which a screenshot can't show.\n\n## Syntax\n\n```md\nThe spinner now stops once the build finishes:\n\n![The build spinner stops and the Deploy button turns green](./recordings/build.mp4){loop muted}\n```\n\n- A `src` that ends in `.mp4`, `.webm`, `.mov`, or `.m4v`, before any `?` or `#`, plays as a video, and one in `.mp3`, `.wav`, `.ogg`, or `.m4a` as audio. Every other image stays an image.\n- The alt text is required. It names the player for screen readers, and it's the text of a link to the file where the browser can't play it.\n- `autoplay`, `loop`, and `muted` are off by default. Browsers start a file by itself only when it's muted, so `autoplay` needs `muted`. `poster=\"frame.png\"` shows an image before a video plays; audio takes none. `{loop=false}` turns a prop off. A video's `width` and `height` must be paired whole pixel dimensions, like `{width=1280 height=720}`, to reserve its room before loading. Audio takes neither. Other props, such as a class, are dropped and reported.\n- A GitHub attachment link, `https://github.com/user-attachments/assets/<id>`, plays as a video when the bare URL sits alone in its paragraph, as on GitHub. With text of its own, or inside a sentence, it stays a link.\n\n## Record the evidence\n\n- Record only the part of the screen that changes, for a few seconds.\n- Encode it as H.264 `.mp4` for every browser, or `.webm` for a smaller file. A `.mov` from macOS doesn't always play in Firefox.\n- Say what happens in the alt text, so a reader who can't play the file still learns what it shows.\n- Add `loop muted` to a clip of one motion, so it reads like an animated screenshot once played.\n- A GitHub attachment from a private repository plays only for readers signed in with access to it. For a page that others read, download the file and serve it with the page.\n\n## Register\n\n- In `comark-ui`: on. `media: { controls: \"native\" }` keeps the browser's own controls and leaves the script out. Measurement reads only data URIs by default. `media: { size: true }` also measures remote videos while parsing; use it only for trusted documents, since requests follow redirects and can reach private networks. `media: { size: false }` disables measurement. Authored dimensions work with any setting. Alone, pass the same options to `media()`.\n- Alone: `pnpm add comark @comark/html comark-media`, then `createHtmlRenderer({ plugins: [media()], components })` with both from `comark-media/html`. `comark-media/vue`, `/react`, and `/svelte` render it too. Its place in `plugins` doesn't matter. It reads the tokens before any plugin sees the tree, so comark-zoom never zooms a video and comark-chips never turns an attachment link into a chip. The HTML renderer inlines the players' script once per document, and the browser's own controls show until it runs and whenever a file can't play.\n"
181
+ "text": "# Videos and audio\n\ncomark-media plays an image that points at a video or a sound file, and a GitHub attachment link alone on its line, with the alt text as the player's label. Use it for a screen recording in PR evidence: a transition, a loading state, or a sound, which a screenshot can't show.\n\n## Syntax\n\n```md\nThe spinner now stops once the build finishes:\n\n![The build spinner stops and the Deploy button turns green](./recordings/build.mp4){loop muted width=1280 height=720}\n```\n\n- A `src` that ends in `.mp4`, `.webm`, `.mov`, or `.m4v`, before any `?` or `#`, plays as a video, and one in `.mp3`, `.wav`, `.ogg`, or `.m4a` as audio. Every other image stays an image.\n- The alt text is required. It names the player for screen readers, and it's the text of a link to the file where the browser can't play it.\n- `autoplay`, `loop`, and `muted` are off by default. Browsers start a file by itself only when it's muted, so `autoplay` needs `muted`. `poster=\"frame.png\"` shows an image before a video plays; audio takes none. `{loop=false}` turns a prop off. Give every video its `width` and `height`, paired whole pixel dimensions like `{width=1280 height=720}`, so the page keeps its room before the file loads; read them with `ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 clip.mp4`. Audio takes neither. Other props, such as a class, are dropped and reported.\n- A GitHub attachment link, `https://github.com/user-attachments/assets/<id>`, plays as a video when the bare URL sits alone in its paragraph, as on GitHub. With text of its own, or inside a sentence, it stays a link.\n\n## Record the evidence\n\n- Record only the part of the screen that changes, for a few seconds.\n- Encode it as H.264 `.mp4` for every browser, or `.webm` for a smaller file. A `.mov` from macOS doesn't always play in Firefox.\n- Say what happens in the alt text, so a reader who can't play the file still learns what it shows.\n- Add `loop muted` to a clip of one motion, so it reads like an animated screenshot once played.\n- A GitHub attachment from a private repository plays only for readers signed in with access to it. For a page that others read, download the file and serve it with the page.\n\n## Register\n\n- In `comark-ui`: on. `media: { controls: \"native\" }` keeps the browser's own controls and leaves the script out. Measurement reads only data URIs by default. `media: { size: true }` also measures remote videos while parsing; use it only for trusted documents, since requests follow redirects and can reach private networks. `media: { size: false }` disables measurement. Authored dimensions work with any setting. Alone, pass the same options to `media()`.\n- Alone: `pnpm add comark @comark/html comark-media`, then `createHtmlRenderer({ plugins: [media()], components })` with both from `comark-media/html`. `comark-media/vue`, `/react`, and `/svelte` render it too. Its place in `plugins` doesn't matter. It reads the tokens before any plugin sees the tree, so comark-zoom never zooms a video and comark-chips never turns an attachment link into a chip. The HTML renderer inlines the players' script once per document, and the browser's own controls show until it runs and whenever a file can't play.\n"
182
182
  },
183
183
  {
184
184
  "path": "references/metrics.md",
@@ -204,6 +204,10 @@ const skills = [
204
204
  "path": "references/reading-time.md",
205
205
  "text": "# Reading time\n\ncomark-reading-time counts a document's words while parsing and writes them to `meta.readingTime` as `{ words, minutes }`. It has no syntax and draws nothing. The host's layout shows the numbers, such as \"7 min read\" under the title.\n\n## What it counts\n\n- The text a reader reads. It leaves out code blocks, frontmatter, and comments, and counts inline code.\n- Each Chinese, Japanese, or Korean character counts as a word, since those languages put no spaces between words.\n- `minutes` is `words` over `wordsPerMinute`, rounded up, so any text takes at least a minute. It is 0 only for a document with no words.\n- `countWords(text)`, also exported, counts any string the same way, such as an excerpt.\n\n## Show it\n\nWith `@comark/html`, parse once, read the meta, and render that document:\n\n```ts\nimport { renderHtmlFromDocument } from \"@comark/html\";\nimport { parseMarkdown } from \"comark\";\nimport comarkUi from \"comark-ui/html\";\n\nconst { plugins, components } = comarkUi();\nconst document = await parseMarkdown(markdown, { plugins });\nconst body = await renderHtmlFromDocument(document, { components });\nconst html = `<p class=\"byline\">${document.meta.readingTime.minutes} min read</p>${body}`;\n```\n\nWith `@comark/vue` or `@comark/react`, read `meta.readingTime` from the parsed document you pass to `MarkdownDocument`.\n\n## Register\n\n- In `comark-ui`: on in every entry. `readingTime: { wordsPerMinute: 230 }` sets the speed; 0 or less counts at 200. The default, 200, is a cautious pace for technical prose; 230 to 250 suits light reading, and a site mostly in CJK may want more.\n- Alone: `pnpm add comark comark-reading-time`, then `parseMarkdown(markdown, { plugins: [readingTime()] })` with `readingTime` from `comark-reading-time`. It has no components, so it works with any renderer.\n"
206
206
  },
207
+ {
208
+ "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 . --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
+ },
207
211
  {
208
212
  "path": "references/sources.md",
209
213
  "text": "# Citations and sources\n\ncomark-sources turns `:cite[n]` into a numbered link to the nth source in the document's `::sources` list, and gives each source a link back to every citation of it. Use it when the claims in a document rest on links, such as the research behind a change.\n\n## Syntax\n\n```md\nPigeons find home by the sun and by the earth's magnetic field :cite[1], and some by smell too :cite[2,3].\n\n::sources\n1. [Magnetoreception in homing pigeons](https://en.wikipedia.org/wiki/Magnetoreception)\n2. [Olfactory navigation](https://en.wikipedia.org/wiki/Pigeon_intelligence), on smell\n3. [How do homing pigeons navigate?](https://www.sciencefocus.com/nature/how-do-homing-pigeons-navigate)\n::\n```\n\n- `:cite[2]` cites the second source, and `:cite[1,3]` several. Each becomes a superscript `[1]` that links to its source. Hovering or focusing it shows a card with the source's title, its site, and the icon the list shows for it, a globe when there's none. Put a space before `:cite`, which the plugin removes; `word:cite[1]` stays text, and nothing reports it.\n- `::sources` holds one numbered list with one source per item, each with its link. Sources count by their place in the list. Text after the link, such as `, on smell`, stays in the item. `title` names the list, \"Sources\" by default. The plugin drops anything beside the list and reports it.\n- A document has one `::sources`, anywhere in it, and citations before and after it both count. A citation of a number the list doesn't have, a source with no link, and a second list are reported.\n\n## Register\n\n- In `comark-ui`: on. It takes no options.\n- Alone: `pnpm add comark @comark/html comark-sources`, then `createHtmlRenderer({ plugins: [sources()], components })` with both from `comark-sources/html`. `comark-sources/vue`, `/react`, and `/svelte` render it too. Citations are plain `sup` links that work in any renderer, and the `sources` component draws the list and carries the styles for both. It has no options. The HTML renderer inlines the card's script once per document, and the components start it when they mount.\n"
@@ -321,6 +325,6 @@ const skills = [
321
325
  }
322
326
  ];
323
327
  /** The comark-ui version the skills come from, for a host's cache key or a digest. */
324
- const version = "0.8.0";
328
+ const version = "0.8.2";
325
329
  //#endregion
326
330
  export { skills, version };
@@ -1,14 +1,61 @@
1
+ import { n as isElement, t as childrenOf } from "./nodes-CwDBFXnO.mjs";
1
2
  import cjk from "comark-cjk";
2
3
  import critic, { css as criticCss } from "comark-critic";
3
4
  import deflist from "comark-deflist";
4
5
  import figure from "comark-figure";
5
6
  import footnotes from "comark-footnotes";
7
+ import { HTML_TAGS } from "comark-github";
6
8
  import { createIcons } from "comark-icon";
7
9
  import include from "comark-include";
8
10
  import keys from "comark-keys";
9
11
  import readingTime from "comark-reading-time";
10
12
  import xref from "comark-xref";
11
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
12
59
  //#region src/typeset.ts
13
60
  /** The node that adds the styles. The plugin adds one at the end of every document. */
14
61
  const TYPESET_TAG = "typeset-styles";
@@ -21,6 +68,34 @@ const typeset = defineComarkPlugin(() => ({
21
68
  }));
22
69
  //#endregion
23
70
  //#region src/index.ts
71
+ const words = (text) => text.split(" ");
72
+ 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");
73
+ const TAGS = [
74
+ ["carousel", words("div")],
75
+ ["codeNotes", words("div span")],
76
+ ["critic", words("del ins mark")],
77
+ ["deflist", words("dd dl dt")],
78
+ ["figure", words("figcaption figure")],
79
+ ["footnotes", words("section sup")],
80
+ ["keys", words("kbd")],
81
+ ["mark", words("mark sup")],
82
+ ["metrics", words("dd div dt")],
83
+ ["outline", words("a div li ul")],
84
+ ["properties", words("dd div dt time")],
85
+ ["quiz", words("div quiz-after quiz-answer quiz-explain quiz-hint quiz-label quiz-option quiz-options quiz-prompt quiz-rationale")],
86
+ ["sources", words("ol sup")],
87
+ ["table", words("button div span")],
88
+ ["tabs", words("div tabs-item")],
89
+ ["tests", words("details div summary")],
90
+ ["xref", words("caption figcaption figure")]
91
+ ];
92
+ const BLOCKS = [
93
+ ["details", words("accordion-item collapsible details")],
94
+ ["metrics", words("progress stats")],
95
+ ["table", words("table")],
96
+ ["tabs", words("code-group tabs-item")],
97
+ ["xref", words("ref")]
98
+ ];
24
99
  const on = (value, fallback = true) => value === void 0 ? fallback : value !== false;
25
100
  const optionsOf = (value) => value === true || value === false ? void 0 : value;
26
101
  const noFiles = { read: () => void 0 };
@@ -49,7 +124,14 @@ function preset(renderer, options = {}) {
49
124
  add(renderer.chips, options.chips);
50
125
  add(renderer.colors, options.colors);
51
126
  add(renderer.diagrams, options.diagrams);
52
- add(renderer.mermaid, options.mermaid);
127
+ if (on(options.mermaid)) {
128
+ const { maxDiagrams, maxSource, ...pele } = optionsOf(options.mermaid) ?? {};
129
+ add(renderer.mermaid, true, pele);
130
+ if (maxDiagrams !== void 0 || maxSource !== void 0) plugins.push(mermaid_limits_default({
131
+ maxDiagrams,
132
+ maxSource
133
+ }));
134
+ }
53
135
  add(renderer.diff, options.diff);
54
136
  add(renderer.embed, options.embed);
55
137
  parse(footnotes, options.footnotes);
@@ -111,10 +193,26 @@ function preset(renderer, options = {}) {
111
193
  plugins.push(typeset());
112
194
  components[TYPESET_TAG] = renderer.typeset;
113
195
  }
196
+ const { annotate, comments, diff, quiz } = renderer;
197
+ const absent = Object.entries({
198
+ annotate,
199
+ comments,
200
+ diff,
201
+ quiz
202
+ }).flatMap(([key, part]) => part ? [] : [key]);
203
+ const enabled = (key) => on(options[key], key !== "include") && !absent.includes(key);
204
+ const tags = /* @__PURE__ */ new Set([
205
+ ...MARKDOWN_TAGS,
206
+ ...on(options.github) && on(optionsOf(options.github)?.html) ? HTML_TAGS : [],
207
+ ...TAGS.flatMap(([key, list]) => enabled(key) ? list : [])
208
+ ]);
209
+ const blocks = BLOCKS.flatMap(([key, list]) => enabled(key) ? list : []);
114
210
  return {
115
211
  plugins,
116
- components
212
+ components,
213
+ tags: [...tags],
214
+ blocks
117
215
  };
118
216
  }
119
217
  //#endregion
120
- export { typeset as i, preset as n, TYPESET_TAG as r, criticCss as t };
218
+ export { typeset as a, TYPESET_TAG as i, criticCss as n, preset as r, commentsCss as t };
@@ -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. */
@@ -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
 
@@ -89,7 +89,7 @@ const rules = `
89
89
  &:where(ul > li, ol > li)::marker { color: var(--_ty-muted); }
90
90
  &:where(ul.contains-task-list) { list-style-type: none; padding-inline-start: 0.25em; }
91
91
  &:where(li.task-list-item > input[type="checkbox"]) {
92
- margin-inline-end: 0.5em; vertical-align: -0.1em; accent-color: var(--ui-primary, var(--primary, currentColor));
92
+ margin-inline-end: 0.5em; vertical-align: -0.1em; accent-color: var(--_comark-primary);
93
93
  }
94
94
 
95
95
  &:where(details) { margin-block: var(--_ty-flow) 0; }
@@ -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
- &:where(p img) { margin-block-start: 0; }
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,10 +176,18 @@ 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. Only spans that carry a dark color switch. */
179
- .shiki:where(.dark *) span[style*="--shiki-dark:"] { color: var(--shiki-dark) !important; }
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[style*="--shiki-dark:"] { color: var(--shiki-dark) !important; }
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
@@ -211,6 +220,12 @@ const rules = `
211
220
  * page alone. The `typeset` layer sits inside `comark`, so every block's own rules win over it, and any plain rule of
212
221
  * the page wins over both.
213
222
  */
214
- const typesetCss = `@layer comark{@layer typeset{@scope{${theme(":scope")}${rules}}}}`.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/ ?([{};,>(]) ?/g, "$1").replace(/: /g, ":").trim();
223
+ const typesetCss = `@layer comark{@layer typeset{@scope{${theme(":scope", "primary")}${rules}}}}`.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/ ?([{};,>(]) ?/g, "$1").replace(/: /g, ":").trim();
224
+ /**
225
+ * The same styles for a page's own stylesheet, scoped to every element that matches `selector`, for Comark content
226
+ * that isn't a rendered document, such as a rich-text editor. `comark-ui/typeset.css` is this with `.comark-typeset`.
227
+ * The rules are the same string as `typesetCss`, so the two can't drift.
228
+ */
229
+ const typesetStylesheet = (selector = ".comark-typeset") => typesetCss.replace("@scope{", `@scope (${selector}){`);
215
230
  //#endregion
216
- export { typesetCss as t };
231
+ 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 };
@@ -0,0 +1 @@
1
+ @layer comark{@layer typeset{@scope (.comark-typeset){:where(:scope){--_comark-background:var(--comark-background,var(--ui-bg,var(--background,#fff)));--_comark-foreground:var(--comark-foreground,var(--ui-text-highlighted,var(--foreground,#0a0a0a)));--_comark-muted:var(--comark-muted,var(--ui-text-muted,var(--muted-foreground,#737373)));--_comark-border:var(--comark-border,var(--ui-border,var(--border,#e5e5e5)));--_comark-accent:var(--comark-accent,var(--ui-bg-elevated,var(--accent,#f5f5f5)));--_comark-ring:var(--comark-ring,var(--ui-primary,var(--ring,#a1a1a1)));--_comark-primary:var(--comark-primary,var(--ui-primary,var(--primary,var(--_comark-foreground))));--_comark-primary-foreground:var(--comark-primary-foreground,var(--ui-text-inverted,var(--primary-foreground,var(--_comark-background))));--_comark-secondary:var(--comark-secondary,var(--ui-secondary,var(--_comark-muted)));--_comark-radius:var(--comark-radius,calc(var(--ui-radius,calc(var(--radius,.625rem)/2))*2));--_comark-font:var(--comark-font,var(--font-sans,ui-sans-serif,system-ui,sans-serif))}@media(prefers-color-scheme:dark){:where(:scope){--_comark-background:var(--comark-background,var(--ui-bg,var(--background,#0a0a0a)));--_comark-foreground:var(--comark-foreground,var(--ui-text-highlighted,var(--foreground,#fafafa)));--_comark-muted:var(--comark-muted,var(--ui-text-muted,var(--muted-foreground,#a1a1a1)));--_comark-border:var(--comark-border,var(--ui-border,var(--border,rgb(255 255 255/.1))));--_comark-accent:var(--comark-accent,var(--ui-bg-elevated,var(--accent,#262626)));--_comark-ring:var(--comark-ring,var(--ui-primary,var(--ring,#737373)));--_comark-primary:var(--comark-primary,var(--ui-primary,var(--primary,var(--_comark-foreground))));--_comark-primary-foreground:var(--comark-primary-foreground,var(--ui-text-inverted,var(--primary-foreground,var(--_comark-background))));--_comark-secondary:var(--comark-secondary,var(--ui-secondary,var(--_comark-muted)))}}:scope{--_ty-flow:var(--typeset-flow,1.25em);--_ty-muted:var(--_comark-muted);--_ty-rule:var(--_comark-border);--_ty-surface:var(--_comark-accent);--_ty-radius:calc(var(--_comark-radius) * 0.8);--_ty-mono:var(--comark-font-mono,var(--font-mono,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace));font-family:var(--_comark-font);font-size:calc(var(--typeset-size,1em) * 1.125);line-height:var(--typeset-leading,1.75);color:var(--_comark-foreground);overflow-wrap:break-word;background-image:var(--comark-grain,url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E"));background-repeat:repeat;background-size:var(--comark-grain-size,256px 256px);@media(min-width:48rem),print{font-size:var(--typeset-size,1em);}}:scope *:not(:where(.not-typeset,[data-not-typeset],.not-typeset *,[data-not-typeset] *)){&:where(p){margin-block:var(--_ty-flow) 0;}&:where(h1,h2,h3,h4,h5,h6){color:var(--_comark-foreground);font-family:var(--comark-font-heading,var(--font-heading,inherit));font-weight:600;margin-block-end:0;}&:where(h1){font-size:1.75em;line-height:1.3;margin-block-start:var(--_ty-flow);}&:where(h2){font-size:1.25em;line-height:1.4;margin-block-start:calc(var(--_ty-flow) * 1.4);}&:where(h3){font-size:1.125em;line-height:1.45;margin-block-start:var(--_ty-flow);}&:where(h4){font-size:1em;line-height:1.5;margin-block-start:var(--_ty-flow);}&:where(h5){font-size:0.875em;line-height:1.5;font-weight:500;color:var(--_ty-muted);margin-block-start:calc(var(--_ty-flow) / 0.875);}&:where(h6){font-size:0.8125em;line-height:1.5;font-weight:500;letter-spacing:0.08em;text-transform:uppercase;color:var(--_ty-muted);margin-block-start:calc(var(--_ty-flow) / 0.8125);}&:where([id]){scroll-margin-block-start:var(--_ty-flow);}&:where(h1 + *,h2 + *,h3 + *,h4 + *,h5 + *,h6 + *){margin-block-start:1em;}&:where(:is(h1,h2,h3,h4) code){font-size:0.9em;}&:where(a){color:inherit;font-weight:500;text-decoration-line:underline;text-decoration-color:color-mix(in oklab,currentColor 30%,transparent);}&:where(a:hover){text-decoration-color:currentColor;}&:where(a:focus-visible){outline:2px solid var(--_comark-ring);outline-offset:2px;border-radius:0.125em;}&:where(:is(h1,h2,h3,h4,h5,h6) a){font-weight:inherit;}&:where(strong,b){font-weight:600;}&:where(mark){background-color:color-mix(in oklab,oklch(0.86 0.17 95) 40%,transparent);color:inherit;padding:0.05em 0.15em;border-radius:0.2em;}&:where(abbr[title]){text-decoration-line:underline;text-decoration-style:dotted;text-underline-offset:0.2em;cursor:help;}&:where(del,s){color:var(--_ty-muted);text-decoration-line:line-through;}&:where(sup,sub){font-size:0.75em;line-height:0;position:relative;vertical-align:baseline;}&:where(sup){top:-0.5em;}&:where(sub){bottom:-0.25em;}&:where(sup a){text-decoration-line:none;font-weight:500;}&:where(ul){list-style-type:disc;}&:where(ol){list-style-type:decimal;}&:where(ul ul){list-style-type:circle;}&:where(ul ul ul){list-style-type:square;}&:where(ol[type="a" i]){list-style-type:lower-alpha;}&:where(ol[type="i" i]){list-style-type:lower-roman;}&:where(ol[type="A" s]){list-style-type:upper-alpha;}&:where(ol[type="I" s]){list-style-type:upper-roman;}&:where(ul,ol){margin-block:var(--_ty-flow) 0;padding-inline-start:1.5em;}&:where(li){margin-block-start:0.5em;padding-inline-start:0.4em;}&:where(li>p,li>ul,li>ol){margin-block-start:0.5em;}&:where(ul>li,ol>li)::marker{color:var(--_ty-muted);}&:where(ul.contains-task-list){list-style-type:none;padding-inline-start:0.25em;}&:where(li.task-list-item>input[type="checkbox"]){margin-inline-end:0.5em;vertical-align:-0.1em;accent-color:var(--_comark-primary);}&:where(details){margin-block:var(--_ty-flow) 0;}&:where(summary){cursor:pointer;font-weight:500;}&:where(summary)::marker{color:var(--_ty-muted);}&:where(kbd){font-family:inherit;font-size:0.85em;font-weight:500;border:1px solid var(--_ty-rule);border-block-end-width:2px;border-radius:0.35em;padding:0.0625em 0.35em;}&:where(dl){margin-block:var(--_ty-flow) 0;}&:where(dt){font-weight:500;margin-block-start:1em;}&:where(dt + dt){margin-block-start:0.25em;}&:where(dd){margin-block-start:0.25em;margin-inline-start:0;padding-inline-start:1em;color:var(--_ty-muted);}&:where(:not(pre)>code){background-color:var(--_ty-surface);font-family:var(--_ty-mono);font-size:0.85em;border-radius:0.35em;padding:0.125em 0.3em;}&:where(pre){background-color:var(--_ty-surface);font-family:var(--_ty-mono);font-size:0.875em;line-height:1.5;tab-size:2;direction:ltr;border-radius:var(--_ty-radius);padding:0.75em 1em;overflow-x:auto;margin-block:calc(var(--_ty-flow) / 0.875) 0;}&:where(pre code){background-color:transparent;font-family:inherit;font-size:inherit;padding:0;border-radius:0;}&:where(blockquote){border-inline-start:2px solid var(--_ty-rule);padding-inline-start:1em;margin-block:var(--_ty-flow) 0;margin-inline:0;}&:where(hr){border:0;border-block-start:1px solid var(--_ty-rule);margin-block:calc(var(--_ty-flow) * 2.4) 0;}&:where(hr + h1,hr + h2,hr + h3,hr + h4){margin-block-start:var(--_ty-flow);}&:where(.footnotes,[data-footnotes]){margin-block-start:calc(var(--_ty-flow) * 2);font-size:0.875em;color:var(--_ty-muted);}&:where(.footnotes>hr:first-child){margin-block-start:0;}&:where(.footnote-ref a,.footnote-backref){text-decoration-line:none;}&:where(math[display="block"]){margin-block:var(--_ty-flow) 0;overflow-x:auto;overflow-y:hidden;padding-block:0.25em;}&:where(img,video){border-radius:var(--_ty-radius);max-width:100%;height:auto;margin-block:var(--_ty-flow) 0;}&:where(:is(p,li,td,th,a) img){margin-block-start:0;}&:where(figure){margin-block:var(--_ty-flow) 0;margin-inline:0;}&:where(figcaption,caption){color:var(--_ty-muted);font-size:0.875em;text-align:center;margin-block-start:calc(0.75em / 0.875);}&:where(caption){caption-side:bottom;}&:where(table){display:block;width:max-content;max-width:100%;overflow-x:auto;font-size:1em;line-height:1.5;font-variant-numeric:tabular-nums;border-collapse:separate;border-spacing:0;border-block-end:1px solid var(--_ty-rule);margin-block:var(--_ty-flow) 0;}&:where(thead th){font-weight:500;text-align:start;padding:0.65em 1em;}&:where(:is(tbody,tfoot) :is(td,th)){padding:0.75em 1em;text-align:start;vertical-align:top;border-block-start:1px solid var(--_ty-rule);}&:where(tbody th,tfoot th){font-weight:500;}&:where(th:first-child,td:first-child){padding-inline-start:0;}&:where(th[align="center"],td[align="center"]){text-align:center;}&:where(th[align="right"],td[align="right"]){text-align:end;}&:where(li>blockquote,li>table,li>figure){margin-block-start:0.5em;}&:where(li>pre){margin-block-start:calc(0.5em / 0.875);}@media print{&:where(pre,table,blockquote,figure){break-inside:avoid;}&:where(h1,h2,h3,h4){break-after:avoid;}}@media(forced-colors:active){&:where(:not(pre)>code){border:1px solid;}}}.shiki:where(.dark *) span{&[style*="--shiki-dark:"]{color:var(--shiki-dark) !important;}&[style*="--shiki-dark-font-style:"]{font-style:var(--shiki-dark-font-style) !important;}&[style*="--shiki-dark-font-weight:"]{font-weight:var(--shiki-dark-font-weight) !important;}}@media(prefers-color-scheme:dark){.shiki:not(:where(.light *)) span{&[style*="--shiki-dark:"]{color:var(--shiki-dark) !important;}&[style*="--shiki-dark-font-style:"]{font-style:var(--shiki-dark-font-style) !important;}&[style*="--shiki-dark-font-weight:"]{font-weight:var(--shiki-dark-font-weight) !important;}}}:scope>[data-wide]{--_ty-start:max(0px,var(--comark-wide-start,0px));--_ty-end:max(0px,var(--comark-wide-end,0px));--_ty-wide:max(100%,min(var(--comark-wide-width,100vw),var(--comark-wide-max,72rem),100% + var(--_ty-start) + var(--_ty-end)));width:var(--_ty-wide);margin-inline:clamp(-1 * var(--_ty-start),(100% - var(--_ty-wide)) / 2,100% + var(--_ty-end) - var(--_ty-wide)) 0;}:scope>:where(:first-child):not(:where(.not-typeset,[data-not-typeset],.not-typeset *,[data-not-typeset] *)),:scope>:where(:first-child)>:where(:first-child):not(:where(.not-typeset,[data-not-typeset],.not-typeset *,[data-not-typeset] *)),:scope :where(li>:first-child,blockquote>:first-child,td>:first-child,th>:first-child,dd>:first-child,figure>:first-child,figure>picture>img ):not(:where(.not-typeset,[data-not-typeset],.not-typeset *,[data-not-typeset] *)){margin-block-start:0;}}}}
package/dist/vue.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { a as criticCss, n as Preset, r as PresetOptions } from "./index-DVGSDzBS.mjs";
1
+ import { a as commentsCss, n as Preset, o as criticCss, r as PresetOptions } from "./index-BTyvRIPX.mjs";
2
2
  import { Component } from "vue";
3
3
  //#region src/vue.d.ts
4
4
  /** Every plugin with components for `@comark/vue`, which leaves out annotate, comments, diff, and quiz. */
5
5
  declare const _default: (options?: PresetOptions) => Preset<Component>;
6
6
  //#endregion
7
- export { type PresetOptions, criticCss, _default as default };
7
+ export { type PresetOptions, commentsCss, criticCss, _default as default };
package/dist/vue.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { t as typesetCss } from "./typeset-css-5aJ7cSmh.mjs";
2
- import { n as preset, t as criticCss } from "./src-PkmYCSNu.mjs";
3
- import { i as startMermaid, n as mermaidBar, t as addMermaidStyles } from "./client-BhzMlpun.mjs";
1
+ import { n as criticCss, r as preset, t as commentsCss } from "./src-Baygj6ZQ.mjs";
2
+ import { t as typesetCss } from "./typeset-css-BYvWbqWz.mjs";
3
+ import { i as startMermaid, n as mermaidBar, t as addMermaidStyles } from "./client-kUEiRbtp.mjs";
4
4
  import * as properties from "comark-properties/vue";
5
5
  import { defineComponent, h, onBeforeUnmount, onMounted } from "vue";
6
6
  import * as badge from "comark-badge/vue";
@@ -110,4 +110,4 @@ var vue_default = (options) => preset({
110
110
  zoom
111
111
  }, options);
112
112
  //#endregion
113
- export { criticCss, vue_default as default };
113
+ export { commentsCss, criticCss, vue_default as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comark-ui",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Every comark-ui block in one install: annotated screenshots, charts, diffs, tabs, zoom, and more, registered in the order they need with shared icons. Turn any of them off or pass its options.",
5
5
  "keywords": [
6
6
  "comark",
@@ -23,7 +23,9 @@
23
23
  "dist"
24
24
  ],
25
25
  "type": "module",
26
- "sideEffects": false,
26
+ "sideEffects": [
27
+ "*.css"
28
+ ],
27
29
  "exports": {
28
30
  ".": "./dist/index.mjs",
29
31
  "./client": "./dist/client.mjs",
@@ -40,7 +42,8 @@
40
42
  "./vue": "./dist/vue.mjs",
41
43
  "./package.json": "./package.json",
42
44
  "./skills": "./dist/skills.mjs",
43
- "./skills/*": "./dist/skills/*"
45
+ "./skills/*": "./dist/skills/*",
46
+ "./typeset.css": "./dist/typeset.css"
44
47
  },
45
48
  "publishConfig": {
46
49
  "access": "public",
@@ -48,50 +51,50 @@
48
51
  },
49
52
  "dependencies": {
50
53
  "comark-pele": "^1.1.0",
51
- "comark-annotate": "^0.8.0",
52
- "comark-cards": "^0.8.0",
53
- "comark-carousel": "^0.8.0",
54
- "comark-cast": "^0.8.0",
55
- "comark-charts": "^0.8.0",
56
- "comark-badge": "^0.8.0",
57
- "comark-chips": "^0.8.0",
58
- "comark-code-notes": "^0.8.0",
59
- "comark-cjk": "^0.8.0",
60
- "comark-colors": "^0.8.0",
61
- "comark-code": "^0.8.0",
62
- "comark-comments": "^0.8.0",
63
- "comark-deflist": "^0.8.0",
64
- "comark-details": "^0.8.0",
65
- "comark-critic": "^0.8.0",
66
- "comark-diagrams": "^0.8.0",
67
- "comark-embed": "^0.8.0",
68
- "comark-fields": "^0.8.0",
69
- "comark-file-tree": "^0.8.0",
70
- "comark-footnotes": "^0.8.0",
71
- "comark-icon": "^0.8.0",
72
- "comark-github": "^0.8.0",
73
- "comark-include": "^0.8.0",
74
- "comark-links": "^0.8.0",
75
- "comark-keys": "^0.8.0",
76
- "comark-magic-move": "^0.8.0",
77
- "comark-figure": "^0.8.0",
78
- "comark-diff": "^0.8.0",
79
- "comark-mathml": "^0.8.0",
80
- "comark-media": "^0.8.0",
81
- "comark-outline": "^0.8.0",
82
- "comark-properties": "^0.8.0",
83
- "comark-mark": "^0.8.0",
84
- "comark-metrics": "^0.8.0",
85
- "comark-qr": "^0.8.0",
86
- "comark-quiz": "^0.8.0",
87
- "comark-sources": "^0.8.0",
88
- "comark-steps": "^0.8.0",
89
- "comark-table": "^0.8.0",
90
- "comark-tabs": "^0.8.0",
91
- "comark-tests": "^0.8.0",
92
- "comark-xref": "^0.8.0",
93
- "comark-reading-time": "^0.8.0",
94
- "comark-zoom": "^0.8.0"
54
+ "comark-annotate": "^0.8.2",
55
+ "comark-cast": "^0.8.2",
56
+ "comark-cards": "^0.8.2",
57
+ "comark-charts": "^0.8.2",
58
+ "comark-carousel": "^0.8.2",
59
+ "comark-badge": "^0.8.2",
60
+ "comark-chips": "^0.8.2",
61
+ "comark-cjk": "^0.8.2",
62
+ "comark-code": "^0.8.2",
63
+ "comark-colors": "^0.8.2",
64
+ "comark-critic": "^0.8.2",
65
+ "comark-details": "^0.8.2",
66
+ "comark-deflist": "^0.8.2",
67
+ "comark-comments": "^0.8.2",
68
+ "comark-diagrams": "^0.8.2",
69
+ "comark-code-notes": "^0.8.2",
70
+ "comark-embed": "^0.8.2",
71
+ "comark-diff": "^0.8.2",
72
+ "comark-fields": "^0.8.2",
73
+ "comark-figure": "^0.8.2",
74
+ "comark-file-tree": "^0.8.2",
75
+ "comark-include": "^0.8.2",
76
+ "comark-icon": "^0.8.2",
77
+ "comark-keys": "^0.8.2",
78
+ "comark-links": "^0.8.2",
79
+ "comark-mathml": "^0.8.2",
80
+ "comark-footnotes": "^0.8.2",
81
+ "comark-mark": "^0.8.2",
82
+ "comark-github": "^0.8.2",
83
+ "comark-magic-move": "^0.8.2",
84
+ "comark-metrics": "^0.8.2",
85
+ "comark-media": "^0.8.2",
86
+ "comark-outline": "^0.8.2",
87
+ "comark-quiz": "^0.8.2",
88
+ "comark-reading-time": "^0.8.2",
89
+ "comark-steps": "^0.8.2",
90
+ "comark-properties": "^0.8.2",
91
+ "comark-table": "^0.8.2",
92
+ "comark-tests": "^0.8.2",
93
+ "comark-tabs": "^0.8.2",
94
+ "comark-xref": "^0.8.2",
95
+ "comark-qr": "^0.8.2",
96
+ "comark-zoom": "^0.8.2",
97
+ "comark-sources": "^0.8.2"
95
98
  },
96
99
  "devDependencies": {
97
100
  "@comark/html": "^0.8.1",