comark-ui 0.7.1 → 0.7.4
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 +2 -2
- package/dist/client-Bjd6VcGQ.mjs +241 -0
- package/dist/client.d.mts +11 -1
- package/dist/client.mjs +3 -26
- package/dist/{editor-BAdok66p.mjs → editor-DmxNPK7K.mjs} +522 -68
- package/dist/{editor-CNivEuMH.d.mts → editor-KD97dL_1.d.mts} +21 -3
- package/dist/editor-vue.d.mts +1 -1
- package/dist/editor-vue.mjs +1 -1
- package/dist/editor.d.mts +2 -2
- package/dist/editor.mjs +1 -1
- package/dist/html.d.mts +7 -1
- package/dist/html.mjs +29 -9
- package/dist/{index-B0YvApci.d.mts → index-BUDHsvnm.d.mts} +11 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/react.d.mts +2 -2
- package/dist/react.mjs +20 -6
- package/dist/skills/before-after-evidence/SKILL.md +72 -0
- package/dist/skills/before-after-evidence/agents/openai.yaml +3 -0
- package/dist/skills/before-after-evidence/references/blocks.md +54 -0
- package/dist/skills/before-after-evidence/references/example.md +37 -0
- package/dist/skills/before-after-evidence/references/measuring.md +50 -0
- package/dist/skills/comark-ui/SKILL.md +9 -2
- package/dist/skills/comark-ui/references/annotate.md +4 -2
- package/dist/skills/comark-ui/references/comments.md +43 -0
- package/dist/skills/comark-ui/references/diagrams.md +7 -1
- package/dist/skills/comark-ui/references/lint.md +1 -1
- package/dist/skills/comark-ui/references/outline.md +9 -1
- package/dist/skills/comark-ui/references/quiz.md +1 -1
- package/dist/skills/comark-ui/references/ui.md +1 -1
- package/dist/skills/review-document/SKILL.md +64 -0
- package/dist/skills/review-document/agents/openai.yaml +3 -0
- package/dist/skills/review-document/references/blocks.md +69 -0
- package/dist/skills/review-document/references/example.md +51 -0
- package/dist/skills/teach-lesson/SKILL.md +78 -0
- package/dist/skills/teach-lesson/agents/openai.yaml +3 -0
- package/dist/skills/teach-lesson/references/blocks.md +91 -0
- package/dist/skills/teach-lesson/references/example.md +70 -0
- package/dist/skills/visual-explanation/SKILL.md +67 -0
- package/dist/skills/visual-explanation/agents/openai.yaml +3 -0
- package/dist/skills/visual-explanation/references/blocks.md +89 -0
- package/dist/skills/visual-explanation/references/example.md +25 -0
- package/dist/skills.d.mts +18 -0
- package/dist/skills.mjs +326 -0
- package/dist/{src-DROB05U6.mjs → src-PkmYCSNu.mjs} +1 -0
- package/dist/svelte/Mermaid.svelte +16 -0
- package/dist/svelte/index.d.ts +1 -1
- package/dist/svelte/index.js +4 -8
- package/dist/{typeset-css-DPB0S1bV.mjs → typeset-css-5aJ7cSmh.mjs} +14 -0
- package/dist/vue.d.mts +2 -2
- package/dist/vue.mjs +33 -6
- package/package.json +46 -44
|
@@ -5,7 +5,7 @@ comark-linter reports what Comark forgives, such as a `::` block never closed, a
|
|
|
5
5
|
## Run the command
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
|
|
8
|
+
vp exec lint-comark "docs/**/*.md" --format json --check-urls
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
- Pass files or globs. Quote a glob so the shell passes it as written; lint-comark expands it and skips `node_modules` and `dist`.
|
|
@@ -30,7 +30,15 @@ The redesign moves the filters into one row and adds an empty state.
|
|
|
30
30
|
- Every `::toc` lists the whole document, wherever it sits. With no heading from h2 to `depth`, it renders nothing, and the plugin reports it.
|
|
31
31
|
- Write `::toc` where other tools use `[[toc]]`, which Comark reads as a span.
|
|
32
32
|
|
|
33
|
+
## Side outline
|
|
34
|
+
|
|
35
|
+
The `rail` option adds an "On this page" outline beside the text of every document with two headings or more to list, and marks the section being read. It isn't Markdown: a document can't turn it on, and `::toc` still works beside it. Write short h2 and h3 headings, since they become its links.
|
|
36
|
+
|
|
37
|
+
- Options: `rail: true`, or `{ title, depth }` (default "On this page" and 3).
|
|
38
|
+
- The page sets `--comark-inset-end` on `:root` for a panel that covers its right edge. While the outline keeps a column beside wide blocks, it sets `data-comark-rail` and `--comark-rail-width` on `<html>`, and the page shrinks `--comark-wide-end` by it, so wide blocks move toward the room before the text.
|
|
39
|
+
- It hides where it doesn't fit, and rather than narrow a diagram.
|
|
40
|
+
|
|
33
41
|
## Register
|
|
34
42
|
|
|
35
|
-
- In `comark-ui`: on in every entry
|
|
43
|
+
- In `comark-ui`: on in every entry; `outline: { rail: true }` adds the side outline.
|
|
36
44
|
- Alone: `pnpm add comark @comark/html comark-outline`, then `createHtmlRenderer({ plugins: [outline()], components })` with both from `comark-outline/html`. `comark-outline/vue`, `/react`, and `/svelte` render it too. It reads the headings itself, so it works in any plugin order and without Comark's toc plugin, which only fills `meta.toc` for a sidebar. The npm name is `comark-outline` because npm refused `comark-toc`.
|
|
@@ -37,7 +37,7 @@ Promise callbacks are microtasks, and a timer's callback is a task.
|
|
|
37
37
|
|
|
38
38
|
A question uses radios when one option is marked `[x]`; `multiple` makes it a checkbox question. Options are shuffled deterministically unless `shuffle="false"`. Questions and recall cards share numbering, while polls are not numbered. A missing id is derived from the block content. Use `#hint` and `#explain` on questions, and `#answer` on recall cards. Polls accept `multiple` and a date in `closes`.
|
|
39
39
|
|
|
40
|
-
The HTML renderer inlines the quiz script and styles. Hosts can listen for `comark-quiz:answer` and `comark-quiz:ready`, and restore answers or results with `comark-quiz:state`; see
|
|
40
|
+
The HTML renderer inlines the quiz script and styles. Hosts can listen for `comark-quiz:answer` and `comark-quiz:ready`, and restore answers or results with `comark-quiz:state`; see [Saving answers](https://comark-ui.onmax.me/quiz/#saving-answers) on its page for the event shapes and storage behavior.
|
|
41
41
|
|
|
42
42
|
## Register
|
|
43
43
|
|
|
@@ -36,7 +36,7 @@ comarkUi({
|
|
|
36
36
|
| `@comark/react` | `comark-ui/react` |
|
|
37
37
|
| `@comark/svelte` | `comark-ui/svelte` |
|
|
38
38
|
|
|
39
|
-
The Vue, React, and Svelte entries leave out annotate
|
|
39
|
+
The Vue, React, and Svelte entries leave out annotate, comments, diff, and quiz, which render only to HTML. Screenshots, compares, diffs, suggestions and comments, and the marks on charts need `comark-ui/html`.
|
|
40
40
|
|
|
41
41
|
To use another renderer, install it in place of `@comark/html`, such as `pnpm add comark @comark/vue comark-ui`, and pass it `plugins` and `components` as you would one package's. The preset needs Vue 3.5, React 18, or Svelte 5.20 or later. Every entry exports the `PresetOptions` type, and `comark-ui` itself exports `preset(renderer, options)`, which builds the same pair from renderer entries you pass.
|
|
42
42
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-document
|
|
3
|
+
description: Turns a review's findings into one short Markdown document a reader can act on, with the verdict first and each finding ranked, located, backed by evidence, and paired with its fix. Use when writing up a code review, a design or QA review, an audit, or any report whose reader must decide what to do next.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Review document
|
|
8
|
+
|
|
9
|
+
A review's reader has one question: what do I do now? Answer it in the first two lines, then give each finding just enough evidence that the reader trusts it without redoing your work. This skill organizes findings you already have; finding them is the review itself.
|
|
10
|
+
|
|
11
|
+
Length is the usual failure. A reader who has to dig through praise, process, and restated findings misses the one that blocks the merge. Every line below the verdict either supports a finding or says what you didn't check.
|
|
12
|
+
|
|
13
|
+
## 1. Decide the verdict
|
|
14
|
+
|
|
15
|
+
Pick one: **approve**, **approve with changes**, **request changes**, or **blocked** on a named thing. Under it, write the reason in one sentence that names the finding behind it.
|
|
16
|
+
|
|
17
|
+
Done when someone who reads only the verdict knows what to do.
|
|
18
|
+
|
|
19
|
+
## 2. Keep the findings that change the decision
|
|
20
|
+
|
|
21
|
+
- Keep a finding when fixing it, or not, changes what the reader should do.
|
|
22
|
+
- Merge findings with one root cause into one, and list every place it appears.
|
|
23
|
+
- Rank each one: **blocking** (wrong results, lost data, a security hole, broken for users), **should fix** (a real cost that can wait), or **nit**, only when the reader asked for them.
|
|
24
|
+
|
|
25
|
+
Order them blocking first. Done when every finding has a rank and no two share a cause.
|
|
26
|
+
|
|
27
|
+
## 3. Back each finding
|
|
28
|
+
|
|
29
|
+
Each finding is four parts, in this order:
|
|
30
|
+
|
|
31
|
+
1. **A title that states the consequence**, for whom: "Small avatars load the 512px image", not "Cache key issue".
|
|
32
|
+
2. **Where**: the file and line, linked when there's a URL.
|
|
33
|
+
3. **Evidence**, the strongest you have: a failing test or command you ran (`::tests`, a fenced block of output), a screenshot of the wrong state (measured as the before-after-evidence skill describes), the lines that cause it (a fence with the line highlighted), then reasoning. Say which it is: a finding you reproduced reads differently from one you read in the code.
|
|
34
|
+
4. **The fix**: a `::diff` of the smallest change, or one sentence when the fix is a decision for the reader.
|
|
35
|
+
|
|
36
|
+
Done when every blocking finding has evidence you ran or captured, or says plainly that it comes from reading the code.
|
|
37
|
+
|
|
38
|
+
## 4. Say what you checked
|
|
39
|
+
|
|
40
|
+
End with two short lists: what you checked and how, and what you didn't check. A review that implies more coverage than it had loses the reader's trust in the parts that were real.
|
|
41
|
+
|
|
42
|
+
## 5. Write it
|
|
43
|
+
|
|
44
|
+
Follow [references/example.md](references/example.md):
|
|
45
|
+
|
|
46
|
+
- The verdict in a callout: `> [!CAUTION]` for request changes or blocked, `> [!WARNING]` for approve with changes, `> [!TIP]` for approve.
|
|
47
|
+
- One `##` heading per finding, numbered, with its rank in the heading. About a dozen lines each, evidence included.
|
|
48
|
+
- No summary that repeats the findings, no section for an empty rank, no list of what's good unless the reader asked.
|
|
49
|
+
|
|
50
|
+
[references/blocks.md](references/blocks.md) has the syntax for the callout, `::tests`, `::diff`, and highlighted lines.
|
|
51
|
+
|
|
52
|
+
## 6. Check the render
|
|
53
|
+
|
|
54
|
+
1. Lint it, and fix until it reports no problems:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
vp dlx --package comark-linter --package comark-ui lint-comark review.md
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
These commands use the [global Vite+ CLI](https://viteplus.dev/guide/global-cli). Without it, `pnpm dlx` takes the same arguments.
|
|
61
|
+
|
|
62
|
+
2. Open the rendered page where it will be read and read it top to bottom as the reader would. Each finding's evidence should be visible without expanding anything except a long log, and each `::diff` should start at the line its finding names.
|
|
63
|
+
|
|
64
|
+
Done when the page renders without problems and you've read it once as the reader.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Blocks for a review document
|
|
2
|
+
|
|
3
|
+
These blocks come with `comark-ui`, or alone with `comark-github`, `comark-tests`, `comark-diff`, and `comark-code`. The comark-ui skill (`vp dlx skills add onmax/comark-ui`) documents every block in full.
|
|
4
|
+
|
|
5
|
+
## The verdict: a callout
|
|
6
|
+
|
|
7
|
+
```md
|
|
8
|
+
> [!CAUTION] Request changes
|
|
9
|
+
> One blocking finding: after a large avatar loads, every smaller one shows the large image.
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The text after the marker is the title. `[!CAUTION]` fits request changes or blocked, `[!WARNING]` approve with changes, `[!TIP]` approve, and `[!NOTE]` context the reader needs before the findings. Any other marker stays a plain quote and is reported.
|
|
13
|
+
|
|
14
|
+
## A test run: `::tests`
|
|
15
|
+
|
|
16
|
+
Paste the JUnit XML the runner wrote, unedited, so the counts are the run's own:
|
|
17
|
+
|
|
18
|
+
````md
|
|
19
|
+
::tests{title="pnpm test src/avatars"}
|
|
20
|
+
```xml
|
|
21
|
+
<testsuite name="cache.test.ts" time="0.18">
|
|
22
|
+
<testcase classname="cache.test.ts" name="returns a cached avatar"/>
|
|
23
|
+
<testcase classname="cache.test.ts" name="returns a 32px avatar after a 512px one">
|
|
24
|
+
<failure message="expected 512 to be 32">AssertionError: expected 512 to be 32</failure>
|
|
25
|
+
</testcase>
|
|
26
|
+
</testsuite>
|
|
27
|
+
```
|
|
28
|
+
::
|
|
29
|
+
````
|
|
30
|
+
|
|
31
|
+
It shows the passed, failed, and skipped counts and each failure's message and stack. Most runners write the report with a flag: `vitest run --reporter=junit --outputFile=junit.xml`, `pytest --junitxml=junit.xml`, `jest --reporters=jest-junit`. For a command that isn't a test run, paste its output in a `txt` or `sh` fence instead.
|
|
32
|
+
|
|
33
|
+
## The lines that cause it: a fence with `mark`
|
|
34
|
+
|
|
35
|
+
````md
|
|
36
|
+
```ts [src/avatars/cache.ts] mark={1}
|
|
37
|
+
const cache = new Map<string, Blob>();
|
|
38
|
+
```
|
|
39
|
+
````
|
|
40
|
+
|
|
41
|
+
`mark={1}` highlights the block's first line; ranges look like `{3-4}` or `{1, 3-4}`. Copy a few lines from the file, the ones your text names.
|
|
42
|
+
|
|
43
|
+
## The fix: `::diff`
|
|
44
|
+
|
|
45
|
+
````md
|
|
46
|
+
::diff{line=18 label="Fix"}
|
|
47
|
+
```ts [src/avatars/cache.ts]
|
|
48
|
+
const key = user.id;
|
|
49
|
+
```
|
|
50
|
+
```ts
|
|
51
|
+
const key = `${user.id}:${size}`;
|
|
52
|
+
```
|
|
53
|
+
::
|
|
54
|
+
````
|
|
55
|
+
|
|
56
|
+
Two fenced blocks, before then after, starting at the file's line `line`. One file per diff, a few lines each.
|
|
57
|
+
|
|
58
|
+
## A long log: `<details>`
|
|
59
|
+
|
|
60
|
+
```md
|
|
61
|
+
<details>
|
|
62
|
+
<summary>Full test output</summary>
|
|
63
|
+
|
|
64
|
+
…
|
|
65
|
+
|
|
66
|
+
</details>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Fold away anything the reader may skip, such as a full log under the failure that matters.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Review: avatar cache
|
|
2
|
+
|
|
3
|
+
> [!CAUTION] Request changes
|
|
4
|
+
> One blocking finding: after a large avatar loads, every smaller one on the page shows the large image. The fix is one line.
|
|
5
|
+
|
|
6
|
+
Reviewed [acme/web#418](https://github.com/acme/web/pull/418) at `9f3c2e1`.
|
|
7
|
+
|
|
8
|
+
## 1. Small avatars load the 512px image (blocking)
|
|
9
|
+
|
|
10
|
+
`src/avatars/cache.ts:18`. The cache key is the user's id alone, so once the profile header loads someone's 512px avatar, the 32px avatars in comments get the same file. Reproduced with a test:
|
|
11
|
+
|
|
12
|
+
::tests{title="pnpm test src/avatars"}
|
|
13
|
+
```xml
|
|
14
|
+
<testsuite name="cache.test.ts" time="0.18">
|
|
15
|
+
<testcase classname="cache.test.ts" name="returns a cached avatar" time="0.01"/>
|
|
16
|
+
<testcase classname="cache.test.ts" name="returns a 32px avatar after a 512px one" time="0.02">
|
|
17
|
+
<failure message="expected 512 to be 32">AssertionError: expected 512 to be 32
|
|
18
|
+
at cache.test.ts:27:31</failure>
|
|
19
|
+
</testcase>
|
|
20
|
+
</testsuite>
|
|
21
|
+
```
|
|
22
|
+
::
|
|
23
|
+
|
|
24
|
+
::diff{line=18 label="Fix"}
|
|
25
|
+
```ts [src/avatars/cache.ts]
|
|
26
|
+
const key = user.id;
|
|
27
|
+
```
|
|
28
|
+
```ts
|
|
29
|
+
const key = `${user.id}:${size}`;
|
|
30
|
+
```
|
|
31
|
+
::
|
|
32
|
+
|
|
33
|
+
## 2. The cache never evicts (should fix)
|
|
34
|
+
|
|
35
|
+
`src/avatars/cache.ts:9`. Read in the code, not measured: the map grows by one entry per user and size for as long as the tab stays open, which on the team directory is thousands of images.
|
|
36
|
+
|
|
37
|
+
```ts [src/avatars/cache.ts] mark={1}
|
|
38
|
+
const cache = new Map<string, Blob>();
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Cap it, such as an LRU of 500 entries, or decide the tab's lifetime is short enough not to.
|
|
42
|
+
|
|
43
|
+
## Checked
|
|
44
|
+
|
|
45
|
+
- Ran `pnpm test src/avatars`: one failure, above.
|
|
46
|
+
- Opened a profile on the preview at 1440px and watched the comment avatars after the header loaded.
|
|
47
|
+
|
|
48
|
+
## Not checked
|
|
49
|
+
|
|
50
|
+
- Server rendering: the cache only runs in the browser, and I didn't confirm the server skips it.
|
|
51
|
+
- Safari's memory use.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: teach-lesson
|
|
3
|
+
description: Write a short lesson on one concept, with simplified English, a worked example, and an application question. Use for a lesson, a self-check, or onboarding on one idea.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Teach lesson
|
|
8
|
+
|
|
9
|
+
A lesson teaches one concept through a worked example and an application question. Keep it short enough to finish in five minutes.
|
|
10
|
+
|
|
11
|
+
## 1. Pin the concept and the outcome
|
|
12
|
+
|
|
13
|
+
Infer the reader's purpose and existing knowledge from the conversation. Focus on the gap their question reveals.
|
|
14
|
+
|
|
15
|
+
Name one concept and write a testable outcome, such as "Predict what happens when two people save the same record." Use a verb such as predict, explain, choose, or spot.
|
|
16
|
+
|
|
17
|
+
If the outcome depends on another concept, link to its prerequisite lesson or teach that prerequisite first.
|
|
18
|
+
|
|
19
|
+
Done when one sentence names the action the reader will perform and the situation they will apply it to.
|
|
20
|
+
|
|
21
|
+
## 2. Find a worked example
|
|
22
|
+
|
|
23
|
+
Pick a case from the reader's world where the concept prevents a visible failure. Trace it in three or four moves with concrete names, status codes, or numbers. Label invented values as illustrative.
|
|
24
|
+
|
|
25
|
+
Verify behavior against a primary source, such as code or a specification. For an existing design, find recorded reasons when available. Distinguish verified behavior, recorded reasons, and inferred reasons. Preserve uncertainty such as "likely", and say when a reason is unknown.
|
|
26
|
+
|
|
27
|
+
Done when each move has a concrete value, its behavior matches the source, and each design reason is sourced or qualified.
|
|
28
|
+
|
|
29
|
+
## 3. Write the lesson
|
|
30
|
+
|
|
31
|
+
Read [references/example.md](references/example.md) as a model for the complete lesson. Write these parts in order:
|
|
32
|
+
|
|
33
|
+
1. **The outcome**, one sentence under the title.
|
|
34
|
+
2. **Why it matters**: the failure the concept prevents, told as the worked example's problem. Three or four sentences.
|
|
35
|
+
3. **The idea**: give the smallest complete explanation in at most four sentences. Start with a plain definition, connect it to the reader's case, and explain how it solves the problem.
|
|
36
|
+
4. **The worked example**: add the supporting detail, one move per step, showing the value that changed. For text and code, use [`::steps`](references/blocks.md#the-worked-example-steps). When diagrams help explain interacting parts, use the [diagram series guidance](references/blocks.md#build-a-diagram-across-steps).
|
|
37
|
+
5. **The check**, below.
|
|
38
|
+
6. **Read next**: one primary source, linked to the exact section, and the lesson that naturally follows.
|
|
39
|
+
|
|
40
|
+
### Use STE 100 writing rules
|
|
41
|
+
|
|
42
|
+
Apply [ASD-STE100 Simplified Technical English](https://asd-ste100.org/) writing rules to the whole lesson, including the example, question, hints, and answer explanations.
|
|
43
|
+
|
|
44
|
+
- Give each sentence one idea. Give each instruction one action.
|
|
45
|
+
- Keep instructions within 20 words and descriptive sentences within 25 words. Use short paragraphs.
|
|
46
|
+
- Use common words and active voice. Write instructions as direct commands.
|
|
47
|
+
- Define each technical term in bold at first use. Use one term for each concept and keep its meaning consistent.
|
|
48
|
+
- Keep technical names, code, units, and UI labels exact.
|
|
49
|
+
- Put a condition before the action it controls. Make each pronoun refer to one clear subject.
|
|
50
|
+
- Use literal wording and explain abbreviations. Split long noun groups into phrases.
|
|
51
|
+
- State the mechanism directly, with the detail needed to follow it.
|
|
52
|
+
|
|
53
|
+
Done when the outcome, problem, idea, worked example, and source link are present. Each term must have a definition before use, and each sentence must meet the applicable word limit. Preserve the example's behavior and evidence confidence.
|
|
54
|
+
|
|
55
|
+
## 4. Write the check
|
|
56
|
+
|
|
57
|
+
Use [`::question`](references/blocks.md#the-check-question) to test the outcome. Change one input of the worked example and ask the reader to predict, explain, choose, or spot the result.
|
|
58
|
+
|
|
59
|
+
- Make each wrong option a real misconception, and give it a `>` rationale that says why it's tempting and why it's wrong.
|
|
60
|
+
- Keep the options parallel, one idea each and close in length, so their shape gives no clue to the answer.
|
|
61
|
+
- Add `#explain` for the one thing the reader should take away whatever they picked.
|
|
62
|
+
- For a fact worth remembering a week later, optionally add a [`::recall` card](references/blocks.md#the-fact-to-keep-recall).
|
|
63
|
+
|
|
64
|
+
Done when the question tests the stated outcome with one changed input, every option has a rationale, and the shared explanation resolves each misconception.
|
|
65
|
+
|
|
66
|
+
## 5. Check the render
|
|
67
|
+
|
|
68
|
+
1. Lint it, and fix until it reports no problems:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
vp dlx --package comark-linter --package comark-ui lint-comark lesson.md
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
These commands use the [global Vite+ CLI](https://viteplus.dev/guide/global-cli). Without it, `pnpm dlx` takes the same arguments.
|
|
75
|
+
|
|
76
|
+
2. Open the rendered page where it will be read. Select each option and check its rationale and the shared explanation.
|
|
77
|
+
|
|
78
|
+
Done when lint reports no problems, every worked step and diagram is visible, and each option reveals the expected result and rationale. Confirm that the correct answer follows from the example and that the lesson fits a five-minute read.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Blocks for a lesson
|
|
2
|
+
|
|
3
|
+
These blocks come with `comark-ui`. The steps and questions also come separately with `comark-steps` and `comark-quiz`.
|
|
4
|
+
|
|
5
|
+
## The worked example: `::steps`
|
|
6
|
+
|
|
7
|
+
````md
|
|
8
|
+
::steps
|
|
9
|
+
### Both load version 7
|
|
10
|
+
|
|
11
|
+
Ana and Ben each `GET /articles/42`, and both get `ETag: "v7"`.
|
|
12
|
+
|
|
13
|
+
### Ana saves first
|
|
14
|
+
|
|
15
|
+
```http
|
|
16
|
+
PUT /articles/42
|
|
17
|
+
If-Match: "v7"
|
|
18
|
+
```
|
|
19
|
+
::
|
|
20
|
+
````
|
|
21
|
+
|
|
22
|
+
Each `###` heading starts a step that holds everything up to the next one, code blocks included. Put any introduction above the block, since a paragraph before the first heading is reported. For steps of one line each, a numbered list inside `::steps` works too.
|
|
23
|
+
|
|
24
|
+
## Build a diagram across steps
|
|
25
|
+
|
|
26
|
+
Use a `mermaid` fence when a diagram helps explain interacting parts. Redraw the previous diagram and add one part or relationship at each move. Keep existing labels and participant order consistent.
|
|
27
|
+
|
|
28
|
+
Use numbered Markdown headings for the worked example. Mermaid fences currently disappear inside `::steps`, so place them directly under these headings:
|
|
29
|
+
|
|
30
|
+
````md
|
|
31
|
+
### 1. The client requests the article
|
|
32
|
+
|
|
33
|
+
```mermaid
|
|
34
|
+
sequenceDiagram
|
|
35
|
+
participant Client
|
|
36
|
+
participant Server
|
|
37
|
+
Client->>Server: GET /articles/42
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. The server returns a version label
|
|
41
|
+
|
|
42
|
+
```mermaid
|
|
43
|
+
sequenceDiagram
|
|
44
|
+
participant Client
|
|
45
|
+
participant Server
|
|
46
|
+
Client->>Server: GET /articles/42
|
|
47
|
+
Server-->>Client: ETag v7
|
|
48
|
+
```
|
|
49
|
+
````
|
|
50
|
+
|
|
51
|
+
These paths and version labels are illustrative. Use `sequenceDiagram` for messages or `flowchart LR` for a flow. comark-ui renders Mermaid as SVG, so the reader needs no diagram script.
|
|
52
|
+
|
|
53
|
+
## The check: `::question`
|
|
54
|
+
|
|
55
|
+
```md
|
|
56
|
+
::question{#etag-no-if-match}
|
|
57
|
+
Ben's editor sends his `PUT` with no `If-Match` header. What happens?
|
|
58
|
+
|
|
59
|
+
- [x] The server saves Ben's version and Ana's fix is lost
|
|
60
|
+
> Without `If-Match` there's no condition to fail.
|
|
61
|
+
- [ ] The server refuses the save with a 412 status code
|
|
62
|
+
> A 412 needs a condition that failed.
|
|
63
|
+
|
|
64
|
+
#hint
|
|
65
|
+
What does the server compare the label against?
|
|
66
|
+
|
|
67
|
+
#explain
|
|
68
|
+
`If-Match` is opt-in.
|
|
69
|
+
::
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- Mark the right option `[x]`. One `[x]` makes radio buttons; add `multiple` to the block for checkboxes with several right options.
|
|
73
|
+
- The `>` quote under an option is its rationale, shown once the reader answers.
|
|
74
|
+
- `#hint` is optional, shown on request; `#explain` shows after any answer.
|
|
75
|
+
- Options are shuffled the same way on every render; write `shuffle="false"` only when their order means something, such as numbers in order.
|
|
76
|
+
- Give the block an `#id` so a host can store the answer. Without one, the id comes from the question's text and changes when you edit it.
|
|
77
|
+
|
|
78
|
+
Without its script, the question and an answer disclosure still render as plain HTML.
|
|
79
|
+
|
|
80
|
+
## The fact to keep: `::recall`
|
|
81
|
+
|
|
82
|
+
```md
|
|
83
|
+
::recall{#etag-412}
|
|
84
|
+
What does a server answer when `If-Match` names an old version?
|
|
85
|
+
|
|
86
|
+
#answer
|
|
87
|
+
`412 Precondition Failed`, and it leaves the resource unchanged.
|
|
88
|
+
::
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The reader answers in their head, then reveals `#answer`.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Keep both writers' changes
|
|
2
|
+
|
|
3
|
+
After this lesson, you can predict how a server handles two changes to the same record, with or without a version check.
|
|
4
|
+
|
|
5
|
+
## Why it matters
|
|
6
|
+
|
|
7
|
+
In this example, Ana and Ben open the same help article. Ana corrects a spelling error and saves the article. Ben adds a paragraph to his older copy and saves it. His save removes Ana's correction without a warning. This is a **lost update**.
|
|
8
|
+
|
|
9
|
+
## The idea
|
|
10
|
+
|
|
11
|
+
An **ETag** is a version label, such as `"v7"`, that the server sends with a resource. The **If-Match** header carries that label back to require the same version before saving a change. If the labels match, the server saves the change. If the labels differ, the server returns `412 Precondition Failed` and keeps the resource unchanged.
|
|
12
|
+
|
|
13
|
+
## Worked example
|
|
14
|
+
|
|
15
|
+
::steps
|
|
16
|
+
### Both load version 7
|
|
17
|
+
|
|
18
|
+
Ana and Ben each `GET /articles/42`, and both get `ETag: "v7"`.
|
|
19
|
+
|
|
20
|
+
### Ana saves first
|
|
21
|
+
|
|
22
|
+
```http
|
|
23
|
+
PUT /articles/42
|
|
24
|
+
If-Match: "v7"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The article is still at `"v7"`. The server saves Ana's change and returns `200 OK` with `ETag: "v8"`.
|
|
28
|
+
|
|
29
|
+
### Ben saves second
|
|
30
|
+
|
|
31
|
+
```http
|
|
32
|
+
PUT /articles/42
|
|
33
|
+
If-Match: "v7"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The article is now at `"v8"`. The labels differ, so the server returns `412 Precondition Failed`. Ana's correction stays.
|
|
37
|
+
|
|
38
|
+
### Ben reloads and merges
|
|
39
|
+
|
|
40
|
+
Ben's editor fetches `"v8"` and shows Ana's correction. Ben adds his paragraph to that version. He saves again with `If-Match: "v8"`.
|
|
41
|
+
::
|
|
42
|
+
|
|
43
|
+
## Check yourself
|
|
44
|
+
|
|
45
|
+
::question{#etag-no-if-match}
|
|
46
|
+
This API accepts requests without `If-Match`. Ben's editor sends his `PUT` without that header. The article is at `"v8"`. What happens?
|
|
47
|
+
|
|
48
|
+
- [x] The server saves Ben's version and removes Ana's correction
|
|
49
|
+
> Without `If-Match`, the server has no version condition to check. The server saves Ben's version. This removes Ana's correction.
|
|
50
|
+
- [ ] The server refuses the save with a 412 status code
|
|
51
|
+
> A 412 response requires a condition that failed. Ben sent no condition, so the server has no labels to compare.
|
|
52
|
+
- [ ] The server merges Ben's paragraph into version 8
|
|
53
|
+
> The server does not combine the two versions. After a 412 response, the client must get the current version and combine the changes.
|
|
54
|
+
|
|
55
|
+
#explain
|
|
56
|
+
The client must send its original version label for `If-Match` to protect the change. An API can require that header. If the header is absent, that API can return `428 Precondition Required`.
|
|
57
|
+
::
|
|
58
|
+
|
|
59
|
+
::recall{#etag-412}
|
|
60
|
+
What does a server answer when `If-Match` names an old version?
|
|
61
|
+
|
|
62
|
+
#answer
|
|
63
|
+
`412 Precondition Failed`, and it leaves the resource unchanged.
|
|
64
|
+
::
|
|
65
|
+
|
|
66
|
+
## Read next
|
|
67
|
+
|
|
68
|
+
[RFC 9110, section 13.1.1](https://www.rfc-editor.org/rfc/rfc9110#name-if-match) defines `If-Match`.
|
|
69
|
+
|
|
70
|
+
The next lesson explains weak ETags and why `W/"v7"` never matches `If-Match`.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: visual-explanation
|
|
3
|
+
description: Explains how something works with the smallest visual that answers the question, such as a sequence diagram, a call tree, a file tree, or a marked-up screenshot, placed beside the sentence it supports. Use when someone asks to be shown how a system, a flow, or a change works, or when the answer would otherwise take paragraphs of prose.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Visual explanation
|
|
8
|
+
|
|
9
|
+
A good explanation answers one question with one picture the reader can check against the code. Prose carries the reasoning, and the visual carries the shape: what happens in what order, what calls what, what lives where. Each visual stands next to the sentence that tells the reader what to look for.
|
|
10
|
+
|
|
11
|
+
## 1. Name the question
|
|
12
|
+
|
|
13
|
+
Write the question in one sentence, in the reader's words: "What happens between `parseMarkdown` and the HTML?" If it's two questions, answer the one they asked and offer the other.
|
|
14
|
+
|
|
15
|
+
Done when the question fits on one line.
|
|
16
|
+
|
|
17
|
+
## 2. Pick the view
|
|
18
|
+
|
|
19
|
+
Choose by the shape of the answer:
|
|
20
|
+
|
|
21
|
+
| The answer is… | Draw |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| The order of events between parts | A `mermaid` sequence diagram or flowchart |
|
|
24
|
+
| Who calls whom | A call tree in a `text` fence, indented by call depth |
|
|
25
|
+
| Where code lives, or which files a change touches | `::file-tree`, with `+`, `-`, and `~` marks |
|
|
26
|
+
| The states something moves between | A `mermaid` `stateDiagram-v2` |
|
|
27
|
+
| Something on screen | `::screenshot` with measured marks |
|
|
28
|
+
| What a few lines do, line by line | A code fence with `// <1>` notes |
|
|
29
|
+
| How a number changed | `::chart` or a two-row table |
|
|
30
|
+
|
|
31
|
+
Use one primary view. Add a second only when it answers the question the first one raises, such as "where is that in my code?" after a flow diagram. [references/blocks.md](references/blocks.md) has the syntax of each.
|
|
32
|
+
|
|
33
|
+
Done when you can say, in one sentence, what the reader will see in the view.
|
|
34
|
+
|
|
35
|
+
## 3. Draw it from the source
|
|
36
|
+
|
|
37
|
+
Read the code, the product, or the data before you draw. Every box, arrow, file, and label in the view is something the reader can find: a real function name, a real file path, a state the code really has. Leave out the parts that don't bear on the question; a diagram of everything answers nothing.
|
|
38
|
+
|
|
39
|
+
Keep a diagram to about eight nodes or participants. Past that, split it by stage or zoom into the part the question is about.
|
|
40
|
+
|
|
41
|
+
Done when every label in the view exists in the source, and nothing in it is there only for completeness.
|
|
42
|
+
|
|
43
|
+
## 4. Write around it
|
|
44
|
+
|
|
45
|
+
[references/example.md](references/example.md) shows the shape:
|
|
46
|
+
|
|
47
|
+
1. A title that is the question, or its answer.
|
|
48
|
+
2. Two or three sentences with the answer in plain words, ending on what to look at in the view.
|
|
49
|
+
3. The view.
|
|
50
|
+
4. One or two sentences for what the view can't show, such as a rule that applies at every step.
|
|
51
|
+
5. A second view only if step 2 allowed one, with a sentence before it saying why it's there.
|
|
52
|
+
|
|
53
|
+
Done when the explanation reads top to bottom in under a minute and every visual has a sentence beside it.
|
|
54
|
+
|
|
55
|
+
## 5. Check the render
|
|
56
|
+
|
|
57
|
+
1. Lint it, and fix until it reports no problems:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
vp dlx --package comark-linter --package comark-ui lint-comark explanation.md
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
These commands use the [global Vite+ CLI](https://viteplus.dev/guide/global-cli). Without it, `pnpm dlx` takes the same arguments.
|
|
64
|
+
|
|
65
|
+
2. Open the rendered page where it will be read and look at each view at the width the reader has. A diagram whose labels are too small to read needs fewer nodes, not a wider page.
|
|
66
|
+
|
|
67
|
+
Done when you've looked at the render and every label reads at its size.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Blocks for a visual explanation
|
|
2
|
+
|
|
3
|
+
These blocks come with `comark-ui`. The comark-ui skill (`vp dlx skills add onmax/comark-ui`) documents every block in full.
|
|
4
|
+
|
|
5
|
+
## Diagrams: a `mermaid` fence
|
|
6
|
+
|
|
7
|
+
````md
|
|
8
|
+
```mermaid
|
|
9
|
+
sequenceDiagram
|
|
10
|
+
participant Browser
|
|
11
|
+
participant Worker
|
|
12
|
+
participant Assets
|
|
13
|
+
Browser->>Worker: GET /zoom/
|
|
14
|
+
Worker->>Assets: zoom/index.html
|
|
15
|
+
Assets-->>Browser: prerendered page
|
|
16
|
+
```
|
|
17
|
+
````
|
|
18
|
+
|
|
19
|
+
comark-ui draws Mermaid as SVG while parsing, so the page needs no script to show it. Use `flowchart LR` for a pipeline, `sequenceDiagram` for messages between parts, and `stateDiagram-v2` for states. Write labels as the code names them. For a diagram in another language, `d2`, `dot`, `plantuml`, `dbml`, `goat`, and `svgbob` fences draw through comark-diagrams.
|
|
20
|
+
|
|
21
|
+
## A call tree: a `text` fence
|
|
22
|
+
|
|
23
|
+
````md
|
|
24
|
+
```text
|
|
25
|
+
renderPage
|
|
26
|
+
parseMarkdown
|
|
27
|
+
plugins' post
|
|
28
|
+
renderHtmlFromDocument
|
|
29
|
+
components
|
|
30
|
+
```
|
|
31
|
+
````
|
|
32
|
+
|
|
33
|
+
Indent by call depth, two spaces a level. Name functions as the code does.
|
|
34
|
+
|
|
35
|
+
## Files: `::file-tree`
|
|
36
|
+
|
|
37
|
+
````md
|
|
38
|
+
::file-tree
|
|
39
|
+
```
|
|
40
|
+
docs/
|
|
41
|
+
build.ts # renders every page
|
|
42
|
+
~ style.css
|
|
43
|
+
+ pages/
|
|
44
|
+
+ skills/
|
|
45
|
+
...
|
|
46
|
+
```
|
|
47
|
+
::
|
|
48
|
+
````
|
|
49
|
+
|
|
50
|
+
Two spaces nest an entry, a trailing `/` makes a folder, `+`, `-`, and `~` mark added, removed, and changed, and ` # ` starts a note. `...` stands for entries left out. Write the tree in a fence; a Markdown list renders names like `__init__.py` as bold.
|
|
51
|
+
|
|
52
|
+
## Line notes: `// <1>` and a list
|
|
53
|
+
|
|
54
|
+
````md
|
|
55
|
+
```ts
|
|
56
|
+
const document = await parseMarkdown(source, { plugins }); // <1>
|
|
57
|
+
const html = await renderHtmlFromDocument(document, { components }); // <2>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
1. Every plugin runs here.
|
|
61
|
+
2. Components only draw.
|
|
62
|
+
````
|
|
63
|
+
|
|
64
|
+
A numbered list right after the fence becomes the notes. The marker goes in a line-end comment in the block's language, such as `# <1>` in Python.
|
|
65
|
+
|
|
66
|
+
## On screen: `::screenshot`
|
|
67
|
+
|
|
68
|
+
```md
|
|
69
|
+
::screenshot{src="https://example.com/settings.png" alt="Settings page with the Save button" viewport="1280x800"}
|
|
70
|
+
:::annotation{x=71.2 y=84.1 w=12.4 h=6.3}
|
|
71
|
+
**Save** writes all three tabs at once.
|
|
72
|
+
:::
|
|
73
|
+
::
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Measure `x y w h` from the page as percentages of the image. The before-after-evidence skill explains how.
|
|
77
|
+
|
|
78
|
+
## Numbers: `::chart`
|
|
79
|
+
|
|
80
|
+
```md
|
|
81
|
+
::chart{type="bar" title="Build time, seconds"}
|
|
82
|
+
| Step | Before | After |
|
|
83
|
+
| --- | --: | --: |
|
|
84
|
+
| Parse | 4.1 | 1.2 |
|
|
85
|
+
| Render | 2.3 | 2.2 |
|
|
86
|
+
::
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The chart draws from the table, so the numbers in your text and the chart can't disagree.
|