brookmd 0.22.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 (59) hide show
  1. package/CHANGELOG.md +1229 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1265 -0
  4. package/dist/block-props.d.ts +18 -0
  5. package/dist/block-props.js +75 -0
  6. package/dist/client.d.ts +370 -0
  7. package/dist/client.js +754 -0
  8. package/dist/decorate.d.ts +24 -0
  9. package/dist/decorate.js +71 -0
  10. package/dist/dom.d.ts +130 -0
  11. package/dist/dom.js +627 -0
  12. package/dist/element.d.ts +20 -0
  13. package/dist/element.js +288 -0
  14. package/dist/hi.d.ts +12 -0
  15. package/dist/hi.js +215 -0
  16. package/dist/html-to-react.d.ts +61 -0
  17. package/dist/html-to-react.js +338 -0
  18. package/dist/index.d.ts +22 -0
  19. package/dist/index.js +18 -0
  20. package/dist/morph.d.ts +28 -0
  21. package/dist/morph.js +166 -0
  22. package/dist/react.d.ts +236 -0
  23. package/dist/react.js +539 -0
  24. package/dist/renderers/CodeBlock.d.ts +7 -0
  25. package/dist/renderers/CodeBlock.js +75 -0
  26. package/dist/renderers/Math.d.ts +14 -0
  27. package/dist/renderers/Math.js +15 -0
  28. package/dist/renderers/Mermaid.d.ts +13 -0
  29. package/dist/renderers/Mermaid.js +15 -0
  30. package/dist/server-react.d.ts +32 -0
  31. package/dist/server-react.js +48 -0
  32. package/dist/server.d.ts +31 -0
  33. package/dist/server.js +82 -0
  34. package/dist/solid.d.ts +104 -0
  35. package/dist/solid.js +54 -0
  36. package/dist/styles.css +188 -0
  37. package/dist/svelte.d.ts +80 -0
  38. package/dist/svelte.js +59 -0
  39. package/dist/types-core.d.ts +436 -0
  40. package/dist/types-core.js +0 -0
  41. package/dist/types-react.d.ts +13 -0
  42. package/dist/types-react.js +0 -0
  43. package/dist/types.d.ts +2 -0
  44. package/dist/types.js +2 -0
  45. package/dist/url-safety.d.ts +12 -0
  46. package/dist/url-safety.js +45 -0
  47. package/dist/vue.d.ts +94 -0
  48. package/dist/vue.js +79 -0
  49. package/dist/wasm/LICENSE +21 -0
  50. package/dist/wasm/README.md +71 -0
  51. package/dist/wasm/brook_md_core.d.ts +166 -0
  52. package/dist/wasm/brook_md_core.js +512 -0
  53. package/dist/wasm/brook_md_core_bg.wasm +0 -0
  54. package/dist/wasm/brook_md_core_bg.wasm.d.ts +26 -0
  55. package/dist/worker-core.d.ts +65 -0
  56. package/dist/worker-core.js +155 -0
  57. package/dist/worker.d.ts +1 -0
  58. package/dist/worker.js +49 -0
  59. package/package.json +87 -0
package/dist/vue.js ADDED
@@ -0,0 +1,79 @@
1
+ import { defineComponent, h, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
2
+ import { BrookClient } from "./client.js";
3
+ import {
4
+ mountBrookMarkdown,
5
+ tailOpenBlockId
6
+ } from "./dom.js";
7
+ function useBrookMarkdown(getOpts) {
8
+ const container = ref(null);
9
+ let handle = null;
10
+ function mount() {
11
+ if (!container.value) return;
12
+ const { client, ...mountOptions } = getOpts();
13
+ handle = mountBrookMarkdown(client, container.value, mountOptions);
14
+ }
15
+ function teardown() {
16
+ handle?.destroy();
17
+ handle = null;
18
+ }
19
+ onMounted(mount);
20
+ watch(
21
+ [
22
+ () => getOpts().client,
23
+ () => getOpts().components,
24
+ () => getOpts().sanitize,
25
+ () => getOpts().virtualize,
26
+ () => getOpts().stickToBottom
27
+ ],
28
+ () => {
29
+ teardown();
30
+ mount();
31
+ }
32
+ );
33
+ onUnmounted(teardown);
34
+ return { container };
35
+ }
36
+ function useTailBlockId(client) {
37
+ const tail = shallowRef(tailOpenBlockId(client.getSnapshot()));
38
+ const unsubscribe = client.subscribe(() => {
39
+ tail.value = tailOpenBlockId(client.getSnapshot());
40
+ });
41
+ onUnmounted(unsubscribe);
42
+ return tail;
43
+ }
44
+ const BrookMarkdown = defineComponent({
45
+ name: "BrookMarkdown",
46
+ props: {
47
+ client: { type: Object, required: true },
48
+ components: { type: Object, default: void 0 },
49
+ sanitize: { type: Function, default: void 0 },
50
+ virtualize: { type: Boolean, default: void 0 },
51
+ stickToBottom: { type: Boolean, default: void 0 }
52
+ },
53
+ setup(props) {
54
+ const { container } = useBrookMarkdown(() => ({
55
+ client: props.client,
56
+ components: props.components,
57
+ sanitize: props.sanitize,
58
+ virtualize: props.virtualize,
59
+ stickToBottom: props.stickToBottom
60
+ }));
61
+ return () => h("div", { ref: container });
62
+ }
63
+ });
64
+ function useBrookMarkdownString(getContent, getOptions) {
65
+ const client = new BrookClient({ config: getOptions?.()?.config });
66
+ const apply = () => {
67
+ client.setContent(getContent(), { done: getOptions?.()?.streaming === false });
68
+ };
69
+ onMounted(apply);
70
+ watch([getContent, () => getOptions?.()?.streaming], apply);
71
+ onUnmounted(() => client.destroy());
72
+ return client;
73
+ }
74
+ export {
75
+ BrookMarkdown,
76
+ useBrookMarkdown,
77
+ useBrookMarkdownString,
78
+ useTailBlockId
79
+ };
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 siinghd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # brookmd-core
2
+
3
+ An incremental, streaming-aware CommonMark + GFM parser core. It is the Rust
4
+ engine behind the [`brookmd`](https://www.npmjs.com/package/brookmd) npm package,
5
+ exposed here as a standalone crate for native Rust consumers.
6
+
7
+ Feed the document in chunks as they arrive. Each `append` returns a `Patch`
8
+ describing which blocks just became permanent ("committed") and which are still
9
+ being built ("active"). Committed blocks never change again; active blocks may
10
+ flicker as more input arrives. Every block carries a stable, monotonic ID so a UI
11
+ layer can reconcile in place. Block scanning, inline tokenizing, and safe HTML
12
+ rendering are all in-house — there are no other parser dependencies.
13
+
14
+ ## Example
15
+
16
+ ```rust
17
+ use brook_md_core::StreamParser;
18
+
19
+ fn main() {
20
+ let mut parser = StreamParser::new();
21
+
22
+ // Feed the document in arbitrary chunks, as they arrive off the wire.
23
+ for chunk in ["# Hello\n\nStreaming ", "markdown ", "core."] {
24
+ let patch = parser.append(chunk);
25
+ // `newly_committed` blocks are final; `active` blocks may still change.
26
+ for block in patch.newly_committed {
27
+ println!("committed #{}: {}", block.id, block.html);
28
+ }
29
+ }
30
+
31
+ // Flush any block still open at end of input.
32
+ let patch = parser.finalize();
33
+ for block in patch.newly_committed {
34
+ println!("committed #{}: {}", block.id, block.html);
35
+ }
36
+ }
37
+ ```
38
+
39
+ Optional extensions (GFM autolinks, alerts, footnotes, math, and more) are
40
+ enabled per parser through builder methods, e.g.
41
+ `StreamParser::new().with_gfm_autolinks(true)`.
42
+
43
+ ## Feature flags
44
+
45
+ - `wasm` (default) — compiles the wasm-bindgen `BrookParser` glue used by the
46
+ `brookmd` JS package.
47
+ - `perf_counters` — deterministic work counters used by the complexity-scaling
48
+ tests. Off by default.
49
+
50
+ Native Rust consumers who only need the `StreamParser` API can skip wasm-bindgen
51
+ entirely:
52
+
53
+ ```toml
54
+ [dependencies]
55
+ brookmd-core = { version = "0.20", default-features = false }
56
+ ```
57
+
58
+ ## Wire format
59
+
60
+ Blocks and patches serialize to a stable, language-agnostic JSON wire format —
61
+ see [WIRE.md](WIRE.md) (wire contract v1.1.0). Native consumers can produce the
62
+ same bytes as the WASM/JS boundary via `wire::patch_to_json` / `wire::blocks_to_json`.
63
+
64
+ ## Links
65
+
66
+ - npm package: <https://www.npmjs.com/package/brookmd>
67
+ - Repository: <https://github.com/siinghd/brookmd>
68
+
69
+ ## License
70
+
71
+ MIT
@@ -0,0 +1,166 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ export class BrookParser {
5
+ free(): void;
6
+ [Symbol.dispose](): void;
7
+ /**
8
+ * All blocks currently parsed (committed + active), in document order — the
9
+ * whole rendered document as a **JSON string** of a `Block[]` (parse with
10
+ * `JSON.parse`). The one-shot / server-side render primitive: feed the full
11
+ * markdown via `append`, call `finalize`, then read `allBlocks()` (no worker,
12
+ * no patch accumulation).
13
+ */
14
+ allBlocks(): string;
15
+ /**
16
+ * Returns the patch as a **JSON string** (parsed with `JSON.parse` on the
17
+ * main thread), not a live JS object. This is deliberate: serializing once to
18
+ * a string in Rust avoids serde-wasm-bindgen's per-node boundary calls, and a
19
+ * string is far cheaper than an object graph to `structuredClone` across the
20
+ * worker→main `postMessage` (the worker forwards the string verbatim).
21
+ */
22
+ append(chunk: string): string;
23
+ bufferLen(): number;
24
+ /**
25
+ * JSON-string patch — see [`BrookParser::append`].
26
+ */
27
+ finalize(): string;
28
+ constructor();
29
+ /**
30
+ * Total bytes the parser is retaining: source buffer + all rendered
31
+ * HTML for committed and active blocks. Use to compare per-parser
32
+ * memory cost against alternatives.
33
+ */
34
+ retainedBytes(): number;
35
+ /**
36
+ * Opt-in accessibility markup that deviates from strict GFM byte-output:
37
+ * `<label>`-wrap a task-list checkbox with its text, and add `scope="col"`
38
+ * to table header cells. Off by default (conformance output unchanged).
39
+ */
40
+ setA11y(on: boolean): void;
41
+ /**
42
+ * Opt-in structured `kind.data` channel for Table blocks: a Table then
43
+ * carries `{ headers, rows, aligns }` (per-cell `{ text, html }`) so a
44
+ * consumer can build a sort/filter/transpose/chart/CSV toolbar from DATA
45
+ * without re-parsing the HTML. Off by default — when off, Table serializes
46
+ * as `{"type":"Table"}` (no `data` key) and output is byte-identical.
47
+ */
48
+ setBlockData(on: boolean): void;
49
+ /**
50
+ * Set the opt-in component-tag allowlist (e.g. `["Thinking", "Callout"]`).
51
+ * A `<Tag>…</Tag>` whose name is listed renders as a component whose inner
52
+ * content is markdown — safely, without unsafe HTML (the tag is allowlisted
53
+ * and its attributes are sanitized). Empty by default (feature off).
54
+ */
55
+ setComponentTags(tags: string[]): void;
56
+ /**
57
+ * Emit `dir="auto"` on block-level text elements so the browser detects
58
+ * each block's direction (LTR/RTL) independently — correct rendering for
59
+ * documents that mix English with Arabic/Hebrew. Off by default; code
60
+ * blocks never get it (code is always LTR).
61
+ */
62
+ setDirAuto(on: boolean): void;
63
+ /**
64
+ * Enable GitHub alerts (`> [!NOTE]` blockquotes render as styled callouts
65
+ * with GitHub-compatible class names). Off by default.
66
+ */
67
+ setGfmAlerts(on: boolean): void;
68
+ /**
69
+ * Enable GFM extended autolinks (bare www./http(s)://ftp:// URLs and email
70
+ * addresses become links). Useful for LLM output, which is full of them.
71
+ */
72
+ setGfmAutolinks(on: boolean): void;
73
+ /**
74
+ * Enable GFM footnotes (`[^1]` references + `[^1]:` definitions → a
75
+ * footnote section emitted at finalize). Off by default.
76
+ */
77
+ setGfmFootnotes(on: boolean): void;
78
+ /**
79
+ * Enable math: `$…$` / `\(…\)` inline and `$$…$$` / `\[…\]` display math.
80
+ * Off by default (so `$` in prose / currency stays literal). The emitted
81
+ * HTML carries the LaTeX in `<span class="math math-inline">` /
82
+ * `<div class="math math-display">` for a KaTeX pass on the JS side.
83
+ */
84
+ setGfmMath(on: boolean): void;
85
+ /**
86
+ * Enable the GFM "Disallowed Raw HTML" extension (tagfilter): with raw
87
+ * HTML passing through (`setUnsafeHtml(true)`), the nine disallowed tags
88
+ * (`<title>`, `<script>`, `<iframe>`, …) get their leading `<` escaped so
89
+ * they display as text instead of taking effect. Off by default; no
90
+ * effect while raw HTML is escaped or sanitized (already inert).
91
+ */
92
+ setGfmTagfilter(on: boolean): void;
93
+ /**
94
+ * Engage the safe raw-HTML sanitizer. When `on`, inline raw HTML renders
95
+ * sanitized without full unsafe HTML: `allow` empty = allow all tags except
96
+ * a built-in dangerous set (`script`, `style`, `iframe`, …); `allow`
97
+ * non-empty = only those render (others escaped); `drop` tags are removed
98
+ * entirely; HTML comments are dropped; every rendered tag's attributes are
99
+ * sanitized. Off by default (raw-HTML handling unchanged).
100
+ */
101
+ setHtmlSanitize(on: boolean, allow: string[], drop: string[]): void;
102
+ /**
103
+ * Set the opt-in INLINE component-tag allowlist (e.g. `["tik", "cite"]`).
104
+ * An allowlisted inline `<tik>…</tik>` (or self-closing `<tik/>`) renders as
105
+ * a custom element (markdown inner, sanitized attributes) so a JSX/DOM layer
106
+ * can dispatch it via `components[tag]` — in paragraphs, headings, table
107
+ * cells, and list items. Empty by default (inline output unchanged).
108
+ */
109
+ setInlineComponentTags(tags: string[]): void;
110
+ /**
111
+ * Enable or disable raw-HTML pass-through. Default off. Do not enable
112
+ * when rendering untrusted input — bypasses XSS protection.
113
+ */
114
+ setUnsafeHtml(on: boolean): void;
115
+ }
116
+
117
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
118
+
119
+ export interface InitOutput {
120
+ readonly memory: WebAssembly.Memory;
121
+ readonly __wbg_brookparser_free: (a: number, b: number) => void;
122
+ readonly brookparser_allBlocks: (a: number, b: number) => void;
123
+ readonly brookparser_append: (a: number, b: number, c: number, d: number) => void;
124
+ readonly brookparser_bufferLen: (a: number) => number;
125
+ readonly brookparser_finalize: (a: number, b: number) => void;
126
+ readonly brookparser_new: () => number;
127
+ readonly brookparser_retainedBytes: (a: number) => number;
128
+ readonly brookparser_setA11y: (a: number, b: number) => void;
129
+ readonly brookparser_setBlockData: (a: number, b: number) => void;
130
+ readonly brookparser_setComponentTags: (a: number, b: number, c: number) => void;
131
+ readonly brookparser_setDirAuto: (a: number, b: number) => void;
132
+ readonly brookparser_setGfmAlerts: (a: number, b: number) => void;
133
+ readonly brookparser_setGfmAutolinks: (a: number, b: number) => void;
134
+ readonly brookparser_setGfmFootnotes: (a: number, b: number) => void;
135
+ readonly brookparser_setGfmMath: (a: number, b: number) => void;
136
+ readonly brookparser_setGfmTagfilter: (a: number, b: number) => void;
137
+ readonly brookparser_setHtmlSanitize: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
138
+ readonly brookparser_setInlineComponentTags: (a: number, b: number, c: number) => void;
139
+ readonly brookparser_setUnsafeHtml: (a: number, b: number) => void;
140
+ readonly __wbindgen_export: (a: number, b: number) => number;
141
+ readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
142
+ readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
143
+ readonly __wbindgen_export3: (a: number, b: number, c: number) => void;
144
+ }
145
+
146
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
147
+
148
+ /**
149
+ * Instantiates the given `module`, which can either be bytes or
150
+ * a precompiled `WebAssembly.Module`.
151
+ *
152
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
153
+ *
154
+ * @returns {InitOutput}
155
+ */
156
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
157
+
158
+ /**
159
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
160
+ * for everything else, calls `WebAssembly.instantiate` directly.
161
+ *
162
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
163
+ *
164
+ * @returns {Promise<InitOutput>}
165
+ */
166
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;