@zigrivers/scaffold 3.29.0 → 3.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/guides/AUTHORING.md +146 -0
- package/content/guides/cli/index.html +1855 -0
- package/content/guides/cli/index.md +206 -0
- package/content/guides/concepts/index.html +1970 -0
- package/content/guides/concepts/index.md +347 -0
- package/content/guides/dashboard/index.html +1913 -0
- package/content/guides/dashboard/index.md +264 -0
- package/content/guides/index.html +368 -15
- package/content/guides/install/.diagrams/diagram-0.svg +1 -0
- package/content/guides/install/.diagrams/manifest.json +3 -0
- package/content/guides/install/index.html +1653 -0
- package/content/guides/install/index.md +186 -0
- package/content/guides/knowledge/.diagrams/diagram-0.svg +1 -0
- package/content/guides/knowledge/.diagrams/manifest.json +3 -0
- package/content/guides/knowledge/index.html +1765 -0
- package/content/guides/knowledge/index.md +209 -0
- package/content/guides/knowledge-freshness/.diagrams/diagram-0.svg +1 -0
- package/content/guides/knowledge-freshness/.diagrams/manifest.json +3 -0
- package/content/guides/knowledge-freshness/index.html +2795 -0
- package/content/guides/knowledge-freshness/index.md +893 -0
- package/content/guides/mmr/index.html +407 -36
- package/content/guides/mmr/index.md +39 -16
- package/content/guides/multi-agent/.diagrams/diagram-0.svg +1 -0
- package/content/guides/multi-agent/.diagrams/manifest.json +3 -0
- package/content/guides/multi-agent/index.html +1715 -0
- package/content/guides/multi-agent/index.md +243 -0
- package/content/guides/observability/.diagrams/diagram-0.svg +1 -0
- package/content/guides/observability/.diagrams/diagram-1.svg +1 -0
- package/content/guides/observability/.diagrams/diagram-2.svg +1 -0
- package/content/guides/observability/.diagrams/diagram-3.svg +1 -0
- package/content/guides/observability/.diagrams/manifest.json +6 -0
- package/content/guides/observability/index.html +3257 -0
- package/content/guides/observability/index.md +1097 -0
- package/content/guides/pipeline/.diagrams/diagram-0.svg +1 -0
- package/content/guides/pipeline/.diagrams/diagram-1.svg +1 -0
- package/content/guides/pipeline/.diagrams/manifest.json +4 -0
- package/content/guides/pipeline/index.html +1973 -0
- package/content/guides/pipeline/index.md +387 -0
- package/content/guides/review-workflow/.diagrams/diagram-0.svg +1 -0
- package/content/guides/review-workflow/.diagrams/diagram-1.svg +1 -0
- package/content/guides/review-workflow/.diagrams/manifest.json +4 -0
- package/content/guides/review-workflow/index.html +1790 -0
- package/content/guides/review-workflow/index.md +248 -0
- package/dist/guides/build.d.ts +1 -1
- package/dist/guides/build.d.ts.map +1 -1
- package/dist/guides/build.js +21 -9
- package/dist/guides/build.js.map +1 -1
- package/dist/guides/build.test.js +47 -0
- package/dist/guides/build.test.js.map +1 -1
- package/dist/guides/chrome.d.ts.map +1 -1
- package/dist/guides/chrome.js +83 -12
- package/dist/guides/chrome.js.map +1 -1
- package/dist/guides/dashboard-theme.css +8 -0
- package/dist/guides/directives-cite.test.d.ts +2 -0
- package/dist/guides/directives-cite.test.d.ts.map +1 -0
- package/dist/guides/directives-cite.test.js +26 -0
- package/dist/guides/directives-cite.test.js.map +1 -0
- package/dist/guides/directives-tabs.test.js +47 -0
- package/dist/guides/directives-tabs.test.js.map +1 -1
- package/dist/guides/directives.d.ts +1 -0
- package/dist/guides/directives.d.ts.map +1 -1
- package/dist/guides/directives.js +38 -0
- package/dist/guides/directives.js.map +1 -1
- package/dist/guides/guides.css +268 -0
- package/dist/guides/index-page.d.ts.map +1 -1
- package/dist/guides/index-page.js +41 -8
- package/dist/guides/index-page.js.map +1 -1
- package/dist/guides/links.d.ts +14 -0
- package/dist/guides/links.d.ts.map +1 -0
- package/dist/guides/links.js +56 -0
- package/dist/guides/links.js.map +1 -0
- package/dist/guides/links.test.d.ts +2 -0
- package/dist/guides/links.test.d.ts.map +1 -0
- package/dist/guides/links.test.js +72 -0
- package/dist/guides/links.test.js.map +1 -0
- package/dist/guides/render.d.ts +1 -0
- package/dist/guides/render.d.ts.map +1 -1
- package/dist/guides/render.js +1 -1
- package/dist/guides/render.js.map +1 -1
- package/dist/guides/sanitize.d.ts.map +1 -1
- package/dist/guides/sanitize.js +5 -0
- package/dist/guides/sanitize.js.map +1 -1
- package/dist/guides/template.d.ts.map +1 -1
- package/dist/guides/template.js +7 -2
- package/dist/guides/template.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The Code-Review Workflow
|
|
3
|
+
topic: review-workflow
|
|
4
|
+
description: When and how to run multi-model review — entry points, input modes, verdicts, the round limit, and degraded mode
|
|
5
|
+
category: workflows
|
|
6
|
+
order: 30
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What this guide covers
|
|
10
|
+
|
|
11
|
+
This is the **workflow** around multi-model review: *when* to review, *which*
|
|
12
|
+
entry point to reach for, *which* input mode matches your target, how to read
|
|
13
|
+
the verdict, and how the fix loop is bounded. It does **not** re-explain the
|
|
14
|
+
review engine itself — channels, reconciliation, the `finding_key`, the verdict
|
|
15
|
+
algorithm, and `.mmr.yaml` all live in the [MMR reference](../mmr/index.md).
|
|
16
|
+
Read that guide for "what is a channel"; read this one for "I have a change, now
|
|
17
|
+
what do I run."
|
|
18
|
+
|
|
19
|
+
:::callout{type=tip}
|
|
20
|
+
**Two layers.** `mmr review` is the engine. `scaffold run review-pr` and
|
|
21
|
+
`review-code` are *workflow wrappers* on top — they pick the input mode, add the
|
|
22
|
+
Superpowers code-reviewer agent channel via `mmr reconcile`, handle auth
|
|
23
|
+
recovery, and bound the fix loop. `post-implementation-review` is a separate,
|
|
24
|
+
release-time full-codebase review with its own flow (see Step 1). This guide is
|
|
25
|
+
about driving those entry points; the engine details are in
|
|
26
|
+
[the MMR reference](../mmr/index.md).
|
|
27
|
+
:::
|
|
28
|
+
|
|
29
|
+
## Step 1 — Pick the entry point
|
|
30
|
+
|
|
31
|
+
Two of these are wrappers over `mmr review` — `review-pr` and `review-code` —
|
|
32
|
+
sharing the same engine and verdict semantics, differing in *scope* and *what
|
|
33
|
+
they synthesize*. `post-implementation-review` is **not** a `mmr review` wrapper:
|
|
34
|
+
it's an independent release-time review of the whole codebase that runs its own
|
|
35
|
+
channel flow and writes its own report under `docs/reviews/`, with MMR
|
|
36
|
+
reconciliation only as an optional add-on. Consult its own doc for specifics.
|
|
37
|
+
|
|
38
|
+
```mermaid
|
|
39
|
+
flowchart TD
|
|
40
|
+
Q{What are you reviewing?} --> A[An open PR]
|
|
41
|
+
Q --> B[Local changes
|
|
42
|
+
before commit/push]
|
|
43
|
+
Q --> C[The whole codebase
|
|
44
|
+
after all tasks land]
|
|
45
|
+
A --> AR["scaffold run review-pr
|
|
46
|
+
(mmr review --pr N)"]
|
|
47
|
+
B --> BR["scaffold run review-code
|
|
48
|
+
(synthesized delivery candidate)"]
|
|
49
|
+
C --> CR["scaffold run post-implementation-review
|
|
50
|
+
(two-phase: systemic + per-story)"]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| Entry point | Target | Notes |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `scaffold run review-pr` | One PR (`--pr N`, auto-detected from the branch) | **MMR wrapper.** Adds auth checks, the Superpowers agent channel, the per-finding round bookkeeping, an opt-in Beads bridge over a bare `mmr review`. |
|
|
56
|
+
| `scaffold run review-code` | Local pre-push: committed branch diff + staged + unstaged, as one synthesized "delivery candidate" | **MMR wrapper.** Adds the same agent channel + round bounding, plus file & standards context for the file-blind CLIs. **Untracked files are not covered.** |
|
|
57
|
+
| `scaffold run post-implementation-review` | The full implemented codebase against stories + standards | **Independent, not an MMR wrapper.** A release-time review (systemic sweep + per-story functional review via parallel agents) with its own report under `docs/reviews/`; MMR injection is optional. Consult its own doc. |
|
|
58
|
+
|
|
59
|
+
:::callout{type=note}
|
|
60
|
+
**The mandatory one.** After `gh pr create`, running `review-pr` is mandatory
|
|
61
|
+
before moving to the next task (a PostToolUse hook reminds you).
|
|
62
|
+
`review-code` is the recommended preflight before a push but isn't gated.
|
|
63
|
+
`post-implementation-review` is a release-time sweep, not a per-change gate.
|
|
64
|
+
:::
|
|
65
|
+
|
|
66
|
+
### When no wrapper fits
|
|
67
|
+
|
|
68
|
+
The wrappers are conveniences, not gates on the engine. For any target a wrapper
|
|
69
|
+
doesn't cover — a branch range, an existing patch file, a single doc — call
|
|
70
|
+
`mmr review` directly with the matching input flag. The
|
|
71
|
+
[MMR reference](../mmr/index.md) lists every flag; the next step is the workflow
|
|
72
|
+
view of the same choice.
|
|
73
|
+
|
|
74
|
+
## Step 2 — Choose the input mode
|
|
75
|
+
|
|
76
|
+
`mmr review` resolves a diff from exactly one input source. Pick the one that
|
|
77
|
+
describes your target. The `--diff -` (stdin) form is the universal escape hatch:
|
|
78
|
+
anything you can express as a unified diff, you can pipe in.
|
|
79
|
+
|
|
80
|
+
```mermaid
|
|
81
|
+
flowchart TD
|
|
82
|
+
S{Where is the change?} --> PR[On a GitHub PR]
|
|
83
|
+
S --> ST[Staged in the index]
|
|
84
|
+
S --> BR[A branch, vs a base]
|
|
85
|
+
S --> DC[Local working tree:
|
|
86
|
+
committed + staged + unstaged]
|
|
87
|
+
S --> UF[A brand-new file
|
|
88
|
+
git doesn't track yet]
|
|
89
|
+
PR --> PRc["mmr review --pr N"]
|
|
90
|
+
ST --> STc["mmr review --staged"]
|
|
91
|
+
BR --> BRc["mmr review --base main --head BRANCH"]
|
|
92
|
+
DC --> DCc["git diff MERGE_BASE | mmr review --diff -
|
|
93
|
+
(this is what review-code synthesizes)"]
|
|
94
|
+
UF --> UFc["(diff -u /dev/null FILE || true) | mmr review --diff -"]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
| Target | Command | Notes |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| A PR | `mmr review --pr 123` | Fetches the diff via `gh pr diff`. This is `review-pr`'s mode. |
|
|
100
|
+
| Staged changes | `mmr review --staged` | Just the index (`git diff --cached`) — the pre-commit slice. |
|
|
101
|
+
| A branch range | `mmr review --base main --head "$BRANCH"` | Committed work only; no staged/unstaged. |
|
|
102
|
+
| Full delivery candidate | `git diff "$MERGE_BASE" \| mmr review --diff -` | Committed branch diff + staged + unstaged in one patch. `review-code` synthesizes this for you. |
|
|
103
|
+
| A single tracked file's pending edits | `git diff HEAD -- path/file.ts \| mmr review --diff -` | Fails with "no diff content" if the file has no local changes — use the next row. |
|
|
104
|
+
| A brand-new / untracked file | `(diff -u /dev/null path/file.ts \|\| true) \| mmr review --diff -` | Synthesizes an "all-added" diff from current contents. |
|
|
105
|
+
| An existing patch | `mmr review --diff path/changes.patch` | Reads diff-format content directly. |
|
|
106
|
+
|
|
107
|
+
:::callout{type=danger}
|
|
108
|
+
**Untracked files are silently skipped — this is the trap.** `review-code`
|
|
109
|
+
reviews the committed branch diff plus staged and unstaged changes *to tracked
|
|
110
|
+
files*. A brand-new file you've never `git add`-ed is **not** in any of those
|
|
111
|
+
diffs, so it sails through review with zero findings — not because it's clean,
|
|
112
|
+
but because no channel ever saw it. To review a new file, pipe it in explicitly:
|
|
113
|
+
`(diff -u /dev/null path/to/new-file.ts || true) | mmr review --diff -`. The
|
|
114
|
+
`|| true` guard is required: `diff` exits non-zero whenever files differ, which
|
|
115
|
+
would otherwise kill the pipeline under `set -o pipefail`. The wrappers cannot
|
|
116
|
+
guess which untracked files you meant to include
|
|
117
|
+
:cite[content/tools/review-code.md:37].
|
|
118
|
+
:::
|
|
119
|
+
|
|
120
|
+
The `--diff` flag expects **diff-format content** — a path to a `.patch`/`.diff`
|
|
121
|
+
file, or `-` for stdin. It does not accept raw document text; wrap the target in
|
|
122
|
+
a diff first.
|
|
123
|
+
|
|
124
|
+
## Step 3 — Read the verdict, then act
|
|
125
|
+
|
|
126
|
+
Every review collapses to exactly one of four verdicts. The verdict, not the raw
|
|
127
|
+
findings, is what decides whether you proceed. The
|
|
128
|
+
[MMR reference](../mmr/index.md) defines the gate, the severity tiers, and the
|
|
129
|
+
exact derivation algorithm; this table is the *action* you take for each outcome.
|
|
130
|
+
|
|
131
|
+
| Verdict | Exit | Do |
|
|
132
|
+
| --- | --- | --- |
|
|
133
|
+
| `pass` | 0 | **Proceed** — merge / push / next task. |
|
|
134
|
+
| `degraded-pass` | 0 | **Proceed**, noting reduced coverage. The max achievable verdict once any channel was compensated. |
|
|
135
|
+
| `blocked` | 2 | **Stop.** Fix the blocking findings (Step 4), then re-review. Do not merge. |
|
|
136
|
+
| `needs-user-decision` | 3 | **Stop and surface to the user.** Automated iteration can't resolve this. |
|
|
137
|
+
|
|
138
|
+
A review is `blocked` when any unacknowledged finding sits at or above the fix
|
|
139
|
+
threshold (:sev[P2]{level=p2} by default; override per-run with
|
|
140
|
+
`--fix-threshold`). See the [MMR reference](../mmr/index.md) for how the verdict
|
|
141
|
+
is derived from gate result + channel health.
|
|
142
|
+
|
|
143
|
+
:::callout{type=warning}
|
|
144
|
+
**Proceed only on `pass` or `degraded-pass`.** On `blocked` or
|
|
145
|
+
`needs-user-decision`, never merge automatically — surface the verdict and the
|
|
146
|
+
remaining findings to the user. The wrappers enforce this: a `blocked` /
|
|
147
|
+
`needs-user-decision` outcome takes the "Stop path" and does **not** print the
|
|
148
|
+
ready-for-merge message :cite[content/tools/review-pr.md:610].
|
|
149
|
+
:::
|
|
150
|
+
|
|
151
|
+
## Step 4 — Fix the blocking findings (bounded)
|
|
152
|
+
|
|
153
|
+
When the verdict is `blocked`, the loop is: fix the findings at or above the
|
|
154
|
+
threshold → re-review → repeat. The guard rail is that this loop is **bounded
|
|
155
|
+
per finding**, so a finding the model can't actually fix doesn't trap you in an
|
|
156
|
+
infinite cycle.
|
|
157
|
+
|
|
158
|
+
### The 3-round-per-finding limit
|
|
159
|
+
|
|
160
|
+
The limit is **3 attempts per finding**, not 3 rounds total. Each round that
|
|
161
|
+
surfaces *genuinely new* findings is healthy iteration — keep going. The loop
|
|
162
|
+
stops only when one specific finding has been attempted three times without
|
|
163
|
+
resolution.
|
|
164
|
+
|
|
165
|
+
A "finding" here is its **stable identity**, not its wording, so a re-worded
|
|
166
|
+
report of the same defect still counts against the same finding. (The
|
|
167
|
+
[MMR reference](../mmr/index.md) covers how that identity is computed.) Co-equal
|
|
168
|
+
stop conditions:
|
|
169
|
+
|
|
170
|
+
- A finding's identity reaches 3 recorded attempts.
|
|
171
|
+
- The same underlying defect recurs across 3 rounds even if the reviewer's
|
|
172
|
+
wording produces a new identity each time.
|
|
173
|
+
- Channels genuinely contradict each other (→ `needs-user-decision`).
|
|
174
|
+
- The user explicitly asks to stop.
|
|
175
|
+
|
|
176
|
+
When you stop, **do not merge**. Document each unresolved finding (severity,
|
|
177
|
+
location, attempt count) and hand the decision to the user
|
|
178
|
+
:cite[content/tools/review-pr.md:610].
|
|
179
|
+
|
|
180
|
+
### Where the bookkeeping lives today
|
|
181
|
+
|
|
182
|
+
The engine ships **native** multi-round sessions — `mmr review --session <id>
|
|
183
|
+
--round N --max-rounds M` :cite[packages/mmr/src/commands/review.ts:289] — and a
|
|
184
|
+
stable, line-number-independent `finding_key`
|
|
185
|
+
:cite[packages/mmr/src/core/stable-id.ts:115]. (See the
|
|
186
|
+
[MMR reference](../mmr/index.md) for how that key collapses the same issue at
|
|
187
|
+
different severities.)
|
|
188
|
+
|
|
189
|
+
The scaffold **wrappers** have not yet migrated to native sessions. Until they
|
|
190
|
+
do, `review-pr` and `review-code` enforce the 3-strike rule with their own
|
|
191
|
+
bookkeeping: a per-session attempts file at
|
|
192
|
+
`.scaffold/review-attempts/<session-id>.json`. Each fix round computes a hash
|
|
193
|
+
over the same four identity components, increments that finding's counter, and
|
|
194
|
+
the `_review_at_strike_limit` helper blocks once it hits 3
|
|
195
|
+
:cite[content/tools/review-pr.md:468]. The wrapper hash deliberately mirrors the
|
|
196
|
+
engine's `finding_key` components, so the eventual swap to
|
|
197
|
+
`mmr review --session` is a clean migration rather than a re-think.
|
|
198
|
+
|
|
199
|
+
:::callout{type=note}
|
|
200
|
+
**Practical takeaway.** You drive the fix loop through the wrapper; the attempts
|
|
201
|
+
file under `.scaffold/review-attempts/` is the current source of truth for the
|
|
202
|
+
strike count. For a very noisy loop you may narrow the gate for one run with
|
|
203
|
+
`--fix-threshold P1` — but don't permanently lower the project default (P2).
|
|
204
|
+
:::
|
|
205
|
+
|
|
206
|
+
## Step 5 — Handle degraded mode
|
|
207
|
+
|
|
208
|
+
A review never silently runs with fewer reviewers. A channel is *degraded* when
|
|
209
|
+
its binary isn't installed, auth fails, it times out, or it errors out. The
|
|
210
|
+
workflow's response is to **compensate and tell you how to recover**, then cap
|
|
211
|
+
the verdict at `degraded-pass`. The mechanics — how compensation runs and the
|
|
212
|
+
per-channel auth checks — live in the [MMR reference](../mmr/index.md); below is
|
|
213
|
+
what it means for *your* workflow.
|
|
214
|
+
|
|
215
|
+
- **Compensating pass.** For each degraded *external* channel, MMR runs a
|
|
216
|
+
compensating pass focused on that channel's strength area and records the
|
|
217
|
+
source as `compensating-<channel>` :cite[packages/mmr/src/core/compensator.ts:162].
|
|
218
|
+
The compensator defaults to `claude -p` unless `defaults.compensator.channel`
|
|
219
|
+
overrides it :cite[packages/mmr/src/core/compensator.ts:74]. These findings
|
|
220
|
+
are single-source, low confidence. With the default compensator, a missing
|
|
221
|
+
Claude CLI has no compensator of its own. (The bracket labels like
|
|
222
|
+
`[compensating: Grok-equivalent]` you'll see are the *manual fallback* summary
|
|
223
|
+
wording, distinct from the engine's `compensating-<channel>` source name.)
|
|
224
|
+
- **Auth recovery is never silent.** The workflow surfaces the exact recovery
|
|
225
|
+
command for the failed channel; the channel's `recovery` string drives this
|
|
226
|
+
:cite[packages/mmr/src/core/auth.ts:65]. See the
|
|
227
|
+
[MMR reference](../mmr/index.md) for the per-channel auth-check + recovery
|
|
228
|
+
table.
|
|
229
|
+
|
|
230
|
+
:::callout{type=warning}
|
|
231
|
+
**Foreground only.** When the `mmr` CLI is unavailable and a wrapper falls back
|
|
232
|
+
to invoking Codex / Gemini / Claude / Grok directly, run them as **foreground**
|
|
233
|
+
Bash calls — never with `run_in_background`, `&`, or `nohup`. Background
|
|
234
|
+
execution produces empty output, which the parser then reads as a degraded
|
|
235
|
+
channel :cite[content/tools/review-code.md:265].
|
|
236
|
+
:::
|
|
237
|
+
|
|
238
|
+
Once any channel was compensated, the best possible verdict is
|
|
239
|
+
`degraded-pass` — full `pass` requires all channels to have completed for real.
|
|
240
|
+
|
|
241
|
+
## See also
|
|
242
|
+
|
|
243
|
+
- [MMR reference](../mmr/index.md){mode=advisory} — channels, reconciliation,
|
|
244
|
+
the `finding_key`, verdict internals, and `.mmr.yaml`.
|
|
245
|
+
- The wrapper meta-prompts themselves:
|
|
246
|
+
:cite[content/tools/review-pr.md:15]{mode=advisory},
|
|
247
|
+
:cite[content/tools/review-code.md:16]{mode=advisory}, and
|
|
248
|
+
:cite[content/tools/post-implementation-review.md:16]{mode=advisory}.
|
package/dist/guides/build.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/guides/build.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/guides/build.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAG3C,wBAAgB,eAAe,IAAI,MAAM,CAexC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CACpD;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAwBpF;AAED,wBAAsB,cAAc,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQxE"}
|
package/dist/guides/build.js
CHANGED
|
@@ -3,17 +3,25 @@ import path from 'node:path';
|
|
|
3
3
|
import { atomicWriteFile, getPackageRoot, getPackageGuidesDir } from '../utils/fs.js';
|
|
4
4
|
import { buildGuidesIndex, extractGuideFrontmatter } from './loader.js';
|
|
5
5
|
import { renderGuideBody } from './render.js';
|
|
6
|
-
import { remarkCallout, remarkTabs, remarkFilterTable, remarkChart, remarkSev } from './directives.js';
|
|
6
|
+
import { remarkCallout, remarkTabs, remarkFilterTable, remarkChart, remarkSev, remarkCite } from './directives.js';
|
|
7
7
|
import { remarkMermaid, pruneDiagrams } from './mermaid.js';
|
|
8
8
|
import { wrapInChrome } from './template.js';
|
|
9
9
|
import { renderIndexPage } from './index-page.js';
|
|
10
10
|
import { lintGuide } from './lint.js';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
import { findBrokenRelativeLinks } from './links.js';
|
|
12
|
+
export function loadGuideStyles() {
|
|
13
|
+
// The guide stylesheet is the design tokens (dashboard-theme.css) followed by
|
|
14
|
+
// the guide-specific layout + component styles (guides.css). Both are inlined
|
|
15
|
+
// into each guide's <style> so the output stays self-contained.
|
|
16
|
+
const dir = path.join(getPackageRoot(), 'dist', 'guides');
|
|
17
|
+
const parts = ['dashboard-theme.css', 'guides.css'].map((name) => {
|
|
18
|
+
const p = path.join(dir, name);
|
|
19
|
+
if (!fs.existsSync(p)) {
|
|
20
|
+
throw new Error(`Missing ${p} — run \`npm run build\` (the build copies lib/${name} into dist/guides/).`);
|
|
21
|
+
}
|
|
22
|
+
return fs.readFileSync(p, 'utf8');
|
|
23
|
+
});
|
|
24
|
+
return parts.join('\n');
|
|
17
25
|
}
|
|
18
26
|
export async function buildGuide(args) {
|
|
19
27
|
const md = fs.readFileSync(path.join(args.guideDir, 'index.md'), 'utf8');
|
|
@@ -23,13 +31,17 @@ export async function buildGuide(args) {
|
|
|
23
31
|
}
|
|
24
32
|
for (const w of lint.warnings)
|
|
25
33
|
process.stderr.write(`warning: ${w}\n`);
|
|
34
|
+
const brokenLinks = findBrokenRelativeLinks(md, args.guideDir);
|
|
35
|
+
if (brokenLinks.length) {
|
|
36
|
+
throw new Error(`guide has broken relative link(s):\n ${brokenLinks.join('\n ')}`);
|
|
37
|
+
}
|
|
26
38
|
const fm = extractGuideFrontmatter(md);
|
|
27
39
|
if (!fm)
|
|
28
40
|
throw new Error(`invalid or missing frontmatter in ${path.join(args.guideDir, 'index.md')}`);
|
|
29
41
|
const diagramIds = [];
|
|
30
42
|
const { body, headings } = await renderGuideBody(md, {
|
|
31
43
|
plugins: [
|
|
32
|
-
remarkCallout, remarkTabs, remarkFilterTable, remarkChart, remarkSev,
|
|
44
|
+
remarkCallout, remarkTabs, remarkFilterTable, remarkChart, remarkSev, remarkCite,
|
|
33
45
|
remarkMermaid({ guideDir: args.guideDir, render: args.mermaidRender, collect: diagramIds }),
|
|
34
46
|
],
|
|
35
47
|
});
|
|
@@ -39,7 +51,7 @@ export async function buildGuide(args) {
|
|
|
39
51
|
return { lint };
|
|
40
52
|
}
|
|
41
53
|
export async function buildAllGuides(projectRoot) {
|
|
42
|
-
const css =
|
|
54
|
+
const css = loadGuideStyles();
|
|
43
55
|
const guidesDir = getPackageGuidesDir(projectRoot);
|
|
44
56
|
const index = buildGuidesIndex(guidesDir);
|
|
45
57
|
for (const entry of index.values()) {
|
package/dist/guides/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/guides/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACrF,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/guides/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACrF,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAClH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAEpD,MAAM,UAAU,eAAe;IAC7B,8EAA8E;IAC9E,8EAA8E;IAC9E,gEAAgE;IAChE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;IACzD,MAAM,KAAK,GAAG,CAAC,qBAAqB,EAAE,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC/D,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CACb,WAAW,CAAC,kDAAkD,IAAI,sBAAsB,CACzF,CAAA;QACH,CAAC;QACD,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAoB;IACnD,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,CAAA;IACxE,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC,CAAA;IAC1B,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACtE,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;IACtE,MAAM,WAAW,GAAG,uBAAuB,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC9D,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yCAAyC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACtF,CAAC;IACD,MAAM,EAAE,GAAG,uBAAuB,CAAC,EAAE,CAAC,CAAA;IACtC,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;IACrG,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,eAAe,CAAC,EAAE,EAAE;QACnD,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU;YAChF,aAAa,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;SAC5F;KACF,CAAC,CAAA;IACF,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;IACxC,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAC7E,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,CAAA;IAC7D,OAAO,EAAE,IAAI,EAAE,CAAA;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,WAAoB;IACvD,MAAM,GAAG,GAAG,eAAe,EAAE,CAAA;IAC7B,MAAM,SAAS,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAA;IAClD,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAA;IACzC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QACnC,MAAM,UAAU,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;IAChD,CAAC;IACD,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,eAAe,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;AAChG,CAAC"}
|
|
@@ -44,6 +44,14 @@ describe('buildGuide', () => {
|
|
|
44
44
|
await expect(buildGuide({ guideDir: dir, css: CSS, mermaidRender: async () => '' }))
|
|
45
45
|
.rejects.toThrow(/text-equivalent/i);
|
|
46
46
|
});
|
|
47
|
+
it('throws when a relative link target does not resolve', async () => {
|
|
48
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gb-'));
|
|
49
|
+
const dir = path.join(root, 'mmr');
|
|
50
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
51
|
+
fs.writeFileSync(path.join(dir, 'index.md'), GUIDE_MD + '\nSee [concepts](../concepts/index.md).\n');
|
|
52
|
+
await expect(buildGuide({ guideDir: dir, css: CSS, mermaidRender: async () => '' }))
|
|
53
|
+
.rejects.toThrow(/broken relative link/i);
|
|
54
|
+
});
|
|
47
55
|
});
|
|
48
56
|
describe('renderIndexPage', () => {
|
|
49
57
|
it('lists guides by order with links', () => {
|
|
@@ -58,6 +66,45 @@ describe('renderIndexPage', () => {
|
|
|
58
66
|
expect(html).toContain('MMR');
|
|
59
67
|
expect(html).toContain('review');
|
|
60
68
|
});
|
|
69
|
+
it('groups guides into ordered category card sections', () => {
|
|
70
|
+
const html = renderIndexPage([
|
|
71
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
72
|
+
({ topic: 'cli', frontmatter: {
|
|
73
|
+
title: 'CLI', topic: 'cli', description: 'commands', category: 'reference', order: 20,
|
|
74
|
+
} }),
|
|
75
|
+
({ topic: 'pipeline', frontmatter: {
|
|
76
|
+
title: 'Pipeline', topic: 'pipeline', description: 'the pipeline', category: 'concepts', order: 10,
|
|
77
|
+
} }),
|
|
78
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
79
|
+
], CSS);
|
|
80
|
+
// category section headings (ids derived by catId) + labels
|
|
81
|
+
expect(html).toContain('id="cat-concepts"');
|
|
82
|
+
expect(html).toContain('id="cat-reference"');
|
|
83
|
+
expect(html).toContain('>Concepts<');
|
|
84
|
+
expect(html).toContain('>Reference<');
|
|
85
|
+
// card markup, not a bare list
|
|
86
|
+
expect(html).toContain('class="guide-card" href="pipeline/index.html"');
|
|
87
|
+
expect(html).toContain('class="guide-card-title"');
|
|
88
|
+
// CATEGORY_ORDER puts concepts before reference regardless of input order
|
|
89
|
+
expect(html.indexOf('cat-concepts')).toBeLessThan(html.indexOf('cat-reference'));
|
|
90
|
+
});
|
|
91
|
+
it('appends unknown categories after the known ones with a capitalized label', () => {
|
|
92
|
+
const html = renderIndexPage([
|
|
93
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
94
|
+
({ topic: 'odd', frontmatter: {
|
|
95
|
+
title: 'Odd', topic: 'odd', description: 'misc', category: 'experimental', order: 5,
|
|
96
|
+
} }),
|
|
97
|
+
({ topic: 'cli', frontmatter: {
|
|
98
|
+
title: 'CLI', topic: 'cli', description: 'commands', category: 'reference', order: 20,
|
|
99
|
+
} }),
|
|
100
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
101
|
+
], CSS);
|
|
102
|
+
// Unknown 'experimental' category gets a capitalized label and an id…
|
|
103
|
+
expect(html).toContain('id="cat-experimental"');
|
|
104
|
+
expect(html).toContain('>Experimental<');
|
|
105
|
+
// …and sorts AFTER the known 'reference' category despite a lower order
|
|
106
|
+
expect(html.indexOf('cat-reference')).toBeLessThan(html.indexOf('cat-experimental'));
|
|
107
|
+
});
|
|
61
108
|
it('escapes < and & in frontmatter fields', () => {
|
|
62
109
|
const html = renderIndexPage([
|
|
63
110
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.test.js","sourceRoot":"","sources":["../../src/guides/build.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"build.test.js","sourceRoot":"","sources":["../../src/guides/build.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../src/guides/chrome.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAAE,MAGqD,CAAA;AAEjF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../src/guides/chrome.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAAE,MAGqD,CAAA;AAEjF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,EAAE,MAmKlB,CAAA"}
|
package/dist/guides/chrome.js
CHANGED
|
@@ -39,12 +39,69 @@ export const CHROME_JS = /* js */ `(function(){
|
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
// ─── Mobile nav
|
|
42
|
+
// ─── Mobile nav (drawer + backdrop; aria-expanded + Escape-to-close) ──────
|
|
43
|
+
function setNav(open) {
|
|
44
|
+
var rail = document.querySelector('.rail');
|
|
45
|
+
if (rail) rail.classList.toggle('open', open);
|
|
46
|
+
var toggle = document.querySelector('.nav-toggle');
|
|
47
|
+
if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
48
|
+
// Modal-drawer focus containment: while open, make the page content inert
|
|
49
|
+
// (out of tab order + a11y tree) and move focus into the drawer; on close,
|
|
50
|
+
// restore content and return focus to the toggle.
|
|
51
|
+
var main = document.querySelector('.content');
|
|
52
|
+
if (main) main.inert = open;
|
|
53
|
+
if (open) {
|
|
54
|
+
var first = rail && rail.querySelector('a, button, [tabindex]:not([tabindex="-1"])');
|
|
55
|
+
if (first) first.focus();
|
|
56
|
+
else if (rail) { rail.setAttribute('tabindex', '-1'); rail.focus(); }
|
|
57
|
+
} else if (toggle) {
|
|
58
|
+
toggle.focus();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// If the viewport grows past the mobile breakpoint while the drawer is open,
|
|
62
|
+
// the rail becomes the desktop sidebar and the toggle hides — clear the open
|
|
63
|
+
// state so .content doesn't stay inert with no way to close it.
|
|
64
|
+
if (window.matchMedia) {
|
|
65
|
+
var mq = window.matchMedia('(max-width: 860px)');
|
|
66
|
+
var onMq = function() {
|
|
67
|
+
if (mq.matches) return;
|
|
68
|
+
var rail = document.querySelector('.rail');
|
|
69
|
+
if (rail) rail.classList.remove('open');
|
|
70
|
+
var toggle = document.querySelector('.nav-toggle');
|
|
71
|
+
if (toggle) toggle.setAttribute('aria-expanded', 'false');
|
|
72
|
+
var main = document.querySelector('.content');
|
|
73
|
+
if (main) main.inert = false;
|
|
74
|
+
};
|
|
75
|
+
if (mq.addEventListener) mq.addEventListener('change', onMq);
|
|
76
|
+
else if (mq.addListener) mq.addListener(onMq);
|
|
77
|
+
}
|
|
43
78
|
document.querySelectorAll('[data-action="nav"]').forEach(function(btn) {
|
|
44
79
|
btn.addEventListener('click', function() {
|
|
45
80
|
var rail = document.querySelector('.rail');
|
|
46
|
-
|
|
81
|
+
setNav(!(rail && rail.classList.contains('open')));
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
// Selecting a TOC link closes the drawer (so the now-active content isn't
|
|
85
|
+
// left inert behind the panel) before the anchor navigation scrolls.
|
|
86
|
+
var drawerRail = document.querySelector('.rail');
|
|
87
|
+
if (drawerRail) {
|
|
88
|
+
drawerRail.querySelectorAll('a').forEach(function(a) {
|
|
89
|
+
a.addEventListener('click', function() {
|
|
90
|
+
if (drawerRail.classList.contains('open')) setNav(false);
|
|
91
|
+
});
|
|
47
92
|
});
|
|
93
|
+
}
|
|
94
|
+
document.addEventListener('keydown', function(e) {
|
|
95
|
+
var rail = document.querySelector('.rail');
|
|
96
|
+
if (!rail || !rail.classList.contains('open')) return;
|
|
97
|
+
if (e.key === 'Escape') { setNav(false); return; } // setNav restores focus to the toggle
|
|
98
|
+
// Trap Tab within the open drawer (modal pattern).
|
|
99
|
+
if (e.key !== 'Tab') return;
|
|
100
|
+
var f = rail.querySelectorAll('a[href], button, [tabindex]:not([tabindex="-1"])');
|
|
101
|
+
if (!f.length) return;
|
|
102
|
+
var first = f[0], last = f[f.length - 1];
|
|
103
|
+
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
|
|
104
|
+
else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
|
|
48
105
|
});
|
|
49
106
|
|
|
50
107
|
// ─── Copy buttons ─────────────────────────────────────────────────────────
|
|
@@ -71,17 +128,31 @@ export const CHROME_JS = /* js */ `(function(){
|
|
|
71
128
|
wrapper.insertBefore(btn, pre);
|
|
72
129
|
});
|
|
73
130
|
|
|
74
|
-
// ─── Tabs
|
|
131
|
+
// ─── Tabs (ARIA pattern: aria-selected + roving tabindex + arrow keys) ────
|
|
132
|
+
function activateTab(group, btn, focus) {
|
|
133
|
+
var idx = btn.getAttribute('data-tab');
|
|
134
|
+
group.querySelectorAll('.tab-btn').forEach(function(b) {
|
|
135
|
+
var on = b === btn;
|
|
136
|
+
b.classList.toggle('active', on);
|
|
137
|
+
b.setAttribute('aria-selected', on ? 'true' : 'false');
|
|
138
|
+
b.setAttribute('tabindex', on ? '0' : '-1');
|
|
139
|
+
});
|
|
140
|
+
group.querySelectorAll('.tabpane').forEach(function(pane) {
|
|
141
|
+
pane.classList.toggle('active', pane.getAttribute('data-tab') === idx);
|
|
142
|
+
});
|
|
143
|
+
if (focus) btn.focus();
|
|
144
|
+
}
|
|
75
145
|
document.querySelectorAll('.tabs').forEach(function(group) {
|
|
76
|
-
group.querySelectorAll('.tab-btn')
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
146
|
+
var btns = [].slice.call(group.querySelectorAll('.tab-btn'));
|
|
147
|
+
btns.forEach(function(btn, i) {
|
|
148
|
+
btn.addEventListener('click', function() { activateTab(group, btn, false); });
|
|
149
|
+
btn.addEventListener('keydown', function(e) {
|
|
150
|
+
var ni = -1;
|
|
151
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') ni = (i + 1) % btns.length;
|
|
152
|
+
else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') ni = (i - 1 + btns.length) % btns.length;
|
|
153
|
+
else if (e.key === 'Home') ni = 0;
|
|
154
|
+
else if (e.key === 'End') ni = btns.length - 1;
|
|
155
|
+
if (ni >= 0) { e.preventDefault(); activateTab(group, btns[ni], true); }
|
|
85
156
|
});
|
|
86
157
|
});
|
|
87
158
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/guides/chrome.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,8DAA8D;IAC9D,4FAA4F;IAC5F,+EAA+E,CAAA;AAEjF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,SAAS,GAAW,QAAQ,CAAC
|
|
1
|
+
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/guides/chrome.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,8DAA8D;IAC9D,4FAA4F;IAC5F,+EAA+E,CAAA;AAEjF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,SAAS,GAAW,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAmKpC,CAAA"}
|
|
@@ -41,9 +41,13 @@
|
|
|
41
41
|
--yellow: #d97706;
|
|
42
42
|
--yellow-bg: #fffbeb;
|
|
43
43
|
--yellow-border:#fde68a;
|
|
44
|
+
--red: #dc2626;
|
|
45
|
+
--red-bg: #fef2f2;
|
|
46
|
+
--red-border: #fecaca;
|
|
44
47
|
--gray: #9ca3af;
|
|
45
48
|
--gray-bg: #f3f4f6;
|
|
46
49
|
--gray-border: #e5e7eb;
|
|
50
|
+
--scrim: rgba(15, 17, 23, 0.45);
|
|
47
51
|
|
|
48
52
|
/* Semantic: Next Banner */
|
|
49
53
|
--next-bg: #eef2ff;
|
|
@@ -126,9 +130,13 @@
|
|
|
126
130
|
--yellow: #fbbf24;
|
|
127
131
|
--yellow-bg: rgba(120, 53, 15, 0.25);
|
|
128
132
|
--yellow-border:rgba(251, 191, 36, 0.20);
|
|
133
|
+
--red: #f87171;
|
|
134
|
+
--red-bg: rgba(127, 29, 29, 0.25);
|
|
135
|
+
--red-border: rgba(248, 113, 113, 0.22);
|
|
129
136
|
--gray: #6b7294;
|
|
130
137
|
--gray-bg: #252940;
|
|
131
138
|
--gray-border: #363c58;
|
|
139
|
+
--scrim: rgba(0, 0, 0, 0.6);
|
|
132
140
|
|
|
133
141
|
/* Semantic: Next Banner */
|
|
134
142
|
--next-bg: rgba(30, 27, 75, 0.50);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directives-cite.test.d.ts","sourceRoot":"","sources":["../../src/guides/directives-cite.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { renderGuideBody } from './render.js';
|
|
3
|
+
import { remarkCite } from './directives.js';
|
|
4
|
+
describe('remarkCite', () => {
|
|
5
|
+
it('renders a blocking citation as an fp span the citation checker matches', async () => {
|
|
6
|
+
const { body } = await renderGuideBody('See :cite[src/foo.ts:42] for details.\n', {
|
|
7
|
+
plugins: [remarkCite],
|
|
8
|
+
});
|
|
9
|
+
expect(body).toContain('class="fp"');
|
|
10
|
+
expect(body).toContain('data-path="src/foo.ts:42"');
|
|
11
|
+
expect(body).toContain('>src/foo.ts:42<');
|
|
12
|
+
});
|
|
13
|
+
it('preserves a line range in data-path', async () => {
|
|
14
|
+
const { body } = await renderGuideBody(':cite[src/bar.ts:10-20]\n', { plugins: [remarkCite] });
|
|
15
|
+
expect(body).toContain('data-path="src/bar.ts:10-20"');
|
|
16
|
+
});
|
|
17
|
+
it('renders an advisory citation without the fp class so the gate does not block on it', async () => {
|
|
18
|
+
const { body } = await renderGuideBody(':cite[docs/x.md:3]{mode=advisory}\n', {
|
|
19
|
+
plugins: [remarkCite],
|
|
20
|
+
});
|
|
21
|
+
expect(body).toContain('class="cite-advisory"');
|
|
22
|
+
expect(body).toContain('data-path="docs/x.md:3"');
|
|
23
|
+
expect(body).not.toContain('class="fp"');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=directives-cite.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directives-cite.test.js","sourceRoot":"","sources":["../../src/guides/directives-cite.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,eAAe,CAAC,yCAAyC,EAAE;YAChF,OAAO,EAAE,CAAC,UAAU,CAAC;SACtB,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAA;QACnD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,eAAe,CAAC,2BAA2B,EAAE,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;QAC9F,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oFAAoF,EAAE,KAAK,IAAI,EAAE;QAClG,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,eAAe,CAAC,qCAAqC,EAAE;YAC5E,OAAO,EAAE,CAAC,UAAU,CAAC;SACtB,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAA;QAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAA;QACjD,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;IAC1C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|