flux-md 0.10.0 → 0.12.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 +30 -0
- package/README.md +33 -0
- package/package.json +18 -7
- package/src/dom.ts +18 -1
- package/src/react.tsx +26 -1
- package/src/renderers/CodeBlock.tsx +1 -4
- package/src/styles.css +182 -0
- package/src/wasm/flux_md_core_bg.wasm +0 -0
- package/src/wasm/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,36 @@ Notable changes to 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.12.0 — 2026-05-30
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Optional default theme — `import "flux-md/styles.css"`.** A drop-in stylesheet
|
|
12
|
+
for good-looking output out of the box, **including the built-in syntax
|
|
13
|
+
highlighter's colors** (without any CSS, `highlight()` output is uncolored).
|
|
14
|
+
Scoped to `.flux-md`, driven by `--flux-*` CSS variables (re-theme by overriding
|
|
15
|
+
a few), light by default with automatic dark via `prefers-color-scheme` (force
|
|
16
|
+
with `class="flux-md flux-dark"` / `flux-light`). Opt-in and zero-runtime — the
|
|
17
|
+
rendered HTML is unchanged; skip the import to bring your own CSS.
|
|
18
|
+
|
|
19
|
+
## 0.11.0 — 2026-05-30
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **Opt-in live region + root attributes** on `<FluxMarkdown>` and
|
|
24
|
+
`mountFluxMarkdown`. The root accepts `className` (appended to `flux-md`),
|
|
25
|
+
`id`, `role`, and `aria-live` / `aria-atomic`. Set `aria-live="polite"` to
|
|
26
|
+
announce streamed content to screen readers — `polite` coalesces rapid updates
|
|
27
|
+
and does **not** read every token. Off by default; covers React and the DOM
|
|
28
|
+
mount (so the Web Component and the Vue/Svelte/Solid adapters too).
|
|
29
|
+
|
|
30
|
+
### Docs
|
|
31
|
+
|
|
32
|
+
- A repository root README, a "Structured block data" guide in the package
|
|
33
|
+
README, and a runnable **Data Studio** demo in the playground — a
|
|
34
|
+
sort/filter/CSV table and a live table of contents built entirely from
|
|
35
|
+
`block.data`, mid-stream.
|
|
36
|
+
|
|
7
37
|
## 0.10.0 — 2026-05-30
|
|
8
38
|
|
|
9
39
|
Server-side rendering safety, plus an opt-in structured-data channel so consumers
|
package/README.md
CHANGED
|
@@ -280,6 +280,32 @@ if you hit a transform edge, `mountFluxMarkdown` from `flux-md/dom` inside
|
|
|
280
280
|
| Heavy renderers (syntax, math, mermaid) | Deferred until block close | Re-run per chunk |
|
|
281
281
|
| XSS sanitization | Allowlist in Rust + URL scheme check | Downstream sanitizer pass on the JS thread |
|
|
282
282
|
|
|
283
|
+
## Styling
|
|
284
|
+
|
|
285
|
+
flux-md emits semantic HTML under a `.flux-md` root and **ships no CSS by
|
|
286
|
+
default** — bring your own design system, or opt into the bundled theme:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
import "flux-md/styles.css";
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
It gives good-looking output out of the box, **including the built-in syntax
|
|
293
|
+
highlighter's colors** (without any CSS, `highlight()` renders uncolored). The
|
|
294
|
+
theme is scoped to `.flux-md`, zero-runtime, and **does not change the rendered
|
|
295
|
+
HTML** — skip the import and nothing is styled.
|
|
296
|
+
|
|
297
|
+
Re-theme by overriding a few CSS variables; it's light by default and switches to
|
|
298
|
+
dark automatically via `prefers-color-scheme` (force a mode with
|
|
299
|
+
`class="flux-md flux-dark"` or `flux-light`):
|
|
300
|
+
|
|
301
|
+
```css
|
|
302
|
+
.flux-md {
|
|
303
|
+
--flux-accent: #7c3aed; /* links */
|
|
304
|
+
--flux-bg-code: #faf7ff; /* code background */
|
|
305
|
+
--flux-t-kw: #c026d3; /* syntax: keywords (also --flux-t-str/num/com/fn/ty/…) */
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
283
309
|
## Public API
|
|
284
310
|
|
|
285
311
|
### `FluxClient`
|
|
@@ -412,6 +438,13 @@ Subscribes to a `FluxClient`, renders each block keyed by its stable parser-assi
|
|
|
412
438
|
<FluxMarkdown client={client} />
|
|
413
439
|
```
|
|
414
440
|
|
|
441
|
+
The root element accepts opt-in `className` (appended to `flux-md`), `id`,
|
|
442
|
+
`role`, and `aria-live` / `aria-atomic`. Set `aria-live="polite"` to make the
|
|
443
|
+
output a live region so screen readers announce streamed content as it settles —
|
|
444
|
+
`polite` coalesces rapid updates and does **not** read every token. The same
|
|
445
|
+
options exist on the DOM mount (`mountFluxMarkdown(client, el, { ariaLive: "polite" })`),
|
|
446
|
+
covering the Web Component and the Vue/Svelte/Solid adapters.
|
|
447
|
+
|
|
415
448
|
#### Custom components / overrides
|
|
416
449
|
|
|
417
450
|
Pass a `components` map to replace how elements render. Keys come in **two
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flux-md",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Zero-dep streaming markdown for the browser. Rust→WASM core, Web Worker per stream, incremental parse with speculative closure.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"sideEffects": ["./src/worker.ts"],
|
|
6
|
+
"sideEffects": ["./src/worker.ts", "./src/styles.css"],
|
|
7
7
|
"main": "./src/index.ts",
|
|
8
8
|
"types": "./src/index.ts",
|
|
9
9
|
"exports": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"./svelte": "./src/svelte.ts",
|
|
17
17
|
"./solid": "./src/solid.tsx",
|
|
18
18
|
"./highlight": "./src/hi.ts",
|
|
19
|
-
"./types": "./src/types.ts"
|
|
19
|
+
"./types": "./src/types.ts",
|
|
20
|
+
"./styles.css": "./src/styles.css"
|
|
20
21
|
},
|
|
21
22
|
"files": [
|
|
22
23
|
"src",
|
|
@@ -30,12 +31,21 @@
|
|
|
30
31
|
"solid-js": "^1.8.0"
|
|
31
32
|
},
|
|
32
33
|
"peerDependenciesMeta": {
|
|
33
|
-
"react": {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
34
|
+
"react": {
|
|
35
|
+
"optional": true
|
|
36
|
+
},
|
|
37
|
+
"vue": {
|
|
38
|
+
"optional": true
|
|
39
|
+
},
|
|
40
|
+
"svelte": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"solid-js": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
37
46
|
},
|
|
38
47
|
"devDependencies": {
|
|
48
|
+
"@types/bun": "^1.3.14",
|
|
39
49
|
"@types/react": "^18.3.12",
|
|
40
50
|
"@types/react-dom": "^18.3.1",
|
|
41
51
|
"happy-dom": "^15.11.6",
|
|
@@ -49,6 +59,7 @@
|
|
|
49
59
|
"scripts": {
|
|
50
60
|
"test": "bun test",
|
|
51
61
|
"test:ssr-cold": "bun test/ssr-cold.mjs",
|
|
62
|
+
"typecheck:test": "tsc --noEmit -p tsconfig.test.json",
|
|
52
63
|
"prepublishOnly": "cd ../.. && bun run build:wasm"
|
|
53
64
|
},
|
|
54
65
|
"keywords": ["markdown", "streaming", "wasm", "rust", "react", "vue", "svelte", "solid", "web-component", "custom-element", "incremental", "llm", "ai", "math", "katex", "latex", "bidi", "rtl"],
|
package/src/dom.ts
CHANGED
|
@@ -61,6 +61,19 @@ export interface MountOptions {
|
|
|
61
61
|
highlightCode?: boolean;
|
|
62
62
|
/** Coalesce patches into one DOM write per animation frame. Default true. */
|
|
63
63
|
batch?: boolean;
|
|
64
|
+
/** Appended to the root's `className` (the `flux-md` class is always present). */
|
|
65
|
+
className?: string;
|
|
66
|
+
/** Set on the root element. */
|
|
67
|
+
id?: string;
|
|
68
|
+
/** Set on the root element (e.g. `"article"`, `"log"`). */
|
|
69
|
+
role?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Make the root a live region so screen readers announce streamed content.
|
|
72
|
+
* `"polite"` coalesces rapid updates (does not read every token). Off by default.
|
|
73
|
+
*/
|
|
74
|
+
ariaLive?: "off" | "polite" | "assertive";
|
|
75
|
+
/** Live-region atomicity; pair with `ariaLive`. Off by default. */
|
|
76
|
+
ariaAtomic?: boolean;
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
// Per-kind off-screen size estimate for `contain-intrinsic-size`. Duplicated
|
|
@@ -99,7 +112,11 @@ export function mountFluxMarkdown(
|
|
|
99
112
|
const batch = options.batch !== false && typeof requestAnimationFrame === "function";
|
|
100
113
|
|
|
101
114
|
const root = document.createElement("div");
|
|
102
|
-
root.className = "flux-md";
|
|
115
|
+
root.className = options.className ? `flux-md ${options.className}` : "flux-md";
|
|
116
|
+
if (options.id) root.id = options.id;
|
|
117
|
+
if (options.role) root.setAttribute("role", options.role);
|
|
118
|
+
if (options.ariaLive) root.setAttribute("aria-live", options.ariaLive);
|
|
119
|
+
if (options.ariaAtomic !== undefined) root.setAttribute("aria-atomic", String(options.ariaAtomic));
|
|
103
120
|
container.appendChild(root);
|
|
104
121
|
|
|
105
122
|
// CSS-only stick-to-bottom: a permanent sentinel pinned as the last child.
|
package/src/react.tsx
CHANGED
|
@@ -101,6 +101,20 @@ interface FluxMarkdownProps {
|
|
|
101
101
|
* run through it. When omitted, rendering is byte-identical and zero-cost.
|
|
102
102
|
*/
|
|
103
103
|
sanitize?: (html: string) => string;
|
|
104
|
+
/** Appended to the root's `className` (the `flux-md` class is always present). */
|
|
105
|
+
className?: string;
|
|
106
|
+
/** Set on the root element. */
|
|
107
|
+
id?: string;
|
|
108
|
+
/** Set on the root element (e.g. `"article"`, `"log"`). */
|
|
109
|
+
role?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Make the root a live region so screen readers announce streamed content.
|
|
112
|
+
* `"polite"` (recommended) coalesces rapid updates and announces when the
|
|
113
|
+
* reader is idle — it does **not** read every token. Off by default.
|
|
114
|
+
*/
|
|
115
|
+
"aria-live"?: "off" | "polite" | "assertive";
|
|
116
|
+
/** Live-region atomicity; pair with `aria-live`. Off by default. */
|
|
117
|
+
"aria-atomic"?: boolean;
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
// The original render path: subscribe to a (required, caller- or hook-owned)
|
|
@@ -111,13 +125,24 @@ function FluxMarkdownFromClient({
|
|
|
111
125
|
virtualize,
|
|
112
126
|
stickToBottom,
|
|
113
127
|
sanitize,
|
|
128
|
+
className,
|
|
129
|
+
id,
|
|
130
|
+
role,
|
|
131
|
+
"aria-live": ariaLive,
|
|
132
|
+
"aria-atomic": ariaAtomic,
|
|
114
133
|
}: FluxMarkdownProps & { client: FluxClient }) {
|
|
115
134
|
const blocks = useSyncExternalStore(client.subscribe, client.getSnapshot, client.getSnapshot);
|
|
116
135
|
// Normalize "no overrides" to a stable `undefined` so memo comparisons and
|
|
117
136
|
// the fast path don't churn on an empty object identity.
|
|
118
137
|
const comps = components && Object.keys(components).length > 0 ? components : undefined;
|
|
119
138
|
return (
|
|
120
|
-
<div
|
|
139
|
+
<div
|
|
140
|
+
className={className ? `flux-md ${className}` : "flux-md"}
|
|
141
|
+
id={id}
|
|
142
|
+
role={role}
|
|
143
|
+
aria-live={ariaLive}
|
|
144
|
+
aria-atomic={ariaAtomic}
|
|
145
|
+
>
|
|
121
146
|
{blocks.map((b) => (
|
|
122
147
|
<BlockView key={b.id} block={b} components={comps} virtualize={virtualize} sanitize={sanitize} />
|
|
123
148
|
))}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { highlight } from "../hi";
|
|
3
|
+
import { extractLang } from "../block-props";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Deferred-highlighting code block. Open (streaming) blocks render plain;
|
|
@@ -19,10 +20,6 @@ function decodeText(html: string): string {
|
|
|
19
20
|
.replace(/&/g, "&");
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function extractLang(html: string): string {
|
|
23
|
-
const m = html.match(/data-lang="([^"]+)"/);
|
|
24
|
-
return m ? m[1] : "";
|
|
25
|
-
}
|
|
26
23
|
|
|
27
24
|
interface Props {
|
|
28
25
|
html: string;
|
package/src/styles.css
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* flux-md — optional default theme.
|
|
3
|
+
*
|
|
4
|
+
* import "flux-md/styles.css";
|
|
5
|
+
*
|
|
6
|
+
* Opt-in: import it for good-looking output out of the box (including the
|
|
7
|
+
* built-in syntax highlighter's colors); skip it to bring your own CSS — the
|
|
8
|
+
* rendered HTML is identical either way. Everything is scoped to `.flux-md`
|
|
9
|
+
* (the renderer's root) and driven by CSS custom properties, so you re-theme by
|
|
10
|
+
* overriding a few `--flux-*` vars rather than rewriting selectors.
|
|
11
|
+
*
|
|
12
|
+
* Light by default; dark automatically via `prefers-color-scheme`. Force a mode
|
|
13
|
+
* with `<div class="flux-md flux-dark">` or `flux-light`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
.flux-md {
|
|
17
|
+
/* surfaces + text (light) */
|
|
18
|
+
--flux-fg: #1f2328;
|
|
19
|
+
--flux-fg-muted: #59636e;
|
|
20
|
+
--flux-fg-faint: #818b98;
|
|
21
|
+
--flux-border: #d1d9e0;
|
|
22
|
+
--flux-bg-code: #f6f8fa;
|
|
23
|
+
--flux-bg-inline: rgba(129, 139, 152, 0.16);
|
|
24
|
+
--flux-bg-quote: rgba(129, 139, 152, 0.08);
|
|
25
|
+
--flux-accent: #0969da;
|
|
26
|
+
/* syntax tokens (light) */
|
|
27
|
+
--flux-t-kw: #cf222e;
|
|
28
|
+
--flux-t-str: #0a3069;
|
|
29
|
+
--flux-t-num: #0550ae;
|
|
30
|
+
--flux-t-com: #59636e;
|
|
31
|
+
--flux-t-fn: #6639ba;
|
|
32
|
+
--flux-t-ty: #953800;
|
|
33
|
+
--flux-t-mac: #1f6feb;
|
|
34
|
+
--flux-t-attr: #116329;
|
|
35
|
+
--flux-t-tag: #116329;
|
|
36
|
+
--flux-t-var: #953800;
|
|
37
|
+
--flux-t-pun: var(--flux-fg);
|
|
38
|
+
/* sizing */
|
|
39
|
+
--flux-radius: 6px;
|
|
40
|
+
--flux-gap: 16px;
|
|
41
|
+
|
|
42
|
+
color: var(--flux-fg);
|
|
43
|
+
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
44
|
+
line-height: 1.6;
|
|
45
|
+
font-size: 1rem;
|
|
46
|
+
word-wrap: break-word;
|
|
47
|
+
overflow-wrap: anywhere;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Dark — automatic, and as an explicit `.flux-dark` escape hatch. */
|
|
51
|
+
@media (prefers-color-scheme: dark) {
|
|
52
|
+
.flux-md:not(.flux-light) {
|
|
53
|
+
--flux-fg: #e6edf3;
|
|
54
|
+
--flux-fg-muted: #9198a1;
|
|
55
|
+
--flux-fg-faint: #6e7681;
|
|
56
|
+
--flux-border: #3d444d;
|
|
57
|
+
--flux-bg-code: #151b23;
|
|
58
|
+
--flux-bg-inline: rgba(101, 108, 118, 0.2);
|
|
59
|
+
--flux-bg-quote: rgba(101, 108, 118, 0.1);
|
|
60
|
+
--flux-accent: #4493f8;
|
|
61
|
+
--flux-t-kw: #ff7b72;
|
|
62
|
+
--flux-t-str: #a5d6ff;
|
|
63
|
+
--flux-t-num: #79c0ff;
|
|
64
|
+
--flux-t-com: #8b949e;
|
|
65
|
+
--flux-t-fn: #d2a8ff;
|
|
66
|
+
--flux-t-ty: #ffa657;
|
|
67
|
+
--flux-t-mac: #79c0ff;
|
|
68
|
+
--flux-t-attr: #7ee787;
|
|
69
|
+
--flux-t-tag: #7ee787;
|
|
70
|
+
--flux-t-var: #ffa657;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
.flux-md.flux-dark {
|
|
74
|
+
--flux-fg: #e6edf3;
|
|
75
|
+
--flux-fg-muted: #9198a1;
|
|
76
|
+
--flux-fg-faint: #6e7681;
|
|
77
|
+
--flux-border: #3d444d;
|
|
78
|
+
--flux-bg-code: #151b23;
|
|
79
|
+
--flux-bg-inline: rgba(101, 108, 118, 0.2);
|
|
80
|
+
--flux-bg-quote: rgba(101, 108, 118, 0.1);
|
|
81
|
+
--flux-accent: #4493f8;
|
|
82
|
+
--flux-t-kw: #ff7b72;
|
|
83
|
+
--flux-t-str: #a5d6ff;
|
|
84
|
+
--flux-t-num: #79c0ff;
|
|
85
|
+
--flux-t-com: #8b949e;
|
|
86
|
+
--flux-t-fn: #d2a8ff;
|
|
87
|
+
--flux-t-ty: #ffa657;
|
|
88
|
+
--flux-t-mac: #79c0ff;
|
|
89
|
+
--flux-t-attr: #7ee787;
|
|
90
|
+
--flux-t-tag: #7ee787;
|
|
91
|
+
--flux-t-var: #ffa657;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ---- block rhythm ---------------------------------------------------------- */
|
|
95
|
+
.flux-md > * { margin: 0 0 var(--flux-gap) 0; }
|
|
96
|
+
.flux-md > *:last-child { margin-bottom: 0; }
|
|
97
|
+
|
|
98
|
+
/* ---- headings -------------------------------------------------------------- */
|
|
99
|
+
.flux-md h1, .flux-md h2, .flux-md h3,
|
|
100
|
+
.flux-md h4, .flux-md h5, .flux-md h6 {
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
line-height: 1.25;
|
|
103
|
+
margin: 24px 0 var(--flux-gap);
|
|
104
|
+
}
|
|
105
|
+
.flux-md h1 { font-size: 2em; padding-bottom: 0.3em; border-bottom: 1px solid var(--flux-border); }
|
|
106
|
+
.flux-md h2 { font-size: 1.5em; padding-bottom: 0.3em; border-bottom: 1px solid var(--flux-border); }
|
|
107
|
+
.flux-md h3 { font-size: 1.25em; }
|
|
108
|
+
.flux-md h4 { font-size: 1em; }
|
|
109
|
+
.flux-md h5 { font-size: 0.875em; }
|
|
110
|
+
.flux-md h6 { font-size: 0.85em; color: var(--flux-fg-muted); }
|
|
111
|
+
.flux-md > h1:first-child, .flux-md > h2:first-child, .flux-md > h3:first-child { margin-top: 0; }
|
|
112
|
+
|
|
113
|
+
/* ---- inline ---------------------------------------------------------------- */
|
|
114
|
+
.flux-md a { color: var(--flux-accent); text-decoration: none; }
|
|
115
|
+
.flux-md a:hover { text-decoration: underline; }
|
|
116
|
+
.flux-md strong { font-weight: 600; }
|
|
117
|
+
.flux-md em { font-style: italic; }
|
|
118
|
+
.flux-md del { color: var(--flux-fg-muted); }
|
|
119
|
+
.flux-md img { max-width: 100%; height: auto; }
|
|
120
|
+
|
|
121
|
+
/* ---- lists ----------------------------------------------------------------- */
|
|
122
|
+
.flux-md ul, .flux-md ol { padding-left: 2em; }
|
|
123
|
+
.flux-md li { margin: 0.25em 0; }
|
|
124
|
+
.flux-md li > ul, .flux-md li > ol { margin: 0.25em 0; }
|
|
125
|
+
.flux-md li::marker { color: var(--flux-fg-faint); }
|
|
126
|
+
.flux-md input[type="checkbox"] { margin: 0 0.4em 0 0; }
|
|
127
|
+
|
|
128
|
+
/* ---- blockquote + alerts --------------------------------------------------- */
|
|
129
|
+
.flux-md blockquote {
|
|
130
|
+
margin: 0 0 var(--flux-gap);
|
|
131
|
+
padding: 0.4em 1em;
|
|
132
|
+
color: var(--flux-fg-muted);
|
|
133
|
+
border-left: 0.25em solid var(--flux-border);
|
|
134
|
+
background: var(--flux-bg-quote);
|
|
135
|
+
}
|
|
136
|
+
.flux-md blockquote > :last-child { margin-bottom: 0; }
|
|
137
|
+
|
|
138
|
+
/* ---- tables ---------------------------------------------------------------- */
|
|
139
|
+
.flux-md table { border-collapse: collapse; width: 100%; display: block; overflow-x: auto; }
|
|
140
|
+
.flux-md th, .flux-md td { padding: 6px 13px; border: 1px solid var(--flux-border); }
|
|
141
|
+
.flux-md th { font-weight: 600; background: var(--flux-bg-code); }
|
|
142
|
+
.flux-md tr:nth-child(2n) td { background: var(--flux-bg-quote); }
|
|
143
|
+
|
|
144
|
+
/* ---- code ------------------------------------------------------------------ */
|
|
145
|
+
.flux-md code, .flux-md pre {
|
|
146
|
+
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
|
|
147
|
+
font-size: 0.9em;
|
|
148
|
+
}
|
|
149
|
+
.flux-md :not(pre) > code {
|
|
150
|
+
padding: 0.2em 0.4em;
|
|
151
|
+
border-radius: var(--flux-radius);
|
|
152
|
+
background: var(--flux-bg-inline);
|
|
153
|
+
}
|
|
154
|
+
.flux-md pre {
|
|
155
|
+
padding: 14px 16px;
|
|
156
|
+
overflow-x: auto;
|
|
157
|
+
border-radius: var(--flux-radius);
|
|
158
|
+
background: var(--flux-bg-code);
|
|
159
|
+
line-height: 1.5;
|
|
160
|
+
}
|
|
161
|
+
.flux-md pre code { padding: 0; background: none; border: 0; }
|
|
162
|
+
|
|
163
|
+
/* ---- rule ------------------------------------------------------------------ */
|
|
164
|
+
.flux-md hr { height: 1px; border: 0; background: var(--flux-border); margin: 24px 0; }
|
|
165
|
+
|
|
166
|
+
/* ---- syntax highlighter (the built-in `highlight()` token spans) ----------- */
|
|
167
|
+
.flux-md .t-kw { color: var(--flux-t-kw); }
|
|
168
|
+
.flux-md .t-str,
|
|
169
|
+
.flux-md .t-rx { color: var(--flux-t-str); }
|
|
170
|
+
.flux-md .t-num,
|
|
171
|
+
.flux-md .t-lt { color: var(--flux-t-num); }
|
|
172
|
+
.flux-md .t-com { color: var(--flux-t-com); font-style: italic; }
|
|
173
|
+
.flux-md .t-fn { color: var(--flux-t-fn); }
|
|
174
|
+
.flux-md .t-ty { color: var(--flux-t-ty); }
|
|
175
|
+
.flux-md .t-mac,
|
|
176
|
+
.flux-md .t-dec { color: var(--flux-t-mac); }
|
|
177
|
+
.flux-md .t-attr,
|
|
178
|
+
.flux-md .t-sel { color: var(--flux-t-attr); }
|
|
179
|
+
.flux-md .t-tag { color: var(--flux-t-tag); }
|
|
180
|
+
.flux-md .t-var { color: var(--flux-t-var); }
|
|
181
|
+
.flux-md .t-pun { color: var(--flux-t-pun); }
|
|
182
|
+
.flux-md .t-txt { color: inherit; }
|
|
Binary file
|
package/src/wasm/package.json
CHANGED