brookmd 0.29.0 → 0.30.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/CHANGELOG.md +170 -1
- package/README.md +643 -30
- package/dist/block-props.js +8 -2
- package/dist/dom.d.ts +25 -3
- package/dist/dom.js +33 -8
- package/dist/element.d.ts +3 -0
- package/dist/element.js +22 -2
- package/dist/hi-inc.d.ts +28 -0
- package/dist/hi-inc.js +40 -16
- package/dist/hi.d.ts +62 -4
- package/dist/hi.js +243 -22
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/react.d.ts +30 -4
- package/dist/react.js +25 -3
- package/dist/renderers/CodeBlock.d.ts +3 -2
- package/dist/renderers/CodeBlock.js +29 -11
- package/dist/solid.js +1 -0
- package/dist/splice.d.ts +58 -11
- package/dist/splice.js +35 -6
- package/dist/styles.css +295 -0
- package/dist/svelte.d.ts +6 -2
- package/dist/svelte.js +6 -3
- package/dist/types-core.d.ts +21 -1
- package/dist/types-react.d.ts +1 -1
- package/dist/vue.d.ts +8 -6
- package/dist/vue.js +11 -3
- package/dist/wasm/brook_md_core.d.ts +1 -1
- package/dist/wasm/brook_md_core.js +1 -1
- package/dist/wasm/brook_md_core_bg.wasm +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,175 @@ Notable changes to brookmd (formerly `flux-md`). Format based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/); this project aims to follow
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 0.30.0 — 2026-09-07
|
|
8
|
+
|
|
9
|
+
**Agent-ready, chat-ready.** brookmd now ships as an installable agent skill,
|
|
10
|
+
and the surface a chat UI needs on day one — a styled theme for the code
|
|
11
|
+
chrome, a streaming caret, a link-click hook, a highlighter you can teach new
|
|
12
|
+
languages, and copy-pasteable recipes for the AI SDK, KaTeX, Mermaid, and
|
|
13
|
+
table toolbars — is in the package and the docs instead of the demo app.
|
|
14
|
+
Requires `brookmd-core` 0.27.0 (parser-side performance fix below; the wire
|
|
15
|
+
contract is unchanged at v1.2.0).
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Agent skill.** `npx skills add siinghd/brookmd` installs a `brookmd` skill
|
|
20
|
+
into any coding agent that reads the `SKILL.md` format (Claude Code, Cursor,
|
|
21
|
+
Codex, …); `/plugin marketplace add siinghd/brookmd` does the same through
|
|
22
|
+
Claude Code's plugin marketplace. The skill (`skills/brookmd/`) carries a
|
|
23
|
+
trigger-listing description, quick setup, the chat-UI defaults, a props
|
|
24
|
+
table with real defaults, twelve gotchas, six reference documents (API,
|
|
25
|
+
frameworks, styling, security, recipes, troubleshooting) and seven typed
|
|
26
|
+
examples. `llms.txt` at the repo root and `llms.txt` / `llms-full.txt` on
|
|
27
|
+
the demo site give agents an index without cloning. Every API fact in the
|
|
28
|
+
skill was verified against source, not the README — and stays that way: CI
|
|
29
|
+
typechecks the examples against `src/`, and `test/skill-docs.test.ts` fails
|
|
30
|
+
on malformed frontmatter, a dead relative link, an import of a
|
|
31
|
+
`brookmd/<subpath>` the package does not export, or a public doc that names
|
|
32
|
+
another rendering library.
|
|
33
|
+
- **`onLinkClick`** on `<BrookMarkdown>` (React), `mountBrookMarkdown`
|
|
34
|
+
(`MountOptions`), the Vue / Svelte / Solid bindings and the
|
|
35
|
+
`<brook-markdown>` element (`.onLinkClick` property): one delegated listener
|
|
36
|
+
on the `.brook-md` root hands back `(event, { href, text, element })`
|
|
37
|
+
(`LinkClickInfo`, exported from `brookmd`, `brookmd/types` and
|
|
38
|
+
`brookmd/dom`); `event.preventDefault()` cancels navigation, so link
|
|
39
|
+
interstitials and in-app routing need no per-anchor override. Links still
|
|
40
|
+
streaming their URL (`<a data-brook-pending>`) are never reported. The hook
|
|
41
|
+
adds zero per-block or per-anchor work — a changing handler identity
|
|
42
|
+
re-renders no blocks (pinned by test).
|
|
43
|
+
- **`registerLanguage(names, { pats, kw })`** on `brookmd/highlight` (also
|
|
44
|
+
re-exported from `brookmd`): teach the built-in streaming highlighter a
|
|
45
|
+
language at runtime, under one name or several aliases. Regexes must be
|
|
46
|
+
sticky, token classes must be ones the stylesheet colours (`ident` is the
|
|
47
|
+
class a `kw` set refines), and re-registering a name replaces it. A
|
|
48
|
+
registered language highlights identically to a built-in one once its block
|
|
49
|
+
closes; while streaming it re-tokenizes instead of growing a frozen prefix,
|
|
50
|
+
since a caller's table does not say which of its forms can run past a
|
|
51
|
+
newline.
|
|
52
|
+
- **Twelve more built-in language families**: yaml/yml, toml, diff, java, c,
|
|
53
|
+
cpp/c++, cs/csharp, php, rb/ruby, swift, kt/kotlin and dockerfile — 36
|
|
54
|
+
language keys in total. Language lookup is now a prototype-free table, so a
|
|
55
|
+
fence tagged `constructor` or `__proto__` is an ordinary miss. The ReDoS
|
|
56
|
+
suite runs 17 adversarial inputs against *every* supported language under a
|
|
57
|
+
per-input time budget, so a new table cannot land a quadratic pattern.
|
|
58
|
+
- **The theme finally styles the fenced-block chrome.** `brookmd/styles.css`
|
|
59
|
+
now gives the built-in code, math and mermaid renderers a bordered
|
|
60
|
+
container, a header bar with the language label, a ghost copy button (hover,
|
|
61
|
+
focus ring, copied state) and a pulsing "streaming" pill — previously the
|
|
62
|
+
theme left a bare button stack above every fence. Also new: GitHub-style
|
|
63
|
+
alerts for all five kinds (`--brook-alert-*` variables in light and dark),
|
|
64
|
+
footnote sections and references, block/inline math, disabled task-list
|
|
65
|
+
checkboxes. A contract test collects every `brook-*` class the renderers
|
|
66
|
+
emit and fails if the theme does not style it.
|
|
67
|
+
- **Opt-in streaming caret.** Add `brook-caret` to the root
|
|
68
|
+
(`<BrookMarkdown className="brook-caret" />`) and the block still streaming
|
|
69
|
+
gets a blinking cursor at its tail (`--brook-caret` retints it). Fences show
|
|
70
|
+
the streaming pill instead. Both animations route through
|
|
71
|
+
`--brook-caret-anim` / `--brook-pill-anim` and switch off under
|
|
72
|
+
`prefers-reduced-motion: reduce`.
|
|
73
|
+
- **Block-state classes are now a documented, stable styling contract**:
|
|
74
|
+
`brook-block`, `brook-block-<kind>`, `brook-open`, `brook-speculative`, and
|
|
75
|
+
`brook-streaming` on the code/math/mermaid slots.
|
|
76
|
+
- `<brook-markdown>` honours the **`stick-to-bottom`** and **`virtualize`**
|
|
77
|
+
attributes the README had advertised; changing either after mount re-applies
|
|
78
|
+
it, including with a caller-owned client.
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- **Streaming parity: a table delimiter row still being typed no longer
|
|
83
|
+
freezes the paragraph above it** (`brookmd-core` 0.27.0). With
|
|
84
|
+
`| name | value |` / `value |` / `|:-----|--`, the moment the buffer held
|
|
85
|
+
`|:-` the parser saw a one-column delimiter under the one-column line
|
|
86
|
+
`value |`, formed a table, and committed `<p>| name | value |</p>` as a
|
|
87
|
+
separate block; one byte later the delimiter widened, the table dissolved,
|
|
88
|
+
but the commit could not be taken back — so the finalized document differed
|
|
89
|
+
from the one-shot parse (one paragraph). A table whose delimiter row sits on
|
|
90
|
+
the buffer's unterminated final line is now held speculative until that line
|
|
91
|
+
completes, matching the existing guard for provisionally classified blocks.
|
|
92
|
+
Found by the coverage-guided parity fuzzer; pinned by a regression test
|
|
93
|
+
across every chunking of 1–8 bytes; a 240 s fuzz run afterwards found
|
|
94
|
+
nothing new.
|
|
95
|
+
- The generic block wrapper was eating the theme's spacing rules: the last
|
|
96
|
+
block's margin escaped the root and the first heading sat indented from the
|
|
97
|
+
top. `.brook-bottom-anchor` (the `stickToBottom` sentinel) no longer occupies
|
|
98
|
+
a full block gap of dead space at the end of the document.
|
|
99
|
+
- README corrections, each verified against source: `<brook-markdown
|
|
100
|
+
stick-to-bottom>` (now real — see above), the root class is `brook-md` (not
|
|
101
|
+
`brookmd`), `new BrookClient({ pool })` (not `new BrookClient(pool)`),
|
|
102
|
+
`MountHandle` also has `openBlockId()`, the real `Components` and
|
|
103
|
+
`BlockComponentProps` declarations, the Blockquote/Alert `{ nested }` →
|
|
104
|
+
`props.container` and `ListData.items` / `ListItemData.start` rows in the
|
|
105
|
+
`blockData` table, and the previously undocumented surface — `deferTail`,
|
|
106
|
+
`childMemo`, `onRenderMetrics`, `onStreamError`, `streamConfig`, `coalesce`,
|
|
107
|
+
`reattach()`, `retainCommittedHtml`, `bootTimeoutMs`, `supportedLangs()`,
|
|
108
|
+
and the `brookmd/types`, `brookmd/html-to-react`, `brookmd/block-props`,
|
|
109
|
+
`brookmd/worker-core` entry points. `SHIPPING.md` no longer claims the
|
|
110
|
+
package ships as source (compiled ESM since 0.17.0).
|
|
111
|
+
- `brookmd-react-native` now depends on `brookmd ^0.30.0` (it pinned `^0.26.0`,
|
|
112
|
+
which a 0.x caret resolves to `<0.27`, so its workspace install had been
|
|
113
|
+
fetching an old brookmd from the registry instead of the sibling package).
|
|
114
|
+
|
|
115
|
+
### Performance
|
|
116
|
+
|
|
117
|
+
- **Open blockquote / alert / component bodies keep a settled HTML prefix**
|
|
118
|
+
(`brookmd-core` 0.27.0). The assembler for a structured container or a
|
|
119
|
+
component block used to re-walk every committed inner sub-block and re-copy
|
|
120
|
+
its HTML on *every* append — the residual wall-clock cliff documented since
|
|
121
|
+
0.28.0. Each committed sub-block is now folded into a per-cache prefix once
|
|
122
|
+
(one prefix per nested-parser twin on the component path, since the settled
|
|
123
|
+
twin's committed HTML differs) and re-emitted as one contiguous copy; only
|
|
124
|
+
the active tail is rebuilt. A streaming component body of paragraphs is
|
|
125
|
+
1.9–5.3× faster at 256 KB (2,048-paragraph body 152 ms → 66 ms; dense
|
|
126
|
+
small-paragraph body 571 ms → 107 ms, growth 34× → 11× over 32→256 KB).
|
|
127
|
+
Output is byte-identical: 652/652 CommonMark + 24/24 GFM byte-exact, every
|
|
128
|
+
parity suite green, and a differential harness hashing the full document
|
|
129
|
+
after every append found no difference from the previous implementation
|
|
130
|
+
across 896 hand-written cases and 20,000 randomized container/component
|
|
131
|
+
documents × 4 chunkings. A new wall-clock scaling guard
|
|
132
|
+
(`wrapper_body_prefix_is_wall_linear`, control-twin primary, retry-once)
|
|
133
|
+
pins the shape — the work counters are structurally blind to this class —
|
|
134
|
+
and was verified to fail on the pre-fix tree. Blockquote/alert bodies whose
|
|
135
|
+
nested parser commits nothing (one giant list) see no change; their residual
|
|
136
|
+
cost is the two unavoidable O(body) `Block.html` materializations per append.
|
|
137
|
+
|
|
138
|
+
### Docs
|
|
139
|
+
|
|
140
|
+
- New README sections written for someone building a chat UI: **Chat UI
|
|
141
|
+
defaults** (`softBreaks`, `dirAuto`, `a11y`, `blockData`, `gfmMath`, hoisted
|
|
142
|
+
`components`, `warm()`, the caret), **With the Vercel AI SDK (`useChat`)**
|
|
143
|
+
including the `streaming: false`-on-finish rule and why brookmd refuses to
|
|
144
|
+
infer it, **Accessible chat**, **Tailwind / design systems** (and why
|
|
145
|
+
`@tailwindcss/typography` users should skip the theme import), and recipes
|
|
146
|
+
for KaTeX, Mermaid, a custom highlighter, interactive task lists, lazy
|
|
147
|
+
images, and a CSV/copy table toolbar over `props.table`.
|
|
148
|
+
|
|
149
|
+
## 0.29.1 — 2026-07-31
|
|
150
|
+
|
|
151
|
+
Performance only; rendered bytes unchanged (asserted: settled markup identical
|
|
152
|
+
with streaming highlight on and off, and mid-stream parity suites untouched).
|
|
153
|
+
|
|
154
|
+
### Performance
|
|
155
|
+
|
|
156
|
+
- **Two quadratic terms removed from the streaming-highlight hot path.** An
|
|
157
|
+
external benchmark of 0.27 showed streaming highlighting costing +36%
|
|
158
|
+
main-thread time on a code-heavy stream. Most of that was the 0.27 DOM
|
|
159
|
+
write pattern, already fixed in 0.28 (the on/off DOM-write delta is 151×
|
|
160
|
+
smaller at this release). Decomposing what remained found two genuinely
|
|
161
|
+
quadratic terms: the revision guard re-scanned the block character-by-
|
|
162
|
+
character on *every* patch to find a divergence point whose exact value
|
|
163
|
+
was never used (~2,070× the source in `charCodeAt` calls — replaced with
|
|
164
|
+
two `startsWith` questions against a cached prefix), and the fence body
|
|
165
|
+
was re-decoded out of its rendered HTML on every patch through a lazy
|
|
166
|
+
regex plus five unconditional entity passes (~5,169× the source — replaced
|
|
167
|
+
with landmark indexing and an `&`-gated entity chain). Streaming a 32 KB
|
|
168
|
+
code block, highlight-only JS cost drops **782 ms → 163 ms**, and both
|
|
169
|
+
terms now scale linearly. A scale-free regression gate
|
|
170
|
+
(`test/streaming-highlight-cost.test.ts`) pins ratios against source and
|
|
171
|
+
markup size — verified to fail on the pre-fix tree.
|
|
172
|
+
- Evaluated and rejected on measurement: computing the highlight in the
|
|
173
|
+
worker (the worker forwards patches as opaque strings; parsing them there
|
|
174
|
+
costs more than the work it would relocate).
|
|
175
|
+
|
|
7
176
|
## 0.29.0 — 2026-07-31
|
|
8
177
|
|
|
9
178
|
**Sublinear resources.** Total parse work has an Ω(n) floor and stays exactly
|
|
@@ -206,7 +375,7 @@ Requires `brookmd-core` 0.25.0.
|
|
|
206
375
|
|
|
207
376
|
- **`softBreaks` — a soft line break renders as `<br>`.** Strict CommonMark
|
|
208
377
|
treats a bare `\n` inside a paragraph as whitespace, so a model that writes one
|
|
209
|
-
thought per line gets one reflowed blob. This is the
|
|
378
|
+
thought per line gets one reflowed blob. This is the
|
|
210
379
|
chat-comment convention where one Enter is one visual line, and it is what most
|
|
211
380
|
chat UIs actually want. Off by default; it only ever *adds* breaks (a hard break
|
|
212
381
|
is `<br>` either way), so no existing output loses a line.
|