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.
- package/CHANGELOG.md +1229 -0
- package/LICENSE +21 -0
- package/README.md +1265 -0
- package/dist/block-props.d.ts +18 -0
- package/dist/block-props.js +75 -0
- package/dist/client.d.ts +370 -0
- package/dist/client.js +754 -0
- package/dist/decorate.d.ts +24 -0
- package/dist/decorate.js +71 -0
- package/dist/dom.d.ts +130 -0
- package/dist/dom.js +627 -0
- package/dist/element.d.ts +20 -0
- package/dist/element.js +288 -0
- package/dist/hi.d.ts +12 -0
- package/dist/hi.js +215 -0
- package/dist/html-to-react.d.ts +61 -0
- package/dist/html-to-react.js +338 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +18 -0
- package/dist/morph.d.ts +28 -0
- package/dist/morph.js +166 -0
- package/dist/react.d.ts +236 -0
- package/dist/react.js +539 -0
- package/dist/renderers/CodeBlock.d.ts +7 -0
- package/dist/renderers/CodeBlock.js +75 -0
- package/dist/renderers/Math.d.ts +14 -0
- package/dist/renderers/Math.js +15 -0
- package/dist/renderers/Mermaid.d.ts +13 -0
- package/dist/renderers/Mermaid.js +15 -0
- package/dist/server-react.d.ts +32 -0
- package/dist/server-react.js +48 -0
- package/dist/server.d.ts +31 -0
- package/dist/server.js +82 -0
- package/dist/solid.d.ts +104 -0
- package/dist/solid.js +54 -0
- package/dist/styles.css +188 -0
- package/dist/svelte.d.ts +80 -0
- package/dist/svelte.js +59 -0
- package/dist/types-core.d.ts +436 -0
- package/dist/types-core.js +0 -0
- package/dist/types-react.d.ts +13 -0
- package/dist/types-react.js +0 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +2 -0
- package/dist/url-safety.d.ts +12 -0
- package/dist/url-safety.js +45 -0
- package/dist/vue.d.ts +94 -0
- package/dist/vue.js +79 -0
- package/dist/wasm/LICENSE +21 -0
- package/dist/wasm/README.md +71 -0
- package/dist/wasm/brook_md_core.d.ts +166 -0
- package/dist/wasm/brook_md_core.js +512 -0
- package/dist/wasm/brook_md_core_bg.wasm +0 -0
- package/dist/wasm/brook_md_core_bg.wasm.d.ts +26 -0
- package/dist/worker-core.d.ts +65 -0
- package/dist/worker-core.js +155 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +49 -0
- 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>;
|