@suzumiyaaoba/mdxr 0.1.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 (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/dist/cli.d.mts +1 -0
  4. package/dist/cli.mjs +2902 -0
  5. package/dist/components.d.mts +791 -0
  6. package/dist/components.mjs +2 -0
  7. package/dist/config-SI9IyFiC.mjs +184 -0
  8. package/dist/doc-context-CEqzMYKv.d.mts +568 -0
  9. package/dist/index.d.mts +20 -0
  10. package/dist/index.mjs +4 -0
  11. package/dist/ui-GKFD0mx5.mjs +15986 -0
  12. package/package.json +131 -0
  13. package/skill/SKILL.md +49 -0
  14. package/skill/references/components/charts.md +172 -0
  15. package/skill/references/components/document.md +98 -0
  16. package/skill/references/components/forms.md +35 -0
  17. package/skill/references/components/investigation.md +198 -0
  18. package/skill/references/components/layout.md +68 -0
  19. package/skill/references/components/output.md +158 -0
  20. package/skill/references/components/planning.md +155 -0
  21. package/skill/references/components/reports.md +252 -0
  22. package/skill/references/components/shadcn.md +19 -0
  23. package/skill/references/components.md +186 -0
  24. package/skill/references/extending.md +48 -0
  25. package/src/ask-sheet.ts +40 -0
  26. package/src/assets/css.ts +392 -0
  27. package/src/assets/scripts.ts +78 -0
  28. package/src/catalog.ts +287 -0
  29. package/src/cli.ts +178 -0
  30. package/src/client/doc-events.ts +1108 -0
  31. package/src/client/entry.ts +27 -0
  32. package/src/client-js.ts +47 -0
  33. package/src/component-map.ts +47 -0
  34. package/src/components/ui/accordion.tsx +77 -0
  35. package/src/components/ui/alert-dialog.tsx +185 -0
  36. package/src/components/ui/alert.tsx +76 -0
  37. package/src/components/ui/aspect-ratio.tsx +22 -0
  38. package/src/components/ui/attachment.tsx +208 -0
  39. package/src/components/ui/avatar.tsx +106 -0
  40. package/src/components/ui/badge.tsx +52 -0
  41. package/src/components/ui/breadcrumb.tsx +121 -0
  42. package/src/components/ui/bubble.tsx +128 -0
  43. package/src/components/ui/button-group.tsx +88 -0
  44. package/src/components/ui/button.tsx +58 -0
  45. package/src/components/ui/calendar.tsx +226 -0
  46. package/src/components/ui/card.tsx +102 -0
  47. package/src/components/ui/carousel.tsx +246 -0
  48. package/src/components/ui/chart.tsx +379 -0
  49. package/src/components/ui/checkbox.tsx +27 -0
  50. package/src/components/ui/collapsible.tsx +19 -0
  51. package/src/components/ui/combobox.tsx +298 -0
  52. package/src/components/ui/command.tsx +193 -0
  53. package/src/components/ui/context-menu.tsx +271 -0
  54. package/src/components/ui/dialog.tsx +159 -0
  55. package/src/components/ui/direction.tsx +4 -0
  56. package/src/components/ui/drawer.tsx +227 -0
  57. package/src/components/ui/dropdown-menu.tsx +269 -0
  58. package/src/components/ui/empty.tsx +104 -0
  59. package/src/components/ui/field.tsx +237 -0
  60. package/src/components/ui/fieldset.tsx +32 -0
  61. package/src/components/ui/frame.tsx +87 -0
  62. package/src/components/ui/hover-card.tsx +50 -0
  63. package/src/components/ui/input-group.tsx +159 -0
  64. package/src/components/ui/input-otp.tsx +83 -0
  65. package/src/components/ui/input.tsx +19 -0
  66. package/src/components/ui/item.tsx +202 -0
  67. package/src/components/ui/kbd.tsx +26 -0
  68. package/src/components/ui/label.tsx +19 -0
  69. package/src/components/ui/marker.tsx +71 -0
  70. package/src/components/ui/menubar.tsx +284 -0
  71. package/src/components/ui/message-scroller.tsx +128 -0
  72. package/src/components/ui/message.tsx +91 -0
  73. package/src/components/ui/meter.tsx +80 -0
  74. package/src/components/ui/native-select.tsx +64 -0
  75. package/src/components/ui/navigation-menu.tsx +170 -0
  76. package/src/components/ui/pagination.tsx +133 -0
  77. package/src/components/ui/popover.tsx +87 -0
  78. package/src/components/ui/progress.tsx +82 -0
  79. package/src/components/ui/questionnaire.tsx +328 -0
  80. package/src/components/ui/radio-group.tsx +35 -0
  81. package/src/components/ui/resizable.tsx +49 -0
  82. package/src/components/ui/scroll-area.tsx +50 -0
  83. package/src/components/ui/select.tsx +201 -0
  84. package/src/components/ui/separator.tsx +22 -0
  85. package/src/components/ui/sheet.tsx +135 -0
  86. package/src/components/ui/sidebar.tsx +730 -0
  87. package/src/components/ui/skeleton.tsx +13 -0
  88. package/src/components/ui/slider.tsx +51 -0
  89. package/src/components/ui/spinner.tsx +16 -0
  90. package/src/components/ui/switch.tsx +31 -0
  91. package/src/components/ui/table.tsx +113 -0
  92. package/src/components/ui/tabs.tsx +82 -0
  93. package/src/components/ui/textarea.tsx +17 -0
  94. package/src/components/ui/toast.tsx +229 -0
  95. package/src/components/ui/toggle-group.tsx +87 -0
  96. package/src/components/ui/toggle.tsx +43 -0
  97. package/src/components/ui/tooltip.tsx +65 -0
  98. package/src/components.ts +78 -0
  99. package/src/config.ts +56 -0
  100. package/src/define.ts +122 -0
  101. package/src/doc-context.ts +20 -0
  102. package/src/editor.ts +94 -0
  103. package/src/format-error.ts +78 -0
  104. package/src/guards.ts +84 -0
  105. package/src/hooks/use-mobile.ts +21 -0
  106. package/src/html.ts +91 -0
  107. package/src/hydrate/export-index.ts +232 -0
  108. package/src/hydrate/import-scan.ts +169 -0
  109. package/src/hydrate/plugins.ts +118 -0
  110. package/src/hydrate/runtime-module.ts +145 -0
  111. package/src/hydrate-runtime.ts +67 -0
  112. package/src/hydrate.ts +147 -0
  113. package/src/index.ts +16 -0
  114. package/src/init.ts +57 -0
  115. package/src/langs.ts +148 -0
  116. package/src/lines.ts +53 -0
  117. package/src/load-user-module.ts +191 -0
  118. package/src/mdx.ts +248 -0
  119. package/src/paths.ts +36 -0
  120. package/src/rehype/shiki.ts +533 -0
  121. package/src/remark/alerts.ts +53 -0
  122. package/src/remark/ast.ts +96 -0
  123. package/src/remark/callouts.ts +25 -0
  124. package/src/remark/code-file.ts +85 -0
  125. package/src/remark/code-meta.ts +21 -0
  126. package/src/remark/directives.ts +170 -0
  127. package/src/remark/file-paths.ts +64 -0
  128. package/src/remark/headings.ts +131 -0
  129. package/src/remark/no-js.ts +40 -0
  130. package/src/render.ts +337 -0
  131. package/src/serve.ts +322 -0
  132. package/src/styles/globals.css +134 -0
  133. package/src/styles/shadcn.css +641 -0
  134. package/src/tailwind.ts +119 -0
  135. package/src/ui/approvals.tsx +76 -0
  136. package/src/ui/ask-question.tsx +386 -0
  137. package/src/ui/ask.tsx +206 -0
  138. package/src/ui/attrs.ts +51 -0
  139. package/src/ui/audit.tsx +139 -0
  140. package/src/ui/bar-chart.tsx +334 -0
  141. package/src/ui/benchmarks.tsx +143 -0
  142. package/src/ui/bits.tsx +537 -0
  143. package/src/ui/board.tsx +173 -0
  144. package/src/ui/bridge.tsx +207 -0
  145. package/src/ui/bumps.tsx +178 -0
  146. package/src/ui/callout.tsx +106 -0
  147. package/src/ui/changes.tsx +89 -0
  148. package/src/ui/chart-bits.tsx +52 -0
  149. package/src/ui/chart.ts +577 -0
  150. package/src/ui/checks.tsx +203 -0
  151. package/src/ui/child-index.tsx +44 -0
  152. package/src/ui/children.ts +43 -0
  153. package/src/ui/chips.tsx +49 -0
  154. package/src/ui/cmd.tsx +27 -0
  155. package/src/ui/columns.tsx +31 -0
  156. package/src/ui/comments.tsx +544 -0
  157. package/src/ui/compare.tsx +67 -0
  158. package/src/ui/decision.tsx +79 -0
  159. package/src/ui/deps.tsx +78 -0
  160. package/src/ui/details.tsx +43 -0
  161. package/src/ui/diff-parse.ts +307 -0
  162. package/src/ui/diff.tsx +458 -0
  163. package/src/ui/diffstat.tsx +58 -0
  164. package/src/ui/due.tsx +65 -0
  165. package/src/ui/effort.tsx +29 -0
  166. package/src/ui/endpoints.tsx +118 -0
  167. package/src/ui/envvars.tsx +96 -0
  168. package/src/ui/figure.tsx +37 -0
  169. package/src/ui/file-icon.ts +1020 -0
  170. package/src/ui/file-link.ts +32 -0
  171. package/src/ui/file-ref.tsx +39 -0
  172. package/src/ui/files.tsx +112 -0
  173. package/src/ui/findings.tsx +85 -0
  174. package/src/ui/flow.tsx +73 -0
  175. package/src/ui/funnel.tsx +121 -0
  176. package/src/ui/gantt.tsx +443 -0
  177. package/src/ui/gauges.tsx +135 -0
  178. package/src/ui/glossary.tsx +29 -0
  179. package/src/ui/graph-layout.ts +149 -0
  180. package/src/ui/graph-specs.tsx +102 -0
  181. package/src/ui/graph.tsx +278 -0
  182. package/src/ui/grid.tsx +119 -0
  183. package/src/ui/hypothesis.tsx +94 -0
  184. package/src/ui/icon.tsx +80 -0
  185. package/src/ui/incident.tsx +130 -0
  186. package/src/ui/index.ts +342 -0
  187. package/src/ui/ins-del.tsx +41 -0
  188. package/src/ui/json.tsx +190 -0
  189. package/src/ui/layout.ts +9 -0
  190. package/src/ui/line-chart.tsx +251 -0
  191. package/src/ui/matrix.tsx +208 -0
  192. package/src/ui/meta.tsx +73 -0
  193. package/src/ui/option.tsx +64 -0
  194. package/src/ui/owner.tsx +40 -0
  195. package/src/ui/packages.tsx +104 -0
  196. package/src/ui/pathway.tsx +96 -0
  197. package/src/ui/phase.tsx +41 -0
  198. package/src/ui/pie-chart.tsx +170 -0
  199. package/src/ui/plan.tsx +77 -0
  200. package/src/ui/pre.tsx +175 -0
  201. package/src/ui/priority.tsx +51 -0
  202. package/src/ui/props.tsx +77 -0
  203. package/src/ui/quadrant.tsx +172 -0
  204. package/src/ui/radar.tsx +200 -0
  205. package/src/ui/ref.tsx +160 -0
  206. package/src/ui/release.tsx +178 -0
  207. package/src/ui/req.tsx +42 -0
  208. package/src/ui/review.tsx +133 -0
  209. package/src/ui/risk.tsx +67 -0
  210. package/src/ui/sankey.tsx +287 -0
  211. package/src/ui/scatter.tsx +237 -0
  212. package/src/ui/schema.tsx +113 -0
  213. package/src/ui/score.tsx +105 -0
  214. package/src/ui/search.tsx +120 -0
  215. package/src/ui/series.tsx +38 -0
  216. package/src/ui/severity.tsx +69 -0
  217. package/src/ui/shadcn.tsx +216 -0
  218. package/src/ui/spark.tsx +86 -0
  219. package/src/ui/stack.tsx +57 -0
  220. package/src/ui/stats.tsx +63 -0
  221. package/src/ui/status-badge.tsx +66 -0
  222. package/src/ui/statuspage.tsx +238 -0
  223. package/src/ui/steps.tsx +74 -0
  224. package/src/ui/summary.tsx +41 -0
  225. package/src/ui/symbol-ref.tsx +73 -0
  226. package/src/ui/terminal.tsx +117 -0
  227. package/src/ui/tests.tsx +218 -0
  228. package/src/ui/timeline.tsx +63 -0
  229. package/src/ui/toc.tsx +56 -0
  230. package/src/ui/tones.ts +187 -0
  231. package/src/ui/trace.tsx +69 -0
  232. package/src/ui/tree.tsx +281 -0
  233. package/src/ui/treemap.tsx +128 -0
  234. package/src/ui/venn.tsx +258 -0
  235. package/src/ui/verdict.tsx +78 -0
  236. package/src/ui/waterfall.tsx +142 -0
@@ -0,0 +1,252 @@
1
+ # Reports — code review, CI checks, audits, metrics, config, ops & releases
2
+
3
+ Index: [../components.md](../components.md). MDX attributes are always strings; `children` is Markdown.
4
+
5
+ This file covers report-shaped deliverables a coding agent produces beyond plans and investigations: review results, verification status, security/dependency scans, quantitative metrics, config documentation, and ops/release summaries.
6
+
7
+ ### `<Review title verdict>` / `<Comment severity title file>` / `:::review`
8
+
9
+ Code-review result. `Review` tallies `<Comment>` children by severity in a summary line and shows an optional `verdict` pill — `approve` (green, "Approved"), `changes` (amber, "Changes requested"), `comment` (sky, "Commented"). `Comment` renders a numbered card: `severity` is `critical|high|medium|low|info` (default `info`), `title` is a bold headline, `file`/`lines`/`href` link to the code location, children are the finding text (a ` ```diff ` fence inside makes a great suggestion block).
10
+
11
+ ```mdx
12
+ <Review title="PR #42 review" verdict="changes">
13
+ <Comment
14
+ severity="high"
15
+ file="src/mdx.ts"
16
+ lines="42-58"
17
+ title="Unbounded recursion"
18
+ >
19
+ The visitor recurses without a depth cap — a hostile doc can overflow the
20
+ stack.
21
+ </Comment>
22
+ <Comment severity="low" title="Naming nit">
23
+ `out` → `result` reads better at the call site.
24
+ </Comment>
25
+ </Review>
26
+ ```
27
+
28
+ ### `<Verdict status title label>` / `:::verdict`
29
+
30
+ Standalone verdict banner — a colored edge panel with a status pill. `status` is `approve`/`pass` (green), `warn` (amber), `fail` (red), `info` (sky); `label` overrides the pill text; children are the rationale. Use at the top or bottom of any report to state the conclusion ("do not merge", "safe to deploy", "migration is reversible").
31
+
32
+ ```mdx
33
+ <Verdict status="warn" title="Deploy with care">
34
+ Schema change ships without a backfill — run it off-peak.
35
+ </Verdict>
36
+ ```
37
+
38
+ ### `<Severity level>`
39
+
40
+ Inline severity pill: `critical|high|medium|low|info`. Reused by `<Comment>`, `<Vuln>` and `<Incident>`; use standalone inside prose or tables.
41
+
42
+ ### `<Checks title context>` / `<Check name status duration required>` / `:::checks`
43
+
44
+ CI/verification status list — the "checks" tab of a PR. `Checks` counts `<Check>` children per status and sums parseable durations in the caption; `context` adds a chip (commit SHA, PR number). `Check` needs `name`; `status` is `pass` (default) / `fail` / `running` / `pending` / `skip`, `duration` shows right-aligned, `required` adds a Required chip, `href` links to the job log. Children render as an indented log excerpt — red-tinted when the check failed.
45
+
46
+ ```mdx
47
+ <Checks title="CI — main" context="a1b2c3d">
48
+ <Check name="lint" status="pass" duration="32s" required />
49
+ <Check name="test" status="fail" duration="4m12s" required>
50
+ AssertionError in render.test.ts
51
+ </Check>
52
+ <Check name="deploy" status="pending" />
53
+ </Checks>
54
+ ```
55
+
56
+ For unit-test runs prefer `<Tests>` (pass/fail/skip/todo per assertion); `Checks` models pipeline stages (lint/build/deploy).
57
+
58
+ ### `<Audit title tool>` / `<Vuln severity id package affected fix>` / `:::audit`
59
+
60
+ Vulnerability/audit report. `Audit` counts `<Vuln>` children per severity in the caption; `tool` adds a scanner chip (`npm audit`, `osv`, `trivy`). `Vuln` renders: severity pill, optional `title`, `id` (CVE/GHSA — `href` links it to the advisory), `package` + `affected` range in mono, `→ fix` version in green, and a `wontfix` badge. Children are the description.
61
+
62
+ ```mdx
63
+ <Audit title="Dependency audit" tool="npm audit">
64
+ <Vuln
65
+ severity="critical"
66
+ id="GHSA-35jh-r3h4-6jhm"
67
+ package="minimatch"
68
+ affected="<3.1.2"
69
+ fix="3.1.2"
70
+ href="https://github.com/advisories/GHSA-35jh-r3h4-6jhm"
71
+ title="ReDoS in brace expansion"
72
+ />
73
+ <Vuln severity="low" package="left-pad" wontfix="true">
74
+ No fix planned upstream.
75
+ </Vuln>
76
+ </Audit>
77
+ ```
78
+
79
+ ### `<Bumps title>` / `<Bump name from to kind breaking cves>` / `:::bumps`
80
+
81
+ Dependency upgrade plan. `Bumps` counts major/minor/patch bumps in the caption. `Bump` needs `name`; `from`/`to` display as `old → new` and the kind (`major`=red, `minor`=amber, `patch`=neutral) is **auto-detected from the versions** — `kind` only overrides. `breaking` adds a warning chip, `cves="2"` shows the CVEs the upgrade fixes, `href` links to the changelog, `note`/children carry the plan detail.
82
+
83
+ ```mdx
84
+ <Bumps title="Dependency upgrades">
85
+ <Bump name="react" from="18.3.1" to="19.3.0" breaking />
86
+ <Bump name="minimatch" from="3.0.9" to="3.1.2" cves="2" note="security" />
87
+ </Bumps>
88
+ ```
89
+
90
+ ### `<Packages title>` / `<Package name version kind license>` / `:::packages`
91
+
92
+ Package inventory — a flat list with a total count in the caption. `Package` needs `name`; `version`, `kind` (`dep`=sky, `dev`=neutral, `peer`=violet, `optional`=amber), `license`, `href` (registry/repo link), `note`/children for the "why it's here".
93
+
94
+ ```mdx
95
+ <Packages title="Direct dependencies">
96
+ <Package name="react" version="19.3.0" kind="dep" license="MIT" />
97
+ <Package name="ultracite" version="7.11.1" kind="dev" />
98
+ </Packages>
99
+ ```
100
+
101
+ ### `<Gauges title unit>` / `<Gauge value target tone label path>` / `:::gauges`
102
+
103
+ Percent-bar list — coverage reports, score breakdowns, progress by area. `Gauge` needs `value` (0–100, or a fraction via `max`). Color is automatic: with `target`, green when met else red; without, graded 80/50 bands — `tone` (`emerald|amber|red|sky|violet|neutral`) pins it. Label via `label` or `path` (a real file becomes an editor link); `detail` adds a muted second line ("412/500 lines").
104
+
105
+ ```mdx
106
+ <Gauges title="Coverage by area" unit="lines">
107
+ <Gauge path="src/mdx.ts" value="82" target="80" detail="412/500 lines" />
108
+ <Gauge path="src/cli.ts" value="45" detail="90/200 lines" />
109
+ </Gauges>
110
+ ```
111
+
112
+ ### `<Score value max label detail>` — 0-100 ring gauge
113
+
114
+ Circular score gauge — health scans, lint scores, quality grades. `value` is required (`max` changes the denominator); 80+ green, 50+ amber, below red. `label` names the metric, `detail` the sub-line. Inline-flex sized — group several in `<Row>` or `<Grid>`.
115
+
116
+ ```mdx
117
+ <Row>
118
+ <Score value="87" label="react-doctor" detail="0 errors, 2 warnings" />
119
+ <Score value="9" max="10" label="a11y audit" />
120
+ </Row>
121
+ ```
122
+
123
+ ### `<Spark values tone label>` — inline sparkline
124
+
125
+ Tiny inline trend line for prose. `values` is a comma/space-separated number list, `tone` a color, `label` the accessible name. Renders nothing with <2 points.
126
+
127
+ ```mdx
128
+ p95 latency improved <Spark values="120,110,96,88,71,65" tone="emerald" /> over 6 runs.
129
+ ```
130
+
131
+ ### `<Benchmarks title better unit>` / `<Bench name before after>` / `:::benchmarks`
132
+
133
+ Before/after measurement table. `Bench` needs `name`; `before`/`after` show as `old → new` and the % delta is computed automatically (`unit` suffixes are display-only). `Benchmarks better="lower"` (for latency/size) flips the delta color: improvement = green trend-up, regression = red trend-down; default is `higher`. `note`/children carry caveats.
134
+
135
+ ```mdx
136
+ <Benchmarks title="render bench" better="lower" unit="ms">
137
+ <Bench name="small doc" before="120" after="98" />
138
+ <Bench name="large doc" before="820ms" after="910ms" />
139
+ </Benchmarks>
140
+ ```
141
+
142
+ For timing _within_ one operation (span offsets) prefer `<Waterfall>`; `Benchmarks` compares two runs.
143
+
144
+ ### `<DiffStat files adds dels>` — diff size summary
145
+
146
+ The `+N −M across F files` stat from a PR header, as an inline chip with a proportional green/red bar. All attributes are numbers; `files` is optional.
147
+
148
+ ```mdx
149
+ The PR touched <DiffStat files="12" adds="340" dels="120" /> in the renderer.
150
+ ```
151
+
152
+ ### `<Schema title engine>` / `<DbTable name note>` / `<DbField name type pk fk>` / `:::schema`
153
+
154
+ Database schema documentation. `Schema` is a section wrapper (`engine` shows muted, e.g. `postgres 16`). `DbTable` is a bordered block per table (`name` required, `note` for row counts/engine). `DbField` is one column row: `name` + `type` required, `pk`/`fk="table.col"`/`unique`/`null` render constraint chips, `default` shows `= value`; children are the column description.
155
+
156
+ ```mdx
157
+ <Schema title="Data model" engine="postgres 16">
158
+ <DbTable name="users" note="core accounts">
159
+ <DbField name="id" type="uuid" pk />
160
+ <DbField name="email" type="text" unique />
161
+ <DbField name="org_id" type="uuid" fk="orgs.id" null />
162
+ </DbTable>
163
+ </Schema>
164
+ ```
165
+
166
+ ### `<EnvVars title>` / `<EnvVar name required secret value default>` / `:::envvars`
167
+
168
+ Environment-variable reference. `EnvVars` shows a count chip and a copy button that copies all variable names. `EnvVar` needs `name`; `required` adds an amber chip, `value`/`default` display the value (`default:` prefix for defaults), `secret` masks the value as `••••••••` plus a secret chip — never write real secrets, mark them instead. Children are the description.
169
+
170
+ ```mdx
171
+ <EnvVars title="Runtime config">
172
+ <EnvVar name="DATABASE_URL" required secret>
173
+ Primary Postgres connection.
174
+ </EnvVar>
175
+ <EnvVar name="LOG_LEVEL" default="info" />
176
+ </EnvVars>
177
+ ```
178
+
179
+ ### `<StatusPage title updated>` / `<Service name status uptime>` / `:::statuspage`
180
+
181
+ Service-health summary. `StatusPage` rolls up the worst `<Service>` status into the caption ("All systems operational" / Degraded / Outage / Maintenance); `updated` shows a muted timestamp. `Service` needs `name`; `status` is `operational` (default) / `degraded` / `outage` / `maintenance` with a status dot + pill, `uptime` shows right-aligned, children describe the impact.
182
+
183
+ ```mdx
184
+ <StatusPage title="System status" updated="2m ago">
185
+ <Service name="API" status="operational" uptime="99.98%" />
186
+ <Service name="Dashboard" status="degraded">
187
+ Elevated p95 since the 14:00 deploy.
188
+ </Service>
189
+ </StatusPage>
190
+ ```
191
+
192
+ ### `<Uptime title pct from to>` / `<Day status date note>` / `:::uptime`
193
+
194
+ Statuspage-style uptime bar — one cell per day. `Uptime` shows `title`, `pct` (`"99.9"` renders `99.9%`), and `from`/`to` end labels under the bar. `Day` status is `up` (green, default) / `degraded` (amber) / `down` (red) / `maint` (sky) / `none` (gray); `date`/`note` compose the hover tooltip.
195
+
196
+ ```mdx
197
+ <Uptime title="API — last 7 days" pct="99.9" from="Sep 12" to="today">
198
+ <Day status="up" date="Sep 12" />
199
+ <Day status="degraded" date="Sep 13" note="deploy" />
200
+ <Day status="down" date="Sep 14" note="INC-12" />
201
+ </Uptime>
202
+ ```
203
+
204
+ ### `<Release version date href title>` / `<Entry kind scope>` / `:::release`
205
+
206
+ Release notes / changelog block. `Release` needs `version` (shown mono in the caption; `date`, compare `href`, optional `title` prefix). `<Entry>` children are **auto-grouped by kind** into headed sections regardless of write order: `breaking` → `added` → `changed` → `deprecated` → `removed` → `fixed` → `security`, each with an icon and count. `scope` adds a `cli:`-style prefix chip; children are the entry text. Non-Entry children render at the bottom.
207
+
208
+ ```mdx
209
+ <Release
210
+ version="v0.2.0"
211
+ date="2026-09-19"
212
+ href="https://github.com/acme/app/compare/v0.1.0...v0.2.0"
213
+ >
214
+ <Entry kind="breaking" scope="cli">
215
+ Renamed `--html` to `--format`.
216
+ </Entry>
217
+ <Entry kind="added">New report components.</Entry>
218
+ <Entry kind="fixed">Stdin piping no longer hangs.</Entry>
219
+ </Release>
220
+ ```
221
+
222
+ ### `<Pathway title>` / `<Stop label status note current>` / `:::pathway`
223
+
224
+ Horizontal stepper for migration/rollout paths — version upgrade routes (v17→v18→v19), environment promotion (dev→staging→prod), phased rollouts. `Stop` needs `label`; `status` is `todo|doing|done|blocked` (same icon set as `<StatusBadge>`), `note` a sub-line, `current` rings the active stop. Connectors draw automatically between stops; overflow scrolls.
225
+
226
+ ```mdx
227
+ <Pathway title="Upgrade path">
228
+ <Stop label="v17" status="done" note="current" />
229
+ <Stop label="v18" status="doing" note="codemod" current />
230
+ <Stop label="v19" status="todo" />
231
+ </Pathway>
232
+ ```
233
+
234
+ For dated milestones prefer `<Timeline>`; `Pathway` is for _sequences_ (no dates).
235
+
236
+ ### `<Incident title severity status …>` / `:::incident`
237
+
238
+ Incident/postmortem header block. `title` is required; `severity` (shared scale) and `status` (`investigating`=orange, `identified`=amber, `monitoring`=sky, `resolved`=emerald) render as pills. `started`/`detected`/`resolved`/`duration` become a labeled meta row, `impact` a red highlighted line. Children are the summary; follow with `<Timeline>` for the event sequence and `<Steps>` for action items.
239
+
240
+ ```mdx
241
+ <Incident
242
+ title="Production DB wiped"
243
+ severity="critical"
244
+ status="resolved"
245
+ started="09:12 UTC"
246
+ resolved="09:46 UTC"
247
+ duration="34m"
248
+ impact="production DB + backups deleted"
249
+ >
250
+ An unscoped API token used by the nightly cleanup job had delete permissions.
251
+ </Incident>
252
+ ```
@@ -0,0 +1,19 @@
1
+ # shadcn/ui components (Base UI)
2
+
3
+ Index: [../components.md](../components.md). MDX attributes are always strings; `children` is Markdown.
4
+
5
+ The full shadcn/ui set (Base UI primitives) is registered: `Button`, `Badge`, `Card`/`CardHeader`/…, `Alert`, `Tabs`, `Accordion`, `Dialog`, `Input`, `Label`, `Table`, `Progress`, `Skeleton`, `Separator`, `Kbd`, `Spinner`, and more — run `mdxr catalog` for the complete list. Use them as plain MDX elements; attributes are strings (`variant="outline"`, `size="sm"`).
6
+
7
+ **Note:** rendered documents include a hydration bundle, so stateful primitives (`Dialog`, `Tabs`, `Accordion`, `Tooltip`, `Select`, `Switch`, menus, …) are interactive in the browser — tabs switch, accordions open, switches flip. `mdxr render --no-hydrate` emits purely static HTML where they render their initial state only. Portal-based overlays (`Dialog`, `Tooltip`, `Select`, menus) still render nothing until opened — prefer `Card`, `Alert`, `Badge`, `Table`, `Kbd`, `Separator`, `Progress`, `Skeleton` for always-visible content.
8
+
9
+ ```mdx
10
+ <Alert>
11
+ <AlertTitle>Heads up</AlertTitle>
12
+ <AlertDescription>Fully static and safe in documents.</AlertDescription>
13
+ </Alert>
14
+
15
+ <Badge variant="secondary">beta</Badge>
16
+ <Button variant="outline">Action</Button>
17
+ ```
18
+
19
+ Theme: shadcn CSS variables (`--primary`, `--background`, …) are emitted with the document CSS; `.dark` variants follow `prefers-color-scheme` via a `<html>` class toggle.
@@ -0,0 +1,186 @@
1
+ # mdxr component reference
2
+
3
+ MDX attributes are always strings (`status="done"`). `children` is Markdown.
4
+
5
+ This file is an index: each group lists what its components do and links to a detail file with full signatures and examples. Read only the detail file(s) the document needs. The syntax cheatsheet at the bottom maps Markdown shorthands (`:::x`, fences, frontmatter) to the component they produce. `mdxr catalog --json` is the machine-readable source of truth for component names and attributes.
6
+
7
+ **File links.** Components carrying `path` (`FileRef`, `SymbolRef`, `File`, `TraceFrame`, `FlowStep`, `Change`) and fenced-code filename headers become editor links — `vscode://file/…` by default — when the file exists on disk (paths resolve relative to the document). Frontmatter `editor:` or `editor` in `mdxr.config.ts` picks another editor: `cursor`, `zed`, `vscode-insiders`, `windsurf`, `sublime`, `textmate`, `idea`, a custom `{path}`/`{line}` URL template, or `none` to disable. `href="…"` on a component overrides the URL entirely. Inline code works too: `` `src/mdx.ts` `` (optional `:40-52` lines suffix) auto-converts to `<FileRef>` when it resolves to a real file — a bare `mdx.ts` without a `/` stays plain code.
8
+
9
+ ## Document scaffolding — details: [components/document.md](components/document.md)
10
+
11
+ | Component | What it is |
12
+ | --- | --- |
13
+ | `<Plan>` | Document root — title header, status badge, meta row (auto-built from frontmatter) |
14
+ | `<Meta>` / `<MetaItem>` | Metadata row (`Date · Owner · …`); custom items with an icon |
15
+ | `<Callout>` | Highlighted block — `:::note`/`:::warning`/`:::goal`/`:::decision`/…, `> [!NOTE]` |
16
+ | `<Details>` | Collapsible section on native `<details>` (works without JS) |
17
+ | `<Toc>` | Collapsible, auto-built table of contents — `:::toc` |
18
+ | `<Glossary>` / `<Term>` | Definition list for domain terms |
19
+ | `<Figure>` | Image with a caption |
20
+ | `<Ref>` / `<Issue>` / `<PR>` / `<Commit>` | Linked reference card / inline GitHub chips |
21
+ | `<Cmd>` | Inline command chip with copy button |
22
+ | `<Icon>` | Inline Iconify SVG — `lucide` + `vscode-icons` bundled, no runtime fetch |
23
+ | fenced code / math | Highlighted code block with filename bar and line markers; ` ```mermaid ` diagrams; KaTeX `$…$` |
24
+
25
+ ## Planning & status — details: [components/planning.md](components/planning.md)
26
+
27
+ | Component | What it is |
28
+ | --- | --- |
29
+ | `<Phase>` | Section heading with a status badge — `:::phase` |
30
+ | `<Steps>` / `<Step>` | Status-aware task list with optional progress bar; owner/effort/priority/due chips |
31
+ | `<Timeline>` / `<Event>` | Dated milestone rail — `:::timeline` |
32
+ | `<Gantt>` / `<Task>` / `<Milestone>` | Date-based schedule chart — `:::gantt` |
33
+ | `<Decision>` | ADR-lite decision record (one-liner → `:::decision` callout) |
34
+ | `<Option>` | Alternative-comparison card (recommended/considered/rejected) |
35
+ | `<Risk>` | Risk block — severity pill + mitigation line |
36
+ | `<Approvals>` / `<Approval>` | Sign-off list |
37
+ | `<Stats>` / `<Stat>` | Metric card grid; `delta` colored by sign |
38
+ | `<Priority>` `<Effort>` `<Due>` `<Owner>` | Inline chips (priority, T-shirt effort, deadline, person) |
39
+ | `<Reqs>` / `<Req>` | Requirement / acceptance-criteria rows |
40
+ | `<Board>` / `<Lane>` / `<BoardCard>` | Interactive kanban — lanes with status dots and card counts; cards drag between lanes, "Copy markdown" copies the updated `<Board>` markup — `:::board` |
41
+ | `<Matrix>` | Comparison grid — nested list rows, `yes`/`no`/`partial`/`✓`/`✗`/`△` cells render as icons — `:::matrix` |
42
+ | `<Summary>` | Progress bar |
43
+ | `<StatusBadge>` | Standalone status pill |
44
+
45
+ ## Code investigation — details: [components/investigation.md](components/investigation.md)
46
+
47
+ | Component | What it is |
48
+ | --- | --- |
49
+ | `<Findings>` / `<Finding>` | Numbered findings with confidence pills — `:::findings` |
50
+ | `<Hypotheses>` / `<Hypothesis>` | Hypothesis ledger (supported/refuted/untested) — `:::hypotheses` |
51
+ | `<Terminal>` | Command transcript (`$` prompts, output, exit badge) — `:::terminal`, ` ```console ` |
52
+ | `<Trace>` / `<TraceFrame>` | Stack/call trace with error line, dimmed lib frames — `:::trace` |
53
+ | `<Searches>` / `<Search>` | Search-query log (pattern/scope/tool/hits) — `:::searches` |
54
+ | `<Files>` / `<File>` | Related-file inventory — `:::files` |
55
+ | `<Deps>` / `<Dep>` | Dependency-edge list — `:::deps` |
56
+ | `<Changes>` / `<Change>` | Change-set list (add/modify/delete/rename) |
57
+ | `<Flow>` / `<FlowStep>` | Numbered call/execution chain — `:::flow` |
58
+ | `<Tree>` | File tree from a nested list — collapsible folders, `…` placeholders, bold highlights, automatic icons |
59
+ | `<FileRef>` | Inline file-reference chip with copy button |
60
+ | `<SymbolRef>` | Inline symbol chip (fn/type/class/…) |
61
+ | `<CodeFile>` | Embeds a real file from disk as a code block |
62
+ | `<Props>` / `<Prop>` | API/props table for a component or function |
63
+
64
+ ## Output artifacts — details: [components/output.md](components/output.md)
65
+
66
+ | Component | What it is |
67
+ | --- | --- |
68
+ | ` ```diff ` / ` ```patch ` fence | Structured per-file diff cards — editor links, `+N −M` stats, hunk line numbers |
69
+ | `<Comments>` / `<Comment>` | GitHub-style comment threads anchored to code/diff lines — `lines`/`side`/`file` anchors, MDX bodies — `:::comments` |
70
+ | `<Graph>` / `<Node>` / `<Edge>` | Static node/edge diagram — dagre layout at render time, SVG edges, editor-linked nodes — `:::graph` |
71
+ | `<Tests>` / `<Test>` | Test-run report — status pills, auto counts and duration sum — `:::tests` |
72
+ | `<Endpoints>` / `<Endpoint>` | API route list — method chips, `base` prefix, `auth`/`deprecated` — `:::endpoints` |
73
+ | `<Json>` | Collapsible JSON tree on nested `<details>` — `value` attr or fenced child |
74
+ | `<Waterfall>` / `<Span>` | Timing waterfall (OTel-trace-style bars) — `:::waterfall` |
75
+ | `<Ins>` / `<Del>` | Inline word-level edits — semantic `<ins>`/`<del>` |
76
+
77
+ ## Reports — details: [components/reports.md](components/reports.md)
78
+
79
+ Review results, verification status, security/dependency scans, metrics, config docs, and ops/release summaries.
80
+
81
+ | Component | What it is |
82
+ | --- | --- |
83
+ | `<Review>` / `<Comment>` | Code-review report — numbered findings with severity pills, `file:line` links, verdict pill + severity tally — `:::review` |
84
+ | `<Verdict>` | Colored verdict banner — approve/pass/warn/fail/info — `:::verdict` |
85
+ | `<Severity>` | Inline severity pill — critical/high/medium/low/info |
86
+ | `<Checks>` / `<Check>` | CI check list — status icons, required chips, auto counts + duration sum — `:::checks` |
87
+ | `<Audit>` / `<Vuln>` | Vulnerability report — severity tally, CVE/GHSA links, fix versions — `:::audit` |
88
+ | `<Bumps>` / `<Bump>` | Dependency upgrade plan — `from → to`, auto major/minor/patch detection — `:::bumps` |
89
+ | `<Packages>` / `<Package>` | Package inventory — kind/license chips, count caption — `:::packages` |
90
+ | `<Gauges>` / `<Gauge>` | Percent-bar list — coverage/scores with auto pass/fail coloring — `:::gauges` |
91
+ | `<Score>` | 0–100 ring gauge — health/quality scores |
92
+ | `<Spark>` | Inline sparkline for prose trends |
93
+ | `<Benchmarks>` / `<Bench>` | Before/after measurement table — auto % delta, `better="lower"` flips colors — `:::benchmarks` |
94
+ | `<DiffStat>` | Inline `+N −M across F files` chip with proportional bar |
95
+ | `<Schema>` / `<DbTable>` / `<DbField>` | Database schema docs — PK/FK/unique/null chips — `:::schema` |
96
+ | `<EnvVars>` / `<EnvVar>` | Env-var reference — required/secret chips, masked values, name-copy button — `:::envvars` |
97
+ | `<StatusPage>` / `<Service>` | Service-health summary — worst-status rollup, uptime figures — `:::statuspage` |
98
+ | `<Uptime>` / `<Day>` | Statuspage-style daily uptime bar — `:::uptime` |
99
+ | `<Release>` / `<Entry>` | Release notes — entries auto-grouped by kind (breaking/added/…/security) — `:::release` |
100
+ | `<Pathway>` / `<Stop>` | Migration/rollout stepper — version paths, env promotion — `:::pathway` |
101
+ | `<Incident>` | Incident/postmortem header — severity + status pills, timeline meta, impact line — `:::incident` |
102
+
103
+ ## Data visualization — details: [components/charts.md](components/charts.md)
104
+
105
+ All charts are static SVG/HTML at render time (no client JS); `tone` pins a color, `unit` labels values.
106
+
107
+ | Component | What it is |
108
+ | --- | --- |
109
+ | `<BarChart>` / `<Bar>` | Category comparison — vertical/horizontal, grouped or stacked series — `:::barchart` |
110
+ | `<LineChart>` / `<Series>` | Trend lines over ordered categories; `area` fill, `dash` series — `:::linechart` |
111
+ | `<PieChart>` / `<Slice>` | Part-of-whole pie/donut with legend — `:::piechart` |
112
+ | `<Scatter>` / `<Point>` | Two-axis correlation; `size` bubbles — `:::scatter` |
113
+ | `<Radar>` / `<Series>` | Spider chart on 3+ shared axes — `:::radar` |
114
+ | `<Funnel>` / `<Stage>` | Stage-by-stage narrowing with conversion percents — `:::funnel` |
115
+ | `<Quadrant>` / `<Pin>` | 2-axis positioning map with labeled regions — `:::quadrant` |
116
+ | `<Bridge>` / `<Delta>` | Running-total waterfall (start→deltas→end) — `:::bridge` |
117
+ | `<Treemap>` / `<Tile>` | Squarified part-of-whole areas — `:::treemap` |
118
+ | `<Sankey>` / `<Link>` / `<Node>` | Flow split/merge across stage columns — `:::sankey` |
119
+ | `<Venn>` / `<Set>` / `<Overlap>` | 2–3 set overlap diagram — `:::venn` |
120
+
121
+ ## Layout — details: [components/layout.md](components/layout.md)
122
+
123
+ | Component | What it is |
124
+ | --- | --- |
125
+ | `<Columns>` / `<Column>` | Simple side-by-side grid (2–4 columns) |
126
+ | `<Grid>` / `<Cell>` | 12-track grid — spans, dense flow, auto-fit card grids |
127
+ | `<Row>` | Horizontal flex-wrap row — groups inline components (`Button`, `Badge`, …) that MDX would otherwise render glued together |
128
+ | `<Stack>` | Vertical stack with `gap` — for margin-less components (`Input`, `Textarea`, `Progress`, …) |
129
+ | `<Before>` / `<After>` | Red/green compare panels |
130
+
131
+ ## Reader input — details: [components/forms.md](components/forms.md)
132
+
133
+ | Component | What it is |
134
+ | --- | --- |
135
+ | `<Ask>` / `<Question>` / `<Choice>` | Native-form question blocks; answers show live as Markdown to copy or save |
136
+
137
+ ## shadcn/ui — details: [components/shadcn.md](components/shadcn.md)
138
+
139
+ The full shadcn/ui (Base UI) set is registered (`Button`, `Card`, `Table`, `Tabs`, …). Rendered documents carry a hydration bundle, so stateful primitives (`Tabs`, `Accordion`, `Switch`, …) are interactive in the browser — pass `--no-hydrate` for purely static output.
140
+
141
+ ## Project-defined components — details: [extending.md](extending.md)
142
+
143
+ Projects can register their own components via `mdxr.config.ts` + `defineComponent`; a same-name component overrides the built-in.
144
+
145
+ ## Syntax cheatsheet
146
+
147
+ Markdown shorthands and what they render as — the reverse lookup of the index above.
148
+
149
+ | Write | Get |
150
+ | --- | --- |
151
+ | `:::note` / `:::warning` / `:::decision` … | `<Callout kind>` |
152
+ | `:::goal` / `:::nongoal` / `:::question` / `:::answer` | goal / non-goal / open-question / conclusion callouts |
153
+ | `> [!NOTE]` GitHub alert | `<Callout>` |
154
+ | `:::phase{title="…" status="doing"}` | `<Phase>` |
155
+ | `:::flow{title="…"}` + `<FlowStep>` | `<Flow>` numbered call/execution chain |
156
+ | `:::findings` + `<Finding confidence>` | findings list with confidence pills |
157
+ | `:::hypotheses` + `<Hypothesis status>` | hypothesis ledger (supported/refuted/untested) |
158
+ | `:::searches` + `<Search pattern hits>` | search-query log |
159
+ | `:::trace` + `<TraceFrame>` | stack/call trace with error line |
160
+ | `:::terminal{cmd="…" exit="…"}` / ` ```console ` fence | terminal transcript |
161
+ | `:::files` / `:::deps` | `<Files>` related-file list / `<Deps>` dependency edges |
162
+ | `:::tests` + `<Test>` / `:::endpoints` + `<Endpoint>` | `<Tests>` run report / `<Endpoints>` API list |
163
+ | `:::review{verdict="changes"}` + `<Comment severity file>` | `<Review>` code-review report |
164
+ | `:::verdict{status="approve"}` | `<Verdict>` conclusion banner |
165
+ | `:::checks` + `<Check status>` / `:::audit` + `<Vuln severity>` | `<Checks>` CI status / `<Audit>` vulnerability report |
166
+ | `:::bumps` + `<Bump from to>` / `:::packages` + `<Package>` | `<Bumps>` upgrade plan / `<Packages>` inventory |
167
+ | `:::gauges` + `<Gauge value>` / `:::benchmarks` + `<Bench before after>` | `<Gauges>` percent bars / `<Benchmarks>` compare table |
168
+ | `:::schema` + `<DbTable>`/`<DbField>` / `:::envvars` + `<EnvVar>` | `<Schema>` DB docs / `<EnvVars>` env reference |
169
+ | `:::statuspage` + `<Service>` / `:::uptime` + `<Day>` | `<StatusPage>` health summary / `<Uptime>` daily bar |
170
+ | `:::release{version="…"}` + `<Entry kind>` / `:::pathway` + `<Stop>` / `:::incident` | `<Release>` notes / `<Pathway>` stepper / `<Incident>` postmortem |
171
+ | `:::board` + `<Lane>`/`<BoardCard>` | `<Board>` kanban |
172
+ | `:::graph` + `<Node>`/`<Edge>` | `<Graph>` static node/edge diagram (dagre layout, no client JS) |
173
+ | `:::waterfall` + `<Span>` / `:::matrix` + list | `<Waterfall>` timing bars / `<Matrix>` comparison grid |
174
+ | `:::timeline{title="…"}` | `<Timeline>` |
175
+ | `:::gantt{title="…"}` + `<Task>`/`<Milestone>` | `<Gantt>` date-based schedule chart |
176
+ | `:::barchart` / `:::linechart` / `:::piechart` / `:::scatter` / `:::radar` / `:::funnel` / `:::quadrant` / `:::bridge` / `:::treemap` / `:::sankey` / `:::venn` | static chart panels — see [components/charts.md](components/charts.md) |
177
+ | frontmatter `status:` / `date:` / `owner:` | document header badge + meta row |
178
+ | `path`-carrying components (`<FileRef>`, `<File>`, `<TraceFrame>`, `<FlowStep>`, `<Change>`, `<SymbolRef path>`) + `title="…"` code headers | `vscode://file/…` editor links when the file exists; frontmatter `editor:` picks the scheme (`cursor`, `zed`, `none`, …) |
179
+ | `` `src/x.ts` `` inline code naming a real file (optional `:L`/`:L-M`) | `<FileRef>` chip — icon, copy button, editor link (a bare `x.ts` stays plain code) |
180
+ | ` ```mermaid ` fenced block | rendered diagram |
181
+ | ` ```diff ` / ` ```patch ` fenced block | structured per-file diff cards |
182
+ | `:::comments` + fence + `<Comment lines>` | line-anchored comment threads on code/diff |
183
+ | ` ```ts title="src/x.ts" ` | highlighted code block + filename bar with file-type icon |
184
+ | `- [ ]` / `- [x]` | styled task list |
185
+ | nested list inside `<Tree>` | file tree |
186
+ | `<Icon name="lucide:rocket">` / `icon-[lucide--rocket]` class | inline Iconify icon |
@@ -0,0 +1,48 @@
1
+ # Project-defined components
2
+
3
+ Index: [components.md](components.md).
4
+
5
+ Create `mdxr.config.ts` in the project root:
6
+
7
+ ```ts
8
+ import { defineConfig } from "@suzumiyaaoba/mdxr";
9
+
10
+ export default defineConfig({
11
+ components: "./components/index.tsx", // named exports become MDX components
12
+ theme: "./mdxr.css", // optional: @theme token overrides
13
+ editor: "vscode", // optional: file-link target — see below
14
+ });
15
+ ```
16
+
17
+ `editor` sets the URL scheme for file links (`path`-carrying components and code-block filename headers): `vscode` (default), `cursor`, `zed`, `vscode-insiders`, `windsurf`, `sublime`, `textmate`, `idea`, a custom template like `"myed://open?f={path}&l={line}"`, or `"none"` to disable. Frontmatter `editor:` overrides it per document.
18
+
19
+ Define components with `defineComponent` (adds a valibot schema — used for runtime validation **and** `mdxr catalog` documentation):
20
+
21
+ ```tsx
22
+ // components/index.tsx
23
+ import { defineComponent, v } from "@suzumiyaaoba/mdxr";
24
+
25
+ export const LinkCard = defineComponent(
26
+ {
27
+ description: "External reference card",
28
+ schema: v.looseObject({ href: v.string(), title: v.string() }),
29
+ },
30
+ ({ href, title, children }) => (
31
+ <a
32
+ href={href}
33
+ target="_blank"
34
+ rel="noopener noreferrer"
35
+ className="block rounded-lg border p-3 no-underline"
36
+ >
37
+ <div className="font-medium">{title} ↗</div>
38
+ {children}
39
+ </a>
40
+ )
41
+ );
42
+ ```
43
+
44
+ - Tailwind classes in custom components are compiled automatically.
45
+ - `import { Callout, StatusBadge } from '@suzumiyaaoba/mdxr/components'` to compose built-ins.
46
+ - A project component with the same name as a built-in overrides it (a warning is printed).
47
+ - Project components join the hydration bundle — hooks (`useState`, `useContext`, …) work, so they can be interactive in the rendered HTML (e.g. a counter). With `--no-hydrate` they render initial state only.
48
+ - Components must still be synchronous — no Suspense / data fetching.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The Markdown answer-sheet format shared by SSR (`<Ask>`'s seeded output)
3
+ * and the live client (`syncAsk` in doc-events.ts): `# title` followed by
4
+ * one `- **label**: answer` line per question. A single formatter keeps the
5
+ * two emitters byte-identical — the client's rewrite must reproduce what
6
+ * SSR wrote or hydration diffs would flash the pane.
7
+ */
8
+
9
+ export interface SheetEntry {
10
+ /** The answer text; `""` renders as an unanswered (blank) item. */
11
+ answer: string;
12
+ /** The raw question label — escaping happens here. */
13
+ label: string;
14
+ }
15
+
16
+ // A newline in the label would split the list item — collapse it; `\`/`*`
17
+ // are escaped so they can't break the surrounding `**…**` emphasis.
18
+ const sheetLabel = (raw: string): string =>
19
+ raw
20
+ .replaceAll(/\s+/gu, " ")
21
+ .trim()
22
+ .replaceAll("\\", "\\\\")
23
+ .replaceAll("*", "\\*");
24
+
25
+ const sheetTitle = (raw: string | undefined): string => {
26
+ const t = (raw ?? "Answers").replaceAll(/\s+/gu, " ").trim();
27
+ return t === "" ? "Answers" : t;
28
+ };
29
+
30
+ /** `# title` + `- **label**: answer` lines; multi-line answers indent. */
31
+ export const formatAnswerSheet = (
32
+ title: string | undefined,
33
+ entries: readonly SheetEntry[]
34
+ ): string => {
35
+ const lines = entries.map(({ answer, label }) => {
36
+ const a = answer.replaceAll("\n", "\n ");
37
+ return `- **${sheetLabel(label)}**:${a === "" ? "" : ` ${a}`}`;
38
+ });
39
+ return `# ${sheetTitle(title)}\n\n${lines.length === 0 ? "(no questions)" : lines.join("\n")}`;
40
+ };