comark-ui 0.7.6 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +2 -2
  2. package/dist/{client-Bjd6VcGQ.mjs → client-BhzMlpun.mjs} +17 -7
  3. package/dist/client.mjs +1 -1
  4. package/dist/{editor-DmxNPK7K.mjs → editor-ASZBrUYa.mjs} +10 -11
  5. package/dist/editor-vue.d.mts +20 -4
  6. package/dist/editor-vue.mjs +40 -9
  7. package/dist/editor.mjs +1 -1
  8. package/dist/html.d.mts +1 -1
  9. package/dist/html.mjs +6 -2
  10. package/dist/{index-Dbqxt4Qg.d.mts → index-DVGSDzBS.d.mts} +1 -1
  11. package/dist/index.d.mts +1 -1
  12. package/dist/react.d.mts +1 -1
  13. package/dist/react.mjs +1 -1
  14. package/dist/skills/before-after-evidence/SKILL.md +11 -13
  15. package/dist/skills/before-after-evidence/references/blocks.md +1 -1
  16. package/dist/skills/comark-ui/SKILL.md +11 -9
  17. package/dist/skills/comark-ui/references/annotate.md +1 -1
  18. package/dist/skills/comark-ui/references/badge.md +1 -1
  19. package/dist/skills/comark-ui/references/cards.md +2 -1
  20. package/dist/skills/comark-ui/references/code-notes.md +1 -1
  21. package/dist/skills/comark-ui/references/code.md +3 -3
  22. package/dist/skills/comark-ui/references/comments.md +3 -3
  23. package/dist/skills/comark-ui/references/embed.md +3 -2
  24. package/dist/skills/comark-ui/references/keys.md +1 -1
  25. package/dist/skills/comark-ui/references/links.md +1 -1
  26. package/dist/skills/comark-ui/references/lint.md +21 -12
  27. package/dist/skills/comark-ui/references/magic-move.md +1 -1
  28. package/dist/skills/comark-ui/references/metrics.md +1 -0
  29. package/dist/skills/comark-ui/references/outline.md +2 -2
  30. package/dist/skills/comark-ui/references/properties.md +1 -1
  31. package/dist/skills/comark-ui/references/quiz.md +15 -3
  32. package/dist/skills/comark-ui/references/reading-time.md +1 -1
  33. package/dist/skills/comark-ui/references/steps.md +1 -1
  34. package/dist/skills/comark-ui/references/table.md +1 -0
  35. package/dist/skills/comark-ui/references/tabs.md +2 -2
  36. package/dist/skills/comark-ui/references/tests.md +1 -1
  37. package/dist/skills/comark-ui/references/ui.md +7 -6
  38. package/dist/skills/teach-lesson/SKILL.md +4 -4
  39. package/dist/skills/teach-lesson/references/blocks.md +6 -6
  40. package/dist/skills.mjs +29 -29
  41. package/dist/text-D8j9clg3.mjs +19 -0
  42. package/dist/vue.d.mts +1 -1
  43. package/dist/vue.mjs +2 -2
  44. package/package.json +45 -45
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![license](https://comark-ui.onmax.me/badge.svg?label=license&value=MIT)](https://npmx.dev/package/comark-ui)
6
6
  [![docs](https://comark-ui.onmax.me/badge.svg?label=docs&value=comark-ui.onmax.me)](https://comark-ui.onmax.me/getting-started/)
7
7
 
8
- Every [Comark](https://comark.dev) block from [comark-ui](https://comark-ui.onmax.me) in one install, registered in the order they need, with one icon resolver shared by `:icon`, cards, details, file trees, tabs, and code actions. It ships in lockstep with the plugins, so updating it brings their latest defaults.
8
+ Every [Comark](https://comark.dev) block from [comark-ui](https://comark-ui.onmax.me) in one install, registered in the order they need, with one icon resolver shared by `:icon`, cards, details, file trees, tabs, and code actions.
9
9
 
10
10
  ```sh
11
11
  pnpm add comark @comark/html comark-ui
@@ -53,7 +53,7 @@ Vue, React, and Svelte leave out annotate and diff, which render only to HTML.
53
53
  | `ComarkKit` | The schema: StarterKit, task lists, inline images (drawn as players for video and audio), tables, footnotes, definition lists, CriticMarkup, and Comark's components, slots, and comments, each keeping the attributes it has no field for. |
54
54
  | `fromMarkdown`, `toMarkdown` | Convert between Markdown and the editor's JSON with Comark's own parser and writer. Front matter stays beside the document. |
55
55
  | `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
- | `Upload` | Uploads pasted and dropped files. Each shows a placeholder with its progress until `upload(file, progress)` returns its URL. The placeholder is a decoration, so a collaborative document never holds a file that hasn't arrived. |
56
+ | `Upload` | Uploads pasted and dropped files. Each shows a placeholder with its progress until `upload(file, progress)` returns its URL. |
57
57
  | `createEditorCodec({ plugins })` | `{ extensions, fromMarkdown, toMarkdown, verify }` for a collaborative room, such as ViteHub Realtime's `editor` option. |
58
58
 
59
59
  `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.
@@ -165,6 +165,20 @@ function start() {
165
165
  return state.stop;
166
166
  }
167
167
  //#endregion
168
+ //#region ../../shared/styles.ts
169
+ /**
170
+ * Adds a package's styles to the page once, unless the page already carries them as a `style` or a `link` with
171
+ * `data-comark-<name>`, which is how the HTML renderer inlines them and how an app that imports `style.css` marks them.
172
+ */
173
+ function addStyles(name, css) {
174
+ const attribute = `data-comark-${name}`;
175
+ if (document.querySelector(`style[${attribute}],link[${attribute}]`)) return;
176
+ const style = document.createElement("style");
177
+ style.setAttribute(attribute, "");
178
+ style.textContent = css;
179
+ document.head.append(style);
180
+ }
181
+ //#endregion
168
182
  //#region src/client.ts
169
183
  /**
170
184
  * comark-pele's diagrams on the shared `--comark-*` tokens, on a card like comark-diagrams'. Pele paints with its
@@ -173,8 +187,8 @@ function start() {
173
187
  */
174
188
  const mermaidCss = `@layer comark{${[
175
189
  theme(".cmm,.pele-diagram"),
176
- theme(":root"),
177
- `: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(--ui-error,var(--destructive,#dc2626));--pele-error:var(--ui-error,var(--destructive,#dc2626));--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)))}`,
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)))}`,
178
192
  ".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}",
179
193
  ".pele-diagram>svg{min-width:min(28rem,var(--cmm-svg,28rem))}",
180
194
  ".pele-source{margin:0;color:var(--_comark-muted);text-align:left}",
@@ -214,11 +228,7 @@ function addMermaidStyles() {
214
228
  block.style.removeProperty("--cmm-svg");
215
229
  }
216
230
  }
217
- if (document.querySelector("style[data-comark-mermaid]")) return;
218
- const style = document.createElement("style");
219
- style.dataset.comarkMermaid = "";
220
- style.textContent = mermaidCss;
221
- document.head.append(style);
231
+ addStyles("mermaid", mermaidCss);
222
232
  }
223
233
  const icon = (name, path) => `<svg class="cmm-${name}" viewBox="0 0 24 24" aria-hidden="true">${path}</svg>`;
224
234
  const done = icon("done", "<path d=\"M20 6 9 17l-5-5\"/>");
package/dist/client.mjs CHANGED
@@ -1,3 +1,3 @@
1
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-Bjd6VcGQ.mjs";
2
+ import { i as startMermaid, n as mermaidBar, r as mermaidCss, t as addMermaidStyles } from "./client-BhzMlpun.mjs";
3
3
  export { addMermaidStyles, mermaidBar, mermaidCss, startMermaid, typesetCss };
@@ -1,3 +1,4 @@
1
+ import { n as scalar } from "./text-D8j9clg3.mjs";
1
2
  import cjk from "comark-cjk";
2
3
  import critic from "comark-critic";
3
4
  import deflist from "comark-deflist";
@@ -539,8 +540,6 @@ const headings = /* @__PURE__ */ new Set([
539
540
  const isText = (node) => !Array.isArray(node);
540
541
  const isElement = (node) => Array.isArray(node) && node[0] !== null;
541
542
  const childrenOf = (element) => element.slice(2);
542
- /** A prop as text. Comark props are strings, numbers, and booleans, or parsed JSON for `:prop` bindings. */
543
- const textOf = (value) => value === void 0 || value === null || value instanceof Object ? void 0 : String(value);
544
543
  const inlineHtml = ({ $: meta }) => meta instanceof Object && "block" in meta && meta.block === 0;
545
544
  const isInline = (node) => isText(node) || isElement(node) && (inlineTags.has(node[0]) || node[0].startsWith(REFERENCE) || inlineHtml(node[1]));
546
545
  const isHtml = (props) => props.$ instanceof Object;
@@ -549,7 +548,7 @@ function rest(props, ...keys) {
549
548
  const left = Object.fromEntries(Object.entries(props).filter(([key]) => !keys.includes(key)));
550
549
  return Object.keys(left).length ? left : void 0;
551
550
  }
552
- const classes = (props) => textOf(props.class)?.split(/\s+/) ?? [];
551
+ const classes = (props) => scalar(props.class)?.split(/\s+/) ?? [];
553
552
  function withoutClass(props, name) {
554
553
  const left = classes(props).filter((value) => value && value !== name);
555
554
  const { class: _, ...others } = props;
@@ -636,7 +635,7 @@ function readComponent(children, reading) {
636
635
  for (const child of children) if (isElement(child) && child[0] === "template") {
637
636
  flush();
638
637
  const { name, ...props } = child[1];
639
- out.push(node("comarkSlot", withProps({ name: textOf(name) ?? "default" }, rest(props)), readBlocks(childrenOf(child), reading)));
638
+ out.push(node("comarkSlot", withProps({ name: scalar(name) ?? "default" }, rest(props)), readBlocks(childrenOf(child), reading)));
640
639
  } else run.push(child);
641
640
  flush();
642
641
  return out;
@@ -644,7 +643,7 @@ function readComponent(children, reading) {
644
643
  function readCode(props, children) {
645
644
  const code = children.find(isElement);
646
645
  const text = (code ? childrenOf(code) : children).filter(isText).join("");
647
- const language = textOf(props.language) ?? null;
646
+ const language = scalar(props.language) ?? null;
648
647
  return node("codeBlock", withProps(language ? { language } : {}, rest(props, "language")), text ? [{
649
648
  type: "text",
650
649
  text
@@ -795,7 +794,7 @@ function writeBlock(json) {
795
794
  ]];
796
795
  case "horizontalRule": return [["hr", props]];
797
796
  case "codeBlock": {
798
- const language = textOf(attrs.language) || void 0;
797
+ const language = scalar(attrs.language) || void 0;
799
798
  const code = plainText(json.content);
800
799
  const pre = [
801
800
  "pre",
@@ -859,7 +858,7 @@ function writeBlock(json) {
859
858
  return [isHtml(props) ? component : own(component, blockComponent)];
860
859
  }
861
860
  case "footnoteDefinition": {
862
- const label = textOf(attrs.label) ?? "";
861
+ const label = scalar(attrs.label) ?? "";
863
862
  return [own([
864
863
  "footnote",
865
864
  {},
@@ -882,7 +881,7 @@ function writeBlock(json) {
882
881
  case "comarkComment": return [[
883
882
  null,
884
883
  {},
885
- textOf(attrs.text) ?? ""
884
+ scalar(attrs.text) ?? ""
886
885
  ]];
887
886
  default: return [];
888
887
  }
@@ -936,8 +935,8 @@ function writeMark(mark, children) {
936
935
  props,
937
936
  ...children
938
937
  ];
939
- const href = textOf(mark.attrs?.href);
940
- const title = textOf(mark.attrs?.title);
938
+ const href = scalar(mark.attrs?.href);
939
+ const title = scalar(mark.attrs?.title);
941
940
  const link = {
942
941
  ...props,
943
942
  href
@@ -1017,7 +1016,7 @@ function writeLeaf(json) {
1017
1016
  ];
1018
1017
  return [isHtml(props) ? component : own(component, inlineComponent)];
1019
1018
  }
1020
- if (json.type === "footnoteReference") return [own(["footnote", {}], footnoteReference(textOf(attrs.label) ?? ""))];
1019
+ if (json.type === "footnoteReference") return [own(["footnote", {}], footnoteReference(scalar(attrs.label) ?? ""))];
1021
1020
  if (json.type === "inlineFootnote") return [own([
1022
1021
  "footnote",
1023
1022
  {},
@@ -2,7 +2,8 @@ import { x as ComarkKitOptions } from "./editor-KD97dL_1.mjs";
2
2
  import { ComarkPlugin } from "comark";
3
3
  import { AnyExtension } from "@tiptap/core";
4
4
  import { Editor } from "@tiptap/vue-3";
5
- import { PropType, Ref } from "vue";
5
+ import { PropertiesOptions } from "comark-properties";
6
+ import { PropType, Ref, VNode } from "vue";
6
7
  //#region src/editor-vue.d.ts
7
8
  export interface UseComarkEditorOptions {
8
9
  /** Added after ComarkKit, such as a placeholder. */
@@ -18,9 +19,17 @@ export interface UseComarkEditorOptions {
18
19
  export declare function useComarkEditor(markdown: Ref<string>, options?: UseComarkEditorOptions): {
19
20
  editor: import("vue").ShallowRef<Editor | undefined, Editor | undefined>;
20
21
  editable: Ref<boolean, boolean>;
22
+ frontmatter: import("vue").ShallowRef<Record<string, unknown>, Record<string, unknown>>;
21
23
  };
22
- /** `useComarkEditor` as a component: `<ComarkEditor v-model="markdown" />`. */
24
+ /**
25
+ * `useComarkEditor` as a component: `<ComarkEditor v-model="markdown" />`. The frontmatter keys that comark-properties
26
+ * shows on the page show above the text the same way, read-only. Pass its options in `properties`, or `false` to hide them.
27
+ */
23
28
  export declare const ComarkEditor: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
29
+ properties: {
30
+ type: PropType<PropertiesOptions | boolean>;
31
+ default: boolean;
32
+ };
24
33
  modelValue: {
25
34
  type: StringConstructor;
26
35
  required: true;
@@ -33,9 +42,15 @@ export declare const ComarkEditor: import("vue").DefineComponent<import("vue").E
33
42
  type: PropType<ComarkPlugin<any, any>[]>;
34
43
  default: undefined;
35
44
  };
36
- }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
45
+ }>, () => VNode<import("vue").RendererNode, import("vue").RendererElement, {
37
46
  [key: string]: any;
38
- }> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
47
+ }> | VNode<import("vue").RendererNode, import("vue").RendererElement, {
48
+ [key: string]: any;
49
+ }>[] | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "update:modelValue"[], "update:modelValue", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
50
+ properties: {
51
+ type: PropType<PropertiesOptions | boolean>;
52
+ default: boolean;
53
+ };
39
54
  modelValue: {
40
55
  type: StringConstructor;
41
56
  required: true;
@@ -51,6 +66,7 @@ export declare const ComarkEditor: import("vue").DefineComponent<import("vue").E
51
66
  }>> & Readonly<{
52
67
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
53
68
  }>, {
69
+ properties: boolean | PropertiesOptions;
54
70
  extensions: AnyExtension[];
55
71
  plugins: ComarkPlugin<any, any>[];
56
72
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -1,6 +1,12 @@
1
- import { c as fromMarkdown, l as toMarkdown, m as ComarkKit, s as checkMarkdown } from "./editor-DmxNPK7K.mjs";
1
+ import { c as fromMarkdown, l as toMarkdown, m as ComarkKit, s as checkMarkdown } from "./editor-ASZBrUYa.mjs";
2
2
  import { Editor, EditorContent } from "@tiptap/vue-3";
3
+ import { rowsOf } from "comark-properties";
4
+ import { Properties } from "comark-properties/vue";
3
5
  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
4
10
  //#region src/editor-vue.ts
5
11
  /**
6
12
  * A Tiptap editor bound to a Markdown string: an edit writes the Markdown back, and a change from outside reloads the
@@ -9,7 +15,8 @@ import { defineComponent, h, onScopeDispose, ref, shallowRef, watch } from "vue"
9
15
  function useComarkEditor(markdown, options = {}) {
10
16
  const editor = shallowRef();
11
17
  const editable = ref(false);
12
- let frontmatter = {};
18
+ /** The document's frontmatter, which the editor keeps beside the text and writes back on each save. */
19
+ const frontmatter = shallowRef({});
13
20
  let written;
14
21
  let loads = 0;
15
22
  let saves = 0;
@@ -17,7 +24,7 @@ function useComarkEditor(markdown, options = {}) {
17
24
  const current = editor.value;
18
25
  if (!current) return;
19
26
  const run = ++saves;
20
- const next = await toMarkdown(current.getJSON(), { frontmatter });
27
+ const next = await toMarkdown(current.getJSON(), { frontmatter: frontmatter.value });
21
28
  if (run !== saves) return;
22
29
  written = next;
23
30
  markdown.value = next;
@@ -27,7 +34,7 @@ function useComarkEditor(markdown, options = {}) {
27
34
  ++saves;
28
35
  const [check, read] = await Promise.all([checkMarkdown(source, { plugins: options.plugins }), fromMarkdown(source)]);
29
36
  if (run !== loads) return;
30
- frontmatter = read.frontmatter;
37
+ frontmatter.value = read.frontmatter;
31
38
  editable.value = check.editable;
32
39
  if (editor.value) {
33
40
  editor.value.commands.setContent(read.doc, { emitUpdate: false });
@@ -47,13 +54,27 @@ function useComarkEditor(markdown, options = {}) {
47
54
  onScopeDispose(() => editor.value?.destroy());
48
55
  return {
49
56
  editor,
50
- editable
57
+ editable,
58
+ frontmatter
51
59
  };
52
60
  }
53
- /** `useComarkEditor` as a component: `<ComarkEditor v-model="markdown" />`. */
61
+ /** A comark node as a Vue node, for the few elements the properties rows hold. */
62
+ function vnode(node) {
63
+ if (!isElement(node)) return Array.isArray(node) ? null : node;
64
+ return h(node[0], node[1], childrenOf(node).map(vnode));
65
+ }
66
+ /**
67
+ * `useComarkEditor` as a component: `<ComarkEditor v-model="markdown" />`. The frontmatter keys that comark-properties
68
+ * shows on the page show above the text the same way, read-only. Pass its options in `properties`, or `false` to hide them.
69
+ */
54
70
  const ComarkEditor = defineComponent({
55
71
  name: "ComarkEditor",
72
+ inheritAttrs: false,
56
73
  props: {
74
+ properties: {
75
+ type: [Object, Boolean],
76
+ default: true
77
+ },
57
78
  modelValue: {
58
79
  type: String,
59
80
  required: true
@@ -68,17 +89,27 @@ const ComarkEditor = defineComponent({
68
89
  }
69
90
  },
70
91
  emits: ["update:modelValue"],
71
- setup(props, { emit }) {
92
+ setup(props, { attrs, emit }) {
72
93
  const markdown = ref(props.modelValue);
73
94
  watch(() => props.modelValue, (value) => markdown.value = value);
74
95
  watch(markdown, (value) => {
75
96
  if (value !== props.modelValue) emit("update:modelValue", value);
76
97
  });
77
- const { editor } = useComarkEditor(markdown, {
98
+ const { editor, frontmatter } = useComarkEditor(markdown, {
78
99
  extensions: props.extensions,
79
100
  plugins: props.plugins
80
101
  });
81
- return () => editor.value ? h(EditorContent, { editor: editor.value }) : null;
102
+ return () => {
103
+ if (!editor.value) return null;
104
+ const content = h(EditorContent, {
105
+ ...attrs,
106
+ editor: editor.value
107
+ });
108
+ if (props.properties === false) return content;
109
+ const options = props.properties === true ? {} : props.properties;
110
+ const { rows } = rowsOf(frontmatter.value, options);
111
+ return rows.length ? [h(Properties, null, () => rows.map(vnode)), content] : content;
112
+ };
82
113
  }
83
114
  });
84
115
  //#endregion
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-DmxNPK7K.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-ASZBrUYa.mjs";
package/dist/html.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as criticCss, n as Preset, r as PresetOptions } from "./index-Dbqxt4Qg.mjs";
1
+ import { a as criticCss, n as Preset, r as PresetOptions } from "./index-DVGSDzBS.mjs";
2
2
  import { NodeHandler } from "comark";
3
3
  //#region src/html.d.ts
4
4
  /**
package/dist/html.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { t as typesetCss } from "./typeset-css-5aJ7cSmh.mjs";
2
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-Bjd6VcGQ.mjs";
3
+ import { a as start, n as mermaidBar, r as mermaidCss } from "./client-BhzMlpun.mjs";
4
+ import { t as rawText } from "./text-D8j9clg3.mjs";
4
5
  import { escapeHtml } from "comark/utils";
5
6
  import * as annotate from "comark-annotate/html";
6
7
  import * as badge from "comark-badge/html";
@@ -38,6 +39,10 @@ import * as table from "comark-table/html";
38
39
  import * as tabs from "comark-tabs/html";
39
40
  import * as tests from "comark-tests/html";
40
41
  import * as zoom from "comark-zoom/html";
42
+ //#region ../../shared/html.ts
43
+ /** A value as the inside of a double-quoted HTML attribute. */
44
+ const attr = (value) => escapeHtml(rawText(value), { "\"": "&quot;" });
45
+ //#endregion
41
46
  //#region src/html.ts
42
47
  const styled = /* @__PURE__ */ new WeakSet();
43
48
  /**
@@ -46,7 +51,6 @@ const styled = /* @__PURE__ */ new WeakSet();
46
51
  * hash without rendering a document first.
47
52
  */
48
53
  const mermaidScript = `(${start.toString()})()`;
49
- const attr = (value) => escapeHtml(value, { "\"": "&quot;" });
50
54
  /**
51
55
  * Wraps Pele's card in `.cmm` with the bar of buttons and the source they copy. The wrapper is a wide block with the
52
56
  * diagram's own width, so a page that gives wide blocks room shows a long flowchart at its size rather than shrinking
@@ -7,6 +7,7 @@ import { KeysOptions } from "comark-keys";
7
7
  import { ReadingTimeOptions } from "comark-reading-time";
8
8
  import { XrefOptions } from "comark-xref";
9
9
  import { ComarkPlugin } from "comark";
10
+ import { PropertiesOptions } from "comark-properties";
10
11
  import { BadgeOptions } from "comark-badge";
11
12
  import { AnnotateOptions } from "comark-annotate";
12
13
  import { CastOptions } from "comark-cast";
@@ -23,7 +24,6 @@ import { MathOptions } from "comark-mathml";
23
24
  import { MediaOptions } from "comark-media";
24
25
  import { OutlineOptions } from "comark-outline";
25
26
  import { PeleConfig } from "comark-pele";
26
- import { PropertiesOptions } from "comark-properties";
27
27
  import { QuizOptions } from "comark-quiz";
28
28
  import { TableOptions } from "comark-table";
29
29
  import { TabsOptions } from "comark-tabs";
package/dist/index.d.mts CHANGED
@@ -1,3 +1,3 @@
1
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-Dbqxt4Qg.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
3
  export { AnyPlugin, Preset, PresetOptions, Renderer, TYPESET_TAG, criticCss, preset, typeset, typesetCss };
package/dist/react.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as criticCss, n as Preset, r as PresetOptions } from "./index-Dbqxt4Qg.mjs";
1
+ import { a as criticCss, n as Preset, r as PresetOptions } from "./index-DVGSDzBS.mjs";
2
2
  import { ComponentType } from "react";
3
3
  //#region src/react.d.ts
4
4
  type AnyComponent = ComponentType<any>;
package/dist/react.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as typesetCss } from "./typeset-css-5aJ7cSmh.mjs";
2
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-Bjd6VcGQ.mjs";
3
+ import { i as startMermaid, n as mermaidBar, t as addMermaidStyles } from "./client-BhzMlpun.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";
@@ -6,25 +6,25 @@ license: MIT
6
6
 
7
7
  # Before/after evidence
8
8
 
9
- A before/after is evidence when a reviewer can check every claim against the two images and nothing else. Three things make it so, and the reader can't repair any of them: two captures of the same view, marks that sit on what changed, and notes in the words the screen shows.
9
+ A before/after is evidence when a reviewer can check every claim against the two images and nothing else. The reader can't repair a bad pair, so get three things right: two captures of the same view, marks on what changed, and notes in the words the screen shows.
10
10
 
11
- This skill writes the document. Where it's published, and who can open it, is up to your host.
11
+ This skill writes the document. Your host decides where it's published and who can open it.
12
12
 
13
13
  ## 1. Capture a comparable pair
14
14
 
15
- Take **after** from the branch or its preview, and **before** from a deployment or commit that still behaves the old way. Capture both with the same viewport, zoom, theme, data, scroll position, and open menus, so the change is the only difference.
15
+ Take the after capture from the branch or its preview, and the before capture from a deployment or commit that still behaves the old way. Capture both with the same viewport, zoom, theme, data, scroll position, and open menus, so the change is the only difference.
16
16
 
17
17
  - Set up the state that shows the change before you capture: the long name that wrapped, the error that stayed hidden, the empty list.
18
18
  - Clip both captures to one region around the change, the same rectangle on each side. A `::compare` gives each image under half the page width, where a full-page capture's text is too small to read.
19
- - Pin anything that moves the layout between the two states, such as a dialog centered vertically that grows by one line, so the same element sits at the same place in both.
19
+ - Pin anything that moves the layout between the two states, such as a vertically centered dialog that grows by one line, so the same element sits at the same place in both.
20
20
 
21
- If one side can't be captured comparably, stop and say what blocked you. A description in place of a screenshot, or one side alone, isn't before/after evidence.
21
+ If you can't capture one side comparably, stop and say what blocked you. A description in place of a screenshot, or one side alone, isn't before/after evidence.
22
22
 
23
23
  Done when both images exist, have the same size, and differ only where the change is.
24
24
 
25
25
  ## 2. Measure every mark
26
26
 
27
- Marks take `x`, `y`, `w`, and `h` as percentages of the image. Measure them: an estimate drifts a few percent, and on a half-width image that's the neighbouring button.
27
+ Marks take `x`, `y`, `w`, and `h` as percentages of the image. Measure them. An estimate drifts a few percent, which on a half-width image lands on the neighbouring button.
28
28
 
29
29
  - **A page you captured:** read the element's box in the same browser, viewport, and state, minus the clip's offset. [references/measuring.md](references/measuring.md) has the snippet.
30
30
  - **A still you didn't capture**, such as a frame from a screen recording, a design export, or an image attached to a bug report: there is no DOM to ask. Measure in image pixels and divide by the image's natural width and height. Confirm each box by cropping the image to it and looking at the crop, as the same reference shows.
@@ -42,7 +42,7 @@ Start from [references/example.md](references/example.md) and keep its shape:
42
42
  3. The `::compare`, each note a few words in the screen's own words. A value the screenshot doesn't show stays out of its note.
43
43
  4. A `::diff` of the line that caused it, only when the reader would otherwise ask.
44
44
 
45
- The images carry the evidence, so the text only points into them. How the work went (the checks you ran, retries, CI) goes in your report to the user, not in the document. [references/blocks.md](references/blocks.md) has the syntax of each block.
45
+ The images carry the evidence, so the text only points into them. Put how the work went (checks, retries, CI) in your report to the user, not in the document. [references/blocks.md](references/blocks.md) has the syntax of each block.
46
46
 
47
47
  Image `src` must be a URL the renderer can reach: a path next to the document, or an https URL your host gives you for the upload.
48
48
 
@@ -56,17 +56,15 @@ Image `src` must be a URL the renderer can reach: a path next to the document, o
56
56
 
57
57
  These commands use the [global Vite+ CLI](https://viteplus.dev/guide/global-cli). Without it, `pnpm dlx` takes the same arguments.
58
58
 
59
- 2. Open the page where it will be read and take a screenshot of it. For a local screenshot-only check, copy the complete `::compare` block, including its nested screenshots and annotations, into `evidence-screenshots.md` beside `evidence.md`.
59
+ 2. Open the page where it will be read and take a screenshot of it. To check the screenshots alone, copy the whole `::compare` block, nested screenshots and annotations included, into `evidence-screenshots.md` beside `evidence.md`.
60
60
 
61
- Ensure comark-annotate is a direct dependency of this workspace package. If it is only transitive, read the installed Comark UI version and add that exact version with the project's package manager: for pnpm, `pnpm add -D comark-annotate@<version>`, substituting that version. Add `-w` when installing at the workspace root. With no Comark UI packages installed, omit `@<version>`. This links the executable without fetching a different renderer release via `@latest`. Then run:
61
+ Make comark-annotate a direct dependency of this workspace package. A transitive one isn't linked, and `@latest` can run a different release from the project's renderers. Match the version of the installed Comark UI packages: `pnpm add -D comark-annotate@<version>`, with `-w` at the workspace root. With no Comark UI packages installed, omit `@<version>`. Then run:
62
62
 
63
63
  ```sh
64
64
  vp exec comark-annotate render evidence-screenshots.md -o evidence-screenshots.html
65
65
  ```
66
66
 
67
- Without Vite+, use `pnpm exec` with the same arguments.
68
-
69
- Open `evidence-screenshots.html` and take a screenshot of it. This CLI renders annotate, carousel, and zoom only; check any `::diff` or other blocks in the host's full Comark UI preview.
70
- 3. Check that each mark sits on its target and each note matches what its side shows. Measure a mark that misses again, from the source, since a box moved by eye stays a guess.
67
+ Without Vite+, use `pnpm exec` with the same arguments. Open `evidence-screenshots.html` and take a screenshot of it. This CLI renders annotate, carousel, and zoom only; check any `::diff` or other blocks in the host's full Comark UI preview.
68
+ 3. Check that each mark sits on its target and each note matches what its side shows. Measure a mark that misses again from the source; a box moved by eye stays a guess.
71
69
 
72
70
  Done when you've looked at the rendered page and every mark lands. Name any mark or block you couldn't check in your report.
@@ -30,7 +30,7 @@ A direct child of a screenshot. Its content is the note, in Markdown.
30
30
  | Prop | What it does |
31
31
  | --- | --- |
32
32
  | `x` `y` `w` `h` | Required. The marked rectangle as percentages of the image, without `%`. |
33
- | `mark` | `box` (default), `circle`, `underline`, `highlight`, `bracket`, `cross`, `check`, or `none`. |
33
+ | `mark` | `box` (default), `circle`, `underline`, `line`, `highlight`, `bracket`, `cross`, `check`, or `none`. |
34
34
  | `color` | `red` for a problem, `green` for the fix, `amber` for a warning, `violet` (default) to point something out, `gray`, or a hex color. |
35
35
  | `side` | `top` or `bottom` moves the note into the margin with an arrow; in a side-by-side compare, `left` and `right` notes join the numbered list. |
36
36
 
@@ -5,11 +5,11 @@ description: Writes PR evidence, explanations of how something works, and docs p
5
5
 
6
6
  # comark-ui
7
7
 
8
- You write Markdown; the comark plugins render it as static HTML, from marks on screenshots and charts drawn from tables to code changes, diagrams, and steps. The reader sees only what you measured and wrote down, so measure every mark from the page, take every number from the data, and check the render before you hand it over.
8
+ You 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.
9
9
 
10
10
  ## Install
11
11
 
12
- Prefer `comark-ui`, every plugin in one install, registered in the order they need:
12
+ Install `comark-ui` by default. It holds every plugin and registers them in the order they need:
13
13
 
14
14
  ```sh
15
15
  pnpm add comark @comark/html comark-ui
@@ -26,7 +26,7 @@ const render = createHtmlRenderer(comarkUi());
26
26
 
27
27
  Every plugin is on except `include`. The [comark-ui reference](references/ui.md) covers its options, the plugins that read files or use the network, and its Vue, React, and Svelte entries.
28
28
 
29
- When a project wants only some plugins, install those packages and register each plugin with its components:
29
+ To use only some plugins, install those packages and register each plugin with its components:
30
30
 
31
31
  ```sh
32
32
  pnpm add comark @comark/html comark-annotate
@@ -41,13 +41,15 @@ const render = createHtmlRenderer({ plugins: [annotate()], components });
41
41
 
42
42
  Each reference ends with a Register section that says how its package registers alone and which options it needs.
43
43
 
44
- Either way, the HTML renderer inlines the styles once per document, and the Vue, React, and Svelte components add them on mount. On a server-rendered page, import each package's `style.css`, such as `comark-cards/style.css`, so the page doesn't shift. The `--comark-*` tokens in https://comark-ui.onmax.me/guide/theming/ theme every plugin at once. In a Nuxt UI app the plugins follow its `--ui-*` variables with no setup; for a page rendered outside the app, such as review evidence, put the CSS from `nuxtUiTheme({ colors: appConfig.ui?.colors, css: mainCss })` in `comark-ui/nuxt-ui` in a `<style>`, so the page matches the app.
44
+ Either way, the HTML renderer inlines the styles once per document, and the Vue, React, and Svelte components add them on mount. On a server-rendered page, import each package's `style.css`, such as `comark-cards/style.css`, so the page doesn't shift.
45
+
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.
45
47
 
46
48
  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.
47
49
 
48
50
  ## Shape the document
49
51
 
50
- This 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 the skill isn't installed, suggest `vp dlx skills add onmax/comark-ui` and picking it from the list.
52
+ This 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.
51
53
 
52
54
  ## Pick the blocks
53
55
 
@@ -133,13 +135,13 @@ Read the reference for each block you use before you write it. Mix the blocks fr
133
135
 
134
136
  Screenshots and charts draw the same marks. Each `::annotation` is one mark and its note, written in Markdown.
135
137
 
136
- - `mark` is `box` (default), `circle`, `underline`, `highlight`, `bracket`, `cross`, `check`, or `none`, which draws nothing, so its note needs a `side` and the arrow alone points.
138
+ - `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.
137
139
  - `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.
138
140
  - `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.
139
141
 
140
- Mark only what your text talks about. Each note says what changed or what is wrong in the words the screen or the table shows, so the reader can check it against the figure; a value the figure doesn't show stays out of the note.
142
+ Mark 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.
141
143
 
142
144
  ## Check the result
143
145
 
144
- 1. Lint the file: `vp exec comark-lint report.md`, in a project with `comark-linter` and the plugins installed, where `comark-ui` counts as every plugin. It prints one line per problem with its line, its rule, and how to fix it, and exits with 1 while any remain. Fix every one, and run it again until it exits with 0. The [lint reference](references/lint.md) covers its flags, its config file, the library, ESLint, and Vite Doctor.
145
- 2. Look at the render, made as the block's reference says. Open it in a browser and take a screenshot: each mark sits on its target, each number matches its note, and each chart says what your text says. If you can't verify something, say so when you hand the document over instead of guessing.
146
+ 1. 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.
147
+ 2. 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.
@@ -53,7 +53,7 @@ For a clipped capture, `getBoundingClientRect()` and Playwright's main-frame `cl
53
53
 
54
54
  ## Preview
55
55
 
56
- Ensure comark-annotate is a direct dependency of the workspace package where you run the CLI. If it is only transitive, read the installed Comark UI version and add that exact version with the project's package manager: for pnpm, `pnpm add -D comark-annotate@<version>`, substituting that version. Add `-w` when installing at the workspace root. With no Comark UI packages installed, omit `@<version>`. This links the executable without fetching a different renderer release via `@latest`.
56
+ Make comark-annotate a direct dependency of the workspace package where you run the CLI, so its executable links. If it is only transitive, read the installed Comark UI version and add that exact version, such as `pnpm add -D comark-annotate@<version>` (add `-w` at the workspace root), because `@latest` could fetch a different renderer release. With no Comark UI packages installed, omit `@<version>`.
57
57
 
58
58
  ```sh
59
59
  vp exec comark-annotate render report.md -o report.html
@@ -42,7 +42,7 @@ Use image mode for README-style label/value badges. Static images embed their SV
42
42
  - `style`: `shadcn` by default, `subtle`, or `solid`; `theme`: `auto` by default, following the page's light or dark theme (on GitHub too), or `light` or `dark`. `color` and `labelColor` accept semantic names or six-digit hex colors. In the default preset, shadcn draws an outline with neutral text; `labelColor` applies to subtle and solid backgrounds. CI chooses its own status color. Images embed colors and do not inherit the document's CSS.
43
43
  - `period`: `day`, `week`, or `month` for downloads. Hosted values cache for five minutes for versions, one hour for downloads, and one minute for CI. Failures display `unavailable` for 30 seconds. Downloaded SVGs are snapshots.
44
44
 
45
- The builder at `/badges/` lives in the docs layout, shares its configuration and export format in the query string, and exports Comark, Markdown, an image URL, and SVG. The image endpoint is `/badge.svg`, with the same props as query parameters. Preserve the existing prose syntax for inline tags that should follow the page's theme.
45
+ The builder at `/badges/` exports Comark, Markdown, an image URL, and SVG. The image endpoint is `/badge.svg`, with the same props as query parameters. For an inline tag that should follow the page's theme, keep the prose syntax above; an image embeds its own colors.
46
46
 
47
47
  ## Register
48
48
 
@@ -23,7 +23,8 @@ Read the code, or open an issue.
23
23
  | `icon` | An icon above the title, in the card's color, named as in [icon](icon.md), such as `lucide:rocket`. The plugin reports one it can't draw. |
24
24
  | `to` | Makes it a link card. One link covers the card and takes the title as its name, and links in the card's text still open on their own. A web address opens in a new tab with an arrow in the corner. |
25
25
  | `target` | Where the link opens, such as `_self` to keep a web address in the tab. |
26
- | `color` | Tints the icon and a hovered link card's border: `primary` (default), `secondary`, `success`, `info`, `warning`, `error`, or `neutral`. |
26
+ | `color` | Tints the icon and a hovered link card's border: `primary` (default), `secondary`, `success`, `info`, `warning`, `error`, or `neutral`. Any other value shows as `primary` and is reported. |
27
+ | `variant` | `outline` (default) keeps the plain card, `soft` washes it in its `color`, and `subtle` washes it and draws its border in the `color` too. |
27
28
 
28
29
  `::card-group` sets its cards in two columns, or one when the group is narrower than 36rem, so a group in a sidebar stacks too. A card works on its own as well.
29
30
 
@@ -30,5 +30,5 @@ Mark the lines a reviewer would ask about: the request that runs in parallel, th
30
30
 
31
31
  ## Register
32
32
 
33
- - In `comark-ui`: on as `codeNotes`, the last plugin. The preset leaves Shiki out; add it right before comark-code, as the [code reference](code.md) shows, which keeps it ahead of code notes.
33
+ - In `comark-ui`: on as `codeNotes`, listed after `code`. The preset leaves Shiki out; add it right before comark-code, as the [code reference](code.md) shows, which keeps it ahead of code notes.
34
34
  - Alone: `pnpm add comark @comark/html comark-code-notes`, then `createHtmlRenderer({ plugins: [shiki(), codeNotes()], components })` with `shiki` from `@comark/html/plugins/shiki`, and `codeNotes` and `components` from `comark-code-notes/html`. It goes after `shiki`; in the other order the code isn't highlighted and `meta.issues` says so. Without Shiki it draws the lines itself. It takes no options, and `comark-code-notes/vue`, `/react`, and `/svelte` render it too.
@@ -28,9 +28,9 @@ function close() {
28
28
 
29
29
  A range is one line, `{7}`, a run, `{3-4}`, or a list of both, `{1, 3-4}`, always in braces. Ranges count the block's own lines from 1, folded lines included, whatever `startLineNumber` says: `startLineNumber=120 ins={3}` marks the line numbered 122.
30
30
 
31
- `sh`, `bash`, `zsh`, `shell`, `console`, and `ansi` blocks draw as terminals unless their title is a file name, such as `install.sh`. A terminal's header shows its title but not its language. In a code group the tab names the block, so a terminal there draws no window.
31
+ `sh`, `bash`, `zsh`, `shell`, `shellscript`, `shellsession`, `console`, and `ansi` blocks draw as terminals unless their title is a file name, such as `install.sh`. A terminal's header shows its title but not its language. In a code group the tab names the block, so a terminal there draws no window.
32
32
 
33
- When the host runs Shiki's notation transformers, comments such as `// [!code ++]`, `// [!code --]`, `// [!code highlight]`, and `// [!code focus]` mark lines too, and comark-code styles them.
33
+ When the host runs Shiki's notation transformers, comments such as `// [!code ++]`, `// [!code --]`, `// [!code highlight]`, `// [!code focus]`, `// [!code error]`, and `// [!code warning]` mark lines too, and comark-code styles them.
34
34
 
35
35
  ## Show a change
36
36
 
@@ -50,7 +50,7 @@ When the host runs Shiki's notation transformers, comments such as `// [!code ++
50
50
  ```
51
51
  ````
52
52
 
53
- A range that lands on the wrong line inside the block isn't reported, so count the lines before you mark them.
53
+ A range past the block's last line is reported. One that lands on the wrong line inside the block isn't, so count the lines before you mark them.
54
54
 
55
55
  ## Add buttons
56
56